Close
0%
0%

ATtiny 1-series with Arduino support

Creating a break-out board for the ATtiny1616 where sketches can be uploaded from Arduino with the Arduino UNO or a modified AVR JTAG ICE

Similar projects worth following
The ATMEL ATtiny’s have been my favorite microcontroller for basic projects. I have been using these to control LED’s in earrings, OLED displays and APA102 (Dotstar) LED’s in wearables. The ATtiny84a comes in a 3 X 3mm footprint, has an internal oscillator and only requires an external capacitor to get it going, and all for less than $1.
Obviously, it does not match the specifications of the ATMEGA328P used in the Arduino UNO, and I do hit limitations all the time. Not enough flash memory is probably the biggest issue, but using peripherals, like SPI, I2C and UART, is very limited as well and I had an issue with running servo’s and APA102 LED’s simultaneously since it only has 2 timers (and 1 is only 8 bits).
This project will follow the development of an Arduino compatible break-out board of the ATtiny1616 (and most likely the ATtiny3217 in the future).

The Arduino Core is ready! Find the core on:

https://github.com/SpenceKonde/megaTinyCore

Please report all issues.

Introduction

ATMEL was acquired by Microchip Technology in 2016 and last year they came out with a new microcontroller family sharing their technology, the tinyAVR and megaAVR. Out of this new offering the ATtiny1616 draw my attention. Looks like it has all the benefits from the ATtiny84a, same foot print, same power specifications, and some great additional features as well as you can see in the table below:

MCU name:

ATtiny84a

ATmega328P

ATtiny1616

Operating Voltage:

1.8 - 5.5V

1.8 - 5.5V

1.8 - 5.5V

Digital I/O pins

12

23

18

PWM Digital

2

6

6

Analog Inputs

8

6

12

Analog Outputs

0

0

1

Flash Memory

8 KB

32 KB

16 KB

SRAM

512

2048

2048

EEPROM

512

1024

256

Clock Speed

8 MHZ

20 MHz

20 MHz

Size

3x3 body

4x4 body

3x3 body

Timers

1x8b, 1x16b

2x8b, 1x16b

1x12b, 3x16b

Digital Communication Peripherals

USI (~SPI)

UART, SPI(2), I2C

UART, SPI, I2C

Able to run this from the 20 MHz internal oscillator, twice as much flash and four times more SRAM (compared to the ATtiny84) and support of most common peripherals excites me! There are just some minor problems. There is no Arduino support (yet) and there is no DIP version to plug into a breadboard.

This project will document the development of the Arduino Core for the tinyAVR 1-series and a breakout board for the ATtiny1616. Throughout different logs different challenges will be explained and resolved.

A regular Arduino UNO can be used to program the ATtiny1616:

ATtiny1616 Break-out Board.pdf

Schematic for the ATtiny1616 break-out Board

Adobe Portable Document Format - 21.73 kB - 06/02/2019 at 18:33

