STM32F4の起動に必要なコードは最小限ですか?


14

STM32F4の起動に必要な最も効率的な方法/最小限のコードはどれですか?STからのスタートアップファイルには、多くの不要なコードが含まれているようです。


「不要」と思われるものを削除して、実行してみてください
タイラー

1
チップベンダーのコードは、すべてに適合する1つのサイズを目指しています。つまり、誰にもうまく適合しないということです。彼らはサポートする意思があるすべての周辺機器と機能のすべての主要なユースケースを処理しようとしているので、それらは常に定義によって肥大化します。彼らのコードを使用すると、そのコードを使用する彼らや他のオンラインからのサポートの恩恵を受けることができます。独自の方法でサイズと速度のメリットを享受できますが、そのホイールを再発明するのはほとんどあなた次第です。
old_timer

またはタイラーが言ったように、あなたが欲しくない/必要なものを切り取ってください。
old_timer

回答:


25

ベンダー提供のスタートアップコードを使用したくない場合があります。これを行う理由はほとんどありません。

より効率的な、または肥大化していないコードを作成します。ベンダーコードが満たされないという特別な要件があります。ものの仕組みを知りたい。多くの異なるMCUで使用する、ある種のユニバーサルコードが必要です。プロセスを完全に制御したい。等..

以下は、Cプログラム(C ++、例外などは不可)、およびCortex Mマイクロコントローラー(メーカー/モデルに関係なく)にのみ適用されます。また、GCCを使用すると仮定しますが、他のコンパイラとの違いはまったくないか、ほとんどありません。最後に、newlibを使用します。

リンカースクリプト

最初に行うことは、リンカースクリプトを作成することです。コンパイラーにメモリー内の処理方法を指示する必要があります。リンカースクリプトはそれ自体がトピックなので、詳細については説明しません。

/*
 * Linker script.
 */ 

/* 
 * Set the output format. Currently set for Cortex M architectures,
 * may need to be modified if the library has to support other MCUs, 
 * or completelly removed.
 */
OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")

/* 
 * Just refering a function included in the vector table, and that
 * it is defined in the same file with it, so the vector table does
 * not get optimized out.
 */
EXTERN(Reset_Handler)

/*
 * ST32F103x8 memory setup.
 */
MEMORY
{
    FLASH     (rx)  : ORIGIN = 0x00000000, LENGTH = 64k
    RAM     (xrw)   : ORIGIN = 0x20000000, LENGTH = 20k
}

/*
 * Necessary group so the newlib stubs provided in the library,
 * will correctly be linked with the appropriate newlib functions,
 * and not optimized out, giving errors for undefined symbols.
 * This way the libraries can be fed to the linker in any order.
 */
GROUP(
   libgcc.a
   libg.a
   libc.a
   libm.a
   libnosys.a
 )

/* 
 * Stack start pointer. Here set to the end of the stack
 * memory, as in most architectures (including all the 
 * new ARM ones), the stack starts from the maximum address
 * and grows towards the bottom.
 */
__stack = ORIGIN(RAM) + LENGTH(RAM);

/*
 * Programm entry function. Used by the debugger only.
 */
ENTRY(_start)

/*
 * Memory Allocation Sections
 */
