Mise en route STM32: Que sont les registres? Comment travailler avec eux?

Examen continu des questions fondamentales


Dans la leçon précédente, nous avons examiné l'utilisation des opérations sur les bits et des nombres binaires, jetant ainsi les bases de l'examen d'un nouveau sujet. Dans cette leçon, nous considérerons une autre question: que sont les registres et comment travailler avec eux ?




Liste d'articles:
  1. Mise en route Étudier STM32 ou gérer la lumière intelligemment
  2. Mise en route STM32: opérations sur les bits
  3. Mise en route STM32: Que sont les registres? Comment travailler avec eux?


Mémoire et registres


L'une des compétences les plus importantes nécessaires lorsque vous travaillez avec des microcontrôleurs est la capacité d'interagir avec les registres. Voyons par nous-mêmes ce que c'est ?

En général, un registre est un type spécial de mémoire à l'intérieur d'un microcontrôleur qui est utilisé pour contrôler le processeur et les périphériques. Chaque registre de l'architecture ARM est une cellule mémoire et a une longueur de 32 bits, où chaque bit peut être représenté comme un minuscule commutateur à l'aide duquel l'un ou l'autre paramètre du microcontrôleur est contrôlé.

Chacun des registres a son propre numéro de série - adresse. L'adresse du registre est indiquée par un nombre de 32 bits représenté en notation hexadécimale. En écrivant à l'adresse du registre une certaine combinaison de uns et de zéros, qui sont généralement présentés en hexadécimal, la configuration et le contrôle de l'un ou l'autre nœud dans MK sont effectués. Rappelons que dans un programme pour travailler avec des opérations sur les bits, nous pourrions représenter un ensemble arbitraire de uns et de zéros comme un nombre hexadécimal. En général, il convient de noter qu'il existe deux types de registres: les registres à usage général et les registres spéciaux. Les premiers sont situés à l'intérieur du noyau MK et les seconds font partie de la mémoire RAM.

Il convient également de noter que le manuel de référence , que nous avons téléchargé dans la première leçon , est un grand livre de référence sur les registres contenus dans le microcontrôleur cible, et la bibliothèque CMSIS nous permet de fonctionner avec des noms de registres symboliques au lieu d'adresses numériques. Par exemple, nous pouvons accéder au registre 0x40011018 en utilisant simplement le nom symbolique GPIOC_BSSR . Nous considérerons des exemples spécifiques de configuration lors de l'analyse de notre programme dès la première leçon .

Ainsi, la structure du registre est généralement décrite sous la forme d'un petit tableau indiquant:

  1. Enregistrer les noms et descriptions de son objectif
  2. Enregistrer des adresses ou un décalage par rapport à l'adresse de base
  3. Valeurs par défaut après réinitialisation
  4. Type d'accès aux cellules du registre (lecture, écriture, lecture / écriture)
  5. Valeurs et descriptions des paramètres des bits enregistrés

Regardons un exemple de travail avec des registres dans une situation spécifique pour avoir une idée générale des principes de configuration du microcontrôleur.

Analyser le code de la première leçon


Donc, rappelons le problème que nous avons résolu dans la première leçon en utilisant un exemple de code prêt à l'emploi: nous devions écrire un programme qui assurerait l'inclusion alternative de deux LED sur la carte Discovery (peut-être pas deux si vous avez une version différente de la carte Discovery) avec un intervalle de temps .

Jetons un autre regard sur le code du programme que nous avons utilisé pour faire sauter notre MK à deux pattes sur lesquelles se trouvent nos LED:

Code main.c
/*      */ #include "stm32f0xx.h" /*    */ int main(void) { /*     GPIO */ RCC->AHBENR |= RCC_AHBENR_GPIOCEN; /*     PC8  PC9  Output*/ GPIOC ->MODER = 0x50000; /*  Output type   Push-Pull */ GPIOC->OTYPER = 0; /*      Low */ GPIOC->OSPEEDR = 0; while(1) { /*   PC8,  PC9 */ GPIOC->ODR = 0x100; for (int i=0; i<500000; i++){} //   /*   PC9,  PC8 */ GPIOC->ODR = 0x200; for (int i=0; i<500000; i++){} //   } } 


