Code minimal requis pour démarrer un STM32F4?

14

Quel est le moyen le plus efficace / le code minimal requis pour démarrer un STM32F4? Les fichiers de démarrage qui proviennent de ST semblent avoir beaucoup de code inutile.

John
la source
Supprimez ce que vous jugez "inutile" et essayez de l'exécuter ...
Tyler
1
Le code des fournisseurs de puces tente d'être unique, ce qui signifie qu'il ne convient à personne. Ils seront toujours gonflés par définition, car ils essaient de gérer tous les principaux cas d'utilisation pour tous les périphériques et fonctionnalités qu'ils sont prêts à prendre en charge. Utilisez leur code et vous bénéficiez d'une assistance de leur part et d'autres en ligne qui utilisent ce code. Suivez votre propre chemin et vous profitez de la taille et de la vitesse, mais c'est surtout à vous de réinventer cette roue.
old_timer
Ou, comme l'a dit Tyler, coupez ce que vous ne voulez pas / n'avez pas besoin.
old_timer

Réponses:

25

Vous ne voudrez peut-être pas utiliser le code de démarrage fourni par le fournisseur. Il y a peu de gens qui font ça:

Créez du code plus efficace ou moins gonflé. Avoir une exigence particulière que le code fournisseur ne respecte pas. Vous voulez savoir comment ça marche. Vous voulez une sorte de code universel, à utiliser dans de nombreux MCU différents. Vous voulez un contrôle total sur vous le processus. etc..

Les éléments suivants s'appliquent uniquement aux programmes C (pas de C ++, exceptions, etc.) et aux microcontrôleurs Cortex M (quelle que soit la marque / le modèle). Je suppose également que vous utilisez GCC, bien qu'il puisse y avoir peu ou pas de différence avec les autres compilateurs. Enfin j'utilise newlib.

Script de l'éditeur de liens

La première chose à faire est de créer un script de l'éditeur de liens. Vous devez dire à votre compilateur comment organiser les choses en mémoire. Je n'entrerai pas dans les détails sur le script de l'éditeur de liens, car c'est un sujet à part entière.

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

}

Vous pouvez directement utiliser le script de l'éditeur de liens fourni. Quelques points à noter:

  • Il s'agit d'une version simplifiée du script de l'éditeur de liens que j'utilise. Pendant le dépouillement, je peux introduire des bogues dans le code, veuillez le vérifier.

  • Étant donné que je l'utilise pour d'autres MCU que vous, vous devez modifier la disposition de la MÉMOIRE pour l'adapter à la vôtre.

  • Vous devrez peut-être modifier les bibliothèques liées ci-dessous pour créer des liens avec les vôtres. Ici, il est lié à newlib.

Tableau vectoriel

Vous devez inclure dans votre code une table vectorielle. Il s'agit simplement d'un tableau de recherche de pointeurs de fonction, auquel le matériel accédera automatiquement en cas d'interruption. C'est assez facile à faire en C.

Jetez un œil au fichier suivant. C'est pour le MCU STM32F103C8, mais il est très facile de s'adapter à vos besoins.

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

Que se passe-t-il ici. - Je déclare d'abord ma fonction _start afin qu'elle puisse être utilisée ci-dessous. - Je déclare un gestionnaire par défaut pour toutes les interruptions et le gestionnaire de réinitialisation - Je déclare tous les gestionnaires d'interruptions nécessaires pour mon MCU. Notez que ces fonctions ne sont que des alias du gestionnaire par défaut, c'est-à-dire que lorsqu'une d'entre elles est appelée, le gestionnaire par défaut sera appelé à la place. Ils sont également déclarés semaine, vous pouvez donc les remplacer par votre code. Si vous avez besoin de l'un des gestionnaires, vous le redéclarez dans votre code et votre code sera lié. Si vous n'en avez pas besoin, il y en a simplement un par défaut et vous n'avez rien à faire. Le gestionnaire par défaut doit être structuré comme tel, si votre application a besoin d'un gestionnaire mais que vous ne l'implémentez pas, il vous aidera à déboguer votre code ou à récupérer le système s'il est à l'état sauvage. - J'obtiens le symbole __stack déclaré dans le script de l'éditeur de liens. Il est nécessaire dans la table vectorielle. - Je définis la table elle-même. Notez que la première entrée est un pointeur vers le début de la pile et les autres sont des pointeurs vers les gestionnaires. - Enfin, je fournis une implémentation simple pour le gestionnaire par défaut et le gestionnaire de réinitialisation. Notez que le gestionnaire de réinitialisation est celui qui est appelé après la réinitialisation et qui appelle le code de démarrage.

