Kode minimal diperlukan untuk memulai STM32F4?

14

Mana yang merupakan cara paling efisien / kode minimal yang diperlukan untuk memulai STM32F4? File startup yang berasal dari ST tampaknya memiliki banyak kode yang tidak perlu.

John
sumber
Hapus apa yang Anda anggap "tidak perlu" dan cobalah untuk menjalankannya ...
Tyler
1
kode vendor chip berusaha menjadi satu ukuran cocok untuk semua, yang berarti tidak cocok untuk siapa pun. Mereka akan selalu kembung karena definisi karena mereka berusaha menangani semua kasus penggunaan utama untuk semua periferal dan fitur yang bersedia mereka dukung. Gunakan kode mereka dan Anda mendapat manfaat dari dukungan dari mereka dan orang lain online yang menggunakan kode itu. Ikuti jalan Anda sendiri dan Anda mendapat manfaat dari ukuran dan kecepatan, tetapi sebagian besar pada Anda untuk menemukan kembali roda itu.
old_timer
Atau seperti kata Tyler, hentikan hal-hal yang tidak Anda inginkan / butuhkan.
old_timer

Jawaban:

25

Anda mungkin tidak ingin menggunakan kode permulaan yang disediakan vendor. Ada beberapa orang yang melakukan ini:

Buat kode yang lebih efisien, atau lebih sedikit kembung. Memiliki persyaratan khusus yang tidak dipenuhi oleh kode vendor. Anda ingin tahu cara kerja barang. Anda ingin semacam kode universal, digunakan di banyak MCU yang berbeda. Anda ingin kontrol penuh, atas Anda prosesnya. dll ..

Berikut ini hanya berlaku untuk program C (tidak ada C ++, pengecualian dll), dan mikrokontroler Cortex M (terlepas dari merek / model). Saya juga berasumsi bahwa Anda menggunakan GCC, meskipun mungkin tidak ada atau sedikit perbedaan dengan kompiler lain. Akhirnya saya menggunakan newlib.

Skrip Linker

Hal pertama yang harus dilakukan adalah membuat skrip tautan. Anda harus memberi tahu kompiler Anda bagaimana mengatur berbagai hal dalam memori. Saya tidak akan membahas detail tentang skrip tautan, karena ini adalah topik tersendiri.

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

}

Anda dapat langsung menggunakan skrip tautan yang disediakan. Beberapa hal yang perlu diperhatikan:

  • Ini adalah versi sederhana dari skrip tautan yang saya gunakan. Selama pengupasan, saya mungkin memperkenalkan bug pada kode, harap periksa ulang.

  • Karena saya menggunakannya untuk MCU lain selain Anda, Anda harus mengubah tata letak MEMORY agar sesuai dengan Anda sendiri.

  • Anda mungkin perlu mengubah perpustakaan yang ditautkan di bawah untuk menautkannya dengan milik Anda. Ini tautannya terhadap newlib.

Tabel Vektor

Anda harus memasukkan tabel vektor dalam kode Anda. Ini hanyalah sebuah tabel look-up dari pointer fungsi, bahwa perangkat keras akan melompat secara otomatis jika terjadi gangguan. Ini cukup mudah dilakukan di C.

Lihatlah file berikut. Ini untuk MCU STM32F103C8, tetapi sangat mudah untuk mengubah kebutuhan Anda.

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

Apa yang terjadi disini. - Pertama saya mendeklarasikan fungsi _start saya sehingga bisa digunakan di bawah. - Saya mendeklarasikan handler default untuk semua interupsi, dan reset handler - Saya menyatakan semua handler interupsi yang diperlukan untuk MCU saya. Perhatikan bahwa fungsi-fungsi ini hanya alias untuk penangan default, yaitu ketika salah satu dari mereka dipanggil, penangan default akan dipanggil sebagai gantinya. Juga mereka dinyatakan minggu, sehingga Anda dapat menimpanya dengan kode Anda. Jika Anda memerlukan salah satu penangan, maka Anda mendeklarasikannya kembali dalam kode Anda, dan kode Anda akan ditautkan. Jika Anda tidak membutuhkannya, hanya ada yang standar dan Anda tidak perlu melakukan apa pun. Handler default harus disusun sedemikian rupa, sehingga jika aplikasi Anda membutuhkan handler tetapi Anda tidak mengimplementasikannya, itu akan membantu Anda dalam mendebug kode Anda, atau memulihkan sistem jika ada di alam liar. - Saya mendapatkan simbol __stack yang dideklarasikan dalam skrip linker. Ini diperlukan dalam tabel vektor. - Saya mendefinisikan tabel itu sendiri. Perhatikan bahwa entri pertama adalah pointer ke awal stack, dan yang lainnya adalah pointer ke handler. - Akhirnya saya memberikan implementasi sederhana untuk handler default dan reset handler. Perhatikan bahwa handler reset adalah yang dipanggil setelah reset, dan yang memanggil kode startup.

