domingo, 5 de agosto de 2012

Instalando o Optiboot em um ATmega328 standalone com oscilador interno de 8MHz


O Optiboot agregou uma gama de qualidades ao Arduino, dentre elas, velocidade no boot, transferência mais rápida dos sketches e menor utilização da memoria flash. Como no universo dos MCUs normalmente a memoria é muito limitada, este recurso é o que mais me agrada nele, já que possui apenas um quarto do tamanho do bootloader anterior, deixando livres 32.256 bytes para o desenvolvedor, no caso do ATmega328, um ganho de 1,5K bytes!

Todas estas vantagens garantiram ao Optiboot o lugar do bootloader padrão nos Arduinos a partir do modelo Uno, porem quem já tentou grava-lo em um ATmega328 standalone que faz uso do oscilador interno de 8MHz, possivelmente teve a decepção de não conseguir utiliza-lo para efetuar o uploads dos sketches.

O curioso é que todo o processo de gravação do bootloader ocorre normalmente, bem como o MCU responde positivamente ao funcionamento do bootloader, efetuando as três piscadas rápidas do LED ao efetuar o reset, sinalizando que o mesmo esta sendo executado.

Mesmo após efetuar varias configurações diferentes de fuses, não obtive sucesso e resolvi estudar mais a fundo o problema. Notei que fazendo uso de um oscilador externo, o mesmo funciona perfeitamente! Isto se deve ao fato de que o Optiboot eleva a velocidade de upload na UART para 115.200 BPS, tornando a transferência de sketches mais rápida. Entretanto o oscilador interno infelizmente não possui muita precisão (cerca de até 10% de tolerância), que é exigida em velocidades de transferencia mais altas.

A solução encontrada foi recompilar o Optiboot de forma a permitir uploads com uma velocidade um pouco mais baixa, em 57.200 BPS. Nesta velocidade a UART do ATmega funciona perfeitamente mesmo com o oscilador interno, e a perda de performance na taxa de upload é perfeitamente aceitável, já que este processo levara apenas poucos segundos a mais, preservando o mais importante que é a utilização de espaço do Optiboot.

Todo o processo é bem simples: Primeiramente é recomendável baixar a ultima versão dos fontes do optiboot em http://code.google.com/p/optiboot. Isto se faz necessário já que dependendo da versão do avr-gcc que esteja instalada, o tamanho do bootloader pode ultrapassar os 512 bytes definidos no setup dos fuses e a versão disponibilizada no site já corrige este bug.

Feito isso será necessário editar o arquivo Makefile (normalmente em /usr/share/arduino/hardware/arduino/bootloaders/optiboot/Makefile) e acrescentar os parâmetros abaixo, preferencialmente ao final da sessão "8MHz clocked platforms".

##### ATmega328 on Breadboard @8MHz Int. Osc. (Optiboot) #####
atmega328_int8: TARGET = atmega328_int_8MHz
atmega328_int8: MCU_TARGET = atmega328p
# Default for FTDI interfaces or equivalent
atmega328_int8: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=57600'
# For low speed serial interface (Nokia cable, Prolific PL2303 or equivalent)
#atmega328_int8: CFLAGS += '-DLED_START_FLASHES=3' '-DBAUD_RATE=19200' '-DTIMEOUT_MS=1000'
atmega328_int8: AVR_FREQ = 8000000L
atmega328_int8: LDSECTIONS = -Wl,--section-start=.text=0x7e00 -Wl,--section-start=.version=0x7ffe
atmega328_int8: $(PROGRAM)_atmega328_int_8MHz.hex
atmega328_int8: $(PROGRAM)_atmega328_int_8MHz.lst
atmega328_int8_isp: atmega328_int8
atmega328_int8_isp: TARGET = atmega328_int_8MHz
atmega328_int8_isp: MCU_TARGET = atmega328p
# 512 byte boot, SPIEN
atmega328_int8_isp: HFUSE = DE
# Internal 8MHz oscillator
atmega328_int8_isp: LFUSE = E2
# 2.7V brownout
atmega328_int8_isp: EFUSE = 05
atmega328_int8_isp: isp
##############################################################

Note que deixei comentada uma segunda entrada CFLAGS, para o caso de se estar utilizando uma UART mais lenta, que além de utilizar uma velocidade menor também necessita de um delay maior no watchdog do optiboot, aumentando em meio segundo o tempo de boot do ATmega. Este inconveniente é um pouco maior que o da taxa de transferencia, uma vez que a cada boot do MCU sofreremos seu impacto, mas ainda assim compensa a economia de memoria e na realidade leva o mesmo tempo do antigo bootloader, o ATmegaBoot.

