Mã tối thiểu cần thiết để khởi động STM32F4?


14

Cách hiệu quả nhất / mã tối thiểu cần thiết để khởi động STM32F4 là gì? Các tập tin khởi động đến từ ST dường như có rất nhiều mã không cần thiết.


Xóa những gì bạn cho là "không cần thiết" và cố gắng chạy nó ...
Tyler

1
Mã nhà cung cấp chip cố gắng trở thành một kích thước phù hợp với tất cả, điều đó có nghĩa là nó không phù hợp với ai. Theo định nghĩa, chúng sẽ luôn bị đầy hơi bởi vì chúng đang cố gắng xử lý tất cả các trường hợp sử dụng chính cho tất cả các thiết bị ngoại vi và tính năng mà chúng sẵn sàng hỗ trợ. Sử dụng mã của họ và bạn được hưởng lợi từ hỗ trợ từ họ và những người khác trực tuyến sử dụng mã đó. Đi theo cách riêng của bạn và bạn được hưởng lợi từ kích thước và tốc độ, nhưng chủ yếu là để bạn phát minh lại bánh xe đó.
old_timer

Hoặc như Tyler nói, hãy cắt bỏ những thứ bạn không muốn / cần.

Câu trả lời:


25

Bạn có thể không muốn sử dụng mã khởi động do nhà cung cấp cung cấp. Có một vài người tái xác nhận làm điều này:

Tạo mã hiệu quả hơn, hoặc ít mã hóa hơn. Có một yêu cầu đặc biệt mà mã nhà cung cấp không đáp ứng. Bạn muốn biết làm thế nào công cụ làm việc. Bạn muốn một số loại mã phổ quát, để sử dụng trong nhiều MCU khác nhau. Bạn muốn kiểm soát toàn bộ, qua bạn quá trình. Vân vân..

Những điều sau đây chỉ áp dụng cho các chương trình C (không có C ++, ngoại lệ, v.v.) và vi điều khiển Cortex M (không phân biệt kiểu dáng / kiểu máy). Ngoài ra tôi giả sử rằng bạn sử dụng GCC, mặc dù có thể không có hoặc có chút khác biệt với các trình biên dịch khác. Cuối cùng tôi sử dụng newlib.

Tập lệnh liên kết

Điều đầu tiên cần làm là tạo một tập lệnh liên kết. Bạn phải nói với trình biên dịch của bạn cách sắp xếp mọi thứ trong bộ nhớ. Tôi sẽ không nhận được thông tin chi tiết về tập lệnh liên kết, vì nó là một chủ đề riêng.

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

}

Bạn có thể trực tiếp sử dụng tập lệnh liên kết được cung cấp. Một số điều cần lưu ý:

  • Đây là phiên bản đơn giản hóa của tập lệnh liên kết tôi sử dụng. Trong quá trình gỡ xuống, tôi có thể đưa ra các lỗi cho mã, vui lòng kiểm tra lại.

  • Vì tôi sử dụng nó cho các MCU khác ngoài bạn, bạn phải thay đổi bố cục NHỚ để phù hợp với chính bạn.

  • Bạn có thể cần phải thay đổi các thư viện được liên kết dưới đây để liên kết với các thư viện của riêng bạn. Ở đây nó liên kết với newlib.

Bảng Vector

Bạn phải bao gồm trong mã của bạn một bảng vector. Đây chỉ đơn giản là một bảng tra cứu các con trỏ hàm, phần cứng sẽ tự động nhảy đến trong trường hợp bị gián đoạn. Điều này khá dễ thực hiện trong C.

Hãy xem tập tin sau. Đây là cho MCU STM32F103C8, nhưng nó rất dễ dàng để thay đổi theo nhu cầu của bạn.

#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);
}

Chuyện gì đang xảy ra ở đây. - Đầu tiên tôi khai báo hàm _start của mình để nó có thể được sử dụng dưới đây. - Tôi khai báo trình xử lý mặc định cho tất cả các ngắt và trình xử lý đặt lại - Tôi khai báo tất cả các trình xử lý ngắt cần thiết cho MCU của tôi. Lưu ý rằng các hàm này chỉ là bí danh cho trình xử lý mặc định, tức là khi bất kỳ hàm nào trong số chúng được gọi, trình xử lý mặc định sẽ được gọi thay thế. Ngoài ra chúng được khai báo tuần, vì vậy bạn có thể ghi đè chúng bằng mã của bạn. Nếu bạn cần bất kỳ trình xử lý nào, thì bạn sẽ phân phối lại mã đó trong mã của mình và mã của bạn sẽ được liên kết. Nếu bạn không cần bất kỳ thứ gì trong số chúng, đơn giản chỉ có một cái mặc định và bạn không phải làm gì cả. Trình xử lý mặc định phải được cấu trúc như vậy, nếu ứng dụng của bạn cần một trình xử lý nhưng bạn không thực hiện nó, nó sẽ giúp bạn gỡ lỗi mã của bạn hoặc khôi phục hệ thống nếu nó không hoạt động. - Tôi nhận được biểu tượng __stack được khai báo trong tập lệnh liên kết. Nó là cần thiết trong bảng vector. - Tôi xác định bảng chính nó. Lưu ý mục nhập đầu tiên là một con trỏ đến điểm bắt đầu của ngăn xếp và các mục khác là con trỏ tới trình xử lý. - Cuối cùng tôi cung cấp một triển khai đơn giản cho trình xử lý mặc định và trình xử lý thiết lập lại. Lưu ý rằng trình xử lý thiết lập lại là trình xử lý được gọi sau khi đặt lại và gọi mã khởi động.