Perlu diingat bahwa atribut ((bagian ())) dalam tabel vektor mutlak diperlukan, sehingga penghubung akan menempatkan tabel pada posisi yang benar (Biasanya alamat 0x00000000).

Modifikasi apa yang diperlukan pada file di atas.

  • Sertakan file CMSIS dari MCU Anda
  • Jika Anda memodifikasi skrip tautan, ubah nama bagian
  • Ubah entri tabel vektor agar sesuai dengan MCU Anda
  • Ubah prototipe penangan agar sesuai dengan MCU Anda

Panggilan Sistem

Karena saya menggunakan newlib, itu mengharuskan Anda untuk menyediakan implementasi dari beberapa fungsi. Anda dapat menerapkan printf, scanf dll, tetapi mereka tidak diperlukan. Secara pribadi saya hanya menyediakan yang berikut ini:

_sbrk yang dibutuhkan oleh malloc. (Tidak perlu modifikasi)

#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, yang tidak diperlukan, tapi saya suka idenya. (Anda mungkin hanya perlu memodifikasi CMSIS termasuk).

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


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

    NVIC_SystemReset();

    while(1);
}

Kode pembuka

Akhirnya kode start-up!

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

Apa yang terjadi disini.

  • Pertama saya mengkonfigurasi Flash controller, karena ini diperlukan oleh MCU saya, sebelum mengubah frekuensi. Anda dapat menambahkan kode yang sangat mendasar dan dibutuhkan untuk perangkat keras Anda di sini. Perhatikan bahwa kode yang ditempatkan di sini tidak boleh mengakses global dalam RAM, karena belum diinisialisasi. Perhatikan juga bahwa MCU masih beroperasi pada frekuensi rendah, jadi hanya panggil yang benar-benar dibutuhkan.
  • Lalu saya memanggil fungsi CMSIS SystemInit (). Ini agak portabel, itu sebabnya saya menggunakannya. Ini sebagian besar menangani inti, bukan MCU ot self, dalam implementasi spesifik saya itu hanya memungkinkan PLL, dan menetapkan MCU ke frekuensi tinggi akhirnya. Anda dapat menggantinya dengan kode yang lebih efisien, tetapi ini bukan masalah besar.
  • Langkah selanjutnya, sekarang MCU cepat, adalah menginisialisasi RAM. Cukup mudah.
  • MCU sudah aktif sekarang. Saya hanya memanggil fungsi CMSIS SystemCoreClockUpdate (), seperti yang saya gunakan dalam kode saya variabel SystemCoreClock, tetapi tidak diperlukan, hanya preferensi saya.
  • Akhirnya saya memanggil fungsi utama. Aplikasi Anda sekarang dijalankan secara normal.
  • Jika main kembali, panggilan ke _exit () adalah praktik yang baik, untuk memulai ulang sistem Anda.

Kurang lebih inilah dia.