SECTIONS
{
    /* 
     * For normal programs should evaluate to 0, for placing the vector
     * table at the correct position.
     */
    . = ORIGIN(FLASH);

    /*
     * First link the vector table.
     */
    .vectors : ALIGN(4)
    {
        FILL(0xFF)
        __vectors_start__ = ABSOLUTE(.); 
        KEEP(*(.vectors))
        *(.after_vectors .after_vectors.*)
    } > FLASH

    /*
     * Start of text.
     */
    _text = .;

    /*
     * Text section
     */
    .text : ALIGN(4)
    {
        *(.text)
        *(.text.*)
        *(.glue_7t)
        *(.glue_7)
        *(.gcc*)
    } > FLASH

    /*
     * Arm section unwinding.
     * If removed may cause random crashes.
     */
    .ARM.extab :
    {
        *(.ARM.extab* .gnu.linkonce.armextab.*)
    } > FLASH

    /*
     * Arm stack unwinding.
     * If removed may cause random crashes.
     */
    .ARM.exidx :
    {
        __exidx_start = .;
        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
        __exidx_end = .;
    } > FLASH

    /*
     * Section used by C++ to access eh_frame.
     * Generaly not used, but it doesn't harm to be there.
     */ 
    .eh_frame_hdr :
    {
        *(.eh_frame_hdr)
    } > FLASH

    /*
     * Stack unwinding code.
     * Generaly not used, but it doesn't harm to be there.
     */ 
    .eh_frame : ONLY_IF_RO
    {
        *(.eh_frame)
    } > FLASH

    /*
     * Read-only data. Consts should also be here.
     */
    .rodata : ALIGN(4)
    {
        . = ALIGN(4);
        __rodata_start__ = .;
        *(.rodata)
        *(.rodata.*)
        . = ALIGN(4);
        __rodata_end__ = .;
    } > FLASH 

    /*
     * End of text.
     */
    _etext = .;

    /*
     * Data section.
     */
    .data : ALIGN(4)
    {
        FILL(0xFF)
        . = ALIGN(4);
        PROVIDE(__textdata__ = LOADADDR(.data));
        PROVIDE(__data_start__ = .);
        *(.data)
        *(.data.*)
        *(.ramtext)
        . = ALIGN(4);
        PROVIDE(__data_end__ = .);
    } > RAM AT > FLASH

    /*
     * BSS section.
     */
    .bss (NOLOAD) : ALIGN(4)
    {
        . = ALIGN(4);
        PROVIDE(_bss_start = .);
        __bss_start__ = .;
        *(.bss)
        *(.bss.*)
        *(COMMON)
        . = ALIGN(4);
        PROVIDE(_bss_end = .);
        __bss_end__ = .;
        PROVIDE(end = .);
    } > RAM

    /*
     * Non-initialized variables section.
     * A variable should be explicitly placed
     * here, aiming in speeding-up boot time.
     */
    .noinit (NOLOAD) : ALIGN(4)
    {
        __noinit_start__ = .;
        *(.noinit .noinit.*) 
         . = ALIGN(4) ;
        __noinit_end__ = .;   
    } > RAM

    /*
     * Heap section.
     */
    .heap (NOLOAD) :
    {
        . = ALIGN(4);
        __heap_start__ = .;
        __heap_base__ = .;
        . = ORIGIN(HEAP_RAM) + LENGTH(HEAP_RAM);
        __heap_end__ = .;
    } > RAM

}

提供されたリンカースクリプトを直接使用できます。注意するべき事柄:

  • これは、私が使用するリンカースクリプトの簡易バージョンです。削除中にコードにバグが発生する可能性がありますので、再確認してください。

  • 私はあなた以外のMCUに使用しているため、MEMORYレイアウトを自分のものに合わせて変更する必要があります。

  • 以下にリンクされているライブラリを変更して、独自のライブラリとリンクする必要がある場合があります。ここでは、newlibにリンクしています。

ベクターテーブル

コードにベクターテーブルを含める必要があります。これは単に関数ポインタのルックアップテーブルであり、ハードウェアは割り込みの場合に自動的にジャンプします。Cでこれを行うのはかなり簡単です。

次のファイルをご覧ください。これは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)に配置するため、ベクターテーブル属性((section()))は絶対に必要であることに注意してください。

上記のファイルに必要な変更。

  • MCUのCMSISファイルを含める
  • リンカースクリプトを変更する場合は、セクション名を変更します
  • 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で必要なため、まずフラッシュコントローラーを構成します。ハードウェアコードに必要な非常に基本的なものをここに追加できます。ここに配置されたコードは、まだ初期化されていないため、RAM内のグローバルにアクセスしないでください。また、MCUは低周波数で動作するため、絶対に必要なものだけを呼び出してください。
  • 次に、CMSIS関数SystemInit()を呼び出します。これはいくぶん移植性が高いため、私はそれを使用しています。これは主にMCUではなくコアを処理し、私の特定の実装ではPLLのみを有効にし、MCUを最終的な高周波に設定します。より効率的なコードで置き換えることもできますが、大したことではありません。
  • MCUが高速になった次のステップは、RAMを初期化することです。とても簡単です。
  • MCUは現在正常に稼働しています。コードでSystemCoreClock変数を使用しているため、CMSIS関数SystemCoreClockUpdate()を呼び出すだけですが、それは必要ではなく、好みだけです。
  • 最後に、メイン関数を呼び出します。これで、アプリケーションが正常に実行されます。
  • メインが戻る場合、_exit()の呼び出しは、システムを再起動するための適切な実践です。

