
Boa tarde Hoje, quero lhe dizer como escrever um programa mínimo que roda em um ARM Cortex-M3 e imprime "Olá, Mundo!". Vamos tentar decifrar em etapas o mínimo necessário para isso. Vamos rodar no emulador QEMU. Portanto, qualquer um pode se reproduzir, mesmo que não tenha um pedaço de ferro na mão.
Então vamos lá!
O emulador QEMU suporta o núcleo Cortex-M3 e emula a plataforma Stellaris LM3S811 da Texas Instruments com base nele. Vamos rodar nesta plataforma. Precisamos da cadeia de ferramentas arm-none-eabi- (você pode baixá-la
aqui ). Em seguida, precisamos escrever a lógica principal do nosso programa, o código inicial, que transferirá o controle para o programa e o script do vinculador.
Em um artigo já muito
bom sobre como piscar um diodo em um pedaço de ferro a partir do zero. Portanto, aqui não vou me aprofundar no que e como funciona, mas darei apenas o conjunto mínimo de conhecimentos necessários para iniciar.
Nosso olá mundo no arquivo test.c:
static volatile unsigned int * const UART_DR = (unsigned int *)0x4000c000; static void uart_print(const char *s) { while (*s != '\0') { *UART_DR = *s; s++; } } void c_entry(void) { uart_print("Hello, World!\n"); while (1) ; }
Esse mesmo endereço 0x4000c000 é retirado da documentação, existe o registro DR zero zero.
Não estaremos envolvidos na configuração do UART (isso precisará ser feito no hardware), mas tentaremos colocar imediatamente os símbolos nele diretamente.
Agora, precisamos transferir de alguma forma o controle para nossa função with_entry no arquivo test.c. Para fazer isso, crie o seguinte código (arquivo startup.S) e, em seguida, coloque-o na imagem ELF final no início.
.type start, %function .word stack_top .word start .global start start: ldr r1, =c_entry bx r1
A primeira palavra em 0x0 deve ser um ponteiro para o topo da pilha (SP). No 0x4, há um PC, que, como SP, é carregado nos registros. Observe que start é declarado precisamente como uma função, e não como um rótulo, porque o código Cortex-M é executado no modo Thumb (este é um conjunto simplificado de comandos ARM) e é necessário que os endereços das funções no vetor de interrupção estejam no formato (endereço | 0x1) - ou seja, o último bit do endereço deve ser 1.
Em seguida, a função start simplesmente carrega o endereço da nossa função c_entry () do arquivo test.c e passa o controle através de "bx r1" lá.
Resta apenas vincular nosso programa com sucesso. Para fazer isso, você precisa configurar o cartão de memória do nosso microcontrolador. Na documentação, você pode encontrar os endereços e tamanhos de memória flash (ROM) e RAM (RAM). Aqui está o script do vinculador test.ld:
SECTIONS { . = 0x0; .text : { startup.o(.text) test.o(.text) } . = 0x20000000; .data : { *(.data) } .bss : { *(.bss) } . = ALIGN(8); . = . + 0x1000; stack_top = .; }
É importante prestar atenção aos endereços. "." no script do vinculador indica a posição atual. Colocamos a seção .text no início da ROM (endereço 0x0), seguindo a ordem - startup.o (.text) ocorre primeiro. Em seguida, vá para RAM (. = 0x20000000;) e coloque os dados (dados globais inicializados) e bss (dados globais não inicializados). Abaixo, vemos ALIGN (8) - O ARM exige o alinhamento do SP (Stack Pointer) por 8. Como a pilha cresce, a alocação de espaço sob a pilha é apenas uma adição. ” =. + 0x1000 ". Conhecemos bem nosso programa, portanto, uma pilha de 4kB é suficiente com uma grande margem.
Isso é tudo, resta reunir tudo. Trago build.sh:
Tudo aqui deve ser mais ou menos entendido, com exceção da bandeira - que está de pé. Nesse caso, você não precisa adicioná-lo (você pode verificá-lo), mas como estamos preparando uma imagem baremetal do zero, é melhor informar o compilador para que ele não preste atenção a funções como main ().
Como resultado, obtivemos o arquivo ELF test.elf. Execute-o no QEMU:
$ qemu-system-arm -M lm3s811evb -kernel test.elf -nographic Hello, World!
Isso funciona.
Obviamente, este é um estudo de caso projetado para entender o que está acontecendo. Se você precisar de uma funcionalidade mais significativa, use coisas prontas. Adicionamos suporte para esta plataforma na
Embox . Esse modelo é chamado platform / stellaris / lm3s811evb. Portanto, se alguém quiser tentar executar algo mais sério (console, timer, interrupções), você poderá coletar e tentar. Nesse caso, repito, você não precisa ter uma placa de hardware.
E aqueles que ainda não têm emuladores suficientes, ou que querem nos fazer perguntas e brincar com os
pedaços de ferro, esperaremos neste sábado e domingo no festival de tecnologia
techtrain.ru em São Petersburgo. Teremos várias peças de ferro no suporte e, na zona de demonstração, tentaremos dizer como programá-las.