Desenvolvimento bare metal para ARM Cortex-M com o GCC no Linux
- por Sergio Prado
Um ambiente integrado de desenvolvimento (IDE) pode facilitar bastante a nossa vida, criando e gerenciando automaticamente projetos e aumentando a produtividade do desenvolvimento.
Mas tudo tem seu preço. E o preço que pagamos pelas facilidades de uma IDE é a falta de um controle maior sobre o projeto e o binário final gerado.
Criando um simples projeto de pisca-led para a Freedom Board FRDM-KL46Z no Kinetis Development Studio da Freescale, o código final consumiu aproximadamente 3K de RAM (dados) e quase 10K de flash (código)!
$ size led.elf text data bss dec hex filename 9612 720 2272 12604 313c led.elf |
É claro que existe muito desperdício no código gerado, e poderíamos estudar algumas opções disponibilizadas pela IDE para otimizar o código e gerar um binário mais enxuto.
Mas trabalhando direto com o compilador podemos ter um controle muito maior sobre o código compilado. Resolvi então criar um projeto bare metal que consumisse o mínimo possível de espaço em RAM e flash para a aplicação de pisca-led.
Para testar, instalei o toolchain para desenvolvimento bare metal baseado no GCC mantido pelo pessoal da própria ARM:
$ mkdir ~/toolchain && cd ~/toolchain $ wget https://launchpad.net/gcc-arm-embedded/4.9/4.9-2014-q4-major/+download/gcc-arm-none-eabi-4_9-2014q4-20141203-linux.tar.bz2 $ tar xjfv gcc-arm-none-eabi-4_9-2014q4-20141203-linux.tar.bz2 |
Após a instalação, você pode verificar se o toolchain está funcionando com o comando abaixo:
$ ./gcc-arm-none-eabi-4_9-2014q4/bin/arm-none-eabi-gcc -v |
Criei um projeto simples para a FRDM-KL46Z e coloquei no GitHub:
$ cd ~ $ git clone https://github.com/sergioprado/bare-metal-arm.git $ cd bare-metal-arm |
O projeto é composto pelo linker script (link.ld) e dois arquivos .c. O arquivo startup.c contém a tabela de vetor de interrupção do microcontrolador e o ponto de entrada da aplicação, responsável por inicializar a memória (seções DATA e BSS) e executar a função main(), implementada no arquivo main.c e responsável por piscar o led.
$ ls includes LICENSE link.ld main.c Makefile startup.c |
Para compilar o projeto, basta digitar make:
$ make ~/toolchain/gcc-arm-none-eabi-4_9-2014q4/bin/arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m0plus -I./includes/ -o main.o main.c ~/toolchain/gcc-arm-none-eabi-4_9-2014q4/bin/arm-none-eabi-gcc -c -mthumb -mcpu=cortex-m0plus -I./includes/ -o startup.o startup.c ~/toolchain/gcc-arm-none-eabi-4_9-2014q4/bin/arm-none-eabi-gcc --specs=nano.specs -Wl,--gc-sections,-Map,main.map,-Tlink.ld -o main.elf main.o startup.o ~/toolchain/gcc-arm-none-eabi-4_9-2014q4/bin/arm-none-eabi-objcopy -O srec main.elf main.srec ~/toolchain/gcc-arm-none-eabi-4_9-2014q4/bin/arm-none-eabi-size main.elf text data bss dec hex filename 452 0 1024 1476 5c4 main.elf |
Veja que o binário final do projeto de pisca-led ficou bem menor, ocupando apenas 452 bytes de flash e 1K de RAM (basicamente alocado para o stack).
Este projeto pode ser facilmente reutilizado em outras plataformas ARM Cortex-M, inclusive de outros fabricantes.
Para isso, basta alterar o linker script de acordo com a configuração de memória do microcontrolador, e implementar o pisca-led na função main().
Se necessário, altere o toolchain definido no Makefile.
Have fun!
Sergio Prado