Background
In 2019, Microchip acquired Atmel and released several new AVR microcontrollers, including the Tiny-0 and Tiny-1 Series. On these mircocontrollers, traditional Atmel ICSP programming was not supported. Instead, they feature a new one-wire programming interface called Unified Programming and Debug Interface (UPDI). The new hardware also made existing TTL-serial bootloader programming obsolete. Thus, one must use a native UPDI programmer to work with these microcontrollers, such as pyupdi.
However, Optiboot's developers have subsquently developed a new version of the bootloader called Optiboot_X and implemented the STK500 programming protocol on these new microcontrollers, allowing one to program these microcontrollers using the traditional TTL-serial bootloader approach, and it appears that the Arduino community has already supported them for a while, with MegaCoreX and megaTinyCore, which was good news to me. Nevertheless, it seems that there's little resource beyond these prepackaged Arduino cores, in particular, there's almost no documentation on how to program them manually from the commandline.
What I've already done
I designed a custom AVR development board based on the Atmel ATTiny1604 microcontroller, which is a member of the new Tiny-0 family. Due to technical considerations, it must be programmed via the serial port using the bootloader similar to a traditional Arduino, not UPDI (UPDI is only used to burn the bootloader).
To achieve this goal, I installed the latest version of binutils, GCC and an experimental patch to avr-libc, so the compiler is able to generate code targeting my microcontroller. Then, I compiled my personal Optiboot_X bootloader.
$ git clone https://github.com/Optiboot/optiboot.git
$ cd optiboot/optiboot/bootloaders/optiboot
$ make -f Makefile.mega0 optiboot_attiny1604.hex UARTTX=A1 TIMEOUT=8 LED=A7 BAUD_RATE=57600 LED_START_FLASHES=10
Using Compiler at: /home/user/code/optiboot/optiboot/bootloaders/optiboot/avr-gcc
avr-gcc -g -Wall -Os -fno-split-wide-types -mrelax -DWDTTIME=8 -DLED_START_FLASHES=10 -DLED=A7 -DUARTTX=A1 -DBAUD_RATE=57600 -Wl,-section-start=.text=0 -Wl,--section-start=.application=0x200 -Wl,--section-start=.version=0x1fe -Wl,--relax -nostartfiles -nostdlib -mmcu=attiny1604 -o optiboot_attiny1604.elf optiboot_x.c
avr-size optiboot_attiny1604.elf
text data bss dec hex filename
494 9 0 503 1f7 optiboot_attiny1604.elf
avr-objdump -S optiboot_attiny1604.elf > optiboot_attiny1604.lst
avr-objcopy -j .text -j .data -j .version --set-section-flags .version=alloc,load -O ihex optiboot_attiny1604.elf optiboot_attiny1604.hex
Then I uploaded the Optiboot_X bootloader to the ATTiny1604 microcontroller via UPDI, using pyupdi
.
# set fuse BOOTEND
$ pyupdi -d tiny1604 -c /dev/ttyUSB0 -fs 8:0x02
# burn Optiboot_X
$ pyupdi -d tiny1604 -c /dev/ttyUSB0 -f optiboot_attiny1604.hex
# reset
$ pyupdi -d tiny1604 -c /dev/ttyUSB0 -r
After burning the Optiboot_X via UPDI, I was able to see 10 pulses during power-on at A7 on my oscilloscope, indicating that Optiboot_X was alive.
Note: Technically the installation is not complete yet. Since UPDI and /RESET
share the same pin, UPDI must be permanently disabled via fuse to allow programming via bootloader, but it means a nonfunctional bootloader can brick the chip. I decided to manually reset the chip by power cycling the board for now.
Next, I wrote a test program.
$ cat blink.c
#define F_CPU 20000000UL
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
PORTB.DIRSET = 0b00000001;
while(1) {
PORTB.OUTSET = 0b00000001;
_delay_ms(500);
PORTB.OUTCLR = 0b00000001;
_delay_ms(500);
}
}
And I compiled it.
$ make
avr-gcc -mmcu=attiny1604 -Wall -Os -o blink.elf blink.c
avr-objcopy -j .text -j .data -j .rodata -O ihex blink.elf blink.hex
Question
Now what? How do I upload blink.hex
to the Optiboot_X on the microcontroller?
On a traditional Arduino, the command is something like...
$ avrdude -C/etc/avrdude/avrdude.conf -v -patmega328p \
-carduino -P/dev/ttyUSB0 -b38400 -D -Uflash:w:blink.hex:i
But this is not an ATMega328P and there's nearly zero documentation on the web, I have a lot of questions and I'm not sure how to proceed.
To begin with, in man avrdude
, there's no mention of any Tiny0 or Tiny1 devices. What should the -p
argument be? Also, do I need to use a patched version of avrdude
? Do I need a customized avrdude.conf
? If so, where can I find them?
-
Since you're using optiboot avrdude shouldn't care what the chip is.Majenko– Majenko2020年11月07日 22:58:06 +00:00Commented Nov 7, 2020 at 22:58
1 Answer 1
After poking around in the git repository for a while, I've solved the problem. To work with these new TinyAVR-0/1 microcontrollers, a new version of avrdude
is needed. Since avrdude
hasn't made a stable release yet, it's necessary to build a new avrdude
from source from SVN.
$ svn co https://svn.savannah.nongnu.org/svn/avrdude/trunk avrdude
$ cd avrdude/avrdude
$ ./bootstrap
$ mkdir build && cd build
$ ../configure
$ make
$ make install
After installation, /usr/local/bin/avrdude
and /usr/local/etc/avrdude.conf
should be available. One can see that a bunch of new TinyAVR parts have been added...
$ avrdude -p?
Valid parts are:
[...]
t10 = ATtiny10
t11 = ATtiny11
t12 = ATtiny12
t13 = ATtiny13
t15 = ATtiny15
t1604 = ATtiny1604
t1606 = ATtiny1606
t1607 = ATtiny1607
t1614 = ATtiny1614
t1616 = ATtiny1616
t1617 = ATtiny1617
t1634 = ATtiny1634
t20 = ATtiny20
t202 = ATtiny202
t204 = ATtiny204
t212 = ATtiny212
t214 = ATtiny214
t2313 = ATtiny2313
t24 = ATtiny24
t25 = ATtiny25
t26 = ATtiny26
t261 = ATtiny261
t28 = ATtiny28
t3214 = ATtiny3214
t3216 = ATtiny3216
t3217 = ATtiny3217
t4 = ATtiny4
t40 = ATtiny40
t402 = ATtiny402
t404 = ATtiny404
t406 = ATtiny406
t412 = ATtiny412
t414 = ATtiny414
t416 = ATtiny416
t417 = ATtiny417
t4313 = ATtiny4313
t43u = ATtiny43u
t44 = ATtiny44
t441 = ATtiny441
t45 = ATtiny45
t461 = ATtiny461
t5 = ATtiny5
t804 = ATtiny804
t806 = ATtiny806
t807 = ATtiny807
t814 = ATtiny814
t816 = ATtiny816
t817 = ATtiny817
t84 = ATtiny84
t841 = ATtiny841
t85 = ATtiny85
t861 = ATtiny861
t88 = ATtiny88
t9 = ATtiny9
[...]
To program the ATTiny1604, simply run...
$ avrdude -C/usr/local/etc/avrdude.conf -v -pt1604 \
-carduino -P/dev/ttyUSB0 -b57600 -D -Uflash:w:blink.hex:i
Don't forget to modify the -b57600
argument to match the baud of your Optiboot_X build.
Lesson learned: When RTFM is impossible, use the source, Luke!
Potential Problems
avrdude: verification error; content mismatch
It's possible that avrdude
successfully establishes communication with the board via the serial port, reads the correct device signature, then attempts to upload a program, but ultimately fails verification.
$ avrdude -v -pattiny1604 -carduino -P/dev/ttyUSB0 -b57600 -D -Uflash:w:blink.hex:i
Reading | ################################################## | 100% 0.00s
avrdude: Device signature = 0x1e9425 (probably t1604)
avrdude: reading input file "blink.hex"
avrdude: writing flash (204 bytes):
Writing | ################################################## | 100% 0.06s
avrdude: 204 bytes of flash written
avrdude: verifying flash memory against blink.hex:
avrdude: load data flash data from input file blink.hex:
avrdude: input file blink.hex contains 204 bytes
avrdude: reading on-chip flash data:
Reading | ################################################## | 100% 0.06s
avrdude: verifying ...
avrdude: verification error, first mismatch at byte 0x0000
0x01 != 0x0c
avrdude: verification error; content mismatch
Solution: In TinyAVR 0/1, the bootloader is located at the beginning of the flash memory, occupies the first 512 bytes. When compiling the program to be uploaded via the bootloader, the correct 512-byte text section offset must be specified. Otherwise, avrdude
will attempt to program from 0x00, which is impossible, because it's the location of the bootloader. Upon readback, instead of your program, avrdude
reads the bootloader, which triggers the verification failure in question (it's worth noticing that 0x01
, the first mismatched byte that triggers verification failure, is actually the first byte in the Optiboot_X binary code).
Instead of using...
avr-gcc -mmcu=attiny1604 -Wall -Os -o blink.elf blink.c
Recompile your program with `
avr-gcc -mmcu=attiny1604 -Wall -Os -o blink.elf blink.c \
-Wl,--section-start=.text=0x200
avr-objcopy -j .text -j .data -j .rodata -O ihex blink.elf blink.hex
And reupload it.
$ avrdude -v -pattiny1604 -carduino -P/dev/ttyUSB0 -b57600 -D -Uflash:w:blink.hex:i
avrdude: Device signature = 0x1e9425 (probably t1604)
avrdude: reading input file "blink.hex"
avrdude: writing flash (716 bytes):
Writing | ################################################## | 100% 0.00s
avrdude: 716 bytes of flash written
avrdude: verifying flash memory against blink.hex:
avrdude: load data flash data from input file blink.hex:
avrdude: input file blink.hex contains 716 bytes
avrdude: reading on-chip flash data:
Reading | ################################################## | 100% 0.00s
avrdude: verifying ...
avrdude: 716 bytes of flash verified
avrdude: safemode: Fuses OK (E:FF, H:FF, L:FF)
avrdude done. Thank you.
avrdude: programmer type jtagice3_updi not found
It's possible that avrdude
reports the following error...
/usr/bin/avrdude -C/usr/local/etc/avrdude.conf -v -pattiny1604 -carduino -P/dev/ttyUSB0 -b38400 -D -Uflash:w:blink.hex:i
avrdude: Version 6.3, compiled on Jul 24 2019 at 00:00:00
Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
Copyright (c) 2007-2014 Joerg Wunsch
System wide configuration file is "/usr/local/etc/avrdude.conf"
avrdude: error at "/usr/local/etc/avrdude.conf:1091: programmer type jtagice3_updi not found
avrdude: error reading system wide configuration file "/usr/local/etc/avrdude.conf"
Solution: You are trying to run an old version of avrdude
with a new configuration file. Make sure the avrdude
executable you are running is actually the up-to-date version compiled from the upstream SVN repository.