ต้องใช้รหัสขั้นต่ำในการเริ่มต้น STM32F4 หรือไม่


14

ต้องใช้วิธีที่มีประสิทธิภาพ / รหัสน้อยที่สุดในการเริ่มต้นใช้งาน STM32F4 อย่างไร ไฟล์เริ่มต้นที่มาจาก ST ดูเหมือนจะมีรหัสที่ไม่จำเป็นจำนวนมาก


ลบสิ่งที่คุณเห็นว่า "ไม่จำเป็น" และลองเรียกใช้ ...
Tyler

1
รหัสผู้ขายชิปพยายามที่จะเป็นหนึ่งขนาดเหมาะกับทุกคนซึ่งหมายความว่ามันเหมาะกับไม่มีใครดี พวกเขาจะถูกเปิดเผยโดยคำจำกัดความเพราะพวกเขาพยายามที่จะจัดการทุกกรณีการใช้งานที่สำคัญสำหรับอุปกรณ์ต่อพ่วงและคุณสมบัติทั้งหมดที่พวกเขายินดีที่จะสนับสนุน ใช้รหัสของพวกเขาและคุณจะได้รับประโยชน์จากการสนับสนุนจากพวกเขาและคนอื่น ๆ ออนไลน์ที่ใช้รหัสนั้น ไปตามทางของคุณและคุณจะได้ประโยชน์จากขนาดและความเร็ว แต่ส่วนใหญ่คุณจะประดิษฐ์ล้อเลื่อนนั้นใหม่
old_timer

หรืออย่างที่ไทเลอร์พูดให้ตัดสิ่งที่คุณไม่ต้องการ / ต้องการ
old_timer

คำตอบ:


25

คุณอาจไม่ต้องการใช้รหัสเริ่มต้นที่ผู้ขายระบุ มีคน reassons ไม่กี่คนที่ทำสิ่งนี้:

สร้างโค้ดที่มีประสิทธิภาพหรือป่องน้อยลง มีข้อกำหนดพิเศษที่รหัสผู้ขายไม่เป็นไปตามข้อกำหนด คุณต้องการที่จะรู้ว่าสิ่งที่ทำงาน คุณต้องการรหัสสากลบางอย่างเพื่อใช้ใน MCU ที่แตกต่างกัน คุณต้องการการควบคุมทั้งหมดเหนือกระบวนการ ฯลฯ ..

ข้อมูลต่อไปนี้ใช้กับโปรแกรม C เท่านั้น (ไม่มี C ++, ข้อยกเว้นอื่น ๆ ) และไมโครคอนโทรลเลอร์ Cortex M (ไม่คำนึงถึงยี่ห้อ / รุ่น) นอกจากนี้ฉันคิดว่าคุณใช้ GCC แม้ว่าอาจไม่มีความแตกต่างเล็กน้อยกับคอมไพเลอร์อื่น ในที่สุดฉันก็ใช้ newlib

สคริปต์ Linker

สิ่งแรกที่ต้องทำคือการสร้างสคริปต์เชื่อมโยง คุณต้องบอกคอมไพเลอร์ของคุณถึงวิธีการจัดเรียงสิ่งต่าง ๆ ในหน่วยความจำ ฉันจะไม่รับรายละเอียดเกี่ยวกับสคริปต์ตัวเชื่อมโยงเนื่องจากเป็นหัวข้อของตัวเอง