Hãy nhớ rằng thuộc tính ((phần ())) trong bảng vectơ là hoàn toàn cần thiết, vì vậy trình liên kết sẽ đặt bảng ở vị trí chính xác (Thông thường địa chỉ 0x00000000).

Những sửa đổi là cần thiết trên các tập tin trên.

  • Bao gồm tệp CMSIS của MCU của bạn
  • Nếu bạn sửa đổi tập lệnh liên kết, thay đổi tên phần
  • Thay đổi các mục trong bảng vectơ để phù hợp với MCU của bạn
  • Thay đổi các nguyên mẫu xử lý để phù hợp với MCU của bạn

Các cuộc gọi hệ thống

Vì tôi sử dụng newlib, nó yêu cầu bạn cung cấp việc triển khai một số chức năng. Bạn có thể thực hiện printf, scanf, v.v., nhưng chúng không cần thiết. Cá nhân tôi chỉ cung cấp như sau:

_sbrk cần thiết bởi malloc. (Không cần sửa đổi)

#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, không cần thiết, nhưng tôi thích ý tưởng đó. (Bạn có thể chỉ cần sửa đổi CMSIS bao gồm).

#include <sys/types.h>
#include <errno.h>
#include "stm32f10x.h"


void __attribute__((noreturn, used)) _exit(int code)
{
    (void) code;

    NVIC_SystemReset();

    while(1);
}

Mã khởi nghiệp

Cuối cùng là mã khởi động!

#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;
}

Chuyện gì đang xảy ra ở đây.

  • Đầu tiên tôi định cấu hình bộ điều khiển Flash, vì MCU của tôi yêu cầu trước khi thay đổi tần số. Bạn có thể thêm bất kỳ rất cơ bản và cần thiết cho mã phần cứng của bạn ở đây. Lưu ý rằng mã được đặt ở đây không nên truy cập bất kỳ toàn cầu nào trong RAM, vì chúng chưa được khởi tạo. Cũng lưu ý rằng MCU vẫn hoạt động ở tần số thấp, vì vậy chỉ gọi những thứ hoàn toàn cần thiết.
  • Sau đó, tôi gọi hàm CMSIS SystemInit (). Cái này hơi di động, đó là lý do tại sao tôi sử dụng nó. Nó chủ yếu xử lý lõi chứ không phải MCU, trong các triển khai cụ thể của tôi, nó chỉ kích hoạt PLL và đặt MCU ở tần số cao cuối cùng. Bạn có thể thay thế nó bằng mã hiệu quả hơn, nhưng nó không phải là vấn đề lớn.
  • Bước tiếp theo, bây giờ MCU nhanh, là khởi tạo RAM. Khá đơn giản.
  • MCU hiện đang hoạt động bình thường. Tôi chỉ gọi hàm CMSIS SystemCoreClockUpdate (), vì tôi sử dụng trong mã của mình là biến SystemCoreClock, nhưng nó không cần thiết, chỉ là sở thích của tôi.
  • Cuối cùng tôi gọi hàm chính. Ứng dụng của bạn bây giờ thực thi bình thường.
  • Nếu trả về chính, một cuộc gọi đến _exit () là một cách thực hành tốt, để khởi động lại hệ thống của bạn.

Nhiều hay ít đây là nó.


4
Do độ dài của câu trả lời có vẻ đáng sợ. Ngoài ra, trong khi cố gắng hiểu điều đó, bạn có thể phải chiến đấu với chuỗi công cụ của mình để khiến nó làm những gì bạn làm. Đừng lo lắng, cuối cùng bạn sẽ hiểu mã đơn giản và linh hoạt như thế nào. Bạn có thể chuyển nó trên bất kỳ MCU ARM nào, chỉ trong một buổi tối khi bạn hiểu cách mọi thứ hoạt động. Hoặc bạn có thể tăng cường nó đáp ứng nhu cầu cá nhân của riêng bạn một cách dễ dàng.
Fotis Panagiotopoulos

