A maior rede de estudos do Brasil

Grátis
256 pág.
Descobrindo o STM32

Pré-visualização | Página 11 de 47

processador é reiniciado, e o valor
inicial da stack pointer (armazenada em 0x80000000).
Esta estrutura de memória é refletida no fragmento do linker script
mostrado na Figura 3. O script começa por definir o ponto de entrada de
código (Reset_Handler) e as duas regiões de memória – flash e ram. Em
seguida, ele coloca as seções chamadas a partir dos objetos de arquivos que
estão sendo linkados em locais apropriados nestas duas regiões de memória.
Do ponto de vista de um executável, há três secções relevantes – “.text” que
é sempre alocado em flash, “.data” e “.bss” que sempre são alocados na região
de memória RAM. As constantes necessárias para inicializar .data em tempo
de execução (runtime) são colocados em flash, bem como o código de iniciali-
zação para copiar. Observe ainda que o script linker define os rótulos chaves
_etext, _sidata, ... que são referenciados pelo código de inicialização
para inicializar a RAM.
O linker GNU é instruído a colocar a seção de dados em FLASH –
especificamente “na” localização de _sidata, mas linka as referências de dados
para locais em RAM pelo seguinte fragmento de código:
.data : AT ( _sidata )
{
...
} >RAM
A ideia chave é que o linker GNU faça distinção entre o endereço virtual
(VMA) e de carga (LMA). O VMA é o endereço que uma seção tem quando o
54 Revision: (None) ((None))
ENTRY(Reset_Handler)
MEMORY
{
RAM (rwx) : ORIGIN = 0x20000000 , LENGTH = 8K
FLASH (rx) : ORIGIN = 0x08000000 , LENGTH = 128K
}
SECTIONS
{
.text :
{
KEEP(*(.isr_vector)) /* vector table */
*(.text) /* code */
*(.text.*) /* remaining code */
*(.rodata) /* read-only data (constants) */
...
} >FLASH
...
_etext = .;
_sidata = _etext;
/* Init data goes in RAM, but is copied after code as well */
.data : AT ( _sidata )
{
...
_sdata = .;
*(.data)
...
_edata = . ; /* used to init data section */
} >RAM
.bss :
{
...
_sbss = .; /* used to init bss */
__bss_start__ = _sbss;
*(.bss)
...
. = ALIGN(4);
_ebss = . ; /* used to init bss */
__bss_end__ = _ebss;
} >RAM
Figura 3.1: Fragmento do Linker Script
programa é executado, o LMA é o endereço em que a seção é carregada. Para
dados, nosso linker script coloca o LMA da seção de dados dentro de flash e
Revision: (None) ((None)) 55
CAPÍTULO 3. PROGRAMA ESQUELETO
o VMA dentro de RAM – observe que _sidata = _etext.
A primeira parte da seção .text é carregada com os vetores de exceção
(.isr_vector) que são posteriormente definidos no código de inicialização.
Esses vetores de exceção começam em 0x08000000, como é exigido quando a
inicialização STM32 é a partir da memória flash.
Há um número de detalhes omitidos que asseguram a criação de rótulos
assumidos pelas bibliotecas de compilação e padrão bem como manipulação
de seções de depuração do binário.
O linker script e o código de inicialização colaboram para criar um
ambiente executável significativo. O linker script é responsável por garantir
que as várias partes do arquivo executável (p. ex.: os vetores de exceção)
estejam em seus devidos lugares e para associar rótulos significativo com re-
giões específicas de memória utilizada pelo código de inicialização. No reset,
o manipulador de reset é chamado. O manipulador (handler) de reset (de-
finido em startup_stm32f10x.c) copia os valores iniciais das variáveis da
flash (onde o linker coloca-os) para SRAM e zera a chamada da parte não
inicializada de SRAM. (veja a Listagem 3.2). Estas etapas são necessárias
sempre que o processador reseta de modo a inicializar o ambiente “C”. Em se-
guida o reset handler chama SystemInit (definido em system_stm32f10x.c
da biblioteca firmware) que inicializa o sistema de clock, desativa e limpa
as interrupções. O flag de compilação STM32F10X_MD_VL definido em nosso
makefile é fundamental porque o código de inicialização do clock é especifico
do processador. Finalmente, o reset handler chama a função main definida
no código do usuário. As variáveis externas exigidas pelo reset handler para
inicializar a memória (p. ex.: _sidata, _sdata...) são definidas pelo linker
script.
Outra função importante do código de inicialização é para definir a
tabela de vetores de interrupção padrão (Listagem 3.3). A fim de permitir
que o código da aplicação redefina convenientemente os vários handlers de
interrupção, cada vetor de interrupção necessário é atribuído a um apelido
substituível (fraco) para um handler padrão (que é um loop eterno). Para criar
um handler de interrupção personalizado no código do aplicativo, é suficiente
definir um procedimento com o nome do handler apropriado. Uma precaução
– você deve ter o cuidado de usar exatamente os nomes definidos na tabela de
vetor para o handler ou então não será vinculado na tabela do vetor carregado!
56 Revision: (None) ((None))
// Linker supplied pointers
extern unsigned long _sidata;
extern unsigned long _sdata;
extern unsigned long _edata;
extern unsigned long _sbss;
extern unsigned long _ebss;
extern int main(void);
void Reset_Handler(void) {
unsigned long *src, *dst;
src = &_sidata;
dst = &_sdata;
// Copy data initializers
while (dst < &_edata)
*(dst++) = *(src++);
// Zero bss
dst = &_sbss;
while (dst < &_ebss)
*(dst++) = 0;
SystemInit();
__libc_init_array();
main();
while(1) {}
}
Listing 3.2: Reset Handler no startup_stm32f10x.c
Revision: (None) ((None)) 57
CAPÍTULO 3. PROGRAMA ESQUELETO
static void default_handler (void) { while(1); }
void NMI_Handler (void) __attribute__ ((weak, alias
↪→(``default_handler'')));
void HardFault_Handler (void) __attribute__ ((weak, alias
↪→(``default_handler'')));
void MemMange_Handler (void) __attribute__ ((weak, alias
↪→(``default_handler'')));
void BusFault_Handler (void) __attribute__ ((weak, alias
↪→(``default_handler'')));
...
__attribute__ ((section(``.isr_vector'')))
void (* const g_pfnVectors[])(void) = {
_estack,
Reset_Handler ,
NMI_Handler ,
HardFault_Handler ,
...
Listing 3.3: Vetores de Interrupção
58 Revision: (None) ((None))
Capítulo 4
Configuração do STM32
Os processadores STM32 são sistemas complexos com diversos perifé-
ricos. Antes que qualquer um desses periféricos possam ser utilizados eles
precisam ser configurados. Algumas dessas configurações são genéricas – por
exemplo a distribuição de clock, e a configuração dos pinos – enquanto outras
são específicas de cada periférico. Ao longo desse capítulo, nós utilizaremos o
programa simples “blinking lights” como um exemplo-guia.
Os passos fundamentais de inicialização que são necessários para utili-
zar qualquer periférico do STM32 são:
1. Habilitar clock para o periférico.
2. Configurar os pinos necessários ao periféricos.
3. Configurar o hardware do periférico.
Os processadores STM32, como membros da família Cortex-M3, tem
todos eles um timer do sistema no núcleo que pode ser usados para fornecer in-
tervalo de tempo regular, “tick”. Nós utilizamos esse timer para fornecer uma
taxa constante de pulso (piscadas) para o led no nosso exemplo. A estrutura
geral desse programa está ilustrada na Figura 4. O programa inicia incluindo
cabeçalhos importantes da biblioteca de firmware – nesse caso para a configu-
ração do clock e dos pinos. A rotina principal segue os passos de inicialização
descritos anteriormente e então entra em um loop que troca o estado anterior
do LED e espera 250ms. A função main é seguida por um código que imple-
menta a função delay que utiliza o timer do sistema. Finalmente, uma função
de ajuda é utilizada para lidar com uma possível violação no firmware da bi-
blioteca ( necessária se USE_FULL_ASSERT é definida quando for compilado o
Revision: (None) ((None)) 59
CAPÍTULO 4. CONFIGURAÇÃO DO STM32
firmware da biblioteca dos módulos). Se por um lado o handler assert_failed
não faz nada, por outro ela é muito útil quando estamos depurando novos

Crie agora seu perfil grátis para visualizar sem restrições.