Estes parâmetros comentados apenas serão necessários nos casos de se estar utilizando interfaces seriais mais baratas baseadas no PL2303 (tipicamente interfaces onde o IC é "estampado" na PCB) ou em adaptações de cabos para celular, que por alguma razão não suportam bem taxas de transferência altas, que mesmo quando não sofrem erro de perda de pacotes podem "engasgar". Na maioria dos casos o uso destas chaves não é preciso, mesmo em interfaces que utilizam o Prolific PL2303 padrão.

Efetuadas as alterações no Makefile basta fazer a compilação do Optiboot, simplesmente digitando o comando abaixo:

> sudo make atmega328_int8

avr-gcc -g -Wall -Os -fno-inline-small-functions -fno-split-wide-types -mshort-calls -mmcu=atmega328p -DF_CPU=8000000L  '-DLED_START_FLASHES=3' '-DBAUD_RATE=57600'   -c -o optiboot.o optiboot.c
avr-gcc -g -Wall -Os -fno-inline-small-functions -fno-split-wide-types -mshort-calls -mmcu=atmega328p -DF_CPU=8000000L  '-DLED_START_FLASHES=3' '-DBAUD_RATE=57600' -Wl,--section-start=.text=0x7e00 -Wl,--section-start=.version=0x7ffe -Wl,--relax -Wl,--gc-sections -nostartfiles -nostdlib -o optiboot_atmega328_int_8MHz.elf optiboot.o 
avr-size optiboot_atmega328_int_8MHz.elf
   text   data    bss    dec    hex filename
    506      0      0    506    1fa optiboot_atmega328_int_8MHz.elf
avr-objcopy -j .text -j .data -j .version --set-section-flags .version=alloc,load -O ihex optiboot_atmega328_int_8MHz.elf optiboot_atmega328_int_8MHz.hex
avr-objdump -h -S optiboot_atmega328_int_8MHz.elf > optiboot_atmega328_int_8MHz.lst
rm optiboot.o optiboot_atmega328_int_8MHz.elf

Ao final da compilação, verifique se o tamanho total do bootloader não ultrapassou os 512 bytes, caso contrário sera necessário calcular novamente os fuses. Conforme podemos notar acima, estamos dentro do nosso limite, se isto não ocorreu é porque provavelmente não está sendo utilizada a versão mais recente do Optiboot. Neste caso tente baixa-lo e efetue este processo novamente.

Agora será feita a gravação do bootloader em si, para isto teremos que utilizar um programador AVR ou na sua ausência podemos usar um Arduino como AVRISP. Não vou descrever este processo aqui, uma vez que esta amplamente documentado no site do Arduino e na internet.

Todo o processo de gravação pode ser feito utilizando apenas o comando make abaixo:

> make ISPTOOL=stk500v1 ISPPORT=/dev/ttyUSB0 ISPSPEED=-b19200 atmega328_int8_isp

avrdude -c stk500v1 -p atmega328p -P /dev/ttyUSB0 -b19200 -e -u -U lock:w:0x3F:m -U efuse:w:0x05:m -U hfuse:w:0xDE:m -U lfuse:w:0xE2:m
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.06s
avrdude: Device signature = 0x1e950f
avrdude: erasing chip
avrdude: reading input file "0x3F"
avrdude: writing lock (1 bytes):
Writing | ################################################## | 100% 0.02s
avrdude: 1 bytes of lock written
avrdude: verifying lock memory against 0x3F:
avrdude: load data lock data from input file 0x3F:
avrdude: input file 0x3F contains 1 bytes
avrdude: reading on-chip lock data:
Reading | ################################################## | 100% 0.02s
avrdude: verifying ...
avrdude: 1 bytes of lock verified
avrdude: reading input file "0x05"
avrdude: writing efuse (1 bytes):
Writing | ################################################## | 100% 0.02s
avrdude: 1 bytes of efuse written
avrdude: verifying efuse memory against 0x05:
avrdude: load data efuse data from input file 0x05:
avrdude: input file 0x05 contains 1 bytes
avrdude: reading on-chip efuse data:
Reading | ################################################## | 100% 0.02s
avrdude: verifying ...
avrdude: 1 bytes of efuse verified
avrdude: reading input file "0xDE"
avrdude: writing hfuse (1 bytes):
Writing | ################################################## | 100% 0.02s
avrdude: 1 bytes of hfuse written
avrdude: verifying hfuse memory against 0xDE:
avrdude: load data hfuse data from input file 0xDE:
avrdude: input file 0xDE contains 1 bytes
avrdude: reading on-chip hfuse data:
Reading | ################################################## | 100% 0.02s
avrdude: verifying ...
avrdude: 1 bytes of hfuse verified
avrdude: reading input file "0xE2"
avrdude: writing lfuse (1 bytes):
Writing | ################################################## | 100% 0.02s
avrdude: 1 bytes of lfuse written
avrdude: verifying lfuse memory against 0xE2:
avrdude: load data lfuse data from input file 0xE2:
avrdude: input file 0xE2 contains 1 bytes
avrdude: reading on-chip lfuse data:
Reading | ################################################## | 100% 0.02s
avrdude: verifying ...
avrdude: 1 bytes of lfuse verified
avrdude done.  Thank you.
avrdude -c stk500v1 -p atmega328p -P /dev/ttyUSB0 -b19200 -U flash:w:optiboot_atmega328_int_8MHz.hex:i -U lock:w:0x0F:m
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.06s
avrdude: Device signature = 0x1e950f
avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed
         