Preview
Download

  • 1 × ATTINY1616-MNR 8-bit microcontroller, 16KB flash, 2KB SRAM, 20MHz
  • 1 × 100nF Capacitor 0603
  • 1 × 4.7K Ohm Resistor 085

  • Using the Interrupt to measure super capacitor voltage

    Sander van de Bor08/31/2019 at 21:51 0 comments

    The Arduino IDE is great for a quick and easy sketch, but since not all functions of the  0- and 1- series are implemented (yet), we have to dig into the datasheet and write some code ourselves. For example, interrupts! It is pretty easy to setup a pin interrupt in Arduino and in a previous log I also showed an interrupt with the Real Time Counter (RTC), but the ATtiny1616 can actually create interrupts on almost every peripheral.

    Peripherals are used to offload the MCU. For example, you can be in the office checking the desk phone every 10 seconds to make sure nobody is on the other side of the line, but you will get no other work done. Instead the phone rings when someone is calling you, and at that moment they will interrupt the task you were doing and you pick up the phone.

    The microcontroller works the same way. It has peripherals that can do certain tasks and they will only notify the MCU when input from the MCU is required, or when they are done with that task. It is even possible for peripherals to do certain tasks after each other without the MCU involved! On the ATtiny this is done through the event system.

    All peripherals and matching interrupts can be found on page#45 of the datasheet (table 7-2). Besides the interrupts for the PORTA to PORTC, and the timers RTC and TCA up to TCD, interrupts are available for the analog I/O and communication peripherals like TWI0 (Wire), SPI and UART (Serial).

    I am very interested in powering my next projects with super capacitors and I got myself one of these 5.5V 4F coin size capacitors that I am going to use for this next experiment. Super capacitors could be the power storage for many devices in the future, but at this moment the capacity is too low, and you must work around a gradual voltage loss while using it. I like to know how long I can power the ATtiny1616 from a super capacitor in the following modes:

    • CPU running at 20MHz
    • CPU running at 3.33MHz
    • CPU running at 1MHz, and use sleep mode

    To measure the differences in power consumption I will use a program on the ATtiny1616 which will measure the voltage supplied by the super capacitor every second and reports this back using the serial port. The serial port is connected to a Raspberry Pi which will record the readings. The program will be written for the sleep mode, but the sleep mode will not be used when testing for 20MHz and 1MHz.

    The program will have the following flow:

    1. Interrupt from the PIT once a second to start the cycle (ISR(RTC_PIT_vect))
    2. Start ADC conversion to read internal voltage. It will take a couple clock cycles to get the actual reading.
    3. Sleep CPU
    4. Interrupt from the ADC results ready (ISR(ADC0_RESRDY_vect))
    5. Convert the results to an actual voltage
    6. Send the information using UART0
    7. Wait for the transmit to complete (wait for TXCIF flag)
    8. Clear the TXCIF flag
    9. Sleep CPU

    I could have entered sleep mode after sending the information using UART and have an UART interrupt triggered after the message was completely send. Or just skip checking for a complete message entirely, but this check is important in case you want to turn off the transmitter and activate the receiver instead when you use the line both ways.

    The while(1) is the main loop (like loop() in Arduino) and will have sleep_cpu() to enter sleep mode. This is commented out when the CPU is tested at 20 and 1MHz.

    Here is the complete code. I haven’t ported this to Arduino yet and used Atmel studio to compile this code:

    #define F_CPU 1000000L
    #define USART0_BAUD_RATE(BAUD_RATE) ((float)(F_CPU * 64 / (16 * (float)BAUD_RATE)) + 0.5)
    
    #include <stdio.h>
    #include <avr/sleep.h>
    #include <avr/interrupt.h>
    
    void RTC_init(void);
    void USART0_init(void);
    void CLKCTRL_init(void);
    void ADC0_init(void);
    
    static void USART0_sendChar(char c)
    {
        while (!(USART0.STATUS & USART_DREIF_bm))
        {
            ;                                   /* Wait for USART ready for receiving next char */
        }
        USART0.TXDATAL = c;
    }
    
    static int USART0_printChar(char c, FILE *stream)
    {
        USART0_sendChar(c);
        return...
    Read more »

  • Typewriter animation on ATtiny1616 with OLED

    Sander van de Bor07/27/2019 at 20:53 0 comments

    The small 0.97" and 1.3" OLED displays are great and easy to work with, here is an example:

    On the older ATtiny's, like the ATtiny84a, I was only able to use the U8glib with U8x8 (text output only). Memory limitations did not allow other fonts available with U8g2. Fortunately the new ATtiny 0- and 1-series, not only have hardware SPI to control the OLED., but also more memory to use U8g2.

    Example from the animation above uses the RESET, DC and CS on pins 9, 10 and 11. By placing the OLED directly underneath these pins on the breadboard it only requires 4 additional wires. 2 wires for power (VCC and GROUND), and 2 wires for the SPI (CLK on pin 16 and MOSI on pin 14).

    I created the typewriter animation as follows:

    #include <U8g2lib.h>
    #include <SPI.h>
    
    U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 11, /* dc=*/ 10, /* reset=*/ 9);
    
    void setup(void) {
      u8g2.begin();
    }
    
    char message[] = {"Hackaday!ATtiny1616"};
    int posX[19] = {0,20,35,48,61,76,90,102,115, /* second row */ 0,18,33,44,54,70,84,95,106,115};
    int posY[19] = {30,29,31,30,28,29,30,33,35, /* second row */ 58,61,61,60,58,59,60,63,61,60};
    
    void loop(void) {
      u8g2.clearBuffer();         // clear the internal memory
      u8g2.sendBuffer();          // transfer internal memory to the display
      delay(500);
      u8g2.setFont(u8g2_font_ncenR18_tr); // choose a suitable font
      for (byte i = 0; i < sizeof(message) - 1; i++) {
        char writeText[] = {message[i],'\0'};   // Add a NULL after character
        u8g2.drawStr(posX[i],posY[i],writeText);  // write something to the internal memory
        u8g2.sendBuffer();          // transfer internal memory to the display
        delay(50);
      }
      delay(1000);  
    }

  • Using voltage reference for ADC

    Sander van de Bor07/04/2019 at 23:14 0 comments

    We probably all got to a point where we must start using a multi-meter to analyze our circuit, and very often we want to measure the voltage. Putting one of the probes on voltage source will not give us any feedback, the second must be connected to a reference and in most cases that will be the ground.

    Analog-to-digital converters (ADC) work very similar. They can take a voltage, for example from one of the pins, and calculate a difference to another reference. There is just one little difference here. The voltage we like to measure must be lower than the voltage on the reference. For example, when we like to measure 5V, we need at least 5V as a reference. Reason is that we are measuring a fraction as follows: Vin / Vref. So when Vin is 5V, and Vref is 5V, we will get 1 back. On the other hand when Vin is 1V, and Vref is still 5, we will get 0.2 as a result.

    So the ADC calculates a fraction, but the microcontroller cannot hold fractions, it calculates in bits. The ATtiny is using a 10 bit register to hold this value between 0 and 1 using these 10 bits. In decimals 10bits can hold a value of 2^10 – 1024, but since we start at 0 the maximum decimal value is 1023. Using that conversion, to go from a fraction to bits, will result in the following equation:

    You can find some information on this equation in the data sheet on page#485:

    http://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny3216_ATtiny1616-data-sheet-40001997B.pdf

    If the Vin is above Vref, for example Vin is 5.5V and Vref is still 5, the ADC will result with the maximum 10bits value of 1023. According to the equation this should result in 1125, but since we can only have 10 bits we will only get a value up to 1023 instead.

    Most used Vin is the analog input pin, on the Arduino UNO pins A0 to A5, and the Vref will be the applied voltage (3.3V or 5V). But there are tons of more combinations possible! Lets start with Vin on page# 495 of the datasheet. This is very similar to the PORTMUX where from an earlier log where we told the controller which serial ports we like to use. In this case we must tell the ADC what we like to convert. Starting at the top you can find AIN0 which the Analog Input Channel linked to a pin (explained in the previous log). Besides the pins we also have other options starting at 0x1B. PTC, DAC0, INTREF and GND. Later I will show an example where we can use these.

    Secondly, we need the V reference and there we have tons of options as well. You will find those on page#491 Bits 5:4 REFSEL. It will get a little bit confusing here! There is not just one Vref. There is one for the ADC, DAC and the AC. so from now on I will call it ADC0.Vref. As you can see there are only 3 options for the reference. INTERNAL, VDD and VREFA. VDD is the applied voltage, so when you apply 5V to the microcontroller, VDD is 5V. Another option is applying an external voltage on VREFA, which is located on pin#1 in this situation. VREFA has nothing to do with VREF, it is just another reference for the ADC. The INTERNAL option is even more interesting. It uses the internal reference voltage of the chip, Vref, but again has nothing to do with ADC0.Vref, unless this INTERNAL option is selected. The Voltage Reference VREF is explained in chapter 18 where you see that it has 5 different settings:

    • 0.55V
    • 1.1V
    • 1.5V
    • 2.5V
    • 4.3V

    Using the INTERNAL voltage reference can be helpful when you VDD is not consistent but you want a consistent reading from an external voltage source.

    In the following examples I will use a super capacitor as a power source to the ATtiny1616. The voltage from the super capacitor will drop as soon as it disconnects from the 5V source used to charge the super capacitor. Measuring the incoming voltage on the ADC from different sources requires different solutions as follows:

    Reading a potmeter attached to VDD. This is very common when using the Arduino....

    Read more »

  • Setting up channels for the Analog-to-digital converter (ADC)

    Sander van de Bor07/04/2019 at 04:28 0 comments

    In one of the earlier logs PORT was explained, but that can only be used for digital I/O, meaning, it can turn outputs on or off, or detect if an input is on or off. The actual pin number for digital I/O consists of a PORT letter followed by a pin (or bit #) in that PORT. Every non-power related leg (like VDD and GND) on the ATtiny is linked to one of those PORTS, but that is usually not the only function of that leg.

    The Analog to Digital Converter (ADC) is a great feature on the MCU to measure input voltage. Instead of using PORT to link a the digital I/O to the leg, it uses channels. The ATtiny1616 has 12 input channels (AIN0 to AIN11) on two ADC’s (ADC0 and ADC1). The location of these input channels (we are going to use ADC0 only) are as follows:

    //                          _____
    //                  VDD   1|*    |20  GND
    // (nSS)  (AIN4) PA4  0~  2|     |19  16~ PA3 (AIN3)(SCK)(EXTCLK)
    //        (AIN5) PA5  1~  3|     |18  15  PA2 (AIN2)(MISO)
    // (DAC)  (AIN6) PA6  2   4|     |17  14  PA1 (AIN1)(MOSI)
    //        (AIN7) PA7  3   5|     |16  17  PA0 (AIN0/nRESET/UPDI)
    //        (AIN8) PB5  4   6|     |15  13  PC3
    //        (AIN9) PB4  5   7|     |14  12  PC2
    // (RXD) (TOSC1) PB3  6   8|     |13  11~ PC1 (PWM only on 1-series)
    // (TXD) (TOSC2) PB2  7~  9|     |12  10~ PC0 (PWM only on 1-series)
    // (SDA) (AIN10) PB1  8~ 10|_____|11   9~ PB0 (AIN11)(SCL)

    So as you can see a leg could have be a digital pin (for example PA4) and an analog input (AIN4). Like the digital I/O we are going to tell Arduino which pin# is linked to each analog channel as follows in pins_arduino.h:

    #define digitalPinToAnalogInput(p)      ((p<6)?(p+4):(p==17?0:((p>13)?(p-13):((p==8)?10:(p==9?11:NOT_A_PIN))))) 

    Big thanks to Spence Konde! He helped enormous in creating all the pre-processor macros. But newbies, like me, are probably wondering what this line above is doing. 

    We are all familiar with an if statement, for example:

    if(p < 6) {
        p = p + 4;
    } else {
        P = NOT_A_PIN;
    } 

    There is actually a shorter method writing the same if statement above, for example  as follows:

    p = (p<6)?(p+4):NOT_A_PIN; 

    Using this same method for the macro above will result in the following if statement:

    if(p < 6) {
        digitalPinToAnalogInput(p) = p + 4;
    } elseif (p == 17 {
        digitalPinToAnalogInput(p) = 0;
    } elseif (p > 13 {
        digitalPinToAnalogInput(p) = p-13;
    } elseif (p == 8 {
        digitalPinToAnalogInput(p) = 10;
    } elseif (p == 9 {
        digitalPinToAnalogInput(p) = 11;
    else {
        NOT_A_PIN
    } 

    With “p” being the Arduino pin number, we can now determine which channel belongs to that pin with the macro above. For example when p=5 (leg #7), digitalPinToAnalogInput(p) will be 5 + 4, resulting in channel 9.

    You can just read the voltage on the input pin by entering the following in Arduino:

    analogRead(5) // reading the analog input on channel 9. 

    But most Arduino’s have a dedicated section for analog inputs, for example on the Arduino UNO A0 up to A5 are used as analog inputs. In order to use these A0, A1 etc numbers we have to add these with macros to the pins_arduino.h as well as follows:  

    #define PIN_A0   (17)
    #define PIN_A1   (14)
    #define PIN_A2   (15)
    #define PIN_A3   (16)
    #define PIN_A4    (0)
    #define PIN_A5    (1)
    #define PIN_A6    (2)
    #define PIN_A7    (3)
    #define PIN_A8    (4)
    #define PIN_A9    (5)
    #define PIN_A10  (10)
    #define PIN_A11  (11)
    
    static const uint8_t  A0 = PIN_A0;
    static const uint8_t  A1 = PIN_A1;
    static const uint8_t  A2 = PIN_A2;
    static const uint8_t  A3 = PIN_A3;
    static const uint8_t  A4 = PIN_A4;
    static const uint8_t  A5 = PIN_A5;
    static const uint8_t  A6 = PIN_A6;
    static const uint8_t  A7 = PIN_A7;
    static const uint8_t  A8 = PIN_A8;
    static const uint8_t  A9 = PIN_A9;
    static const uint8_t A10 = PIN_A10;
    static const uint8_t A11 = PIN_A11; 

    Instead of only using the pin#, now analogRead(A9) can be used as well, resulting with the same readings.

    In the next log I will explain the internal voltages which can be used to get a reliable analog input reading.

  • Use the AVR-GCC tool chain libraries with Sleep example, using low power

    Sander van de Bor06/16/2019 at 05:59 0 comments

    In the first log the structure of the Arduino was explained, together with the required components like the Device Family Pack (DFP) and the AVR-GCC compiler. We have been using the DFP in the last examples by using the macros defined in the IO files for each microcontroller to set registers. While most macros are very well described and could be used for almost everything there is an even more convenient solution for some peripherals; the AVR-GCC tool chain libraries!

    In the first log was explained how certain commands in Arduino, like delay, are just functions called from the Arduino core (a collection of libraries). While the Arduino core has the major functions for the most used controls to pins and communication peripherals it just cannot do them all.

    In the previous log for example the Real Time Counter (RTC) was used by setting registers since the RTC has not been developed for the Arduino core. At the end of that log I also mentioned that the CPU is still running while it is doing nothing. We have to put the CPU in sleep mode if we want to conserve energy. Like the RTC, sleep is not part of the Arduino core, so we must write to the registers ourselves.

    Page#96 of the ATtiny1616 manual describes the different sleep modes: http://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny3216_ATtiny1616-data-sheet-40001997B.pdf

    11.3.1 Initialization describes the steps to setup the sleep mode. First we need an interrupt to get out of sleep mode which we already had set with the RTC of the previous log (ISR(RTC_CNT_vect)). Next, we must select the sleep mode (will get back to that later) and enable sleep mode with the enable bit. Enabling the sleep mode will not put the CPU to sleep, it just enabled sleep mode so that is can put the device to sleep when needed.

      SLPCTRL.CTRLA |= SLPCTRL_SMODE_PDOWN_gc;
      SLPCTRL.CTRLA |= SLPCTRL_SEN_bm; 

    After setting these bits in the register we must to send an instruction to put the MCU to sleep. While setting up the sleep mode and enabling is probably done only once and for that reason can be done in the setup, putting the CPU to sleep must be done after each time it wakes up, and in this example we can just add it to the main loop. Writing an instruction is new in this series of logs, but can be done as follows:

    __asm__ __volatile__ ( "sleep" "\n\t" :: ); 

    The code should be update as follows:

    void setup() {      
        pinMode(LED_BUILTIN, OUTPUT);   
        RTC_init(1000);     
        SLPCTRL.CTRLA |= SLPCTRL_SMODE_PDOWN_gc;    
        SLPCTRL.CTRLA |= SLPCTRL_SEN_bm;
    }
    
    void loop()
    {  
        // time too sleep!     
        __asm__ __volatile__ ( "sleep" "\n\t" :: );
    }
    

    While this works great, there is actually a cleaner method available within the AVR-GCC compiler libraries. We can find these libraries in the following location:

    C:\Users\svandebor\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino5\avr\include\avr

    There is a file called sleep.h which we will use on the sketch. On the top of the sketch add a line:

    #include <avr/sleep.h>

    #include <avr/sleep.h> 

    Now we can use the functions within this library instead of writing directly to the registers and creating instructions. The new code will look as follows:

    #include <avr/sleep.h>
    
    void RTC_init(int RTCdelay)
    {     
      RTC.CLKSEL = RTC_CLKSEL_INT32K_gc;    // 32.768kHz Internal Crystal Oscillator (INT32K) 
    
      while (RTC.STATUS > 0);               // Wait for all register to be synchronized
      RTC.PER = RTCdelay;                   // Set period for delay
      RTC.INTCTRL |= RTC_OVF_bm;            // Enable overflow Interrupt which will trigger ISR
      RTC.CTRLA = RTC_PRESCALER_DIV32_gc    // 32768 / 32 = 1024 (sec) ~ 1 ms
      | RTC_RTCEN_bm                        // Enable: enabled 
      | RTC_RUNSTDBY_bm;                    // Run In Standby: enabled 
    }
    
    ISR(RTC_CNT_vect)
    {
      RTC.INTFLAGS = RTC_OVF_bm;            // Clear flag by writing '1'
    ...
    Read more »

  • Timers, use the RTC to blink LED

    Sander van de Bor06/15/2019 at 04:16 0 comments

    In our world we use clocks to keep track of time and in the micro-controller world we use timers to count the clock. Clocks used in microcontrollers do not keep track of time!

    Timers on the micro-controller can be confusing and could be hard to understand but are so important for the microcontroller to operate. For example, when we like to flash a single LED every 500 millisecond, then we must keep track of time somehow. Almost every micro-controller out there has at least one or two timers, the ATtiny84a had 2, the ATtiny1616 has 4. But a timer will not count (the method to keep track of time) by itself. It needs an input, a source from something that pulses, called a clock. The most common clock is an oscillator and some microcontrollers have some build in. Most of the older controllers uses external oscillators for a better accuracy, for example the Arduino UNO has two:

    Fortunately, the ATtiny has internal oscillators that are sufficient for most project. The internal oscillator for the Attiny84a used to be calibrated for 8MHz, this new ATtiny1616 uses a 16/20 MHz low-power RC oscillator.

    These clocks are used as an input to the timers and currently configured as follows (taken from the Atmel Start application):  

    On the left you see the different oscillators, where the 3 options in the middle (20MHz, 16MHz and 32KHz) are internal and the other two external. In the middle are the sources, where the clock can be manipulated before getting used. For example, the Main Clock can be divided by 2 which will result in a 10MHz source for peripherals on the right (and the CPU will run only at half the speed). Running the CPU at a slower speed can help to reduce the power consumption.

    Under components you see the actual timers used by the micro-controller, and if you are paying attention you might notice that there are 5 timers listed, not 4 I mentioned above. It gets even more complicated! According to the ATtiny1616 spec sheet there are 4 timers: Timer/Counter Type A (TCA0), Timer/Counter Type B (TCB0 and TCB1), and the fourth Timer/Counter Type D (TCD0). TCD0 is actually listed under sources, probably because it is always active while the other components could be turned off (and they do not appear on the picture above).

    The WDT is the Watch Dog Timer. Yes, it is a timer, but cannot be used to execute some code on the ATtiny1616. The WDT can only be used to reset the microcontroller when it gets stuck in some code (an infinite loop).

    RTC is the real time counter. It is not a timer, it is a time counter. To be honest that confuses the heck out of me as well. The RTC is actually a great counter which you will appreciate for your low power consumption projects. On the older ATtiny’s the WDT could be used to wake-up the micro-controller, but that is not longer an option on the ATtiny1616; it is done with the RTC.

    You can see above that almost all the timers use the 20MHz oscillator. The 20MHz is accurate (±2%), but will use some power to run it. The RTC and WDT are using the 32.768 kHz (note it can be written as 32Khz as well which is 32khz*1024), which is slow and not so accurate (±10% when not calibrated), but the power consumption is very low! It is also a timer which is probably not going to be used by anything else on the Arduino, so it will be available without any interference with other existing code.

    In this example I am going to demonstrate how to use RTC to blink an LED, the “hello world” of the Arduino. I will introduce some new instructions which can be used.

    We probably all know how to make an LED blink in Arduino, there is even an example in the IDE called blink:

    void setup() {
      pinMode(LED_BUILTIN, OUTPUT);
    }
    
    void loop() {
     digitalWrite(LED_BUILTIN,...
    Read more »

  • New method for writting registers on the 0- and 1-series

    Sander van de Bor06/08/2019 at 05:21 0 comments

    Arduino uses a lot of functions and macros to make it easier for an end-user, with some minor programming and no micro-controller experience, to develop a sketch. Without using these functions, but still using macros, a program for the ATMEGA328P will look like this:

    int main(void) {
      DDRB |= (1 << 2);
      while (1) {
        PORTB |= (1 << 2);
        _delay_ms(1000);
        PORTB &= ~(1 << 2);
        _delay_ms(1000);
      }
      return 0;
    } 

    DDRB and PORTB are both macros and are referring to a register on the ATMEGA328P.

    This example above sets PB2 as an output and toggles it on and off. Read by someone experienced in C or C++, but with no experience with the AVR, this code will probably not make any sense. What is DDRB, and why do we write a value to PORTB?

    DDRB and PORTB are macro’s and converted by the preprocessor to a register address. DDRB is address 0x04 and PORTB is 0x05. See page#100 of the datasheet for more information: http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf

    But why use macro names that are so hard to understand. Often I hear that a good written code does not need any comments since the variables should be clear to understand.

    Here comes the beauty of these new 0- and 1-series micro-controllers where almost every register and every variable going into those registers has a label or name that makes more sense. You can find all of these labels in the C:\Users\username\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino5\avr\include\avr folder and just compare IOM4809.IO (229KB for ATMEGA4809) vs iom328p.h (20KB for ATMEGA328P). Here is a simple example of the same code above for the ATtiny1616:

    int main(void)
    {
      PORTB.DIRSET= PIN2_bm;
      While (1) {
          PORTB.OUTSET = PIN2_bm;
          _delay_ms(1000);
          PORTB.OUTCLR = PIN2_bm;    
          _delay_ms(1000);
      }
    }
    

    It is a little bit easier to read. DIRSET set the direction of PIN2 of PORTB followed by setting and clearing the output with OUTSET and OUTCLR using PIN#2 bitmask.  

    See this blog for some more information:
    http://leoninstruments.blogspot.com/2014/05/xmega-tutorial-ports-04.html  

    Everything we do on the micro-controller is done by setting bits in registers. As you can see above you can shift bits in and out or use macros. Macros are not slowing down your code or increasing your compiled code, because the values for the macros replaced in your code by the preprocessor before it will be compiled. These macros are getting very helpful when you work with the registers for peripherals.

    The addresses of registers for peripherals can be found in the datasheet for the ATtiny1616 on page#44 (http://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny3216_ATtiny1616-data-sheet-40001997B.pdf)

    You will see the base address in the first column and a description in the second. This is just a base address, a lot more registers for these peripherals after this base address could be reserved to store data, for example PORTMUX has a base of 0x0200. Going to page#138 shows the registers following this PORTMUX baseline of 0x0200 and the needed offset. For example, CTRLB has an offset of 0x01, so it has the address of 0x0200 + 0x01 = 0x0201.

    In this register you can set the alternative pin locations for SPI0 and USART0. Just shifting in a bit to turn on the first bit will activate USART0 on the alternative pin. Instead if using bit shifts, there are actually macros created for each possible combination, which will result in the following code:

    PORTMUX.CTRLB|= PORTMUX_USART0_DEFAULT_gc; // does this -> (0x0200 + 0x01 |= (0x00<<0) 

    I recommend looking at the log of Simon’s project  since it has some other great examples of how to use the macros on this new 0- and 1-series AVR’s as well:

    https://hackaday.io/project/165439-attiny-0-series-programming-on-the-cheap...

    Read more »

  • PORTMUX on the 0- and 1- series explained

    Sander van de Bor06/08/2019 at 03:34 1 comment

    Grocery shopping was easy where I grew up. The store was small, and the options limited. Need milk, you grab a carton, tea did not have thousands of different flavors and beer was just a Pilsener. Today is different and you can spend hours in the store since there are too many options, just like the peripherals on these new micro-controllers.

    The ATMEGA328P used in the Arduino UNO is pretty good controller and suit most of my projects. It has some nice peripherals to other devices as you can see in the picture below:

    You can create an SPI connection with pins PB2 to PB5 (SS, MOSI, MISO and SCK). Have a “serial” (UART) connection with PD0 (RXD) and PD1 (TXD) and create a wire connection with PC4 (SDA) and PC5 (SCL). All the analog inputs are on PC0 (ADCx) up to PC5, but there is also a conflict since PC4 and PC5 were used for wire already. So, when you use wire, you basically only have ADC0 up ADC3 left for analog inputs since the others (ADC4 and ADC5) are used.

    The more advanced 32 bits micro-controllers like the SAMD21G18A, used on the Arduino M0, has even more peripherals per pin. A pin could have up to 8 different peripherals and a conflict between pins is more common. Fortunately, unlike the ATMEGA328P, you can move certain peripherals around between certain pins when pins are used for something else. For example, with the issue above where we want to use all the analog input pins, there will be an option on more advanced micro-controllers to move the SDA and SCL pins to a different pins of even a PORT. This is all described in the PORT Function Multiplexing table and called PORTMUX. The ATMEGA4809 supports PORTMUX and so does the ATtiny1616. I will skip the more advanced micro-controllers and go straight to the ATtiny1616 since that has the easiest PORTMUX table I have worked with so far.

    The PORTMUX table can be found on page#16 (chapter 5) of the ATtiny1616 datasheet:

    http://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny3216_ATtiny1616-data-sheet-40001997B.pdf

    For our ATtiny1616 break-out board we want at least all the 3 serial communication options (USART, SPI and TWI) and as many PWM (TCA) and analog input pins (ADC). The ATtiny1616 even supports 1 analog output (DAC)! The PORTMUX table mentioned above is added to the pins_arduino.h variant file for reference as wel (see: https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/variants/csat1616/pins_arduino.h).

    You will notice that the columns USART0, SPI0, TWI0, TCA0, TCBn and CCL have the same description on multiple pins. For example, MOSI is shown behind PA1 and PC1. That does not mean the ATtiny1616 support two individual SPI peripherals, you must pick one of the two. You might also notice that one is in typewriter font which means that it is the alternative pin location for that peripheral.

    Based on this table I was able to update the ASCII board lay-out from my previous log:

                              _____ 
                      VDD   1|*    |20  GND
     (nSS)  (AIN4) PA4  0~  2|     |19  16~ PA3 (AIN3)(EXTCLK)
            (AIN5) PA5  1~  3|     |18  15  PA2 (AIN2)(MISO)
     (DAC)  (AIN6) PA6  2   4|     |17  14  PA1 (AIN1)(MOSI)
            (AIN7) PA7  3   5|     |16      PA0 (nRESET/UPDI)
            (AIN8) PB5  4   6|     |15  13  PC3
            (AIN9) PB4  5   7|     |14  12  PC2 
     (RXD) (TOSC1) PB3  6   8|     |13  11  PC1
     (TXD) (TOSC2) PB2  7~  9|     |12  10  PC0
     (SDA) (AIN10) PB1  8~ 10|_____|11   9~ PB0 (AIN11)(SCL)

    For now I just picked all the standard locations, but I might move the SPI pins to PC0 to PC3 or use the ADC1 in the future since those pin in PORTC are available and not really used for something else besides regular I/O. Using alternative pin locations requires register changes which I will describe in another log. The following changes are made to the pins_arduino.h with the following settings for all Serial communication:

    #define SPI_MUX              (PORTMUX_SPI0_DEFAULT_gc)
    #define PIN_SPI_MISO    (15)
    ...
    Read more »

  • Adding a custom board to Arduino, setting up I/O

    Sander van de Bor06/06/2019 at 00:32 3 comments

    Most of us start off with the Arduino UNO, but after a while you might try out more advanced boards, or boards from other suppliers, and you must add these board to the board manager. You might end up like me with an endless long list of boards. Adafruit boards, ESP32 and ESP8266, ATtinyCore etc.

    I will not go into details how these are created, but when you are interested in the process and want to learn more, there is a good description in the wiki section of the Arduino Github page:

    https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification

    I have done multiple custom boards before. Added the Micronucleus bootloader to SpenceKonde ATTinyCore; created custom M0+ board and my latest was a custom board with an ATSAME54N20A based on the Metro M4 from Adafruit. So adding a custom variant to MegaAvr should be a piece of cake, at least that was what I thought.

    Each board in the board manager has a variant file where mainly all Arduino related naming is linked to a pin, timer, output etc. by mainly using macros. At least, it used to be like that. The MegaAvr variant file still contains a lot of macro’s, but some information is actually stored in the flash memory if the micro-controller. Something new to get used to. The files in the variants folder are named different as well and do contain some additional information. All this information is spread over the following file which you can find in the variants folder for the Arduino Uno WiFi Rev2 and Arduino Nano Every:

    • pins_arduino.h
    • timers.h
    • variant.c

    To be flat out honest with everybody, I don’t know where the timers.h is used for (something with time tracking, but isn’t that the Real Time Counter (RTC)?), and the variant.c is currently going over my head and need some more time to understand, so for today I will only focus on pin_arduino.h

    When you write to a pin number in Arduino, you are actually setting a bit in a so called PORT. Each PORT is a register, and the length of that register is determined by the type of micro-controller you are using. For example the Arduino UNO is 8 bits, so each PORT controls 8 I/O pins. Since the ATMEGA328P, the micro-controller used on the Arduino UNO, has 23 I/O pins it needs at least 3 registers to control all of these. PORTB controlling 8 I/O pins, PORTC 7, and PORTD 8. You are probably wondering what happened to PORTA. I do not know, but I am sure Atmel at that time had a good reason to start at PORTB, probably because it is sharing the same architecture with other (larger) controllers where PORTA was required for additional I/O pins. While most 32 bits micro-controller have more I/O pins, they require less PORTS since each port can control 32 I/O pins. For example the ATSAMD21G18A, used on the M0, has 38 I/O pins, but only 2 PORTS.

    So each PORT is identified by an alphabetic letter and each pin with a number, starting at 0, which will result in pin identifications like PB5, PORTB pin number 5 (keep in mind this is the sixth pin). This concept is important to understand when we start working with the pin_arduino.h file.

    We will use the pin_arduino.h located in the C:\Users\username\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.1\variants

    The code start as follows:

    #ifndef Pins_Arduino_h
    #define Pins_Arduino_h
    
    #include <avr/pgmspace.h>
    #include "timers.h"
    
    #define NUM_DIGITAL_PINS            22 // (14 on digital headers + 8 on analog headers)
    #define NUM_ANALOG_INPUTS           14
    #define NUM_RESERVED_PINS           6  // (TOSC1/2, VREF, RESET, DEBUG USART Rx/Tx)
    #define NUM_INTERNALLY_USED_PINS    10 // (2 x Chip select + 2 x UART + 4 x IO + LED_BUILTIN + 1 unused pin)
    #define NUM_I2C_PINS                2  // (SDA / SCL)
    #define NUM_SPI_PINS                3  // (MISO / MOSI / SCK)
    #define NUM_TOTAL_FREE_PINS         (NUM_DIGITAL_PINS)
    #define NUM_TOTAL_PINS              (NUM_DIGITAL_PINS + NUM_RESERVED_PINS + NUM_INTERNALLY_USED_PINS + NUM_I2C_PINS + NUM_SPI_PINS)
    #define ANALOG_INPUT_OFFSET...
    Read more »

  • Preparing Arduino IDE to write sketches for the ATtiny 0- and 1-series

    Sander van de Bor06/04/2019 at 01:36 1 comment

    Arduino is a great tool for makers and hobbyists to develop code for tons of different micro-controllers. For the user the code always looks very similar and most code can be compiled for different devices. But in order to make this all happen a lot is happening behind the scenes of Arduino, and a lot of different parties are involved.

    Different “translations” are done from the sketch in Arduino to the actual machine code which will be uploaded to the micro-controller. The language the micro-controller is speaking differs for each controller and must be provided by the manufacturer. In this case the ATtiny 0- and 1-series are provided by Microchip and Microchip provides the language in a so-called Device Family Pack (DFP).

    When you compile a sketch in Arduino with verbose output turned on (in settings) you will get the location of the compiler and the DFP in the Arduino IDE output window. For example, when you compile a sketch for the Arduino UNO you might see lines like:

    C:\Users\username\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\5.4.0-atmel3.6.1-arduino2\lib\gcc\avr\5.4.0

    When you browse to that folder it will show all the different devices that are similar to the micro-controller used on the Arduino UNO (ATMEGA328P), with a complete list shown in the folder device-specs. The files in the device-specs folder are loaded, specific for the micro-controller, to tell the compiler where to find libraries for this device.  

    Some might notice that the ATtiny1616 is part of the list. Adding the ATtiny 0- and 1-series DFP to Arduino was requested a couple of months ago and they were so kind to make it standard in the latest releases. But just updating the DFP with new packages will not always do the trick. Sometimes new functions are added, and these must be understood by the translator, in this case the compiler.

    Arduino is using avr-gcc compiler for the 8-bit microcontrollers. Version 5.4.0 was sufficient for micro-controllers like the ATMEGA328P and the ATtiny series like the ATtiny85 and ATtiny84a. Unfortunately, this compiler version had issues with the new DFP for the 0- and 1-series and a newer compiler version is required (and available).

    While the DFP is provided by the manufacturer of the chip, the actual Arduino Core is developed by the Arduino team. It is a collection of libraries which are used to translate the language spoken in Arduino to commands or write to registers described in the DFP. For example, we are all familiar with the delay function. On the Arduino UNO the following function is used (all in the background, so you probably did not even know you were calling out functions):

    void delay(unsigned long ms)
    {
        uint32_t start = micros();
    
        while (ms > 0) {
            yield();
            while ( ms > 0 && (micros() - start) >= 1000) {
                ms--;
                start += 1000;
            }
        }
    }

    Unfortunately, this will not work for the new ATtiny 0- and 1-series, a new core is required.

    Luckily enough the Arduino team did come out with a new core and compiler when they released the Arduino UNO WIFI REV2. This new device is using the ATMEGA4809, part of the megaAVR 0-Series, and in the same family as the ATtiny 0- and 1-series. You can see the changes by adding the board from the board manager and just compile an empty sketch with the Arduino Uno WiFi Rev2 board selected. You will notice that the avr-gcc version 7.3.0 is used. The Arduino core is different too, for example the function for delay is now as follows:

    void delay(unsigned long ms)
    {
        uint32_t start_time = micros(), delay_time = 1000*ms;
    
        /* Calculate future time to return */
        uint32_t return_time = start_time + delay_time;
    
        /* If return time overflows */
        if(return_time < delay_time){
            /* Wait until micros overflows */
            while(micros() > return_time);
        }
    
        /* Wait until return time */
        while(micros() < return_time);
    }

    With the Arduino core, compiler and DFP in place it should be pretty straight forward to make a variant...

    Read more »

View all 12 project logs

Enjoy this project?

Share

Discussions

Alan Green wrote 06/05/2019 at 06:02 point

I've been using the 3217 extensively for the last few months, so much so that I gave Blue Board #01 the appropriate 24-pin VQFN footprint. I have also been using the 1616, 416 and 404. In my case, I've been using an Atmel ICE (left over from a previous project). I'm also planning to get hold of the $15 MPLAB Snap programmer.

With regards to the reset pin: the factory default configuration allows the reset pin to be used for programming without any further configuration.

Like Simon, I'm very much looking forward to hearing how you got the Arduino bootloader and environment going!

  Are you sure? yes | no

Sander van de Bor wrote 06/06/2019 at 03:16 point

I was actually looking at the MPLAB PICkit 4 because creating the Arduino core without some proper debugging tools is very challenging, but fortunately I have Serial working now and I am able to get some feedback. I was not aware of the MPLAB Snap programmer, that sound like an even better deal!

  Are you sure? yes | no

Simon Merrett wrote 06/03/2019 at 13:30 point

This is great! I'm not as skilled as you on getting the core running for the ATtiny 0 and 1 series, so I'll be paying close attention. Let me know if I can contribute in any way. You can see what I've been playing with at #ATtiny 0 Series programming on the cheap  

  Are you sure? yes | no

Sander van de Bor wrote 06/03/2019 at 14:22 point

Looks like the core is going to be pretty easy since most of the work is done for the Arduino Uno Wifi v2 and the new Arduino Nano Every. I like your ATtiny 0 series. There is not a lot of documentation out there yet, so any bit of information others can share is nice. Looks like you use the Nano as UPDI programmer? Did you have to disable the reset pin in order for it to work? You might have mentioned it in one of the logs, but haven't read it all yet.

  Are you sure? yes | no

Simon Merrett wrote 06/03/2019 at 16:22 point

I do use the Nano as the UPDI programmer. I don't recall having to change anything from the default jtag2updi repository regarding reset pin. One thing I tried a couple of times but gave up on was editing the baud rates to get jtag2updi running on a 3.3V pro mini. I thought that would be a useful  thing to program low voltage systems but they're not something I've needed yet, so haven't persevered with that hardware configuration. 

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates