Como Carregar Programas sem o Bootloader

Encontrei um potencial problema essa semana. Ao tentar usar a minha placa “BSFrance LoRa32u4 II”, a qual, como diz no próprio nome é baseada no chip ATMega 32u4, vi que o mesmo tem apenas 28672 bytes disponíveis para programas. Isso porque o bootloader deste chip consome 4096 bytes (4K). Eu planejo usar a biblioteca da MCCI, chamada LMIC, para conectar os meus projetos ao LoRaWAN da TTN/TTS (The Things Network/Stack). Esta é uma biblioteca massiva e sozinha consome uns 26Kb da memória de código. Isso e mais alguns recursos que uso, como I2C e EEPROM, eu acabarei atingindo o limite máximo de memória. Até o momento eu estava fazendo meus testes e desenvolvimento usando um Arduino UNO, e o bootloader nesse é de apenas 512 bytes e por isso não havia ainda atingido o teto máximo de memória. O meu melhor plano então é o de conseguir usar o espaço de memória reservado ao bootloader e usar uma interface SPI de hardware para programar o processador, no meu caso, um dispositivo USBasp. Felizmente eu já tenho esse dispositivo, e após montar um circuito para conectar a placa (inicialmente estou testando com um clone do SparkFun Pro Micro (também baseado no chip 32u4), consegui fazer a programação direta. Também ao usar a programação através de SPI, o bootloader é automaticamente sobrescrito. Um truque com a conexão do dispositivo USBasp na porta USB no Windows 10 é a necessidade de se instalar um driver. Para isso usei o driver instalado pelo utilitário zadig-2.7.exe que baixei através do site https://zadig.akeo.ie . Os passos para instalar o driver são:
  1. Conectar o dispositivo USB/SPI em uma porta USB
  2. Executar zadig-2.7.exe
  3. Selecionar menu Options -> List All Devices
  4. Selecionar USBasp
  5. Em driver, mudar para libusbK(v3.1.0.0)
Com isso, o programa ‘avrdude’ usado pelo IDE Arduino conseguiu encontrar o dispositivo de programação por hardware USBasp e completar a gravação. Mas ainda tinha um problema. O compilador do arduino continuava a mostrar que apenas 28672 bytes estavam disponíveis:
Sketch uses 26186 bytes (91%) of program storage space. Maximum is 28672 bytes.
Global variables use 1364 bytes of dynamic memory.
Para resolver isso adicionei uma nova placa no arquivo de configuração do Arduino, ‘boards.txt’. A localização deste arquivo parece que depende se eu estou usando uma placa nativa Arduino (Arduino AVR Boards), ou se são definições instaladas depois, como por exemplo as definições da Adafruit ou da SparkFun. No meu caso eu verifiquei que para as placas nativas do Arduino o ‘boot.txt’ está localizado no diretório de instalação do IDE, ou seja: ‘C:\Program Files (x86)\Arduino\hardware\arduino\avr’. As demais definições eu encontrei na pasta ‘C:\Users\[usuario]\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.3’, o qual também corresponde ao local referido na página oficial do site arduino.cc. Para eu poder usar toda a memória eu preciso então modificar a definição da minha placa para dizer ao compilador que agora tenho todos os 32Kb disponíveis. Além disso, é preciso programar um dos fuses do controlador para indicar que a área de memória normalmente dedicada ao bootloader está disponível para a execução do firmware. Os fuses que controlam isso estão no byte mais significativo, ou High Fuses, o que no arquivo ‘boards.txt’ corresponde à definição ‘bootloader.high_fuses’. O valor originalmente definido aqui éra de 0xD8 (ou em binário 11011000). De acordo com uma calculadora online dos bits de fuse para os microcontroladores AVR da Atmel, basta mudar este valor para 0xDF (ou em binário 11011111), mas aproveitando a oportunidade também quis desabilitar a função de resetar a memória EEPROM toda vez que um novo sketch é gravado no microcontrolador. Para isso configurei o High Fuse byte para 0xD7 (em binário 11010111). É importante observar que na configuração dos fuses ‘1’ corresponde a desabilitado e ‘0’ a habilitado. Visualmente ficou assim: A modificação que fiz no ‘boards.txt’ foi de duplicar todas as entradas do [Pro Micro], mudar o prefixo de ‘promicro’ para ‘promicronbl’, mudar o título, a quantidade máxima de memória e o byte de High Fuse conforme indicado abaixo:
################################################################################
########################### Pro Micro No Bootloader ############################
################################################################################
promicronbl.name=SparkFun Pro Micro No Bootloader

promicronbl.upload.tool=avrdude
promicronbl.upload.protocol=avr109
promicronbl.upload.maximum_size=32672
promicronbl.upload.maximum_data_size=2560
promicronbl.upload.speed=57600
promicronbl.upload.disable_flushing=true
promicronbl.upload.use_1200bps_touch=true
promicronbl.upload.wait_for_upload_port=true
promicronbl.bootloader.tool=avrdude
promicronbl.bootloader.unlock_bits=0x3F
promicronbl.bootloader.lock_bits=0x2F
promicronbl.bootloader.low_fuses=0xFF
promicronbl.bootloader.high_fuses=0xD7

promicronbl.build.board=AVR_PROMICRO
promicronbl.build.core=arduino:arduino
promicronbl.build.variant=promicro
promicronbl.build.mcu=atmega32u4
promicronbl.build.usb_product="SparkFun Pro Micro No Bootloader"
promicronbl.build.vid=0x1b4f
promicronbl.build.extra_flags={build.usb_flags}
######################### Pro Micro 3.3V / 8MHz ################################
promicronbl.menu.cpu.8MHzatmega32U4=ATmega32U4 (3.3V, 8 MHz)

promicronbl.menu.cpu.8MHzatmega32U4.build.pid.0=0x9203
promicronbl.menu.cpu.8MHzatmega32U4.build.pid.1=0x9204
promicronbl.menu.cpu.8MHzatmega32U4.build.pid=0x9204
promicronbl.menu.cpu.8MHzatmega32U4.build.f_cpu=8000000L

promicronbl.menu.cpu.8MHzatmega32U4.bootloader.extended_fuses=0xFE
promicronbl.menu.cpu.8MHzatmega32U4.bootloader.file=caterina/Caterina-promicro8.hex
############################# Pro Micro 5V / 16MHz #############################
promicronbl.menu.cpu.16MHzatmega32U4=ATmega32U4 (5V, 16 MHz)

promicronbl.menu.cpu.16MHzatmega32U4.build.pid.0=0x9205
promicronbl.menu.cpu.16MHzatmega32U4.build.pid.1=0x9206
promicronbl.menu.cpu.16MHzatmega32U4.build.pid=0x9206
promicronbl.menu.cpu.16MHzatmega32U4.build.f_cpu=16000000L

promicronbl.menu.cpu.16MHzatmega32U4.bootloader.extended_fuses=0xCB
promicronbl.menu.cpu.16MHzatmega32U4.bootloader.file=caterina/Caterina-promicro16.hex
Não é muito intuitivo, mas para forçar a IDE Arduino a gravar a configuração de não usar o bootloader é necessário re-gravar o bootloader, isso irá forçar a alteração do novo valor para o High Fuse.  Primeiro, fecho e rodo novamente a IDE Arduino, seleciono a definição de placa que adicionei, com a respectiva versão (voltagem e frequência). E seleciono gravar o bootloader: Para verificar que os fuses foram gravados conforme as especificações em ‘boards.txt’ eu usei um utilitário chamado AVRDUDESS, conectei usando a USBasp e li (read) os valores dos bytes dos fuses. Tudo certo! Finalmente, uma última verificação. Escrevi um sketch onde eu preencho todos os bytes iniciais com um vetor grande, forçando o código de piscar o led a ficar bem no final da memória flash:
Sketch uses 32672 bytes (100%) of program storage space. Maximum is 32672 bytes.
Global variables use 438 bytes (17%) of dynamic memory, leaving 2122 bytes for local variables. Maximum is 2560 bytes.
Usando a opção de fazer o upload do sketch usando o gravador USBasp, carrego o programa e verifico se o LED pisca: SUCESSO! Agora posso usar todos os 32Kb de memória flash disponíveis no meu clone do Arduino/SparkFun Pro Micro. Meu próximo passo será repetir o mesmo procedimento na minha placa “BSFrance LoRa32u4 II”.