STM32 Parte 1: Noções Básicas

Você não pode confiar no código que você não escreveu completamente. - Ken Thompson
Talvez minha citação favorita. Foi ela quem se tornou a razão pela qual eu decidi mergulhar nas profundezas da toca do coelho. Comecei minha jornada no mundo da programação recentemente, apenas cerca de um mês e decidi escrever artigos para consolidar o material. Tudo começou com uma tarefa simples: sincronizar as lâmpadas no seu estúdio de fotografia usando o Arduina. O problema foi resolvido, mas eu não entrei mais no estúdio de fotografia, não há tempo. A partir desse momento, eu decidi me engajar na programação do microcontrolador. Arduin, apesar de atraente em sua simplicidade, não gostei da plataforma. A escolha recaiu sobre a empresa ST e seus produtos populares. Naquele momento, eu ainda não tinha ideia da diferença, mas como consumidor típico comparei a velocidade do “processador” e a quantidade de memória, comprei uma placa impressionante com uma tela STM32F746NG - Discovery.Sentirei falta dos momentos de desespero e vou direto ao ponto.

Imerso na imagem de um programador, li muito, estudei, experimentei. E como eu já descrevi acima, eu queria estudar bem, só isso. E para isso, estabeleci uma meta, não soluções prontas, apenas a minha. E se tudo der certo para mim, você terá sucesso.

Lista de tudo que você precisa:

  1. Máquina virtual Ubuntu 16+ ou qualquer outra coisa
  2. compilador de arm - faça o download em developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads
  3. depurador e programador openocd - você não poderá fazer o download a partir do link, coletamos das fontes, as instruções estão anexadas

    git clone https://git.code.sf.net/p/openocd/code openocd
  4. Editor de texto ao seu gosto

Depois que tudo estiver instalado e montado, podemos prosseguir para o primeiro projeto! E não, nem é uma lâmpada piscando. Para começar, precisamos nos aprofundar no processo de inicialização do próprio micropoço.

O que precisamos:

  1. Makefile
  2. Linker.ld
  3. Init.c

Vamos começar com o último parágrafo Init.c. Antes de tudo, nosso mk deve carregar o endereço "ponteiro da pilha" é um ponteiro para o endereço de memória usado nas instruções PUSH e POP. Eu recomendo fortemente que você estude cuidadosamente essas duas instruções, pois não explicarei em detalhes todas as instruções. Como implementar isso, veja abaixo.

extern void *_estack;
void *vectors[] __attribute__((section(".isr_vector"), used)) = {
    &_estack
}

Agora vamos ver este exemplo. Extern significa que o símbolo é externo e, como declaramos esse símbolo no arquivo Linker.ld, retornaremos a ele um pouco mais tarde.

 __attribute__((section(".isr_vector"), used))

Aqui, usamos um atributo que diz ao compilador para colocar o array na seção isr_vector e que, mesmo que não o utilizemos no código, ele ainda deve ser incluído no programa. E seu primeiro elemento será o mesmo ponteiro.

Agora vamos descobrir por que esse array é e com o que ele será consumido. Ao contrário de um processador convencional, o micron na arquitetura do braço inicia a execução quando não é do endereço zero, mas do endereço apontado pelo ponteiro nessa matriz, tudo é complicado. Além disso, o primeiro ponteiro nessa matriz sempre aponta para o início da pilha, mas o segundo já aponta para o início do nosso código.

Vou dar um exemplo. é dado que a pilha começa com 0x20010000 e o código do programa é 0x0800008. então a matriz pode ser escrita como

void *vectors[] __attribute__((section(".isr_vector"), used)) = {
    0x20010000,
    0x08000008
}

Ou seja, o controlador inicializa a pilha e depois considera o endereço da primeira instrução e o carrega no registrador do contador de programa. Agora, o mais importante, dependendo do modelo, esses números podem ser diferentes, mas com o exemplo de stm32f7, posso dizer com segurança que esse array deve estar na memória no endereço 0x08000000. É a partir desse endereço que o mk inicia seu trabalho após ligar ou redefinir.

Agora vamos parar e prestar atenção em como colocar esse array na seção que precisamos. Isso é feito por "ld" ou vinculador. Este programa coleta todo o nosso programa e o script Linker.ld é usado para isso. Dou um exemplo e o analiso ainda mais.

MEMORY{
	ROM_AXIM (rx) : ORIGIN = 0x08000000, LENGTH = 1M
	RAM_DTCM (rwx): ORIGIN = 0x20000000, LENGTH = 64K
}

_estack = LENGTH(RAM_DTCM) + ORIGIN(RAM_DTCM);

SECTIONS{
	.isr_vector : {
	KEEP(*(.isr_vector))
	} >ROM_AXIM</code>
}

Vamos ver o que acontece aqui. MEMORY define seções da memória e SECTIONS define seções. aqui vemos nossa pilha e o fato de ser igual à soma do começo da memória e seu comprimento, isto é, o fim da nossa memória. A seção .isr_vector na qual colocamos nossa matriz também é definida. >ROM_AXIMno final de nossa seção significa que esta seção deve ser colocada na seção de memória que começa com 0x08000000 conforme exigido por nossos mícrons.

Colocamos a matriz onde é necessário agora precisamos de algum tipo de instrução para que nosso micron funcione. Aqui está o init.c aumentado:

extern void *_estack;

void Reset_Handler();

void *vectors[] __attribute__((section(".isr_vector"), used)) = {
    &_estack,
    &Reset_Handler
};

void __attribute__((naked, noreturn)) Reset_Handler()
{
    while(1);
}

Como mencionei anteriormente, o segundo endereço deve ser um ponteiro para a primeira função ou instrução. E, novamente, os atributos, mas tudo é simples, a função não retorna nenhum valor e segue qualquer ABI na entrada, ou seja, nu "nu". Em tais funções, o montador usual é empurrado.

Agora é a hora de compilar nosso código e ver o que está por trás. Por enquanto, não tocaremos no Makefile.

arm-none-eabi-gcc -c init.c -o init.o -mthumb

arm-none-eabi-gcc -TLinker.ld -o prog.elf init.o -Wl,--gc-sections -nostartfiles -nodefaultlibs -nostdlib

E aqui vemos muito incompreensível. em ordem:

  1. -mthumb compile para armv7, que usa apenas instruções de polegar
  2. -TLinker.ld especifica o script do vinculador. por padrão, ele compila para execução no ambiente Linux
  3. -Wl, - gc-section -nostartfiles -nodefaultlibs -nostdlib remove todas as bibliotecas padrão, arquivos de inicialização do ambiente C e todas as outras bibliotecas auxiliares, como matemática.

Obviamente, não faz sentido carregar isso em mícrons. Mas há um sentido em visualizar e estudar o binário.

objcopy -O ihex prog.elf prog.bin

hexdump prog.bin

E então veremos a conclusão. "00000000: 00 01 00 02 09 00 00 08", que simbolizarão nosso sucesso. Este é o meu primeiro artigo e não pude revelar completamente todo o material e a essência; portanto, no próximo, descreverei os mecanismos com mais detalhes e nós, amigos, poderemos passar para um programa que não pisca, mas configura o relógio do processador e dos barramentos.

All Articles