Gardez à l'esprit que l' attribut ((section ())) dans la table vectorielle est absolument nécessaire, de sorte que l'éditeur de liens place la table à la bonne position (normalement l'adresse 0x00000000).

Quelles modifications sont nécessaires sur le fichier ci-dessus.

  • Incluez le fichier CMSIS de votre MCU
  • Si vous modifiez le script de l'éditeur de liens, changez les noms des sections
  • Modifiez les entrées de la table vectorielle pour qu'elles correspondent à votre MCU
  • Modifiez les prototypes des gestionnaires pour qu'ils correspondent à votre MCU

Appels système

Puisque j'utilise newlib, il vous oblige à fournir les implémentations de certaines fonctions. Vous pouvez implémenter printf, scanf, etc., mais ils ne sont pas nécessaires. Personnellement, je ne fournis que les éléments suivants:

_sbrk dont malloc a besoin. (Aucune modification nécessaire)

#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, ce qui n'est pas nécessaire, mais j'aime l'idée. (Il se peut que vous ayez seulement besoin de modifier le CMSIS).

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


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

    NVIC_SystemReset();

    while(1);
}

Code de démarrage

Enfin le code de démarrage!

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

Que se passe-t-il ici.

  • Je configure d'abord le contrôleur Flash, comme cela est requis par mon MCU, avant de changer de fréquence. Vous pouvez ajouter tout code très basique et nécessaire pour votre code matériel ici. Notez que le code placé ici ne doit pas accéder aux globaux dans la RAM, car ils ne sont pas encore initialisés. Notez également que le MCU fonctionne toujours à basse fréquence, alors n'appelez que le absolument nécessaire.
  • Ensuite, j'appelle la fonction CMSIS SystemInit (). C'est un peu portable, c'est pourquoi je l'utilise. Il gère principalement le cœur, pas le MCU ou lui-même, dans mes implémentations spécifiques, il active uniquement la PLL et définit le MCU à sa haute fréquence finale. Vous pouvez le remplacer par votre code plus efficace, mais ce n'est pas grave.
  • La prochaine étape, maintenant que le MCU est rapide, est d'initialiser la RAM. Assez simple.
  • Le MCU est désormais opérationnel. J'appelle simplement la fonction CMSIS SystemCoreClockUpdate (), car j'utilise dans mon code la variable SystemCoreClock, mais ce n'est pas nécessaire, juste ma préférence.
  • Enfin j'appelle la fonction principale. Votre application s'exécute désormais normalement.
  • Si le principal retourne, un appel à _exit () est une bonne pratique pour redémarrer votre système.

Plus ou moins c'est ça.