To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "optiboot_atmega328_int_8MHz.hex"
avrdude: writing flash (32768 bytes):
Writing | ################################################## | 100% 0.62s
avrdude: 32768 bytes of flash written
avrdude: verifying flash memory against optiboot_atmega328_int_8MHz.hex:
avrdude: load data flash data from input file optiboot_atmega328_int_8MHz.hex:
avrdude: input file optiboot_atmega328_int_8MHz.hex contains 32768 bytes
avrdude: reading on-chip flash data:
Reading | ################################################## | 100% 24.32s
avrdude: verifying ...
avrdude: 32768 bytes of flash verified
avrdude: reading input file "0x0F"
avrdude: writing lock (1 bytes):
Writing | ################################################## | 100% 0.05s
avrdude: 1 bytes of lock written
avrdude: verifying lock memory against 0x0F:
avrdude: load data lock data from input file 0x0F:
avrdude: input file 0x0F contains 1 bytes
avrdude: reading on-chip lock data:
Reading | ################################################## | 100% 0.02s
avrdude: verifying ...
avrdude: 1 bytes of lock verified
avrdude: safemode: Fuses OK
avrdude done.  Thank you.

Finalizada a gravação do bootloader, ainda será necessário efetuar a configuração na lista de placas para sua utilização dentro do Arduino IDE. Para isso acrescente as configurações abaixo em ~/sketchbook/hardware/breadboard/boards.txt ou em /usr/share/arduino/hardware/arduino/boards.txt.

##############################################################
optb328int8.name=ATmega328 on a breadboard @8MHz Int. Oscillator (OptiBoot)
optb328int8.upload.protocol=arduino
optb328int8.upload.maximum_size=32256
optb328int8.upload.speed=57600
optb328int8.bootloader.low_fuses=0xE2
optb328int8.bootloader.high_fuses=0xDE
optb328int8.bootloader.extended_fuses=0x05
optb328int8.bootloader.path=arduino:optiboot
optb328int8.bootloader.file=optiboot_atmega328_int_8MHz.hex
optb328int8.bootloader.unlock_bits=0x3F
optb328int8.bootloader.lock_bits=0x0F
optb328int8.build.mcu=atmega328p
optb328int8.build.f_cpu=8000000L
optb328int8.build.core=arduino:arduino
optb328int8.build.variant=standard
##############################################################

Agora temos que abrir a IDE do Arduino e selecionar a nova placa na lista de modelos. Feito isso, basta re-iniciar a IDE e fazer o upload de um sketch de exemplo para testar. Caso apresente algum erro durante o upload, podemos tentar efetua-lo manualmente para fins de debug, já que desta forma teremos uma resposta mais clara do avrdude (neste exemplo utilizei o .HEX previamente compilado atraves da IDE, substitua os XXXXX pelo número gerado na pasta tmp):
> avrdude -c arduino -p atmega328p -P /dev/ttyUSB1 -b57600 -D -U flash:w:/tmp/buildXXXXXXXXXXXXXXXXXXX.tmp/Blink.cpp.hex:i
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.00s
avrdude: Device signature = 0x1e950f
avrdude: reading input file "/tmp/buildXXXXXXXXXXXXXXXXXXX.tmp/Blink.cpp.hex"
avrdude: writing flash (1130 bytes):
Writing | ################################################## | 100% 0.28s
avrdude: 1130 bytes of flash written
avrdude: verifying flash memory against /tmp/buildXXXXXXXXXXXXXXXXXXX.tmp/Blink.cpp.hex:
avrdude: load data flash data from input file /tmp/buildXXXXXXXXXXXXXXXXXXX.tmp/Blink.cpp.hex:
avrdude: input file /tmp/buildXXXXXXXXXXXXXXXXXXX.tmp/Blink.cpp.hex contains 1130 bytes
avrdude: reading on-chip flash data:
Reading | ################################################## | 100% 0.24s
avrdude: verifying ...
avrdude: 1130 bytes of flash verified
avrdude: safemode: Fuses OK
avrdude done.  Thank you.

