Jaki jest najbardziej wydajny sposób / minimalny kod wymagany do uruchomienia STM32F4? Pliki startowe, które pochodzą z ST, wydają się zawierać dużo niepotrzebnego kodu.
Usuń to, co uważasz za „niepotrzebne”, i spróbuj je uruchomić ...
Tyler,
1
kod dostawcy chipów próbuje być uniwersalny, co oznacza, że nie pasuje do nikogo dobrze. Z definicji zawsze będą one rozdęte, ponieważ próbują obsłużyć wszystkie główne przypadki użycia wszystkich urządzeń peryferyjnych i funkcji, które są skłonne wesprzeć. Skorzystaj z ich kodu, a uzyskasz wsparcie od nich i innych osób korzystających z tego kodu online. Idź własną drogą, a czerpiesz korzyści z rozmiaru i prędkości, ale to od Ciebie zależy, czy wymyślisz to koło.
old_timer
Lub, jak powiedział Tyler, wytnij rzeczy, których nie chcesz / potrzebujesz.
old_timer
Odpowiedzi:
25
Możesz nie chcieć używać kodu startowego dostarczonego przez dostawcę. Jest kilka osób, które tak robią:
Utwórz bardziej wydajny lub mniej rozdęty kod. Mają specjalny wymóg, że kod dostawcy nie spełnia. Chcesz wiedzieć, jak działają rzeczy. Chcesz jakiegoś uniwersalnego kodu, który będzie używany w wielu różnych MCU. Chcesz mieć całkowitą kontrolę nad sobą. itp..
Poniższe informacje dotyczą tylko programów w języku C (bez C ++, wyjątków itp.) I mikrokontrolerów Cortex M (niezależnie od marki / modelu). Zakładam również, że używasz GCC, chociaż może nie być żadnej różnicy z innymi kompilatorami. Wreszcie używam newlib.
Skrypt Linkera
Pierwszą rzeczą do zrobienia jest stworzenie skryptu linkera. Musisz powiedzieć swojemu kompilatorowi, jak uporządkować rzeczy w pamięci. Nie będę wchodził w szczegóły dotyczące skryptu linkera, ponieważ jest to temat sam w sobie.
/*
* 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}
Możesz bezpośrednio użyć dostarczonego skryptu linkera. Kilka rzeczy do zapamiętania:
To jest uproszczona wersja skryptu linkera, którego używam. Podczas usuwania kodu mogę wprowadzać błędy do kodu, proszę dokładnie to sprawdzić.
Ponieważ używam go do innych MCU niż ty, musisz zmienić układ MEMORY, aby pasował do twojego.
Może być konieczna zmiana bibliotek połączonych poniżej, aby łączyły się z własnymi. Tutaj łączy się z newlib.
Tabela wektorów
Musisz dołączyć do kodu tabelę wektorów. Jest to po prostu tabela wskaźników wskaźników funkcji, do której sprzęt przeskoczy automatycznie w przypadku przerwania. Jest to dość łatwe do zrobienia w C.
Spójrz na następujący plik. Dotyczy to MCU STM32F103C8, ale bardzo łatwo można go dostosować do swoich potrzeb.
#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);}
Co tu się dzieje. - Najpierw deklaruję moją funkcję _start, aby można było jej użyć poniżej. - Deklaruję domyślną procedurę obsługi dla wszystkich przerwań i procedurę resetowania - Deklaruję wszystkie procedury obsługi przerwań potrzebne dla mojego MCU. Zauważ, że te funkcje są tylko aliasami do domyślnego modułu obsługi, tzn. Gdy dowolna z nich zostanie wywołana, zamiast tego zostanie wywołany domyślny moduł obsługi. Są one również deklarowane jako tydzień, więc możesz zastąpić je kodem. Jeśli potrzebujesz któregokolwiek z programów obsługi, ponownie umieść go w kodzie, a kod zostanie połączony. Jeśli nie potrzebujesz żadnego z nich, jest po prostu domyślny i nie musisz nic robić. Domyślny moduł obsługi powinien mieć taką strukturę, że jeśli aplikacja potrzebuje modułu obsługi, ale go nie zaimplementujesz, pomoże ci to w debugowaniu kodu lub w odzyskaniu systemu, jeśli jest na wolności. - Otrzymuję symbol __stack zadeklarowany w skrypcie linkera. Jest potrzebny w tabeli wektorów. - Definiuję sam stół. Zauważ, że pierwszy wpis jest wskaźnikiem na początku stosu, a pozostałe są wskaźnikami do procedur obsługi. - Wreszcie zapewniam prostą implementację domyślnego modułu obsługi i modułu resetowania. Zauważ, że procedura resetowania jest wywoływana po resecie i wywołuje kod startowy.
Należy pamiętać, że atrybut ((sekcja ())) w tabeli wektorów jest absolutnie potrzebny, aby linker umieścił tabelę we właściwej pozycji (zwykle adres 0x00000000).
Jakie modyfikacje są potrzebne w powyższym pliku.
Dołącz plik CMSIS swojego MCU
Jeśli zmodyfikujesz skrypt linkera, zmień nazwy sekcji
Zmień wpisy tabeli wektorów, aby pasowały do MCU
Zmień prototypy modułów obsługi, aby pasowały do MCU
Połączenia systemowe
Ponieważ używam newlib, wymaga to implementacji niektórych funkcji. Możesz zaimplementować printf, scanf itp., Ale nie są one potrzebne. Osobiście zapewniam tylko następujące elementy:
_sbrk, którego potrzebuje Malloc. (Nie wymaga modyfikacji)
#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, który nie jest potrzebny, ale podoba mi się ten pomysł. (Konieczne może być jedynie zmodyfikowanie uwzględnienia 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;}
Co tu się dzieje.
Najpierw konfiguruję kontroler Flash, ponieważ jest to wymagane przez mój MCU, przed zmianą częstotliwości. Możesz dodać tutaj bardzo podstawowy i potrzebny kod sprzętowy. Zauważ, że umieszczony tutaj kod nie powinien mieć dostępu do globali w pamięci RAM, ponieważ nie zostały one jeszcze zainicjowane. Zauważ też, że MCU nadal działa na niskiej częstotliwości, więc zadzwoń tylko absolutnie potrzebny.
Następnie wywołuję funkcję CMSIS SystemInit (). Jest to trochę przenośne, dlatego go używam. Przeważnie obsługuje rdzeń, a nie MCU ot self, w moich konkretnych implementacjach włącza tylko PLL i ustawia MCU na końcową wysoką częstotliwość. Możesz zastąpić go bardziej wydajnym kodem, ale to nie jest wielka sprawa.
Następnym krokiem, teraz, gdy MCU jest szybki, jest inicjalizacja pamięci RAM. Całkiem proste.
MCU działa teraz normalnie. Po prostu wywołuję funkcję CMSIS SystemCoreClockUpdate (), ponieważ używam w swoim kodzie zmiennej SystemCoreClock, ale nie jest to potrzebne, tylko moje preferencje.
Wreszcie wywołuję główną funkcję. Twoja aplikacja działa teraz normalnie.
Jeśli główny zwraca, wywołanie _exit () jest dobrą praktyką, aby zrestartować system.
Ze względu na długość odpowiedzi może się to wydawać przerażające. Również starając się to zrozumieć, być może będziesz musiał walczyć z łańcuchem narzędzi, aby zrobić to, co robisz. Nie martw się, w końcu zrozumiesz, jak prosty i wszechstronny jest powyższy kod. Możesz być w stanie przenieść go na dowolną jednostkę MCU ARM, tylko wieczorem, gdy zrozumiesz, jak to działa. Możesz też z łatwością ulepszyć go, zaspokajając własne potrzeby.
Fotis Panagiotopoulos,
Myślę, że może chcesz zadzwonić __initialize_data()i __initialize_bss()wcześniej niż ty, mimo że będzie działać w zwolnionym tempie. W przeciwnym razie musisz się upewnić, że SystemInit()twoje Flash_*()procedury w ogóle nie używają globałów.
Pål-Kristian Engstad
To więcej niż mógłbym prosić! Dzięki za szczegółową odpowiedź!
John
Jest naprawdę miło mieć to wszystko w jednym miejscu. Dziękujemy za poświęcony czas i wiedzę!
bitsmack
@ Pål-Kristian Engstad Dokładnie. Powinien był to wyjaśnić. Mogę edytować odpowiedź, kiedy mam wolny czas, aby osoby kopiujące i wklejające kod były bezpieczne.
Fotis Panagiotopoulos
5
Kora-ms w przeciwieństwie do ramion pełnowymiarowych, użyj tabeli wektorów. Nie mają również trybów i rejestrów bankowych. A w przypadku zdarzeń / przerwań są one zgodne ze standardem kodowania ARM. Co oznacza absolutne minimum, którego potrzebujesz, jednak decydujesz się na to, aby pierwsze słowo pod adresem zero było wartością początkową wskaźnika stosu, a drugie słowo to adres, na który należy rozgałęzić się po zresetowaniu. Bardzo łatwe do wykonania przy użyciu dyrektyw montażowych.
.globl _start
_start:.word 0x20001000.word main
Ale znowu możesz robić, co chcesz, o ile pierwsze dwa słowa mają odpowiednie wartości. Zauważ, że adres kciuka do rozgałęzienia ma ustawiony lsbit. To nie jest tak naprawdę część adresu, to tylko wskazuje, że jesteśmy (pozostajemy) w trybie kciuka.
Musisz zużywać te cztery bajty, ale jeśli masz jakiś inny kod, którego używasz do ustawiania wskaźnika stosu, nie musisz używać tabeli wektorów, załaduje to, co tam umieścisz, a następnie zawsze możesz to zmienić. Jest tylko jeden wskaźnik stosu, ale nie tak jak ramiona pełnowymiarowe / starsze.
Słowo „startup” jest bardzo niejasne, więc mógłbym opisać je już tymi dyrektywami lub zajęłoby ci wiele tysięcy wierszy kodu C, aby zakończyć uruchamianie mikrokontrolera w zależności od tego, co miałeś na myśli.
W przypadku STM32 musisz włączyć urządzenia peryferyjne, których chcesz używać, musisz skonfigurować je tak, aby działały i tak dalej. Tak naprawdę nie różni się niczym od żadnego innego mikrokontrolera, z tym wyjątkiem, że każdy dostawca i rodzina produktów ma inną logikę i inicjuje się w inny sposób.
Pliki startowe pochodzące od producenta są zwykle zaprojektowane do obsługi środowiska kompilatora C. Będzie to obejmowało całą masę rzeczy związanych z konfigurowaniem mapy pamięci, zerowaniem inicjalizacji pamięci, inicjowaniem zmiennych i konfigurowaniem uruchamiania (wektor resetowania).
Niektóre pliki startowe obejmują również konfigurację wektorów przerwań i kontrolera przerwań, chociaż niektóre środowiska, z którymi pracowałem, mają to w osobnym pliku języka asemblera.
Czasami w pliku startowym widać złożoność, ponieważ obsługiwane są różne modele oparte na architekturze procesora. Modele można nazwać „kompaktowymi” i „dużymi”.
Minimalny sposób, o który prosisz, będzie prawie całkowicie zależał od tego, czego potrzebujesz. Tak naprawdę sprowadza się to do pełnego zrozumienia architektury, potrzebnego środowiska i sposobu działania platformy. Następnie możesz skrócić pliki dostarczone przez dostawcę, aby dostosować je do swoich potrzeb, LUB napisać własne od zera.
Ale wszystko to powiedziawszy, jeśli zamierzasz napisać kod w C, najlepiej zostawić kod startowy w spokoju i po prostu skonfigurować elementy dla modelu programowania i skoncentrować swój kod jak na main ().
Odpowiedzi:
Możesz nie chcieć używać kodu startowego dostarczonego przez dostawcę. Jest kilka osób, które tak robią:
Utwórz bardziej wydajny lub mniej rozdęty kod. Mają specjalny wymóg, że kod dostawcy nie spełnia. Chcesz wiedzieć, jak działają rzeczy. Chcesz jakiegoś uniwersalnego kodu, który będzie używany w wielu różnych MCU. Chcesz mieć całkowitą kontrolę nad sobą. itp..
Poniższe informacje dotyczą tylko programów w języku C (bez C ++, wyjątków itp.) I mikrokontrolerów Cortex M (niezależnie od marki / modelu). Zakładam również, że używasz GCC, chociaż może nie być żadnej różnicy z innymi kompilatorami. Wreszcie używam newlib.
Skrypt Linkera
Pierwszą rzeczą do zrobienia jest stworzenie skryptu linkera. Musisz powiedzieć swojemu kompilatorowi, jak uporządkować rzeczy w pamięci. Nie będę wchodził w szczegóły dotyczące skryptu linkera, ponieważ jest to temat sam w sobie.
Możesz bezpośrednio użyć dostarczonego skryptu linkera. Kilka rzeczy do zapamiętania:
To jest uproszczona wersja skryptu linkera, którego używam. Podczas usuwania kodu mogę wprowadzać błędy do kodu, proszę dokładnie to sprawdzić.
Ponieważ używam go do innych MCU niż ty, musisz zmienić układ MEMORY, aby pasował do twojego.
Może być konieczna zmiana bibliotek połączonych poniżej, aby łączyły się z własnymi. Tutaj łączy się z newlib.
Tabela wektorów
Musisz dołączyć do kodu tabelę wektorów. Jest to po prostu tabela wskaźników wskaźników funkcji, do której sprzęt przeskoczy automatycznie w przypadku przerwania. Jest to dość łatwe do zrobienia w C.
Spójrz na następujący plik. Dotyczy to MCU STM32F103C8, ale bardzo łatwo można go dostosować do swoich potrzeb.
Co tu się dzieje. - Najpierw deklaruję moją funkcję _start, aby można było jej użyć poniżej. - Deklaruję domyślną procedurę obsługi dla wszystkich przerwań i procedurę resetowania - Deklaruję wszystkie procedury obsługi przerwań potrzebne dla mojego MCU. Zauważ, że te funkcje są tylko aliasami do domyślnego modułu obsługi, tzn. Gdy dowolna z nich zostanie wywołana, zamiast tego zostanie wywołany domyślny moduł obsługi. Są one również deklarowane jako tydzień, więc możesz zastąpić je kodem. Jeśli potrzebujesz któregokolwiek z programów obsługi, ponownie umieść go w kodzie, a kod zostanie połączony. Jeśli nie potrzebujesz żadnego z nich, jest po prostu domyślny i nie musisz nic robić. Domyślny moduł obsługi powinien mieć taką strukturę, że jeśli aplikacja potrzebuje modułu obsługi, ale go nie zaimplementujesz, pomoże ci to w debugowaniu kodu lub w odzyskaniu systemu, jeśli jest na wolności. - Otrzymuję symbol __stack zadeklarowany w skrypcie linkera. Jest potrzebny w tabeli wektorów. - Definiuję sam stół. Zauważ, że pierwszy wpis jest wskaźnikiem na początku stosu, a pozostałe są wskaźnikami do procedur obsługi. - Wreszcie zapewniam prostą implementację domyślnego modułu obsługi i modułu resetowania. Zauważ, że procedura resetowania jest wywoływana po resecie i wywołuje kod startowy.
Należy pamiętać, że atrybut ((sekcja ())) w tabeli wektorów jest absolutnie potrzebny, aby linker umieścił tabelę we właściwej pozycji (zwykle adres 0x00000000).
Jakie modyfikacje są potrzebne w powyższym pliku.
Połączenia systemowe
Ponieważ używam newlib, wymaga to implementacji niektórych funkcji. Możesz zaimplementować printf, scanf itp., Ale nie są one potrzebne. Osobiście zapewniam tylko następujące elementy:
_sbrk, którego potrzebuje Malloc. (Nie wymaga modyfikacji)
_exit, który nie jest potrzebny, ale podoba mi się ten pomysł. (Konieczne może być jedynie zmodyfikowanie uwzględnienia CMSIS).
Kod startowy
Wreszcie kod startowy!
Co tu się dzieje.
Mniej więcej to jest to.
źródło
__initialize_data()
i__initialize_bss()
wcześniej niż ty, mimo że będzie działać w zwolnionym tempie. W przeciwnym razie musisz się upewnić, żeSystemInit()
twojeFlash_*()
procedury w ogóle nie używają globałów.Kora-ms w przeciwieństwie do ramion pełnowymiarowych, użyj tabeli wektorów. Nie mają również trybów i rejestrów bankowych. A w przypadku zdarzeń / przerwań są one zgodne ze standardem kodowania ARM. Co oznacza absolutne minimum, którego potrzebujesz, jednak decydujesz się na to, aby pierwsze słowo pod adresem zero było wartością początkową wskaźnika stosu, a drugie słowo to adres, na który należy rozgałęzić się po zresetowaniu. Bardzo łatwe do wykonania przy użyciu dyrektyw montażowych.
Ale znowu możesz robić, co chcesz, o ile pierwsze dwa słowa mają odpowiednie wartości. Zauważ, że adres kciuka do rozgałęzienia ma ustawiony lsbit. To nie jest tak naprawdę część adresu, to tylko wskazuje, że jesteśmy (pozostajemy) w trybie kciuka.
Musisz zużywać te cztery bajty, ale jeśli masz jakiś inny kod, którego używasz do ustawiania wskaźnika stosu, nie musisz używać tabeli wektorów, załaduje to, co tam umieścisz, a następnie zawsze możesz to zmienić. Jest tylko jeden wskaźnik stosu, ale nie tak jak ramiona pełnowymiarowe / starsze.
Słowo „startup” jest bardzo niejasne, więc mógłbym opisać je już tymi dyrektywami lub zajęłoby ci wiele tysięcy wierszy kodu C, aby zakończyć uruchamianie mikrokontrolera w zależności od tego, co miałeś na myśli.
W przypadku STM32 musisz włączyć urządzenia peryferyjne, których chcesz używać, musisz skonfigurować je tak, aby działały i tak dalej. Tak naprawdę nie różni się niczym od żadnego innego mikrokontrolera, z tym wyjątkiem, że każdy dostawca i rodzina produktów ma inną logikę i inicjuje się w inny sposób.
źródło
Pliki startowe pochodzące od producenta są zwykle zaprojektowane do obsługi środowiska kompilatora C. Będzie to obejmowało całą masę rzeczy związanych z konfigurowaniem mapy pamięci, zerowaniem inicjalizacji pamięci, inicjowaniem zmiennych i konfigurowaniem uruchamiania (wektor resetowania).
Niektóre pliki startowe obejmują również konfigurację wektorów przerwań i kontrolera przerwań, chociaż niektóre środowiska, z którymi pracowałem, mają to w osobnym pliku języka asemblera.
Czasami w pliku startowym widać złożoność, ponieważ obsługiwane są różne modele oparte na architekturze procesora. Modele można nazwać „kompaktowymi” i „dużymi”.
Minimalny sposób, o który prosisz, będzie prawie całkowicie zależał od tego, czego potrzebujesz. Tak naprawdę sprowadza się to do pełnego zrozumienia architektury, potrzebnego środowiska i sposobu działania platformy. Następnie możesz skrócić pliki dostarczone przez dostawcę, aby dostosować je do swoich potrzeb, LUB napisać własne od zera.
Ale wszystko to powiedziawszy, jeśli zamierzasz napisać kod w C, najlepiej zostawić kod startowy w spokoju i po prostu skonfigurować elementy dla modelu programowania i skoncentrować swój kod jak na main ().
źródło