Fotis Panagiotopoulos
la source
4
En raison de la longueur de la réponse, cela peut sembler effrayant. Aussi, tout en essayant de comprendre cela, vous devrez peut-être combattre votre chaîne d'outils pour qu'elle fasse ce que vous faites. Ne vous inquiétez pas, vous comprendrez enfin à quel point le code ci-dessus est simple et polyvalent. Vous pourrez peut-être le porter sur n'importe quel ARM MCU, en une seule soirée lorsque vous comprendrez comment les choses fonctionnent. Ou vous pouvez l'améliorer en satisfaisant facilement vos besoins personnels.
Fotis Panagiotopoulos
Je pense que vous voudrez peut-être appeler __initialize_data()et __initialize_bss()plus tôt que vous, même si cela fonctionnera à vitesse lente. Sinon, vous devez vous assurer que SystemInit()vos Flash_*()routines n'utilisent pas du tout les globaux.
Pål-Kristian Engstad
C'est plus que ce que je pouvais demander! Merci pour la réponse détaillée!
John
C'est vraiment agréable d'avoir tout cela en un seul endroit. Merci pour votre temps et vos connaissances!
bitsmack
@ Pål-Kristian Engstad Exactement. Aurait dû être plus clair. Je peux modifier la réponse quand j'ai du temps libre, donc ceux qui copient-collent le code sont en sécurité.
Fotis Panagiotopoulos
5

Le cortex-ms, contrairement aux bras de taille normale, utilise une table vectorielle. Ils n'ont pas non plus de modes et de registres en banque. Et pour les événements / interruptions, ils sont conformes à la norme de codage ARM. Cela signifie que le strict minimum dont vous avez besoin, mais vous choisissez de l'obtenir, il y a le premier mot à l'adresse zéro est la valeur initiale pour le pointeur de pile, et le deuxième mot est l'adresse à laquelle se ramifier lors de la réinitialisation. Très facile à faire en utilisant les directives d'assemblage.

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

Mais encore une fois, vous pouvez faire ce que vous voulez tant que les deux premiers mots ont les bonnes valeurs. Notez qu'une adresse de pouce pour la ramification a le lsbit défini. Cela ne fait pas vraiment partie de l'adresse, cela indique simplement que nous (restons) en mode pouce.

Vous devez consommer ces quatre octets avec quelque chose, mais si vous avez un autre code que vous utilisez pour définir le pointeur de pile, vous n'avez pas besoin d'utiliser la table vectorielle, il chargera ce que vous y mettez, vous pouvez toujours le changer. Il n'y a qu'un seul pointeur de pile, mais pas comme les bras de taille normale / plus anciens.

Le mot démarrage est très vague, donc j'aurais pu le couvrir déjà avec ces directives ou cela pourrait vous prendre plusieurs milliers de lignes de code C pour terminer le démarrage de votre microcontrôleur en fonction de ce que vous vouliez dire.

En particulier avec un STM32, vous devez activer l'horloge des périphériques que vous souhaitez utiliser, vous devez les configurer pour ce que vous voulez qu'ils fassent et ainsi de suite. Pas vraiment différent de tout autre microcontrôleur, sauf que chaque fournisseur et famille de produits a une logique différente et initialise une manière différente.

old_timer
la source
2

Les fichiers de démarrage provenant d'un fabricant sont normalement conçus pour prendre en charge un environnement de compilateur C. Cela comprendra beaucoup de choses liées à la configuration de la carte mémoire, à la mémoire d'initialisation zéro, à l'initialisation des variables et à la configuration du démarrage (vecteur de réinitialisation).

Certains fichiers de démarrage incluront également la configuration des vecteurs d'interruption et du contrôleur d'interruption, bien que certains environnements avec lesquels j'ai travaillé aient cela dans un fichier de langage d'assemblage séparé.

Parfois, la complexité est observée dans un fichier de démarrage car différents modèles sont pris en charge en fonction de l'architecture du processeur. Les modèles peuvent être nommés des choses comme "compact" et "grand".

Un minimum de la manière dont vous avez demandé dépendra presque entièrement de ce dont vous avez besoin. Il s'agit donc vraiment de bien comprendre votre architecture, l'environnement nécessaire et le fonctionnement de votre plateforme. Ensuite, vous pouvez soit réduire les fichiers fournis par le fournisseur pour répondre à vos besoins OU écrire les vôtres à partir de zéro.

Mais, cela dit, si vous avez l'intention d'écrire du code en C, il vaut mieux laisser le code de démarrage seul et simplement configurer les choses pour le modèle de programmation et concentrer votre code en commençant par main ().

Michael Karas
la source