Neste teste o upload foi bem sucedido, caso o seu não tenha sido, verifique se as portas estão corretas (aqui foram utilizadas a ttyUSB0 no Arduino e a ttyUSB1 na FTDI). Se ainda assim o problema persistir, verifique atentamente as mensagens de erro ou tente recompilar o optiboot utilizando uma velocidade menor, descomentando no Makefile a entrada para a UART da Prolific. Em todos os exemplos descritos acima foi utilizado o sistema operacional GNU/Linux, em outras plataformas são necessários pequenos ajustes no caminho dos arquivos e portas.

Caso não queira ter o trabalho de compilar o Optiboot, segue abaixo a imagem já preparada para o ATmega328 com UART a 57.200BPS e oscilador interno a 8MHz:

:107E00000F92CDB7DEB7112484B714BE81FFF0D036
:107E100085E08093810082E08093C00088E18093B8
:107E2000C10086E08093C20080E18093C4008EE0B0
:107E3000C9D0259A26E088E19EEF31E09093850035
:107E40008093840036BBB09BFECF1D9AA89521502D
:107E5000A9F788249924CC24C394F5E0DF2EE1E12E
:107E6000EE2E73E0F72EA2D0813479F49FD089836F
:107E7000AFD08981823811F485E005C0813811F4D2
:107E800084E001C083E08BD087C0823411F484E1A8
:107E900003C0853419F485E0A3D07EC0853561F434
:107EA00085D0082F10E082D090E0982E8824802A78
:107EB000912A880C991C6FC0863521F484E090D0FB
:107EC00080E0E1CF843609F040C070D06FD0B82E8A
:107ED0006DD080E0881680E7980618F4F401F7BEAC
:107EE000E89500E011E062D0F80181938F01BE16A1
:107EF000D1F7F0E08F16F0E79F0618F0F401F7BE17
:107F0000E89566D007B600FCFDCFF401A0E0B1E033
:107F10002C9130E011968C91119790E0982F882742
:107F2000822B932B12960C01C7BEE8951124329632
:107F300081E0A038B80761F7F401D7BEE89507B62D
:107F400000FCFDCFE7BEE89527C08437B9F42ED0FA
:107F50002DD0A82E2BD03CD0BA2CF40101C0F801B2
:107F60008F010F5F1F4F84911AD0BA94C1F7089404
:107F7000811C911CAA948A0C911C0EC0853739F47F
:107F800027D08EE10CD085E90AD08FE07CCF8135F7
:107F900011F488E017D01CD080E101D064CF90911B
:107FA000C00095FFFCCF8093C60008958091C0006B
:107FB00087FFFCCF8091C00084FD01C0A89580910F
:107FC000C6000895E0E6F0E098E19083808308958C
:107FD000EDDF803219F088E0F5DFFFCF84E1DFCFFD
:107FE0001F93182FE3DF1150E9F7F2DF1F91089577
:0A7FF00080E0E8DFEE27FF27099488
:027FFE00050478
:0400000300007E007B
:00000001FF


Salve o texto acima no arquivo optiboot_atmega328_int_8MHz.hex. Antes de subir a imagem é preciso setar os fuses do ATmega:

> avrdude -c stk500v1 -p atmega328p -P /dev/ttyUSB0 -b19200 -e -u -U lock:w:0x3F:m -U efuse:w:0x05:m -U hfuse:w:0xDE:m -U lfuse:w:0xE2:m

Em seguida faça o upload do bootloader manualmente utilizando o comando abaixo:

> avrdude -c stk500v1 -p atmega328p -P /dev/ttyUSB0 -b19200 -U flash:w:optiboot_atmega328_int_8MHz.hex:i -U lock:w:0x0F:m

Concluido isto, certifique-se de ter adicionado no arquivo boards.txt as definições do novo Arduino e efetue um teste fazendo o upload de um sketch de exemplo. Seu ATmega standalone agora está pronto para uso!

Related Posts with Thumbnails