多かれ少なかれこれです。


4
答えの長さのために、それは怖いように見えるかもしれません。また、それを理解しようとしている間、あなたが何をするかを得るためにあなたのツールチェーンと戦わなければならないかもしれません。心配しないでください。最後に、上記のコードがいかにシンプルで汎用性があるかを理解できます。物事がどのように機能するかを理解した夜、ARM MCUに移植できるかもしれません。または、自分の個人的なニーズを簡単に満たすように拡張することもできます。
Fotis Panagiotopoulos

遅い速度で実行されますが、あなたはあなたよりも早く電話__initialize_data()したいと思うかもしれません__initialize_bss()。そうでない場合は、それを確認する必要がSystemInit()あり、Flash_*()ルーチンはグローバルをまったく使用しません。
PAL-クリスチャンEngstad

それは私が尋ねることができる以上のものです!詳細な回答をありがとう!
ジョン

これをすべて1か所に収めることは本当に素晴らしいことです。あなたの時間と知識をありがとう!
ビットマック

@Pål-KristianEngstadまさに。もっと明確にすべきだった。暇なときに答えを編集することができますので、コードをコピーして貼り付けるのは安全です。
Fotis Panagiotopoulos

5

cortex-msはフルサイズの腕とは異なり、ベクターテーブルを使用します。また、モードとバンクレジスタもありません。また、イベント/割り込みについては、ARMコーディング標準に準拠しています。つまり、最低限必要なものですが、アドレス0の最初のワードはスタックポインターの初期値であり、2番目のワードはリセット時に分岐するアドレスです。アセンブリディレクティブを使用すると非常に簡単です。

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

しかし、最初の2つの単語が正しい値を持っている限り、何でも好きなことができます。分岐のサムアドレスにはlsbitが設定されていることに注意してください。これは実際にはアドレスの一部ではなく、単に親指モードになっている(とどまっている)ことを示しています。

これらの4バイトを何かで消費する必要がありますが、ベクターテーブルを使用する必要のないスタックポインターを設定するために使用する他のコードがある場合は、そこに置いたものをロードし、いつでも変更できます。フルサイズ/古いアームとは異なりますが、スタックポインターは1つだけです。

スタートアップという言葉は非常にあいまいなので、すでにそれらのディレクティブでカバーしていたかもしれませんし、何を意味するかによってマイクロコントローラーの起動を完了するのに何千行ものCコードが必要になるかもしれません。

Esp STM32では、使用する周辺機器のクロックを有効にする必要があります。また、必要な機能に合わせて設定する必要があります。実際には、他のマイクロコントローラーと違いはありませんが、ベンダーと製品ファミリーごとに異なるロジックがあり、異なる方法で初期化されます。


2

製造元から提供されるスタートアップファイルは、通常、Cコンパイラ環境をサポートするように設計されています。これには、メモリマップのセットアップ、メモリのゼロ初期化、変数の初期化、およびスタートアップ(リセットベクトル)のセットアップに関連するものがすべて含まれます。

いくつかのスタートアップファイルには、割り込みベクターと割り込みコントローラーの設定も含まれますが、私が作業した環境の中には、これを別のアセンブリ言語ファイルに設定するものがあります。

CPUアーキテクチャに基づいて異なるモデルがサポートされているため、スタートアップファイルに複雑さが見られる場合があります。モデルには「コンパクト」や「大」などの名前が付けられている場合があります。

あなたが求めてきた方法での最小限は、あなたが必要とするものにほぼ完全に駆り立てられます。つまり、アーキテクチャ、必要な環境、プラットフォームの動作を完全に理解することになります。次に、ベンダーが提供するファイルをニーズに合わせてトリミングするか、独自のファイルを最初から作成します。

ただし、Cでコードを作成する場合は、スタートアップコードをそのままにして、プログラミングモデルの設定を行い、main()から開始するようにコードを集中するのが最善です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.