ต้องใช้วิธีที่มีประสิทธิภาพ / รหัสน้อยที่สุดในการเริ่มต้นใช้งาน STM32F4 อย่างไร ไฟล์เริ่มต้นที่มาจาก ST ดูเหมือนจะมีรหัสที่ไม่จำเป็นจำนวนมาก
ต้องใช้วิธีที่มีประสิทธิภาพ / รหัสน้อยที่สุดในการเริ่มต้นใช้งาน STM32F4 อย่างไร ไฟล์เริ่มต้นที่มาจาก ST ดูเหมือนจะมีรหัสที่ไม่จำเป็นจำนวนมาก
คำตอบ:
คุณอาจไม่ต้องการใช้รหัสเริ่มต้นที่ผู้ขายระบุ มีคน reassons ไม่กี่คนที่ทำสิ่งนี้:
สร้างโค้ดที่มีประสิทธิภาพหรือป่องน้อยลง มีข้อกำหนดพิเศษที่รหัสผู้ขายไม่เป็นไปตามข้อกำหนด คุณต้องการที่จะรู้ว่าสิ่งที่ทำงาน คุณต้องการรหัสสากลบางอย่างเพื่อใช้ใน MCU ที่แตกต่างกัน คุณต้องการการควบคุมทั้งหมดเหนือกระบวนการ ฯลฯ ..
ข้อมูลต่อไปนี้ใช้กับโปรแกรม C เท่านั้น (ไม่มี C ++, ข้อยกเว้นอื่น ๆ ) และไมโครคอนโทรลเลอร์ Cortex M (ไม่คำนึงถึงยี่ห้อ / รุ่น) นอกจากนี้ฉันคิดว่าคุณใช้ GCC แม้ว่าอาจไม่มีความแตกต่างเล็กน้อยกับคอมไพเลอร์อื่น ในที่สุดฉันก็ใช้ newlib
สิ่งแรกที่ต้องทำคือการสร้างสคริปต์เชื่อมโยง คุณต้องบอกคอมไพเลอร์ของคุณถึงวิธีการจัดเรียงสิ่งต่าง ๆ ในหน่วยความจำ ฉันจะไม่รับรายละเอียดเกี่ยวกับสคริปต์ตัวเชื่อมโยงเนื่องจากเป็นหัวข้อของตัวเอง
/*
* 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
}
คุณสามารถใช้สคริปต์ตัวเชื่อมโยงที่ให้ไว้โดยตรง บางสิ่งที่ควรทราบ:
นี่เป็นสคริปต์ตัวลิงก์ที่ฉันใช้ ในระหว่างการถอดสายฉันอาจใช้บั๊กกับโค้ดได้โปรดตรวจสอบอีกครั้ง
เนื่องจากฉันใช้มันสำหรับ MCUs อื่นที่ไม่ใช่คุณคุณต้องเปลี่ยนเค้าโครง MEMORY เพื่อให้เหมาะกับตัวคุณเอง
คุณอาจจำเป็นต้องเปลี่ยนไลบรารี่ที่เชื่อมโยงเพื่อลิงค์ด้วยตัวคุณเอง ที่นี่มันเชื่อมโยงกับ newlib
คุณต้องรวมตารางเวกเตอร์ในโค้ดของคุณด้วย นี่เป็นเพียงตารางตัวชี้ฟังก์ชั่นค้นหาซึ่งฮาร์ดแวร์จะข้ามไปยังอัตโนมัติในกรณีที่มีการขัดจังหวะ นี่เป็นเรื่องง่ายที่จะทำในซี
ลองดูที่ไฟล์ต่อไปนี้ นี่คือ STM32F103C8 MCU แต่มันง่ายมากที่จะเปลี่ยนความต้องการของคุณ
#include "stm32f10x.h"
#include "debug.h"
//Start-up code.
extern void __attribute__((noreturn, weak)) _start (void);
// Default interrupt handler
void __attribute__ ((section(".after_vectors"), noreturn)) __Default_Handler(void);
// Reset handler
void __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 */
void HardFault_Handler(void) __attribute__ ((interrupt, weak));
/** Memory management */
void MemManage_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** Pre-fetch fault, memory access fault */
void BusFault_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** Undefined instruction or illegal state */
void UsageFault_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 */
void DebugMon_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** Pendable request for system service */
void PendSV_Handler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** System tick timer */
void SysTick_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 */
void RTCAlarm_IRQHandler(void) __attribute__ ((interrupt, weak, alias("__Default_Handler")));
/** USB wakeup from suspend through EXTI line interrupt */
void USBWakeup_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.
extern unsigned int __stack;
// Typedef for the vector table entries.
typedef void (* const pHandler)(void);
/** STM32F103 Vector Table */
__attribute__ ((section(".vectors"), used)) pHandler vectors[] =
{
(pHandler) &__stack, // The initial stack pointer
Reset_Handler, // The reset handler
NMI_Handler, // The NMI handler
HardFault_Handler, // The hard fault handler
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
MemManage_Handler, // The MPU fault handler
BusFault_Handler,// The bus fault handler
UsageFault_Handler,// The usage fault handler
#else
0, 0, 0, // Reserved
#endif
0, // Reserved
0, // Reserved
0, // Reserved
0, // Reserved
SVC_Handler, // SVCall handler
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
DebugMon_Handler, // Debug monitor handler
#else
0, // Reserved
#endif
0, // Reserved
PendSV_Handler, // The PendSV handler
SysTick_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] interrupts
RTCAlarm_IRQHandler, // RTC alarm through EXTI line interrupt
USBWakeup_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);
}
เกิดอะไรขึ้นที่นี่ - ก่อนอื่นฉันประกาศฟังก์ชั่น _start ของฉันเพื่อที่จะสามารถใช้การร้องได้ - ฉันประกาศตัวจัดการเริ่มต้นสำหรับการขัดจังหวะทั้งหมดและตัวจัดการการรีเซ็ต - ฉันประกาศตัวจัดการการขัดจังหวะทั้งหมดที่จำเป็นสำหรับ MCU ของฉัน โปรดทราบว่าฟังก์ชั่นเหล่านี้เป็นเพียงนามแฝงของตัวจัดการเริ่มต้นเช่นเมื่อใด ๆ ของพวกเขาถูกเรียกตัวจัดการเริ่มต้นจะถูกเรียกแทน นอกจากนี้พวกเขาจะประกาศสัปดาห์เพื่อให้คุณสามารถแทนที่พวกเขาด้วยรหัสของคุณ หากคุณต้องการเครื่องมือจัดการใด ๆ คุณต้องประกาศใหม่ในรหัสของคุณและรหัสของคุณจะถูกเชื่อมโยง หากคุณไม่ต้องการสิ่งใดสิ่งหนึ่งมีเพียงค่าเริ่มต้นและคุณไม่ต้องทำอะไรเลย ตัวจัดการเริ่มต้นควรมีโครงสร้างเช่นนั้นหากแอปพลิเคชันของคุณต้องการตัวจัดการ แต่คุณไม่ได้ใช้มันจะช่วยคุณในการดีบั๊กโค้ดของคุณหรือกู้คืนระบบถ้ามันอยู่ในป่า - ฉันได้รับสัญลักษณ์ __stack ที่ประกาศในสคริปต์ตัวลิงก์ มันจำเป็นในตารางเวกเตอร์ - ฉันกำหนดตารางเอง หมายเหตุรายการแรกเป็นตัวชี้ไปยังจุดเริ่มต้นของสแต็กและรายการอื่น ๆ เป็นตัวชี้ไปยังตัวจัดการ - ในที่สุดฉันก็จัดเตรียมการใช้งานอย่างง่ายสำหรับตัวจัดการเริ่มต้นและตัวจัดการรีเซ็ต โปรดทราบว่าตัวจัดการการรีเซ็ตเป็นตัวที่ถูกเรียกหลังจากรีเซ็ตและเรียกรหัสเริ่มต้น
โปรดทราบว่าแอตทริบิวต์ ((ส่วน ())) ในตารางเวกเตอร์เป็นสิ่งจำเป็นอย่างยิ่งดังนั้นตัวเชื่อมโยงจะวางตารางในตำแหน่งที่ถูกต้อง (ปกติแล้วจะอยู่ 0x00000000)
การแก้ไขใดที่จำเป็นสำหรับไฟล์ด้านบน
เนื่องจากฉันใช้ newlib คุณจึงต้องเตรียมการใช้งานฟังก์ชั่นบางอย่าง คุณอาจใช้งาน printf, scanf ฯลฯ แต่ไม่จำเป็นต้องใช้ โดยส่วนตัวฉันให้เฉพาะสิ่งต่อไปนี้:
_sbrk ซึ่ง malloc ต้องการ (ไม่จำเป็นต้องดัดแปลง)
#include <sys/types.h>
#include <errno.h>
caddr_t __attribute__((used)) _sbrk(int incr)
{
extern char __heap_start__; // Defined by the linker.
extern char __heap_end__; // Defined by the linker.
static char* 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 4
if (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 ซึ่งไม่จำเป็น แต่ฉันชอบความคิด (คุณอาจต้องแก้ไข CMSIS เท่านั้น)
#include <sys/types.h>
#include <errno.h>
#include "stm32f10x.h"
void __attribute__((noreturn, used)) _exit(int code)
{
(void) code;
NVIC_SystemReset();
while(1);
}
ในที่สุดรหัสเริ่มต้นขึ้น!
#include <stdint.h>
#include "stm32f10x.h"
#include "gpio.h"
#include "flash.h"
/** Main program entry point. */
extern int main(void);
/** Exit system call. */
extern void _exit(int code);
/** Initializes the data section. */
static void __attribute__((always_inline)) __initialize_data (unsigned int* from, unsigned int* region_begin, unsigned int* region_end);
/** Initializes the BSS section. */
static void __attribute__((always_inline)) __initialize_bss (unsigned int* region_begin, unsigned int* 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);
}
static inline void __initialize_data (unsigned int* from, unsigned int* region_begin, unsigned int* region_end)
{
// Iterate and copy word by word.
// It is assumed that the pointers are word aligned.
unsigned int *p = region_begin;
while (p < region_end)
*p++ = *from++;
}
static inline void __initialize_bss (unsigned int* region_begin, unsigned int* region_end)
{
// Iterate and clear word by word.
// It is assumed that the pointers are word aligned.
unsigned int *p = region_begin;
while (p < region_end)
*p++ = 0;
}
เกิดอะไรขึ้นที่นี่
มากหรือน้อยนี่ก็คือ
__initialize_data()
และ__initialize_bss()
เร็วกว่านั้นถึงแม้ว่ามันจะทำงานด้วยความเร็วต่ำ มิฉะนั้นคุณต้องทำให้แน่ใจว่าSystemInit()
และFlash_*()
กิจวัตรของคุณไม่ได้ใช้กลมทั้งหมด
เยื่อหุ้มสมอง-ms ซึ่งแตกต่างจากแขนเต็มขนาดให้ใช้ตารางเวกเตอร์ พวกเขายังไม่มีโหมดและการลงทะเบียนที่เป็นธนาคาร และสำหรับเหตุการณ์ / การขัดจังหวะพวกเขาเป็นไปตามมาตรฐานการเข้ารหัส ARM ซึ่งหมายความว่าขั้นต่ำเปล่าที่คุณต้องการ แต่คุณเลือกที่จะรับมันมีคำแรกที่ศูนย์ที่อยู่คือค่าเริ่มต้นสำหรับตัวชี้สแต็คและคำที่สองคือที่อยู่ที่สาขาที่จะรีเซ็ต ง่ายมากที่จะใช้คำสั่งการชุมนุม
.globl _start
_start:
.word 0x20001000
.word main
แต่อีกครั้งคุณสามารถทำสิ่งที่คุณต้องการตราบใดที่สองคำแรกมีค่าที่เหมาะสม โปรดทราบว่าที่อยู่หัวแม่มือสำหรับการแยกมีชุด lsbit มันไม่ได้เป็นส่วนหนึ่งของที่อยู่เพียงแค่บ่งบอกว่าเราอยู่ในโหมดใช้นิ้วหัวแม่มือ
คุณต้องใช้สี่ไบต์เหล่านั้นกับบางอย่าง แต่ถ้าคุณมีรหัสอื่นที่คุณใช้ในการตั้งค่าตัวชี้สแต็กคุณไม่จำเป็นต้องใช้ตารางเวกเตอร์มันจะโหลดสิ่งที่คุณใส่ที่นั่นแล้วคุณสามารถเปลี่ยนได้ มีเพียงหนึ่งสแต็คพอยเตอร์ แต่ไม่เหมือนแขนที่มีขนาดเต็ม / ใหญ่กว่า
การเริ่มต้นคำนั้นคลุมเครือมากดังนั้นฉันสามารถครอบคลุมมันไปแล้วด้วยคำสั่งเหล่านั้นหรืออาจใช้รหัส C หลายพันบรรทัดให้คุณเริ่มไมโครคอนโทรลเลอร์ของคุณให้เสร็จสิ้นขึ้นอยู่กับสิ่งที่คุณหมายถึง
Esp กับ STM32 ที่คุณต้องเปิดใช้งานอุปกรณ์ต่อพ่วงที่คุณต้องการคุณต้องกำหนดค่าสิ่งที่คุณต้องการให้พวกเขาทำ ไม่แตกต่างจากไมโครคอนโทรลเลอร์อื่น ๆ ยกเว้นผู้จำหน่ายและตระกูลผลิตภัณฑ์แต่ละรายมีตรรกะที่แตกต่างกันและเริ่มต้นวิธีที่แตกต่างกัน
ไฟล์เริ่มต้นที่มาจากผู้ผลิตได้รับการออกแบบตามปกติเพื่อรองรับสภาพแวดล้อมคอมไพเลอร์ C ซึ่งจะรวมถึงสิ่งต่าง ๆ มากมายที่เกี่ยวข้องกับการตั้งค่าการแมปหน่วยความจำการกำหนดค่าเริ่มต้นของหน่วยความจำการเตรียมใช้งานตัวแปรและการตั้งค่าเริ่มต้น (รีเซ็ตเวกเตอร์)
ไฟล์เริ่มต้นบางไฟล์จะรวมถึงการตั้งค่าเวกเตอร์อินเตอร์รัปต์และคอนโทรลเลอร์อินเตอร์รัปต์แม้ว่าสภาพแวดล้อมบางอย่างที่ฉันเคยทำงานด้วยจะมีไฟล์ภาษาแอสเซมบลีแยกต่างหาก
บางครั้งความซับซ้อนจะเห็นในไฟล์เริ่มต้นเนื่องจากรุ่นต่าง ๆ ได้รับการสนับสนุนตามสถาปัตยกรรม CPU แบบจำลองอาจมีชื่อต่าง ๆ เช่น "กะทัดรัด" และ "ใหญ่"
น้อยที่สุดในวิธีที่คุณถามจะถูกขับเคลื่อนเกือบทั้งหมดตามที่คุณต้องการ ดังนั้นจึงเป็นการทำความเข้าใจสถาปัตยกรรมของคุณอย่างสมบูรณ์สภาพแวดล้อมที่ต้องการและวิธีการทำงานของแพลตฟอร์มของคุณ จากนั้นคุณสามารถตัดทอนไฟล์ของผู้จำหน่ายตามความต้องการของคุณหรือเขียนเองตั้งแต่ต้น
แต่ทั้งหมดที่กล่าวว่าถ้าคุณตั้งใจจะเขียนโค้ดใน C คุณควรปิดโค้ดเริ่มต้นเพียงอย่างเดียวและตั้งค่าสำหรับโมเดลการเขียนโปรแกรมและจดจ่อกับโค้ดของคุณโดยเริ่มต้นที่ main ()