Tôi nghĩ rằng bạn có thể muốn gọi __initialize_data()__initialize_bss()sớm hơn bạn, mặc dù điều đó sẽ chạy ở tốc độ chậm. Mặt khác, bạn phải đảm bảo rằng SystemInit()Flash_*()thói quen của bạn không sử dụng toàn cầu.
Pal-Kristian Engstad

Đó là nhiều hơn tôi có thể yêu cầu! Cảm ơn các câu trả lời chi tiết!
John

Đó là thực sự tốt đẹp để có tất cả điều này ở một nơi. Cảm ơn thời gian và kiến ​​thức của bạn!
bitsmack 28/03/2016

@ Pål-Kristian Engstad Chính xác. Nên đã làm cho nó rõ ràng hơn. Tôi có thể chỉnh sửa câu trả lời khi có thời gian rảnh, vì vậy những bản sao chép mã đó là an toàn.
Fotis Panagiotopoulos

5

Vỏ não-ms không giống như cánh tay có kích thước đầy đủ, sử dụng bảng vectơ. Họ cũng không có chế độ và thanh ghi bị ràng buộc. Và đối với các sự kiện / gián đoạn, chúng tuân thủ tiêu chuẩn mã hóa ARM. Điều đó có nghĩa là mức tối thiểu bạn cần, tuy nhiên bạn chọn để có nó, có từ đầu tiên tại địa chỉ 0 là giá trị ban đầu cho con trỏ ngăn xếp và từ thứ hai là địa chỉ để phân nhánh. Rất dễ dàng để làm bằng cách sử dụng chỉ thị lắp ráp.

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

Nhưng một lần nữa, bạn có thể làm bất cứ điều gì bạn muốn miễn là hai từ đầu tiên có giá trị đúng. Lưu ý rằng một địa chỉ ngón tay cái để phân nhánh có bộ lsbit. Nó không thực sự là một phần của địa chỉ, nó chỉ cho biết rằng chúng tôi đang ở trong chế độ ngón tay cái.

Bạn phải tiêu thụ bốn byte đó với một cái gì đó, nhưng nếu bạn có một số mã khác bạn sử dụng để đặt con trỏ ngăn xếp, bạn không phải sử dụng bảng vectơ, nó sẽ tải những gì bạn đặt ở đó thì bạn luôn có thể thay đổi nó. Chỉ có một con trỏ ngăn xếp mặc dù không giống như cánh tay có kích thước đầy đủ / cũ hơn.

Việc khởi động từ rất mơ hồ, vì vậy tôi có thể đã bao quát nó bằng các chỉ thị đó hoặc nó có thể khiến bạn mất thêm hàng ngàn dòng mã C để hoàn thành việc khởi động vi điều khiển của bạn tùy thuộc vào ý của bạn.

Esp với STM32 bạn phải bật đồng hồ cho phép các thiết bị ngoại vi bạn muốn sử dụng, bạn phải định cấu hình chúng cho những gì bạn muốn chúng làm và cứ thế. Không thực sự khác biệt so với bất kỳ vi điều khiển khác, ngoại trừ mỗi nhà cung cấp và họ sản phẩm có logic khác nhau và khởi tạo một cách khác nhau.


2

Các tệp khởi động đến từ nhà sản xuất thường được thiết kế để hỗ trợ môi trường trình biên dịch C. Điều đó sẽ bao gồm rất nhiều thứ liên quan đến việc thiết lập bản đồ bộ nhớ, không khởi tạo bộ nhớ, khởi tạo biến và thiết lập khởi động (thiết lập lại vector).

Một số tệp khởi động cũng sẽ bao gồm thiết lập các vectơ ngắt và bộ điều khiển ngắt mặc dù một số môi trường tôi đã làm việc có tệp này trong một tệp ngôn ngữ lắp ráp riêng.

Đôi khi sự phức tạp được nhìn thấy trong một tệp khởi động vì các mô hình khác nhau được hỗ trợ dựa trên kiến ​​trúc CPU. Các mô hình có thể được đặt tên những thứ như "nhỏ gọn" và "lớn".

Tối thiểu theo cách bạn đã yêu cầu sẽ được điều khiển gần như hoàn toàn dựa trên những gì bạn cần. Vì vậy, nó thực sự đi xuống để hiểu đầy đủ kiến ​​trúc của bạn, môi trường cần thiết và cách nền tảng của bạn hoạt động. Sau đó, bạn có thể cắt bớt các tập tin do nhà cung cấp cung cấp cho phù hợp với nhu cầu của bạn HOẶC tự viết từ đầu.

Nhưng, tất cả những gì đã nói, nếu bạn có ý định viết mã bằng C, tốt nhất bạn chỉ nên để mã khởi động một mình và chỉ cần thiết lập mọi thứ cho mô hình lập trình và tập trung mã của bạn là bắt đầu từ main ().

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.