¿Se requiere un código mínimo para iniciar un STM32F4?
14
¿Cuál es la forma más eficiente / código mínimo requerido para iniciar un STM32F4? Los archivos de inicio que provienen de ST parecen tener mucho código innecesario.
Elimina lo que consideres "innecesario" e intenta ejecutarlo ...
Tyler
1
El código de proveedores de chips intenta ser un tamaño único para todos, lo que significa que no le queda bien a nadie. Siempre estarán hinchados por definición porque están tratando de manejar todos los casos de uso principales para todos los periféricos y características que están dispuestos a admitir. Use su código y se beneficiará del soporte de ellos y de otros en línea que usan ese código. Siga su propio camino y se beneficiará del tamaño y la velocidad, pero depende principalmente de usted reinventar esa rueda.
old_timer
O como dijo Tyler, corta las cosas que no quieres / necesitas.
old_timer
Respuestas:
25
Es posible que no desee utilizar el código de inicio proporcionado por el proveedor. Hay pocas razones por las que la gente hace esto:
Crea un código más eficiente o menos hinchado. Tiene un requisito especial que el código del proveedor no cumple. Quieres saber cómo funcionan las cosas. Desea algún tipo de código universal, para usar en muchas MCU diferentes. Desea un control total sobre usted el proceso. etc.
Lo siguiente se aplica solo a los programas C (sin C ++, excepciones, etc.) y los microcontroladores Cortex M (independientemente de la marca / modelo). También supongo que usa GCC, aunque puede haber poca o ninguna diferencia con otros compiladores. Finalmente uso newlib.
Linker Script
Lo primero que debe hacer es crear un script vinculador. Tienes que decirle a tu compilador cómo organizar las cosas en la memoria. No entraré en detalles sobre el script del enlazador, ya que es un tema en sí mismo.
/*
* 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}
Puede usar directamente el script de enlace proporcionado. Algunas cosas a tener en cuenta:
Esta es una versión simplificada del script enlazador que uso. Durante la eliminación, puedo introducir errores en el código, verifíquelo dos veces.
Como lo uso para otros MCU que no sean usted, debe cambiar el diseño de MEMORY para que se ajuste al suyo.
Es posible que deba cambiar las bibliotecas vinculadas a continuación para vincularlas con las suyas. Aquí se vincula contra newlib.
Tabla de vectores
Debe incluir en su código una tabla de vectores. Esta es simplemente una tabla de búsqueda de punteros de función, a la que el hardware saltará automáticamente en caso de una interrupción. Esto es bastante fácil de hacer en C.
Echa un vistazo al siguiente archivo. Esto es para la MCU STM32F103C8, pero es muy fácil de cambiar según sus necesidades.
#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 DEBUGwhile(1);#else
NVIC_SystemReset();while(1);#endif}/** Reset handler */void __attribute__ ((section(".after_vectors"), noreturn))Reset_Handler(void){
_start();while(1);}
Que está sucediendo aquí. - Primero declaro mi función _start para que pueda usarse a continuación. - Declaro un controlador predeterminado para todas las interrupciones, y el controlador de reinicio - Declaro todos los controladores de interrupciones necesarios para mi MCU. Tenga en cuenta que estas funciones son solo alias del controlador predeterminado, es decir, cuando se llama a cualquiera de ellas, se llamará al controlador predeterminado. También se declaran semana, por lo que puede anularlos con su código. Si necesita alguno de los controladores, debe volver a declararlo en su código y su código se vinculará. Si no necesita ninguno de ellos, simplemente hay uno predeterminado y no tiene que hacer nada. El controlador predeterminado debe estar estructurado de tal forma que, si su aplicación necesita un controlador pero no lo implementa, lo ayudará a depurar su código o a recuperar el sistema si está en estado salvaje. - Obtengo el símbolo __stack declarado en el script del enlazador. Es necesario en la tabla de vectores. - Defino la tabla en sí. Tenga en cuenta que la primera entrada es un puntero al inicio de la pila, y los otros son punteros a los controladores. - Finalmente proporciono una implementación simple para el controlador predeterminado y el controlador de reinicio. Tenga en cuenta que el controlador de reinicio es el que se llama después del reinicio y que llama al código de inicio.
Tenga en cuenta que el atributo ((sección ())) en la tabla de vectores es absolutamente necesario, por lo que el enlazador colocará la tabla en la posición correcta (normalmente dirección 0x00000000).
Qué modificaciones son necesarias en el archivo anterior.
Incluya el archivo CMSIS de su MCU
Si modifica la secuencia de comandos del vinculador, cambie los nombres de las secciones
Cambie las entradas de la tabla de vectores para que coincida con su MCU
Cambie los prototipos de controladores para que coincidan con su MCU
Llamadas al sistema
Como uso newlib, requiere que proporciones implementaciones de algunas funciones. Puede implementar printf, scanf, etc., pero no son necesarios. Personalmente proporciono solo lo siguiente:
_sbrk que necesita malloc. (No se necesitan modificaciones)
#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 no es necesario, pero me gusta la idea. (Es posible que solo necesite modificar el CMSIS incluido).
#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;}
Que está sucediendo aquí.
Primero configuro el controlador Flash, ya que mi MCU lo requiere, antes de cambiar la frecuencia. Puede agregar aquí cualquier código muy básico y necesario para su código de hardware. Tenga en cuenta que el código colocado aquí no debe acceder a ningún global en la RAM, ya que aún no se han inicializado. También tenga en cuenta que la MCU todavía funciona a baja frecuencia, por lo que solo llame a lo absolutamente necesario.
Luego llamo a la función CMSIS SystemInit (). Esto es algo portátil, por eso lo uso. Principalmente maneja el núcleo, no el MCU de sí mismo, en mis implementaciones específicas solo habilita el PLL y configura el MCU a su alta frecuencia final. Puede sustituirlo por un código más eficiente, pero no es gran cosa.
El siguiente paso, ahora que el MCU es rápido, es inicializar la RAM. Muy claro.
El MCU está funcionando normalmente ahora. Acabo de llamar a la función CMSIS SystemCoreClockUpdate (), ya que uso en mi código la variable SystemCoreClock, pero no es necesaria, solo mi preferencia.
Finalmente llamo a la función principal. Su aplicación ahora se ejecuta normalmente.
Si el principal regresa, una llamada a _exit () es una buena práctica, para reiniciar su sistema.
Debido a la longitud de la respuesta, puede parecer aterrador. Además, al intentar comprender eso, es posible que deba combatir su cadena de herramientas para que haga lo que usted hace. No se preocupe, finalmente comprenderá cuán simple y versátil es el código anterior. Es posible que pueda portarlo en cualquier MCU ARM, en solo una tarde, cuando comprenda cómo funcionan las cosas. O puede mejorarlo satisfaciendo fácilmente sus propias necesidades personales.
Fotis Panagiotopoulos
Creo que es posible que desee llamar __initialize_data()y __initialize_bss()antes de lo que lo hace, a pesar de que funcionará a baja velocidad. De lo contrario, debe asegurarse de que SystemInit()sus Flash_*()rutinas no usen globals en absoluto.
Pål-Kristian Engstad
¡Eso es más de lo que podría pedir! ¡Gracias por la respuesta detallada!
John
Es realmente agradable tener todo esto en un solo lugar. Gracias por su tiempo y conocimiento!
bitsmack
@ Pål-Kristian Engstad Exactamente. Debería haberlo dejado más claro. Puedo editar la respuesta cuando tengo tiempo libre, por lo que los que copian y pegan el código están a salvo.
Fotis Panagiotopoulos
5
La corteza-ms, a diferencia de los brazos de tamaño completo, utiliza una tabla de vectores. Tampoco tienen modos y registros almacenados. Y para eventos / interrupciones se ajustan al estándar de codificación ARM. Lo que significa el mínimo necesario que necesita, sin embargo, si elige obtenerlo, la primera palabra en la dirección cero es el valor inicial para el puntero de la pila, y la segunda palabra es la dirección a la que se bifurca al reiniciar. Muy fácil de hacer usando directivas de montaje.
.globl _start
_start:.word 0x20001000.word main
Pero, de nuevo, puede hacer lo que quiera siempre que las dos primeras palabras tengan los valores correctos. Tenga en cuenta que una dirección de pulgar para bifurcación tiene el conjunto lsbit Realmente no es parte de la dirección, solo indica que estamos (permaneciendo) en modo de pulgar.
Tienes que consumir esos cuatro bytes con algo, pero si tienes algún otro código que utilizas para configurar el puntero de la pila, no tienes que usar la tabla de vectores, cargará lo que pones allí, siempre puedes cambiarlo. Solo hay un puntero de pila, aunque no como brazos de tamaño completo / anteriores.
La palabra inicio es muy vaga, por lo que podría haberlo cubierto ya con esas directivas o podría tomarle miles de líneas más de código C para terminar de iniciar su microcontrolador, según lo que haya querido decir.
Esp con un STM32, debe activar el reloj para los periféricos que desea usar, debe configurarlos para lo que desea que hagan, etc. En realidad, no es diferente de cualquier otro microcontrolador, excepto que cada proveedor y familia de productos tiene una lógica diferente e inicializa de una manera diferente.
Los archivos de inicio que provienen de un fabricante normalmente están diseñados para admitir un entorno de compilador C. Eso incluirá un montón de cosas relacionadas con la configuración del mapa de memoria, la memoria de inicialización cero, las variables de inicialización y la configuración del inicio (vector de reinicio).
Algunos archivos de inicio también incluirán la configuración de los vectores de interrupción y el controlador de interrupción, aunque algunos entornos con los que he trabajado tienen esto en un archivo de lenguaje ensamblador separado.
A veces, la complejidad se ve en un archivo de inicio porque se admiten diferentes modelos basados en la arquitectura de la CPU. Los modelos pueden denominarse cosas como "compacto" y "grande".
Mínimo en la forma en que ha pedido se basará casi por completo en lo que necesita. Por lo tanto, todo se reduce a comprender completamente su arquitectura, el entorno necesario y cómo funciona su plataforma. Luego puede recortar los archivos suministrados por el proveedor para satisfacer sus necesidades O escribir los suyos desde cero.
Pero, dicho todo esto, si tiene la intención de escribir código en C, es mejor que deje el código de inicio solo y simplemente configure las cosas para el modelo de programación y concentre su código como comenzando en main ().
Respuestas:
Es posible que no desee utilizar el código de inicio proporcionado por el proveedor. Hay pocas razones por las que la gente hace esto:
Crea un código más eficiente o menos hinchado. Tiene un requisito especial que el código del proveedor no cumple. Quieres saber cómo funcionan las cosas. Desea algún tipo de código universal, para usar en muchas MCU diferentes. Desea un control total sobre usted el proceso. etc.
Lo siguiente se aplica solo a los programas C (sin C ++, excepciones, etc.) y los microcontroladores Cortex M (independientemente de la marca / modelo). También supongo que usa GCC, aunque puede haber poca o ninguna diferencia con otros compiladores. Finalmente uso newlib.
Linker Script
Lo primero que debe hacer es crear un script vinculador. Tienes que decirle a tu compilador cómo organizar las cosas en la memoria. No entraré en detalles sobre el script del enlazador, ya que es un tema en sí mismo.
Puede usar directamente el script de enlace proporcionado. Algunas cosas a tener en cuenta:
Esta es una versión simplificada del script enlazador que uso. Durante la eliminación, puedo introducir errores en el código, verifíquelo dos veces.
Como lo uso para otros MCU que no sean usted, debe cambiar el diseño de MEMORY para que se ajuste al suyo.
Es posible que deba cambiar las bibliotecas vinculadas a continuación para vincularlas con las suyas. Aquí se vincula contra newlib.
Tabla de vectores
Debe incluir en su código una tabla de vectores. Esta es simplemente una tabla de búsqueda de punteros de función, a la que el hardware saltará automáticamente en caso de una interrupción. Esto es bastante fácil de hacer en C.
Echa un vistazo al siguiente archivo. Esto es para la MCU STM32F103C8, pero es muy fácil de cambiar según sus necesidades.
Que está sucediendo aquí. - Primero declaro mi función _start para que pueda usarse a continuación. - Declaro un controlador predeterminado para todas las interrupciones, y el controlador de reinicio - Declaro todos los controladores de interrupciones necesarios para mi MCU. Tenga en cuenta que estas funciones son solo alias del controlador predeterminado, es decir, cuando se llama a cualquiera de ellas, se llamará al controlador predeterminado. También se declaran semana, por lo que puede anularlos con su código. Si necesita alguno de los controladores, debe volver a declararlo en su código y su código se vinculará. Si no necesita ninguno de ellos, simplemente hay uno predeterminado y no tiene que hacer nada. El controlador predeterminado debe estar estructurado de tal forma que, si su aplicación necesita un controlador pero no lo implementa, lo ayudará a depurar su código o a recuperar el sistema si está en estado salvaje. - Obtengo el símbolo __stack declarado en el script del enlazador. Es necesario en la tabla de vectores. - Defino la tabla en sí. Tenga en cuenta que la primera entrada es un puntero al inicio de la pila, y los otros son punteros a los controladores. - Finalmente proporciono una implementación simple para el controlador predeterminado y el controlador de reinicio. Tenga en cuenta que el controlador de reinicio es el que se llama después del reinicio y que llama al código de inicio.
Tenga en cuenta que el atributo ((sección ())) en la tabla de vectores es absolutamente necesario, por lo que el enlazador colocará la tabla en la posición correcta (normalmente dirección 0x00000000).
Qué modificaciones son necesarias en el archivo anterior.
Llamadas al sistema
Como uso newlib, requiere que proporciones implementaciones de algunas funciones. Puede implementar printf, scanf, etc., pero no son necesarios. Personalmente proporciono solo lo siguiente:
_sbrk que necesita malloc. (No se necesitan modificaciones)
_exit, que no es necesario, pero me gusta la idea. (Es posible que solo necesite modificar el CMSIS incluido).
Código de inicio
Finalmente el código de inicio!
Que está sucediendo aquí.
Más o menos esto es todo.
fuente
__initialize_data()
y__initialize_bss()
antes de lo que lo hace, a pesar de que funcionará a baja velocidad. De lo contrario, debe asegurarse de queSystemInit()
susFlash_*()
rutinas no usen globals en absoluto.La corteza-ms, a diferencia de los brazos de tamaño completo, utiliza una tabla de vectores. Tampoco tienen modos y registros almacenados. Y para eventos / interrupciones se ajustan al estándar de codificación ARM. Lo que significa el mínimo necesario que necesita, sin embargo, si elige obtenerlo, la primera palabra en la dirección cero es el valor inicial para el puntero de la pila, y la segunda palabra es la dirección a la que se bifurca al reiniciar. Muy fácil de hacer usando directivas de montaje.
Pero, de nuevo, puede hacer lo que quiera siempre que las dos primeras palabras tengan los valores correctos. Tenga en cuenta que una dirección de pulgar para bifurcación tiene el conjunto lsbit Realmente no es parte de la dirección, solo indica que estamos (permaneciendo) en modo de pulgar.
Tienes que consumir esos cuatro bytes con algo, pero si tienes algún otro código que utilizas para configurar el puntero de la pila, no tienes que usar la tabla de vectores, cargará lo que pones allí, siempre puedes cambiarlo. Solo hay un puntero de pila, aunque no como brazos de tamaño completo / anteriores.
La palabra inicio es muy vaga, por lo que podría haberlo cubierto ya con esas directivas o podría tomarle miles de líneas más de código C para terminar de iniciar su microcontrolador, según lo que haya querido decir.
Esp con un STM32, debe activar el reloj para los periféricos que desea usar, debe configurarlos para lo que desea que hagan, etc. En realidad, no es diferente de cualquier otro microcontrolador, excepto que cada proveedor y familia de productos tiene una lógica diferente e inicializa de una manera diferente.
fuente
Los archivos de inicio que provienen de un fabricante normalmente están diseñados para admitir un entorno de compilador C. Eso incluirá un montón de cosas relacionadas con la configuración del mapa de memoria, la memoria de inicialización cero, las variables de inicialización y la configuración del inicio (vector de reinicio).
Algunos archivos de inicio también incluirán la configuración de los vectores de interrupción y el controlador de interrupción, aunque algunos entornos con los que he trabajado tienen esto en un archivo de lenguaje ensamblador separado.
A veces, la complejidad se ve en un archivo de inicio porque se admiten diferentes modelos basados en la arquitectura de la CPU. Los modelos pueden denominarse cosas como "compacto" y "grande".
Mínimo en la forma en que ha pedido se basará casi por completo en lo que necesita. Por lo tanto, todo se reduce a comprender completamente su arquitectura, el entorno necesario y cómo funciona su plataforma. Luego puede recortar los archivos suministrados por el proveedor para satisfacer sus necesidades O escribir los suyos desde cero.
Pero, dicho todo esto, si tiene la intención de escribir código en C, es mejor que deje el código de inicio solo y simplemente configure las cosas para el modelo de programación y concentre su código como comenzando en main ().
fuente