/*
 * 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)

การแก้ไขใดที่จำเป็นสำหรับไฟล์ด้านบน

  • รวมไฟล์ CMSIS ของ MCU ของคุณ
  • หากคุณแก้ไขสคริปต์ตัวลิงก์ให้เปลี่ยนชื่อส่วน
  • เปลี่ยนรายการตารางเวกเตอร์เพื่อให้ตรงกับ MCU ของคุณ
  • เปลี่ยนต้นแบบตัวจัดการเพื่อจับคู่ MCU ของคุณ

ระบบโทร

เนื่องจากฉันใช้ 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;
}

เกิดอะไรขึ้นที่นี่

  • ก่อนอื่นฉันกำหนดค่าตัวควบคุมแฟลชตามที่ MCU ของฉันต้องการก่อนที่จะเปลี่ยนความถี่ คุณสามารถเพิ่มพื้นฐานและจำเป็นสำหรับรหัสฮาร์ดแวร์ของคุณที่นี่ โปรดทราบว่ารหัสที่วางไว้ที่นี่ไม่ควรเข้าถึง globals ใด ๆ ใน RAM เนื่องจากยังไม่เริ่มต้น นอกจากนี้โปรดทราบว่า MCU ยังคงทำงานที่ความถี่ต่ำดังนั้นจึงเรียกเฉพาะที่จำเป็นเท่านั้น
  • ฉันจะเรียกใช้ฟังก์ชัน CMSIS SystemInit () นี่มันค่อนข้างพกพาฉันจึงใช้มัน ส่วนใหญ่จะจัดการกับแกนไม่ใช่ MCU ot ตัวเองในการใช้งานเฉพาะของฉันมันช่วยให้ PLL และตั้ง MCU เป็นความถี่สูงสุดท้าย คุณอาจแทนที่ด้วยรหัสที่มีประสิทธิภาพมากขึ้นของคุณ แต่มันไม่ใช่เรื่องใหญ่
  • ขั้นตอนต่อไปในตอนนี้ว่า MCU เร็วแล้วคือการเริ่มต้น RAM ตรงไปตรงมาสวย
  • MCU กำลังทำงานและทำงานตามปกติในขณะนี้ ฉันเพิ่งเรียกฟังก์ชั่น CMSIS SystemCoreClockUpdate () ตามที่ฉันใช้ในรหัสของฉันตัวแปร SystemCoreClock แต่ไม่จำเป็นต้องใช้เพียงแค่การตั้งค่าของฉัน
  • ในที่สุดฉันก็เรียกฟังก์ชั่นหลัก แอปพลิเคชันของคุณดำเนินการตามปกติ
  • หากผลตอบแทนหลักการเรียกไปยัง _exit () เป็นแนวปฏิบัติที่ดีเพื่อเริ่มระบบของคุณใหม่

มากหรือน้อยนี่ก็คือ


4
เนื่องจากความยาวของคำตอบอาจดูน่ากลัว ในขณะที่พยายามเข้าใจว่าคุณอาจต้องต่อสู้กับ toolchain ของคุณเพื่อทำสิ่งที่คุณทำ ไม่ต้องกังวลในที่สุดคุณจะเข้าใจว่าโค้ดด้านบนและใช้งานได้ง่ายและหลากหลายคืออะไร คุณอาจจะสามารถพอร์ตบน ARM MCU ใด ๆ ในเวลาเย็นเมื่อคุณเข้าใจว่าสิ่งต่าง ๆ ทำงานอย่างไร หรือคุณอาจปรับปรุงมันให้ตรงกับความต้องการส่วนตัวของคุณเองได้อย่างง่ายดาย
Fotis Panagiotopoulos

ฉันคิดว่าคุณอาจต้องการโทร__initialize_data()และ__initialize_bss()เร็วกว่านั้นถึงแม้ว่ามันจะทำงานด้วยความเร็วต่ำ มิฉะนั้นคุณต้องทำให้แน่ใจว่าSystemInit()และFlash_*()กิจวัตรของคุณไม่ได้ใช้กลมทั้งหมด
Pål-Kristian Engstad

นั่นเป็นมากกว่าที่ฉันจะถามได้! ขอบคุณสำหรับคำตอบอย่างละเอียด!
John

มันดีจริงๆที่มีทั้งหมดนี้ในที่เดียว ขอบคุณสำหรับเวลาและความรู้ของคุณ!
bitsmack

@ Pål-Kristian Engstad อย่างแน่นอน ควรทำให้ชัดเจนยิ่งขึ้น ฉันอาจแก้ไขคำตอบเมื่อฉันมีเวลาว่างดังนั้นผู้ที่คัดลอกโค้ดจะปลอดภัย
Fotis Panagiotopoulos

5

เยื่อหุ้มสมอง-ms ซึ่งแตกต่างจากแขนเต็มขนาดให้ใช้ตารางเวกเตอร์ พวกเขายังไม่มีโหมดและการลงทะเบียนที่เป็นธนาคาร และสำหรับเหตุการณ์ / การขัดจังหวะพวกเขาเป็นไปตามมาตรฐานการเข้ารหัส ARM ซึ่งหมายความว่าขั้นต่ำเปล่าที่คุณต้องการ แต่คุณเลือกที่จะรับมันมีคำแรกที่ศูนย์ที่อยู่คือค่าเริ่มต้นสำหรับตัวชี้สแต็คและคำที่สองคือที่อยู่ที่สาขาที่จะรีเซ็ต ง่ายมากที่จะใช้คำสั่งการชุมนุม

.globl _start
_start:
.word 0x20001000
.word main

แต่อีกครั้งคุณสามารถทำสิ่งที่คุณต้องการตราบใดที่สองคำแรกมีค่าที่เหมาะสม โปรดทราบว่าที่อยู่หัวแม่มือสำหรับการแยกมีชุด lsbit มันไม่ได้เป็นส่วนหนึ่งของที่อยู่เพียงแค่บ่งบอกว่าเราอยู่ในโหมดใช้นิ้วหัวแม่มือ

คุณต้องใช้สี่ไบต์เหล่านั้นกับบางอย่าง แต่ถ้าคุณมีรหัสอื่นที่คุณใช้ในการตั้งค่าตัวชี้สแต็กคุณไม่จำเป็นต้องใช้ตารางเวกเตอร์มันจะโหลดสิ่งที่คุณใส่ที่นั่นแล้วคุณสามารถเปลี่ยนได้ มีเพียงหนึ่งสแต็คพอยเตอร์ แต่ไม่เหมือนแขนที่มีขนาดเต็ม / ใหญ่กว่า

การเริ่มต้นคำนั้นคลุมเครือมากดังนั้นฉันสามารถครอบคลุมมันไปแล้วด้วยคำสั่งเหล่านั้นหรืออาจใช้รหัส C หลายพันบรรทัดให้คุณเริ่มไมโครคอนโทรลเลอร์ของคุณให้เสร็จสิ้นขึ้นอยู่กับสิ่งที่คุณหมายถึง

Esp กับ STM32 ที่คุณต้องเปิดใช้งานอุปกรณ์ต่อพ่วงที่คุณต้องการคุณต้องกำหนดค่าสิ่งที่คุณต้องการให้พวกเขาทำ ไม่แตกต่างจากไมโครคอนโทรลเลอร์อื่น ๆ ยกเว้นผู้จำหน่ายและตระกูลผลิตภัณฑ์แต่ละรายมีตรรกะที่แตกต่างกันและเริ่มต้นวิธีที่แตกต่างกัน


2

ไฟล์เริ่มต้นที่มาจากผู้ผลิตได้รับการออกแบบตามปกติเพื่อรองรับสภาพแวดล้อมคอมไพเลอร์ C ซึ่งจะรวมถึงสิ่งต่าง ๆ มากมายที่เกี่ยวข้องกับการตั้งค่าการแมปหน่วยความจำการกำหนดค่าเริ่มต้นของหน่วยความจำการเตรียมใช้งานตัวแปรและการตั้งค่าเริ่มต้น (รีเซ็ตเวกเตอร์)

ไฟล์เริ่มต้นบางไฟล์จะรวมถึงการตั้งค่าเวกเตอร์อินเตอร์รัปต์และคอนโทรลเลอร์อินเตอร์รัปต์แม้ว่าสภาพแวดล้อมบางอย่างที่ฉันเคยทำงานด้วยจะมีไฟล์ภาษาแอสเซมบลีแยกต่างหาก

บางครั้งความซับซ้อนจะเห็นในไฟล์เริ่มต้นเนื่องจากรุ่นต่าง ๆ ได้รับการสนับสนุนตามสถาปัตยกรรม CPU แบบจำลองอาจมีชื่อต่าง ๆ เช่น "กะทัดรัด" และ "ใหญ่"

น้อยที่สุดในวิธีที่คุณถามจะถูกขับเคลื่อนเกือบทั้งหมดตามที่คุณต้องการ ดังนั้นจึงเป็นการทำความเข้าใจสถาปัตยกรรมของคุณอย่างสมบูรณ์สภาพแวดล้อมที่ต้องการและวิธีการทำงานของแพลตฟอร์มของคุณ จากนั้นคุณสามารถตัดทอนไฟล์ของผู้จำหน่ายตามความต้องการของคุณหรือเขียนเองตั้งแต่ต้น

แต่ทั้งหมดที่กล่าวว่าถ้าคุณตั้งใจจะเขียนโค้ดใน C คุณควรปิดโค้ดเริ่มต้นเพียงอย่างเดียวและตั้งค่าสำหรับโมเดลการเขียนโปรแกรมและจดจ่อกับโค้ดของคุณโดยเริ่มต้นที่ main ()

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.