STM32F4를 시작하는 데 최소 코드가 필요합니까?


14

STM32F4를 시작하는 데 가장 효율적인 방법 / 최소 코드는 무엇입니까? ST에서 나온 시작 파일에는 불필요한 코드가 많이있는 것 같습니다.


"불필요한"것으로 간주되는 것을 제거하고 실행 해보십시오.
Tyler

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)에서 테이블을 배치 할 것이다 그래서 같이 벡터 테이블은 ((섹션 ())) 절대적으로 필요하다.

위 파일에서 어떤 수정이 필요합니다.

  • MCU의 CMSIS 파일 포함
  • 링커 스크립트를 수정하는 경우 섹션 이름을 변경하십시오.
  • MCU와 일치하도록 벡터 테이블 항목을 변경하십시오.
  • MCU와 일치하도록 핸들러 프로토 타입 변경

시스템 호출

newlib을 사용하기 때문에 일부 함수의 구현을 제공해야합니다. printf, scanf 등을 구현할 수 있지만 필요하지는 않습니다. 개인적으로 나는 다음을 제공합니다 :

malloc에 ​​필요한 _sbrk. (수정 필요 없음)

#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 Panagiotootopoulos

난 당신이 전화를 할 수 있습니다 생각 __initialize_data()하고 __initialize_bss()그 느린 속도로 실행됩니다에도 불구하고, 당신보다 이전. 그렇지 않으면, 당신은 있는지 확인해야 SystemInit()하고 Flash_*()루틴은 전혀 전역을 사용하지 마십시오.
Pål-Kristian Engstad

내가 물어볼 수있는 것 이상입니다! 자세한 답변 주셔서 감사합니다!
John

이 모든 것을 한곳에 두는 것이 정말 좋습니다. 시간과 지식에 감사드립니다!
bitsmack

@ Pål-Kristian Engstad 정확합니다. 좀 더 명확하게해야합니다. 자유 시간이있을 때 답을 편집 할 수 있으므로 코드를 복사하여 붙여 넣는 사람은 안전합니다.
Fotis Panagiotootopoulos

5

피질 -ms는 전체 크기의 팔과 달리 벡터 테이블을 사용합니다. 또한 모드와 뱅크 레지스터가 없습니다. 그리고 이벤트 / 인터럽트의 경우 ARM 코딩 표준을 준수합니다. 즉, 필요한 최소값을 의미하지만 주소 0의 첫 번째 단어는 스택 포인터의 초기 값이고 두 번째 단어는 재설정시 분기 할 주소입니다. 어셈블리 지시문을 사용하면 매우 쉽습니다.

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

그러나 처음 두 단어가 올바른 값을 갖는 한 원하는대로 무엇이든 할 수 있습니다. 분기를위한 썸 주소에는 lsbit가 설정되어 있습니다. 그것은 실제로 주소의 일부가 아니며 단지 엄지 모드에 있다는 것을 나타냅니다.

이 4 바이트를 무언가로 소비해야하지만 스택 포인터를 설정하는 데 사용하는 다른 코드가있는 경우 벡터 테이블을 사용할 필요가 없으며 거기에 넣은 내용을로드하면 언제든지 변경할 수 있습니다. 전체 크기 / 이전 팔과는 다르지만 스택 포인터는 하나만 있습니다.

스타트 업이라는 단어는 매우 모호하므로 이미 그 지시어로 다룰 수도 있고, 의미에 따라 마이크로 컨트롤러 시작을 마치려면 수천 줄 이상의 C 코드가 필요할 수도 있습니다.

STM32를 사용하는 Esp는 클록을 사용하여 사용하려는 주변 장치를 활성화하고 원하는 주변 장치를 구성해야합니다. 각 공급 업체와 제품군이 서로 다른 논리를 가지고 있고 다른 방식으로 초기화한다는 점을 제외하고는 다른 마이크로 컨트롤러와 크게 다르지 않습니다.


2

제조업체에서 제공하는 시작 파일은 일반적으로 C 컴파일러 환경을 지원하도록 설계되었습니다. 여기에는 메모리 맵 설정, 메모리 초기화 없음, 변수 초기화 및 시작 설정 (재설정 벡터)과 관련된 많은 것들이 포함됩니다.

일부 시작 파일에는 인터럽트 벡터 및 인터럽트 컨트롤러 설정이 포함되어 있지만 작업 한 일부 환경에는 별도의 어셈블리 언어 파일이 있습니다.

CPU 아키텍처에 따라 다른 모델이 지원되기 때문에 시작 파일에서 복잡성이 나타나는 경우가 있습니다. 모델의 이름은 "compact"및 "large"와 같이 지정할 수 있습니다.

당신이 요구 한 방식은 최소한 당신이 필요로하는 것에 거의 전적으로 달려 있습니다. 따라서 아키텍처, 필요한 환경 및 플랫폼 작동 방식을 완전히 이해해야합니다. 그런 다음 필요에 따라 공급 업체에서 제공 한 파일을 정리하거나 처음부터 직접 작성할 수 있습니다.

그러나 C로 코드를 작성하려면 시작 코드를 그대로두고 프로그래밍 모델을 설정하고 main ()에서 시작하는 것처럼 코드를 집중시키는 것이 가장 좋습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.