Qual é a maneira mais eficiente / código mínimo necessária para iniciar um STM32F4? Os arquivos de inicialização que vêm do ST parecem ter muitos códigos desnecessários.
Remover que você considera "desnecessária" e tentar executá-lo ...
Tyler
1
o código dos fornecedores de chips tenta ser do tamanho único, o que significa que não se encaixa bem em ninguém. Eles sempre serão inchados por definição, porque estão tentando lidar com todos os principais casos de uso de todos os periféricos e recursos que desejam apoiar. Use o código deles e você se beneficia do suporte deles e de outros online que usam esse código. Siga o seu próprio caminho e você se beneficia do tamanho e da velocidade, mas depende principalmente de você reinventar essa roda.
old_timer
Ou, como Tyler disse, corte as coisas que você não quer / precisa.
old_timer
Respostas:
25
Você pode não querer usar o código de inicialização fornecido pelo fornecedor. Existem poucas pessoas que fazem isso:
Crie um código mais eficiente ou menos inchado. Tenha um requisito especial que o código do fornecedor não atenda. Você quer saber como as coisas funcionam. Você deseja algum tipo de código universal para usar em muitos MCUs diferentes. Você quer controle total sobre o processo. etc ..
O seguinte se aplica apenas aos programas C (sem C ++, exceções, etc.) e aos microcontroladores Cortex M (independentemente da marca / modelo). Também suponho que você use o GCC, embora possa haver pouca ou nenhuma diferença com outros compiladores. Finalmente eu uso newlib.
Script do vinculador
A primeira coisa a fazer é criar um script vinculador. Você precisa informar ao seu compilador como organizar as coisas na memória. Não entrarei em detalhes sobre o script do vinculador, pois é um tópico por si só.
/*
* Linker script.
*//*
* Set the output format. Currently set for Cortex M architectures,
* may need to be modified if the library has to support other MCUs,
* or completelly removed.
*/
OUTPUT_FORMAT ("elf32-littlearm","elf32-bigarm","elf32-littlearm")/*
* Just refering a function included in the vector table, and that
* it is defined in the same file with it, so the vector table does
* not get optimized out.
*/
EXTERN(Reset_Handler)/*
* ST32F103x8 memory setup.
*/
MEMORY
{
FLASH (rx): ORIGIN =0x00000000, LENGTH =64k
RAM (xrw): ORIGIN =0x20000000, LENGTH =20k}/*
* Necessary group so the newlib stubs provided in the library,
* will correctly be linked with the appropriate newlib functions,
* and not optimized out, giving errors for undefined symbols.
* This way the libraries can be fed to the linker in any order.
*/
GROUP(
libgcc.a
libg.a
libc.a
libm.a
libnosys.a
)/*
* Stack start pointer. Here set to the end of the stack
* memory, as in most architectures (including all the
* new ARM ones), the stack starts from the maximum address
* and grows towards the bottom.
*/
__stack = ORIGIN(RAM)+ LENGTH(RAM);/*
* Programm entry function. Used by the debugger only.
*/
ENTRY(_start)/*
* Memory Allocation Sections
*/
SECTIONS
{/*
* For normal programs should evaluate to 0, for placing the vector
* table at the correct position.
*/.= ORIGIN(FLASH);/*
* First link the vector table.
*/.vectors : ALIGN(4){
FILL(0xFF)
__vectors_start__ = ABSOLUTE(.);
KEEP(*(.vectors))*(.after_vectors .after_vectors.*)}> FLASH
/*
* Start of text.
*/
_text =.;/*
* Text section
*/.text : ALIGN(4){*(.text)*(.text.*)*(.glue_7t)*(.glue_7)*(.gcc*)}> FLASH
/*
* Arm section unwinding.
* If removed may cause random crashes.
*/.ARM.extab :{*(.ARM.extab*.gnu.linkonce.armextab.*)}> FLASH
/*
* Arm stack unwinding.
* If removed may cause random crashes.
*/.ARM.exidx :{
__exidx_start =.;*(.ARM.exidx*.gnu.linkonce.armexidx.*)
__exidx_end =.;}> FLASH
/*
* Section used by C++ to access eh_frame.
* Generaly not used, but it doesn't harm to be there.
*/.eh_frame_hdr :{*(.eh_frame_hdr)}> FLASH
/*
* Stack unwinding code.
* Generaly not used, but it doesn't harm to be there.
*/.eh_frame : ONLY_IF_RO
{*(.eh_frame)}> FLASH
/*
* Read-only data. Consts should also be here.
*/.rodata : ALIGN(4){.= ALIGN(4);
__rodata_start__ =.;*(.rodata)*(.rodata.*).= ALIGN(4);
__rodata_end__ =.;}> FLASH
/*
* End of text.
*/
_etext =.;/*
* Data section.
*/.data : ALIGN(4){
FILL(0xFF).= ALIGN(4);
PROVIDE(__textdata__ = LOADADDR(.data));
PROVIDE(__data_start__ =.);*(.data)*(.data.*)*(.ramtext).= ALIGN(4);
PROVIDE(__data_end__ =.);}> RAM AT > FLASH
/*
* BSS section.
*/.bss (NOLOAD): ALIGN(4){.= ALIGN(4);
PROVIDE(_bss_start =.);
__bss_start__ =.;*(.bss)*(.bss.*)*(COMMON).= ALIGN(4);
PROVIDE(_bss_end =.);
__bss_end__ =.;
PROVIDE(end =.);}> RAM
/*
* Non-initialized variables section.
* A variable should be explicitly placed
* here, aiming in speeding-up boot time.
*/.noinit (NOLOAD): ALIGN(4){
__noinit_start__ =.;*(.noinit .noinit.*).= ALIGN(4);
__noinit_end__ =.;}> RAM
/*
* Heap section.
*/.heap (NOLOAD):{.= ALIGN(4);
__heap_start__ =.;
__heap_base__ =.;.= ORIGIN(HEAP_RAM)+ LENGTH(HEAP_RAM);
__heap_end__ =.;}> RAM
}
Você pode usar diretamente o script vinculador fornecido. Algumas coisas a serem observadas:
Esta é uma versão simplificada do script vinculador que eu uso. Durante a remoção, posso introduzir erros no código, por favor, verifique-o.
Como eu o uso para outros MCUs que você, você deve alterar o layout da MEMORY para se adequar ao seu.
Pode ser necessário alterar as bibliotecas vinculadas abaixo para vincular as suas. Aqui está vinculado ao newlib.
Tabela de vetores
Você precisa incluir no seu código uma tabela de vetores. Esta é simplesmente uma tabela de pesquisa de ponteiros de função, para a qual o hardware saltará automaticamente em caso de interrupção. Isso é bastante fácil de fazer em C.
Dê uma olhada no seguinte arquivo. Isso é para o MCU STM32F103C8, mas é muito fácil mudar para suas necessidades.
#include"stm32f10x.h"#include"debug.h"//Start-up code.externvoid __attribute__((noreturn, weak)) _start (void);// Default interrupt handlervoid __attribute__ ((section(".after_vectors"), noreturn)) __Default_Handler(void);// Reset handlervoid __attribute__ ((section(".after_vectors"), noreturn))Reset_Handler(void);/** Non-maskable interrupt (RCC clock security system) */void NMI_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** All class of fault */voidHardFault_Handler(void) __attribute__ ((interrupt, weak));/** Memory management */voidMemManage_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** Pre-fetch fault, memory access fault */voidBusFault_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** Undefined instruction or illegal state */voidUsageFault_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** System service call via SWI instruction */void SVC_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** Debug monitor */voidDebugMon_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** Pendable request for system service */voidPendSV_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** System tick timer */voidSysTick_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** Window watchdog interrupt */void WWDG_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** PVD through EXTI line detection interrupt */void PVD_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** Tamper interrupt */void TAMPER_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** RTC global interrupt */void RTC_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** Flash global interrupt */void FLASH_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** RCC global interrupt */void RCC_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** EXTI Line0 interrupt */void EXTI0_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** EXTI Line1 interrupt */void EXTI1_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** EXTI Line2 interrupt */void EXTI2_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** EXTI Line3 interrupt */void EXTI3_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** EXTI Line4 interrupt */void EXTI4_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** DMA1 Channel1 global interrupt */void DMA1_Channel1_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** DMA1 Channel2 global interrupt */void DMA1_Channel2_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** DMA1 Channel3 global interrupt */void DMA1_Channel3_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** DMA1 Channel4 global interrupt */void DMA1_Channel4_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** DMA1 Channel5 global interrupt */void DMA1_Channel5_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** DMA1 Channel6 global interrupt */void DMA1_Channel6_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** DMA1 Channel7 global interrupt */void DMA1_Channel7_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** ADC1 and ADC2 global interrupt */void ADC1_2_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** USB high priority or CAN TX interrupts */void USB_HP_CAN_TX_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** USB low priority or CAN RX0 interrupts */void USB_LP_CAN_RX0_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** CAN RX1 interrupt */void CAN_RX1_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** CAN SCE interrupt */void CAN_SCE_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** EXTI Line[9:5] interrupts */void EXTI9_5_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** TIM1 break interrupt */void TIM1_BRK_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** TIM1 update interrupt */void TIM1_UP_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** TIM1 trigger and commutation interrupts */void TIM1_TRG_COM_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** TIM1 capture compare interrupt */void TIM1_CC_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** TIM2 global interrupt */void TIM2_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** TIM3 global interrupt */void TIM3_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** TIM4 global interrupt */void TIM4_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** I2C1 event interrupt */void I2C1_EV_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** I2C1 error interrupt */void I2C1_ER_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** I2C2 event interrupt */void I2C2_EV_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** I2C2 error interrupt */void I2C2_ER_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** SPI1 global interrupt */void SPI1_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** SPI2 global interrupt */void SPI2_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** USART1 global interrupt */void USART1_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** USART2 global interrupt */void USART2_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** USART3 global interrupt */void USART3_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** EXTI Line[15:10] interrupts */void EXTI15_10_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** RTC alarm through EXTI line interrupt */voidRTCAlarm_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** USB wakeup from suspend through EXTI line interrupt */voidUSBWakeup_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** TIM8 break interrupt */void TIM8_BRK_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** TIM8 update interrupt */void TIM8_UP_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** TIM8 trigger and commutation interrupts */void TIM8_TRG_COM_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** TIM8 capture compare interrupt */void TIM8_CC_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** ADC3 global interrupt */void ADC3_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** FSMC global interrupt */void FSMC_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** SDIO global interrupt */void SDIO_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** TIM5 global interrupt */void TIM5_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** SPI3 global interrupt */void SPI3_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** UART4 global interrupt */void UART4_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** UART5 global interrupt */void UART5_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** TIM6 global interrupt */void TIM6_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** TIM7 global interrupt */void TIM7_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** DMA2 Channel1 global interrupt */void DMA2_Channel1_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** DMA2 Channel2 global interrupt */void DMA2_Channel2_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** DMA2 Channel3 global interrupt */void DMA2_Channel3_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));/** DMA2 Channel4 and DMA2 Channel5 global interrupts */void DMA2_Channel4_5_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));// Stack start variable, needed in the vector table.externunsignedint __stack;// Typedef for the vector table entries.typedefvoid(*const pHandler)(void);/** STM32F103 Vector Table */
__attribute__ ((section(".vectors"), used)) pHandler vectors[]={(pHandler)&__stack,// The initial stack pointerReset_Handler,// The reset handler
NMI_Handler,// The NMI handlerHardFault_Handler,// The hard fault handler#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)MemManage_Handler,// The MPU fault handlerBusFault_Handler,// The bus fault handlerUsageFault_Handler,// The usage fault handler#else0,0,0,// Reserved#endif0,// Reserved0,// Reserved0,// Reserved0,// Reserved
SVC_Handler,// SVCall handler#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)DebugMon_Handler,// Debug monitor handler#else0,// Reserved#endif0,// ReservedPendSV_Handler,// The PendSV handlerSysTick_Handler,// The SysTick handler// ----------------------------------------------------------------------
WWDG_IRQHandler,// Window watchdog interrupt
PVD_IRQHandler,// PVD through EXTI line detection interrupt
TAMPER_IRQHandler,// Tamper interrupt
RTC_IRQHandler,// RTC global interrupt
FLASH_IRQHandler,// Flash global interrupt
RCC_IRQHandler,// RCC global interrupt
EXTI0_IRQHandler,// EXTI Line0 interrupt
EXTI1_IRQHandler,// EXTI Line1 interrupt
EXTI2_IRQHandler,// EXTI Line2 interrupt
EXTI3_IRQHandler,// EXTI Line3 interrupt
EXTI4_IRQHandler,// EXTI Line4 interrupt
DMA1_Channel1_IRQHandler,// DMA1 Channel1 global interrupt
DMA1_Channel2_IRQHandler,// DMA1 Channel2 global interrupt
DMA1_Channel3_IRQHandler,// DMA1 Channel3 global interrupt
DMA1_Channel4_IRQHandler,// DMA1 Channel4 global interrupt
DMA1_Channel5_IRQHandler,// DMA1 Channel5 global interrupt
DMA1_Channel6_IRQHandler,// DMA1 Channel6 global interrupt
DMA1_Channel7_IRQHandler,// DMA1 Channel7 global interrupt
ADC1_2_IRQHandler,// ADC1 and ADC2 global interrupt
USB_HP_CAN_TX_IRQHandler,// USB high priority or CAN TX interrupts
USB_LP_CAN_RX0_IRQHandler,// USB low priority or CAN RX0 interrupts
CAN_RX1_IRQHandler,// CAN RX1 interrupt
CAN_SCE_IRQHandler,// CAN SCE interrupt
EXTI9_5_IRQHandler,// EXTI Line[9:5] interrupts
TIM1_BRK_IRQHandler,// TIM1 break interrupt
TIM1_UP_IRQHandler,// TIM1 update interrupt
TIM1_TRG_COM_IRQHandler,// TIM1 trigger and commutation interrupts
TIM1_CC_IRQHandler,// TIM1 capture compare interrupt
TIM2_IRQHandler,// TIM2 global interrupt
TIM3_IRQHandler,// TIM3 global interrupt
TIM4_IRQHandler,// TIM4 global interrupt
I2C1_EV_IRQHandler,// I2C1 event interrupt
I2C1_ER_IRQHandler,// I2C1 error interrupt
I2C2_EV_IRQHandler,// I2C2 event interrupt
I2C2_ER_IRQHandler,// I2C2 error interrupt
SPI1_IRQHandler,// SPI1 global interrupt
SPI2_IRQHandler,// SPI2 global interrupt
USART1_IRQHandler,// USART1 global interrupt
USART2_IRQHandler,// USART2 global interrupt
USART3_IRQHandler,// USART3 global interrupt
EXTI15_10_IRQHandler,// EXTI Line[15:10] interruptsRTCAlarm_IRQHandler,// RTC alarm through EXTI line interruptUSBWakeup_IRQHandler,// USB wakeup from suspend through EXTI line interrupt
TIM8_BRK_IRQHandler,// TIM8 break interrupt
TIM8_UP_IRQHandler,// TIM8 update interrupt
TIM8_TRG_COM_IRQHandler,// TIM8 trigger and commutation interrupts
TIM8_CC_IRQHandler,// TIM8 capture compare interrupt
ADC3_IRQHandler,// ADC3 global interrupt
FSMC_IRQHandler,// FSMC global interrupt
SDIO_IRQHandler,// SDIO global interrupt
TIM5_IRQHandler,// TIM5 global interrupt
SPI3_IRQHandler,// SPI3 global interrupt
UART4_IRQHandler,// UART4 global interrupt
UART5_IRQHandler,// UART5 global interrupt
TIM6_IRQHandler,// TIM6 global interrupt
TIM7_IRQHandler,// TIM7 global interrupt
DMA2_Channel1_IRQHandler,// DMA2 Channel1 global interrupt
DMA2_Channel2_IRQHandler,// DMA2 Channel2 global interrupt
DMA2_Channel3_IRQHandler,// DMA2 Channel3 global interrupt
DMA2_Channel4_5_IRQHandler // DMA2 Channel4 and DMA2 Channel5 global interrupts};/** Default exception/interrupt handler */void __attribute__ ((section(".after_vectors"), noreturn)) __Default_Handler(void){#ifdef DEBUG
while(1);#else
NVIC_SystemReset();while(1);#endif}/** Reset handler */void __attribute__ ((section(".after_vectors"), noreturn))Reset_Handler(void){
_start();while(1);}
O que esta acontecendo aqui. - Primeiro declaro minha função _start para que possa ser usada abaixo. - Declaro um manipulador padrão para todas as interrupções e o manipulador de redefinição - Declaro todos os manipuladores de interrupções necessários para o meu MCU. Observe que essas funções são apenas aliases para o manipulador padrão, ou seja, quando alguma delas é chamada, o manipulador padrão será chamado. Eles também são declarados semana, para que você possa substituí-los pelo seu código. Se você precisar de algum dos manipuladores, redefina-o no seu código e seu código será vinculado. Se você não precisar de nenhum deles, existe simplesmente um padrão e você não precisa fazer nada. O manipulador padrão deve ser estruturado como tal, para que, se seu aplicativo precisar de um manipulador, mas você não o implementar, ele ajudará na depuração do seu código ou na recuperação do sistema, se estiver em estado selvagem. - Eu recebo o símbolo __stack declarado no script do vinculador. É necessário na tabela de vetores. - Eu defino a tabela em si. Observe que a primeira entrada é um ponteiro para o início da pilha e as outras são ponteiros para os manipuladores. - Finalmente, forneço uma implementação simples para o manipulador padrão e o manipulador de redefinição. Observe que o manipulador de redefinição é aquele que é chamado após redefinição e que chama o código de inicialização.
Lembre-se de que o atributo ((seção ())) na tabela de vetores é absolutamente necessário, para que o vinculador coloque a tabela na posição correta (normalmente endereço 0x00000000).
Quais modificações são necessárias no arquivo acima.
Inclua o arquivo CMSIS do seu MCU
Se você modificar o script do vinculador, altere os nomes das seções
Altere as entradas da tabela de vetores para corresponder ao seu MCU
Altere os protótipos de manipuladores para corresponder ao seu MCU
Chamadas do sistema
Como eu uso o newlib, é necessário fornecer as implementações de algumas funções. Você pode implementar o printf, scanf etc, mas eles não são necessários. Pessoalmente, forneço apenas o seguinte:
_sbrk, que é necessário para o malloc. (Não são necessárias modificações)
#include<sys/types.h>#include<errno.h>caddr_t __attribute__((used)) _sbrk(int incr){externchar __heap_start__;// Defined by the linker.externchar __heap_end__;// Defined by the linker.staticchar* current_heap_end;char* current_block_address;if(current_heap_end ==0){
current_heap_end =&__heap_start__;}
current_block_address = current_heap_end;// Need to align heap to word boundary, else will get// hard faults on Cortex-M0. So we assume that heap starts on// word boundary, hence make sure we always add a multiple of// 4 to it.
incr =(incr +3)&(~3);// align value to 4if(current_heap_end + incr >&__heap_end__){// Heap has overflowed
errno = ENOMEM;return(caddr_t)-1;}
current_heap_end += incr;return(caddr_t) current_block_address;}
_exit, que não é necessário, mas eu gosto da ideia. (Você pode precisar modificar apenas a inclusão do CMSIS).
#include<stdint.h>#include"stm32f10x.h"#include"gpio.h"#include"flash.h"/** Main program entry point. */externint main(void);/** Exit system call. */externvoid _exit(int code);/** Initializes the data section. */staticvoid __attribute__((always_inline)) __initialize_data (unsignedint* from,unsignedint* region_begin,unsignedint* region_end);/** Initializes the BSS section. */staticvoid __attribute__((always_inline)) __initialize_bss (unsignedint* region_begin,unsignedint* region_end);/** Start-up code. */void __attribute__ ((section(".after_vectors"), noreturn, used)) _start(void);void _start (void){//Before switching on the main oscillator and the PLL,//and getting to higher and dangerous frequencies,//configuration of the flash controller is necessary.//Enable the flash prefetch buffer. Can be achieved when CCLK//is lower than 24MHz.Flash_prefetchBuffer(1);//Set latency to 2 clock cycles. Necessary for setting the clock//to the maximum 72MHz.Flash_setLatency(2);// Initialize hardware right after configuring flash, to switch//clock to higher frequency and have the rest of the//initializations run faster.SystemInit();// Copy the DATA segment from Flash to RAM (inlined).
__initialize_data(&__textdata__,&__data_start__,&__data_end__);// Zero fill the BSS section (inlined).
__initialize_bss(&__bss_start__,&__bss_end__);//Core is running normally, RAM and FLASH are initialized//properly, now the system must be fully functional.//Update the SystemCoreClock variable.SystemCoreClockUpdate();// Call the main entry point, and save the exit code.int code = main();//Main should never return. If it does, let the system exit gracefully.
_exit (code);// Should never reach this, _exit() should have already// performed a reset.while(1);}staticinlinevoid __initialize_data (unsignedint* from,unsignedint* region_begin,unsignedint* region_end){// Iterate and copy word by word.// It is assumed that the pointers are word aligned.unsignedint*p = region_begin;while(p < region_end)*p++=*from++;}staticinlinevoid __initialize_bss (unsignedint* region_begin,unsignedint* region_end){// Iterate and clear word by word.// It is assumed that the pointers are word aligned.unsignedint*p = region_begin;while(p < region_end)*p++=0;}
O que esta acontecendo aqui.
Primeiro, eu configuro o controlador Flash, conforme exigido pelo meu MCU, antes de alterar a frequência. Você pode adicionar itens muito básicos e necessários para o seu código de hardware aqui. Observe que o código colocado aqui não deve acessar os globais na RAM, pois eles ainda não foram inicializados. Observe também que o MCU ainda opera em baixa frequência, portanto, ligue apenas para o absolutamente necessário.
Então eu chamo a função CMSIS SystemInit (). Isso é um pouco portátil, é por isso que eu o uso. Ele lida principalmente com o núcleo, e não com o MCU, nas minhas implementações específicas, ele apenas habilita o PLL e define o MCU para sua alta frequência final. Você pode substituí-lo pelo seu código mais eficiente, mas não é grande coisa.
O próximo passo, agora que o MCU é rápido, é inicializar a RAM. Bem direto.
O MCU está funcionando normalmente agora. Eu apenas chamo a função CMSIS SystemCoreClockUpdate (), pois utilizo no meu código a variável SystemCoreClock, mas não é necessária, apenas minha preferência.
Finalmente eu chamo a função principal. Seu aplicativo agora é executado normalmente.
Se o principal retornar, uma chamada para _exit () é uma boa prática, para reiniciar o sistema.
Devido ao tamanho da resposta, pode parecer assustador. Além disso, ao tentar entender isso, você pode ter que combater sua cadeia de ferramentas para que ela faça o que quiser. Não se preocupe, finalmente você entenderá o quão simples e versátil é o código acima. Você poderá portá-lo em qualquer ARM MCU, apenas uma noite, quando entender como as coisas funcionam. Ou você pode melhorá-lo satisfazendo suas necessidades pessoais facilmente.
Fotis Panagiotopoulos
Eu acho que você pode querer ligar __initialize_data()e __initialize_bss()mais cedo do que você, mesmo que isso funcione em velocidade lenta. Caso contrário, você deve garantir que SystemInit()suas Flash_*()rotinas não usem globais.
22416 Pål-Kristian Engstad
É mais do que eu poderia pedir! Obrigado pela resposta detalhada!
John
É realmente bom ter tudo isso em um só lugar. Obrigado pelo seu tempo e conhecimento!
bitsmack
@ Pål-Kristian Engstad Exatamente. Deveria ter deixado mais claro. Posso editar a resposta quando tiver tempo livre, para que as cópias coladas no código sejam seguras.
Fotis Panagiotopoulos
5
O córtex-ms, diferentemente dos braços de tamanho normal, usa uma tabela de vetores. Eles também não têm modos e registros bancários. E para eventos / interrupções, eles estão em conformidade com o padrão de codificação ARM. O que significa o mínimo necessário, mas você escolhe a primeira palavra no endereço zero é o valor inicial para o ponteiro da pilha e a segunda palavra é o endereço para o qual ramificar na redefinição. Muito fácil de usar usando diretivas de montagem.
.globl _start
_start:.word 0x20001000.word main
Mas, novamente, você pode fazer o que quiser, desde que as duas primeiras palavras tenham os valores corretos. Observe que um endereço de miniatura para ramificação tem o lsbit definido. Não é realmente parte do endereço, apenas indica que estamos (permanecendo) no modo de polegar.
Você precisa consumir esses quatro bytes com alguma coisa, mas se você tiver algum outro código usado para definir o ponteiro da pilha, não precisará usar a tabela de vetores, ele carregará o que você colocou lá, então você sempre poderá alterá-lo. Existe apenas um ponteiro de pilha, mas não como braços de tamanho grande / mais velhos.
A palavra inicialização é muito vaga, então eu já poderia ter coberto essas diretivas ou pode levar milhares de outras linhas de código C para concluir a inicialização do microcontrolador, dependendo do que você quis dizer.
Esp. Com um STM32, você precisa sincronizar os periféricos que deseja usar, precisa configurá-los para o que deseja que eles façam e assim por diante. Não é realmente diferente de qualquer outro microcontrolador, exceto que cada fornecedor e família de produtos têm uma lógica diferente e inicializam de uma maneira diferente.
Os arquivos de inicialização que vêm de um fabricante normalmente são projetados para oferecer suporte a um ambiente de compilador C. Isso incluirá muitas coisas relacionadas à configuração do mapa de memória, zero de inicialização de memória, inicialização de variáveis e configuração da inicialização (vetor de redefinição).
Alguns arquivos de inicialização também incluem a configuração dos vetores de interrupção e do controlador de interrupção, embora alguns ambientes com os quais trabalhei tenham isso em um arquivo de linguagem assembly separado.
Às vezes, a complexidade é vista em um arquivo de inicialização porque modelos diferentes são suportados com base na arquitetura da CPU. Os modelos podem ter nomes como "compactos" e "grandes".
O mínimo da maneira que você pediu será direcionado quase inteiramente ao que você precisa. Então, realmente se resume a entender completamente sua arquitetura, o ambiente necessário e como sua plataforma funciona. Em seguida, você pode cortar os arquivos fornecidos pelo fornecedor para atender às suas necessidades ou escrever os seus próprios a partir do zero.
Mas, dito tudo isso, se você deseja escrever código em C, é melhor deixar o código de inicialização em paz e simplesmente configurar as coisas para o modelo de programação e concentrar seu código como iniciando em main ().
Respostas:
Você pode não querer usar o código de inicialização fornecido pelo fornecedor. Existem poucas pessoas que fazem isso:
Crie um código mais eficiente ou menos inchado. Tenha um requisito especial que o código do fornecedor não atenda. Você quer saber como as coisas funcionam. Você deseja algum tipo de código universal para usar em muitos MCUs diferentes. Você quer controle total sobre o processo. etc ..
O seguinte se aplica apenas aos programas C (sem C ++, exceções, etc.) e aos microcontroladores Cortex M (independentemente da marca / modelo). Também suponho que você use o GCC, embora possa haver pouca ou nenhuma diferença com outros compiladores. Finalmente eu uso newlib.
Script do vinculador
A primeira coisa a fazer é criar um script vinculador. Você precisa informar ao seu compilador como organizar as coisas na memória. Não entrarei em detalhes sobre o script do vinculador, pois é um tópico por si só.
Você pode usar diretamente o script vinculador fornecido. Algumas coisas a serem observadas:
Esta é uma versão simplificada do script vinculador que eu uso. Durante a remoção, posso introduzir erros no código, por favor, verifique-o.
Como eu o uso para outros MCUs que você, você deve alterar o layout da MEMORY para se adequar ao seu.
Pode ser necessário alterar as bibliotecas vinculadas abaixo para vincular as suas. Aqui está vinculado ao newlib.
Tabela de vetores
Você precisa incluir no seu código uma tabela de vetores. Esta é simplesmente uma tabela de pesquisa de ponteiros de função, para a qual o hardware saltará automaticamente em caso de interrupção. Isso é bastante fácil de fazer em C.
Dê uma olhada no seguinte arquivo. Isso é para o MCU STM32F103C8, mas é muito fácil mudar para suas necessidades.
O que esta acontecendo aqui. - Primeiro declaro minha função _start para que possa ser usada abaixo. - Declaro um manipulador padrão para todas as interrupções e o manipulador de redefinição - Declaro todos os manipuladores de interrupções necessários para o meu MCU. Observe que essas funções são apenas aliases para o manipulador padrão, ou seja, quando alguma delas é chamada, o manipulador padrão será chamado. Eles também são declarados semana, para que você possa substituí-los pelo seu código. Se você precisar de algum dos manipuladores, redefina-o no seu código e seu código será vinculado. Se você não precisar de nenhum deles, existe simplesmente um padrão e você não precisa fazer nada. O manipulador padrão deve ser estruturado como tal, para que, se seu aplicativo precisar de um manipulador, mas você não o implementar, ele ajudará na depuração do seu código ou na recuperação do sistema, se estiver em estado selvagem. - Eu recebo o símbolo __stack declarado no script do vinculador. É necessário na tabela de vetores. - Eu defino a tabela em si. Observe que a primeira entrada é um ponteiro para o início da pilha e as outras são ponteiros para os manipuladores. - Finalmente, forneço uma implementação simples para o manipulador padrão e o manipulador de redefinição. Observe que o manipulador de redefinição é aquele que é chamado após redefinição e que chama o código de inicialização.
Lembre-se de que o atributo ((seção ())) na tabela de vetores é absolutamente necessário, para que o vinculador coloque a tabela na posição correta (normalmente endereço 0x00000000).
Quais modificações são necessárias no arquivo acima.
Chamadas do sistema
Como eu uso o newlib, é necessário fornecer as implementações de algumas funções. Você pode implementar o printf, scanf etc, mas eles não são necessários. Pessoalmente, forneço apenas o seguinte:
_sbrk, que é necessário para o malloc. (Não são necessárias modificações)
_exit, que não é necessário, mas eu gosto da ideia. (Você pode precisar modificar apenas a inclusão do CMSIS).
Código de Inicialização
Finalmente, o código de inicialização!
O que esta acontecendo aqui.
Mais ou menos é isso.
fonte
__initialize_data()
e__initialize_bss()
mais cedo do que você, mesmo que isso funcione em velocidade lenta. Caso contrário, você deve garantir queSystemInit()
suasFlash_*()
rotinas não usem globais.O córtex-ms, diferentemente dos braços de tamanho normal, usa uma tabela de vetores. Eles também não têm modos e registros bancários. E para eventos / interrupções, eles estão em conformidade com o padrão de codificação ARM. O que significa o mínimo necessário, mas você escolhe a primeira palavra no endereço zero é o valor inicial para o ponteiro da pilha e a segunda palavra é o endereço para o qual ramificar na redefinição. Muito fácil de usar usando diretivas de montagem.
Mas, novamente, você pode fazer o que quiser, desde que as duas primeiras palavras tenham os valores corretos. Observe que um endereço de miniatura para ramificação tem o lsbit definido. Não é realmente parte do endereço, apenas indica que estamos (permanecendo) no modo de polegar.
Você precisa consumir esses quatro bytes com alguma coisa, mas se você tiver algum outro código usado para definir o ponteiro da pilha, não precisará usar a tabela de vetores, ele carregará o que você colocou lá, então você sempre poderá alterá-lo. Existe apenas um ponteiro de pilha, mas não como braços de tamanho grande / mais velhos.
A palavra inicialização é muito vaga, então eu já poderia ter coberto essas diretivas ou pode levar milhares de outras linhas de código C para concluir a inicialização do microcontrolador, dependendo do que você quis dizer.
Esp. Com um STM32, você precisa sincronizar os periféricos que deseja usar, precisa configurá-los para o que deseja que eles façam e assim por diante. Não é realmente diferente de qualquer outro microcontrolador, exceto que cada fornecedor e família de produtos têm uma lógica diferente e inicializam de uma maneira diferente.
fonte
Os arquivos de inicialização que vêm de um fabricante normalmente são projetados para oferecer suporte a um ambiente de compilador C. Isso incluirá muitas coisas relacionadas à configuração do mapa de memória, zero de inicialização de memória, inicialização de variáveis e configuração da inicialização (vetor de redefinição).
Alguns arquivos de inicialização também incluem a configuração dos vetores de interrupção e do controlador de interrupção, embora alguns ambientes com os quais trabalhei tenham isso em um arquivo de linguagem assembly separado.
Às vezes, a complexidade é vista em um arquivo de inicialização porque modelos diferentes são suportados com base na arquitetura da CPU. Os modelos podem ter nomes como "compactos" e "grandes".
O mínimo da maneira que você pediu será direcionado quase inteiramente ao que você precisa. Então, realmente se resume a entender completamente sua arquitetura, o ambiente necessário e como sua plataforma funciona. Em seguida, você pode cortar os arquivos fornecidos pelo fornecedor para atender às suas necessidades ou escrever os seus próprios a partir do zero.
Mas, dito tudo isso, se você deseja escrever código em C, é melhor deixar o código de inicialização em paz e simplesmente configurar as coisas para o modelo de programação e concentrar seu código como iniciando em main ().
fonte