STM32 Partie 3: Premier projet

Nous observons une société qui est de plus en plus dépendante des machines, mais en même temps les utilise toutes de manière plus inefficace. - Douglas Rushkoff


Cette phrase devrait servir de motivation à chaque programmeur. Après tout, c'est vous qui décidez comment la machine utilise ses ressources. Mais dès le début des temps, une personne confie son droit de décider à des tiers en échange de la facilité. Avant de me poser des questions sur les avantages de mes articles quand il y a un «cube», demandez-vous pourquoi le «cube» décide pour moi.

STM32 Partie 1: Bases de
STM32 Partie 2: Initialisation

Alors, continuons notre aventure. Nous avons déjà écrit le script d'initialisation, compris l'éditeur de liens et le compilateur. Il est temps de faire clignoter la LED. Dans cet article, nous allons passer en revue les bases du bloc RCC et GPIO, et ajouter quelques en-têtes, que nous utiliserons dans de futurs projets. Aller.

RCC - Reset and Clock Control, un bloc de registres qui contrôle l'horloge du processeur et des périphériques. Plusieurs sources d'horloge sont contrôlées par cet appareil: HSI (High speed internal), HSE (high speed external), PLL / PLLSAI / PLLI2S (Phased loop lock). Ces trois sources d'horloge peuvent être utilisées par le processeur, bien sûr, si les registres de bloc RCC sont correctement configurés. Nous en parlerons en détail dans le prochain article.

GPIO - Entrée et sortie à usage général. Le bloc de registres créé pour les LED clignotantes, c'est-à-dire un bloc pour contrôler les jambes de nos microns, un portail vers le monde réel. Contrairement à l'Atmega328p (Arduino), le stm32 offre une personnalisation plus large des pieds. Par exemple, la fonction de pied Open Drain n'est pas disponible sur le méga. En outre, le contrôle de la vitesse à laquelle le Royaume-Uni augmente la tension sur la jambe peut être configuré.

Clignotons donc déjà, oui. Nous avons d'abord besoin de deux en-têtes dans lesquels nous décrivons la structure de ces registres. Par conséquent, nous obtenons le manuel de référence et l'étudions à la recherche des informations dont nous avons besoin. Je travaille sur STM32F746NG - Discovery donc tout le code de cette carte.

La première chose dont nous avons besoin est l'adresse de ces blocs, de base. Commençons par le bloc RCC et créons l'en-tête rcc.h. Dans celui-ci, nous allons créer la macro RCC_BASE, et également décrire la structure du bloc de registre, et créer un pointeur vers la structure.

rcc.h

#define RCC_BASE   0x40023800

typedef struct
{
  volatile unsigned int CR;           
  volatile unsigned int PLLCFGR;      
  volatile unsigned int CFGR;         
  volatile unsigned int CIR;          
  volatile unsigned int AHB1RSTR;     
  volatile unsigned int AHB2RSTR;     
  volatile unsigned int AHB3RSTR;     
  unsigned int          RESERVED0;    
  volatile unsigned int APB1RSTR;     
  volatile unsigned int APB2RSTR;     
  unsigned int          RESERVED1[2]; 
  volatile unsigned int AHB1ENR;      
  volatile unsigned int AHB2ENR;      
  volatile unsigned int AHB3ENR;      
  unsigned int          RESERVED2;    
  volatile unsigned int APB1ENR;      
  volatile unsigned int APB2ENR;      
  unsigned int          RESERVED3[2]; 
  volatile unsigned int AHB1LPENR;    
  volatile unsigned int AHB2LPENR;    
  volatile unsigned int AHB3LPENR;    
  unsigned int          RESERVED4;    
  volatile unsigned int APB1LPENR;    
  volatile unsigned int APB2LPENR;    
  unsigned int          RESERVED5[2]; 
  volatile unsigned int BDCR;         
  volatile unsigned int CSR;          
  unsigned int          RESERVED6[2]; 
  volatile unsigned int SSCGR;        
  volatile unsigned int PLLI2SCFGR;   
  volatile unsigned int PLLSAICFGR;   
  volatile unsigned int DCKCFGR1;     
  volatile unsigned int DCKCFGR2;     

} RCC_Struct;

#define RCC ((RCC_Struct *) RCC_BASE)