Tout d'abord, lorsque vous travaillez avec STM32, même pour une tâche aussi simple que d'allumer et d'éteindre la LED, nous devons d'abord répondre à un certain nombre de questions:

  1. Où nos LED sont-elles connectées? Quelle est la conclusion du microcontrôleur?
  2. Comment activer la synchronisation sur le port GPIO souhaité?
  3. Comment configurer les broches du port GPIO dont nous avons besoin pour que la LED puisse être allumée?
  4. Comment allumer et éteindre la LED?

Nous leur répondrons dans l'ordre.

Où nos LED sont-elles connectées? Quelle est la conclusion du microcontrôleur?


Afin de voir où se trouve la carte Discovery, et en particulier les LED dont nous avons besoin, vous devez ouvrir le fichier Schematic, soit celui que nous avons téléchargé depuis le site Web de ST, soit directement depuis Keil:


Schéma d'ouverture, nous verrons un diagramme de tout ce qui est sur la carte - un diagramme ST-Link, une liaison de toute la périphérie, et bien plus encore. En ce moment, nous sommes intéressés par deux LED, nous recherchons leur désignation:


Comme nous pouvons le voir, nos LED sont connectées au port GPIOC sur 8 et 9 broches.

Comment activer la synchronisation sur le port GPIO souhaité?


En général, tout travail avec des périphériques dans les microcontrôleurs STM32 se résume à une séquence d'actions standard:

  1. Activation de la synchronisation du module périphérique correspondant. Cela se fait via le registre RCC en appliquant un signal d'horloge directement à partir du bus sur lequel se trouve ce module. Par défaut, la synchronisation de tous les périphériques est désactivée pour minimiser la consommation d'énergie.
  2. Réglage via les registres de contrôle, en modifiant les paramètres spécifiques à un périphérique spécifique
  3. Démarrage direct et utilisation des résultats de fonctionnement du module

Autrement dit, pour commencer, nous devons exécuter l'horloge sur le port GPIOC. Cela se fait directement en accédant au registre RCC chargé de cadencer tout et de tout et d'activer le signal d'horloge du bus auquel notre port GPIO est connecté.

Attention! La question concernant le système d'horloge, ses paramètres et son utilisation sera discutée en détail dans un article séparé.

Vous trouverez le bus auquel notre port GPIOC est connecté dans la fiche technique de notre MK dans la section Mappage de la mémoire du tableau 16. Adresses limites du registre périphérique STM32F051xx.


Comme vous l'avez déjà remarqué, le bus dont nous avons besoin est appelé AHB2. Afin de vous familiariser avec le registre dans lequel la synchronisation avec le port GPIO du bus AHB dont nous avons besoin est activée, vous devez vous rendre dans la section correspondante du Manuel de référence. Par le nom des registres, nous pouvons déterminer celui dont nous avons besoin:


Nous allons à ce point et nous voyons notre registre 32 bits, son adresse de décalage, sa valeur par défaut, la façon d'accéder au registre et de répertorier les responsabilités de chaque bit du registre.


Nous regardons le tableau et voyons quelque chose qui rappelle l'option d'activer la synchronisation sur les ports GPIO. Accédez à la description et trouvez l'option dont nous avons besoin:


Par conséquent, si nous définissons 19 bits à la valeur «1», cela garantira que le cadencement est activé sur le port I / OC, c'est-à-dire sur notre GPIOC. De plus, nous devons activer séparément un bit du groupe, sans affecter le reste car nous ne devons pas interférer avec ou modifier d'autres paramètres inutilement.

Sur la base des matériaux de la dernière leçon, nous savons que pour définir un bit spécifique, vous devez utiliser l'opération logique «OU» pour ajouter la valeur actuelle du registre avec un masque qui contient les bits qui doivent être activés. Par exemple, ajoutons la valeur par défaut du registre RCC-> AHBENR, c'est-à-dire 0x14 et le nombre 0x80000 activent ainsi l'horloge GPIOC en définissant 19 bits:



Comment pouvons-nous faire cela à partir d'un programme? Tout est assez simple. Dans ce cas, nous avons deux options:

  1. Ecrire directement dans le registre la valeur numérique du registre directement via son adresse.
  2. Configuration à l'aide de la bibliothèque CMSIS

Il n'y a pas de problème particulier lors de l'écriture directe d'une valeur dans le registre, mais il y a quelques inconvénients importants. Premièrement, un tel code devient illisible et deuxièmement, nous ne pouvons pas déterminer immédiatement à quel registre telle ou telle adresse fait référence en mémoire.

Autrement dit, nous pourrions adresser les adresses des registres directement à l'adresse et écrire ceci:

 __IO uint32_t * register_address = (uint32_t *) 0x40021014U; //      *(__IO uint32_t *)register_address |= 0x80000; //  19     

La deuxième option me semble la plus attractive, car La bibliothèque CMSIS est organisée de manière à ce que le registre soit accessible en utilisant uniquement son nom. Pendant le prétraitement du texte du programme, le préprocesseur remplacera automatiquement toutes les valeurs numériques de l'adresse de registre avant la compilation. Regardons cette question plus en détail.

Je suggère d'ouvrir notre projet, ce que nous avons fait dans la première leçon, ou de télécharger celui précédemment préparé à partir d'ici et de supprimer tout le contenu du programme, ne laissant que le fichier d'en-tête connecté, la fonction main () et les instructions pour allumer l'horloge (nous en aurons besoin pour une analyse détaillée du code).

Notre code ressemblera à ceci:

 /*      */ #include "stm32f0xx.h" /*    */ int main(void) { /*     GPIO */ RCC->AHBENR|=RCC_AHBENR_GPIOCEN; } 

Examinons plus en profondeur la bibliothèque CMSIS pour la familiarisation.

Afin d'aller rapidement à l'endroit où telle ou telle constante ou variable est déclarée, une fonction pratique est implémentée dans Keil. Faites un clic droit sur la constante dont nous avons besoin, par exemple sur RCC:


Et nous sommes transportés dans les profondeurs de la bibliothèque CMSIS, dans laquelle nous verrons que tous les registres disponibles pour le contrôle programmatique ont la forme de structures TypeDef, y compris notre RCC:


Ayant échoué de manière similaire dans RCC_TypeDef, nous verrons une structure dans laquelle tous les champs de notre registre sont décrits:


En conséquence, nous pouvons accéder en toute sécurité au registre dont nous avons besoin avec un enregistrement de la forme PERIPH_MODULE-> INSCRIPTION et lui affecter une valeur spécifique.

Outre la désignation mnémonique des registres, il existe également des désignations de bits spécifiques. Si nous ne déclarons pas le paramètre RCC_AHBENR_GPIOCEN de notre programme, nous verrons également la déclaration de tous les paramètres:


Ainsi, en utilisant la bibliothèque CMSIS, nous obtenons un enregistrement concis et lisible du paramètre dont nous avons besoin dans le registre, grâce à l'installation dont nous commençons le cadencement sur le port dont nous avons besoin:

 /*     GPIO */ RCC->AHBENR|=RCC_AHBENR_GPIOCEN; 

En tant que tâche: déterminer, à l'aide des capacités de Keil, comment l'adresse du registre RCC-> AHBENR a été obtenue en tant que 0x40021014.

Comment configurer les broches GPIO dont nous avons besoin pour pouvoir allumer la LED?


Ainsi, nous savons que les LED dont nous avons besoin sont connectées au port GPIOC aux broches PC8 et PC9. Nous devons les configurer de telle sorte que la LED s'allume. Je voudrais immédiatement faire une réserve que nous examinerons les ports GPIO plus en détail dans un autre article et ici nous nous concentrerons spécifiquement sur le travail avec les registres.

Tout d'abord, nous devons mettre les broches PC8 et PC9 en mode de sortie. D'autres paramètres de port peuvent être laissés par défaut. Accédez au manuel de référence à la section 9. E / S à usage général (GPIO) et ouvrez l'élément responsable du mode de fonctionnement des broches du port GPIO et vérifiez que le registre MODER est responsable de ce paramètre:


A en juger par la description, pour mettre les broches PC8 et PC9 en mode sortie, nous devons écrire 01 dans les champs correspondants du registre GPIOC.

Cela peut être fait par une installation directe en utilisant des valeurs numériques:

  1. Nous formons un numéro pour écrire:

  2. Nous attribuons cette valeur à notre registre:

     /*     PC8  PC9  Output*/ GPIOC->MODER |= 0x50000; 


Ou grâce à l'utilisation des définitions de la bibliothèque:

 /*     GPIO */ GPIOC->MODER |= GPIO_MODER_MODER8_0 | GPIO_MODER_MODER9_0; 

Après cette instruction, nos broches PC8 et PC9 passeront en mode Sortie.

Comment allumer la LED?


Si nous prêtons attention à la liste des registres disponibles pour contrôler le port GPIO, nous pouvons voir le registre ODR:


Chacun des bits correspondants est responsable d'une des broches du port. Vous pouvez voir sa structure ci-dessous:


Afin de permettre un changement alternatif des états des LED, il est nécessaire d'activer / désactiver 8 et 9 bits avec un certain intervalle de temps. Autrement dit, affectez alternativement la valeur 0x100 et 0x200 au registre.

Nous pouvons le faire en affectant directement des valeurs au registre:

 GPIOC->ODR = 0x100; //  PC8,  PC9 GPIOC->ODR = 0x200; //  PC9,  PC8 

Nous pouvons grâce à l'utilisation des définitions de la bibliothèque:

 GPIOC->ODR = GPIO_ODR_8; //  PC8,  PC9 GPIOC->ODR = GPIO_ODR_9; //  PC9,  PC8 

Mais comme le microcontrôleur fonctionne très rapidement - nous ne remarquerons pas le changement d'état des LED et visuellement il semblera qu'elles s'allument toutes les deux en permanence. Pour qu'ils clignotent vraiment alternativement, nous introduirons un retard artificiel sous la forme d'un cycle qui amènera MK à des calculs inutiles pendant un certain temps. Le code suivant se révélera:

 /*   PC8,  PC9 */ GPIOC->ODR = GPIO_ODR_8; for (int i=0; i<500000; i++){} //   /*   PC9,  PC8 */ GPIOC->ODR = GPIO_ODR_9; for (int i=0; i<500000; i++){} //   

Sur ce point, nous pouvons mettre fin à la connaissance initiale des registres et des méthodes de travail avec eux.

Vérification des résultats de notre code


Un petit ajout agréable à la fin de l'article: Keil a un excellent outil de débogage avec lequel nous pouvons étape par étape exécuter notre programme et afficher l'état actuel de tout bloc périphérique. Pour ce faire, après avoir téléchargé le firmware après la compilation, nous pouvons cliquer sur le bouton Démarrer la session de débogage:


L'espace de travail Keil passera en mode débogage. Nous pouvons contrôler la progression du programme en utilisant ces boutons:


Et il existe une autre fonction pratique pour travailler avec des périphériques en mode débogage, elle vous permet de visualiser l'état actuel des registres et de changer leur état d'un simple clic de souris.

Pour l'utiliser, vous devez vous rendre sur l'unité périphérique correspondante et une fenêtre s'ouvrira à droite avec les registres et leur valeur.


Si vous cliquez sur l'un des éléments de ce menu, vous verrez l'adresse du registre et sa brève description. Vous pouvez également afficher la description de chaque paramètre de registre individuel:


Essayez d'exécuter indépendamment le programme étape par étape, allumez / éteignez les LED n'utilisant pas le programme, mais utilisant ce mode de fonctionnement avec le microcontrôleur. Les possibilités d'imagination sont vastes. Essayez également de jouer avec la durée des retards, faites clignoter simultanément les deux LED. En général, expérimentez! )

Rendez-vous dans les articles suivants!
Liste d'articles:
  1. Mise en route Étudier STM32 ou gérer la lumière intelligemment
  2. Mise en route STM32: opérations sur les bits
  3. Mise en route STM32: Que sont les registres? Comment travailler avec eux?

Source: https://habr.com/ru/post/fr407083/


All Articles