Fotis Panagiotopoulos
sumber
4
Karena panjangnya jawaban itu mungkin tampak menakutkan. Juga ketika mencoba memahami itu, Anda mungkin harus memerangi rantai alat untuk membuatnya melakukan apa yang Anda lakukan. Jangan khawatir, akhirnya Anda akan mengerti betapa sederhana dan fleksibelnya kode di atas. Anda mungkin dapat mem-portingnya pada MCU ARM apa pun, hanya dalam waktu malam ketika Anda memahami cara kerja berbagai hal. Atau Anda dapat meningkatkannya memuaskan kebutuhan pribadi Anda dengan mudah.
Fotis Panagiotopoulos
Saya pikir Anda mungkin ingin menelepon __initialize_data()dan __initialize_bss()lebih awal dari yang Anda lakukan, meskipun itu akan berjalan dengan kecepatan lambat. Kalau tidak, Anda harus memastikan itu SystemInit()dan Flash_*()rutinitas Anda tidak menggunakan global sama sekali.
Pål-Kristian Engstad
Itu lebih dari yang bisa saya tanyakan! Terima kasih atas jawaban terinci!
John
Hal ini benar-benar baik untuk memiliki semua ini dalam satu tempat. Terima kasih atas waktu dan pengetahuan Anda!
bitsmack
@ Pål-Kristian Engstad Persis. Seharusnya membuatnya lebih jelas. Saya dapat mengedit jawaban ketika saya memiliki waktu luang, jadi mereka yang menyalin kode itu aman.
Fotis Panagiotopoulos
5

Korteks-ms tidak seperti lengan berukuran penuh, menggunakan tabel vektor. Mereka juga tidak memiliki mode dan register yang membelok. Dan untuk acara / interupsi mereka sesuai dengan standar pengkodean ARM. Yang berarti minimum yang Anda butuhkan, tetapi Anda memilih untuk mendapatkannya ada kata pertama di address nol adalah nilai awal untuk penunjuk tumpukan, dan kata kedua adalah alamat untuk bercabang untuk di reset. Sangat mudah dilakukan dengan menggunakan arahan perakitan.

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

Tetapi sekali lagi Anda dapat melakukan apa pun yang Anda inginkan selama dua kata pertama memiliki nilai yang benar. Perhatikan bahwa alamat ibu jari untuk percabangan memiliki set lsbit. Itu bukan bagian dari alamat, itu hanya menunjukkan bahwa kita (tetap) dalam mode jempol.

Anda harus mengkonsumsi keempat byte tersebut dengan sesuatu, tetapi jika Anda memiliki beberapa kode lain yang Anda gunakan untuk mengatur stack pointer Anda tidak harus menggunakan tabel vektor, itu akan memuat apa yang Anda letakkan di sana maka Anda selalu dapat mengubahnya. Hanya ada satu stack pointer meskipun tidak seperti lengan berukuran penuh / lebih tua.

Kata startup sangat kabur, jadi saya bisa membahasnya dengan arahan-arahan itu atau Anda mungkin membutuhkan ribuan baris kode C untuk menyelesaikan memulai mikrokontroler Anda tergantung pada apa yang Anda maksudkan.

Esp dengan STM32 Anda harus mengaktifkan clock periferal yang ingin Anda gunakan, Anda harus mengkonfigurasi mereka untuk apa yang Anda ingin mereka lakukan dan seterusnya. Tidak benar-benar berbeda dari mikrokontroler lainnya, kecuali setiap vendor dan kelompok produk memiliki logika yang berbeda dan menginisialisasi dengan cara yang berbeda.

old_timer
sumber
2

File startup yang berasal dari pabrikan biasanya dirancang untuk mendukung lingkungan kompiler C. Itu akan mencakup banyak hal yang berkaitan dengan pengaturan peta memori, nol inisialisasi memori, menginisialisasi variabel dan menyiapkan startup (reset vector).

Beberapa file startup juga akan mencakup pengaturan vektor interupsi dan pengontrol interupsi meskipun beberapa lingkungan yang pernah saya gunakan memiliki ini dalam file bahasa assembly terpisah.

Terkadang kompleksitas terlihat dalam file startup karena model yang berbeda didukung berdasarkan arsitektur CPU. Model-model itu mungkin dinamai hal-hal seperti "kompak" dan "besar".

Minimal dalam cara yang Anda minta akan hampir sepenuhnya didorong pada apa yang Anda butuhkan. Jadi itu benar-benar turun untuk sepenuhnya memahami arsitektur Anda, lingkungan yang dibutuhkan dan bagaimana platform Anda bekerja. Kemudian Anda dapat memangkas file yang disediakan vendor untuk memenuhi kebutuhan Anda ATAU menulis sendiri dari awal.

Tetapi, semua yang dikatakan, jika Anda berniat untuk menulis kode dalam C Anda lebih baik meninggalkan kode startup saja dan cukup mengatur hal-hal untuk model pemrograman dan berkonsentrasi kode Anda sebagai mulai dari main ().

Michael Karas
sumber