Faisons la même opération avec le bloc de registre GPIO, uniquement sans pointeur et ajoutons une autre macro, GPIO_OFFSET. en parler ci-dessous.

gpio.h

#define GPIO_BASE   0x40020000
#define GPIO_OFFSET 0x400

typedef struct
{
   volatile unsigned int MODER;   
   volatile unsigned int OTYPER;  
   volatile unsigned int OSPEEDR; 
   volatile unsigned int PUPDR;   
   volatile unsigned int IDR;     
   volatile unsigned int ODR;     
   volatile unsigned int BSRR;    
   volatile unsigned int LCKR;    
   volatile unsigned int AFR[2];  

} GPIO_Struct;


Voyons quel est le problème ici et pourquoi avons-nous besoin de structures. Le fait est que nous pouvons accéder au registre via un pointeur sur une structure, nous n'avons pas besoin de l'initialiser car physiquement il existe déjà. Par exemple:

RCC->AHB1ENR = 0; 


Cela économise de l'espace mémoire et parfois ne l'exige pas du tout. Mais pas pour épargner aujourd'hui.

Nous avons donc deux en-têtes prêts, il reste à apprendre à sauter à l'aide de ces registres et à créer int main();. Tout est simple et pas tout à fait. Dans STM32, pour accéder au bloc registre, il faut d'abord lui appliquer une horloge, sinon les données y parviendront. Je n'entrerai pas profondément dans la structure du pneu, mais dis simplement tel quel. Les blocs sont placés sur différents pneus. Notre unité est située sur le bus AHB1. Autrement dit, nous devons activer un port spécifique sur le bus AHB1, dans mon cas, c'est le port I. Pour les débutants, bien sûr, nous avons besoin de la fonction principale.

Mettons un peu à jour notre fichier de démarrage et y ajoutons int main (); puis créez le fichier main.c lui-même

extern void *_estack;

void Reset_Handler();
void Default_Handler();

//   
int  main();

void NMI_Handler()                    __attribute__ ((weak, alias ("Default_Handler")));
void HardFault_Handler()              __attribute__ ((weak, alias ("Default_Handler")));

void *vectors[] __attribute__((section(".isr_vector"), used)) = {
    &_estack,
	&Reset_Handler,
	&NMI_Handler,
	&HardFault_Handler
};

extern void *_sidata, *_sdata, *_edata, *_sbss, *_ebss;



void __attribute__((naked, noreturn)) Reset_Handler()
{


	void **pSource, **pDest;
	for (pSource = &_sidata, pDest = &_sdata; pDest != &_edata; pSource++, pDest++)
		*pDest = *pSource;

	for (pDest = &_sbss; pDest != &_ebss; pDest++)
		*pDest = 0;

    //   
    main();

	while(1);
}

void __attribute__((naked, noreturn)) Default_Handler(){
    while(1);
}


Et maintenant, nous créons le fichier main.c. lui-même Dans le fichier, j'ai essayé de tout peindre dans les commentaires du code, nous l'avons donc lu et fouillé. Si vous avez des questions, écrivez dans les commentaires ci-dessous, je répondrai.

#include "rcc.h"
#include "gpio.h"


int main()
{
    //  GPIO I ,   8 ( )
    RCC->AHB1ENR |= (1<<8);

    //     
    //          
    //      
    volatile GPIO_Struct *GPIOI = (GPIO_Struct *)(GPIO_BASE + (GPIO_OFFSET*8));  

    //     ""
    GPIOI->MODER |= (1<<2);

    //   ,   , push-pull,   .
    //   push pull
    GPIOI->OTYPER &= ~(1<<1);

    //      ,       
    GPIOI->OSPEEDR |= (2<<2);

    //   
    while(1){
        for(volatile int i = 0; i < 1000000; i++){
            // 
        }

        // ,   1   0  .
        GPIOI->ODR ^= (1<<1);
    }

    return 0;
}


Il nous fallait maintenant collecter le projet et le lancer sur microns, pour des raisons de vérification. J'ai posté un référentiel sur Github tout ce qui doit être fait est d'exécuter l'utilitaire Make.
make


Merci à tous pour votre attention, dans le prochain article, nous parlerons davantage de l'unité RCC et de la façon de travailler avec elle.

All Articles