EasySTM32 - Ports de microcontrôleur. Ports de microcontrôleur STM32

Dans la deuxième leçon de la série, consacrée au travail avec les microcontrôleurs STM32, nous parlerons des ports d'E/S.
Les ports du microcontrôleur permettent la communication avec périphériques externes allant de la LED et du bouton à des appareils plus complexes : écrans, modems GPS et GSM, etc. Les ports vous permettent également de communiquer avec d'autres appareils, comme un ordinateur.

Entrée/sortie à usage général (GPIO). GPIO est la méthode principale et couramment utilisée de communication avec l'environnement externe. Les ports peuvent fonctionner selon deux modes : entrée (signal de réception) et sortie (signal de transmission). Ils ne fonctionnent qu'avec les niveaux logiques 0 (niveau bas) ou 1 (niveau haut).
Par exemple, si vous connectez une LED au port en mode de sortie, lorsque le signal haut niveau la LED s'allumera et lorsqu'elle sera basse, elle s'éteindra.
Si vous activez la sortie en mode d'entrée et connectez-y un bouton, vous pouvez alors surveiller son état à l'aide du microcontrôleur: enfoncé ou relâché.
En fait, GPIO est le moyen le plus simple et le plus primitif d'organiser le travail avec des périphériques externes, mais l'utilisation de la gestion des interruptions et des minuteries étend considérablement les possibilités. Nous en parlerons un peu plus tard.

Résolvons la première tâche pratique : contrôler les LED et lire l'état du bouton.
Un point très important doit être noté - les ports du microcontrôleur peuvent fournir un courant ne dépassant pas 20 mA. Bien qu'il puisse les donner, mais une fois et pas longtemps, avant le coton et la fumée grise ;). Pour connecter des charges plus puissantes, des interrupteurs d'alimentation doivent être utilisés.

Alors, commençons. Prenons la carte STM32F4 Discovery pour le travail. Il dispose d'un bouton défini par l'utilisateur initialement connecté au port PA0 et de 4 LED connectées aux ports PD12-PD15.

Le schéma de connexion du bouton et des LED est représenté sur la figure.

La résistance R1 avec une valeur nominale de 10kOhm - "pull-to-ground", évite la situation où le port n'est pas connecté à "0" ou "1" - cela doit être évité, et la résistance résout ce problème. Un tel lifting peut également être activé par programmation, mais il est préférable de vous sécuriser de cette façon.

Les résistances R2-R5 330Ω limitent le courant traversant les LED. Ils peuvent être sélectionnés dans la plage de 200Ω à 1kΩ, tout dépend de la luminosité requise.

Passons maintenant à l'écriture du programme. En tant qu'environnement de développement, j'utilise. L'environnement est libre et confortable à mon avis. Je ne vais pas vous dire comment commencer à y travailler - il y a suffisamment d'informations à ce sujet sur Internet, pour le firmware, j'utilise l'utilitaire STM32 ST-LINK.
Tout d'abord, activez la synchronisation du port A, auquel le bouton est connecté :

RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOA, ACTIVER);

Vous devez maintenant configurer le port correctement :

// Structure contenant les paramètres de port GPIO_InitTypeDef GPIO_InitStructure; // définissez le numéro de broche, si le bouton est connecté, par exemple, au port 6, puis écrivez GPIO_Pin_6 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // le port agira comme une entrée numérique GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;

Il existe plusieurs options pour le mode de fonctionnement du port :
GPIO_Mode_IN - entrée numérique ;
GPIO_Mode_OUT - sortie numérique ;
GPIO_Mode_AF - fonction alternative (UART, etc.);
GPIO_Mode_AN - mode analogique.

// active le pull-up au sol GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;

Les modes "pull-up" suivants sont possibles :
GPIO_PuPd_NOPULL - pas de pull-up, la sortie "pend dans les airs"
GPIO_PuPd_UP - pull-up à 3,3 V
GPIO_PuPd_DOWN - tirer au sol

// appelle la fonction d'initialisation GPIO_Init (GPIOA, & GPIO_InitStructure);

Configurons maintenant les broches auxquelles les LED sont connectées :

// Activer la synchronisation du port D RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOD, ENABLE); // Sélectionnez les broches requises GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15 ; // Activer le mode de sortie GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; // appelle la fonction d'initialisation GPIO_Init (GPIOD, & GPIO_InitStructure);

Ça y est, les ports sont configurés. Écrivons maintenant le traitement dans la boucle principale du programme :

Tant que (1) (// Si le bouton est enfoncé, alors... if (GPIO_ReadInputDataBit (GPIOA, GPIO_Pin_0) == 1) (GPIO_SetBits (GPIOD, GPIO_Pin_12) ; // Envoie "1" au délai PD12 (); / / Fonction de retard GPIO_SetBits (GPIOD, GPIO_Pin_13) ; // Acheminer "1" au délai PD13 (); GPIO_SetBits (GPIOD, GPIO_Pin_14); // Acheminer "1" au délai PD14 (); GPIO_SetBits (GPIOD, GPIO_Pin_15); // Alimenter "1" sur le délai PD15 (); GPIO_ResetBits (GPIOD, GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15); // Réinitialiser toutes les broches sur le délai "0" ();))

C'est tout, le programme est prêt. Version complète dans l'archive avec le projet. Le travail du conseil est montré dans la vidéo.

Chaque port STM32 se compose de 16 broches, et chaque broche peut être configurée de l'une des 8 manières suivantes.

Ci-dessous se trouve la structure du port I/O.


Pour que le port fonctionne, il doit être connecté au bus APB2 en réglant le bit approprié IOPxFR, dans le registre d'autorisation de cadencement des blocs périphériques RCC_APB2ENR.
RCC-> APB2ENR | = RCC_APB2ENR_IOPxEN; // Activer la synchronisation PORTx.
Après la mise sous tension, toutes les sorties sont dans l'état entrée flottante il est entrée haute impédance il est Salut-Z il est troisième état.
  • Pilote de sortie désactivé
  • Déclencheur de Schmitt désactivé
  • Résistances pull-up désactivées
  • L'IDR du registre est toujours « 0 »

En mode connexion

  • Pilote de sortie désactivé
  • Selon le réglage, les résistances pull-up sont activées.
  • Chaque cycle d'horloge du bus APB2, les données de l'entrée vont au registre IDR, en lisant ce registre, vous pouvez connaître l'état de la jambe

En mode sortie

  • En mode Open Drain, lors de l'écriture "0", le transistor inférieur s'ouvre, lors de l'écriture "1", la ligne reste déconnectée
  • En mode Push Pull, lors de l'écriture "1", le transistor supérieur s'ouvre, lors de l'écriture "0", le transistor inférieur
  • Déclencheur de Schmitt d'entrée activé
  • Résistances de pullup désactivées

En mode de fonction alternative

  • Le driver s'allume en mode Push Pull ou Open Drain, selon la configuration
  • Le pilote de sortie est piloté par des signaux périphériques, pas par le registre ODR
  • Déclencheur d'entrée Schmitt activé
  • Résistances de pullup désactivées
  • Pour chaque cycle du bus APB2, les données de la sortie sont transférées vers le registre IDR, à partir de là elles peuvent être lues en mode Open Drain
  • La lecture du registre ODR renvoie la dernière valeur écrite en mode Push Pull

On peut voir dans le tableau que deux options de configuration sont possibles, dans le mode de fonctionnement alternatif : Pousser tirer et Vidange à ciel ouvert... Par exemple, nous voulons configurer la jambe responsable de la réception des données via USART dans le mode de fonctionnement alternatif. Pour ce faire, dans le Manuel de Référence RM0008, à partir de la page 161, il existe des tableaux dans lesquels vous pouvez voir comment configurer la sortie pour différents périphériques.


Convient pour nous Entrée flottante ou alors Pull-up d'entrée.

La configuration des broches est définie dans les registres GPIOx_CRL, GPIOx_CRH, dans ces registres, 4 bits sont alloués pour la configuration de chaque broche, MODE et CNF... DANS GPIOx_CRL les broches 0 à 7 sont configurées, et en GPIOx_CRH de 8 à 15.



Si MODE = 00, alors la sortie est définie sur l'entrée, la configuration d'entrée dans ce cas est définie dans les registres CNF... Si MODE n'est pas égal à 00, auquel cas la broche est configurée comme une sortie, et la valeur MODE définit la fréquence maximale avec laquelle il peut commuter.
// Nous supposons que les broches après réinitialisation en mode d'entrée flottant // activent la synchronisation du port A RCC-> APB2ENR | = RCC_APB2ENR_IOPAEN; // entrée pull-up vers + GPIOA-> CRL & = ~ GPIO_CRL_CNF0; GPIOA-> CRL | = GPIO_CRL_CNF0_1; GPIOA-> ODR | = GPIO_ODR_ODR0 ; // entrée avec pull-up vers - GPIOA-> CRL & = ~ GPIO_CRL_CNF1; GPIOA-> CRL | = GPIO_CRL_CNF1_1; GPIOA-> ODR & = ~ GPIO_ODR_ODR1; // mode analogique GPIOA-> CRL & = ~ GPIO_CRL_CNF2; // sortie drain ouvert 2MHz GPIOA-> CRL & = ~ GPIO_CRL_CNF3; GPIOA-> CRL | = GPIO_CRL_CNF3_0; GPIOA-> CRL | = GPIO_CRL_MODE3_1; // sortie push-pull 10MHz GPIOA-> CRL & = ~ GPIO_CRL_CNF4; GPIOA-> CRL | = GPIO_CRL_MODE4_0; // fonction alternative, sortie push-pull, 50 MHz GPIOA-> CRL & = ~ GPIO_CRL_CNF5; GPIOA-> CRL | = GPIO_CRL_CNF5_1; GPIOA-> CRL | = GPIO_CRL_MODE5; // fonction alternative, sortie drain ouvert, 50 MHz GPIOA-> CRL | = GPIO_CRL_CNF6; GPIOA-> CRL | = GPIO_CRL_MODE6;
Vous pouvez lire l'état de l'entrée à l'aide du registre de données d'entrée du port ou brièvement GPIOx_IDR, où x est le nom du port, il peut aller de A à G. L'état de n'importe quelle sortie peut être lu à partir des 16 bits les moins significatifs, les 16 bits les plus significatifs ne sont pas utilisés.


// vérifier la valeur de la broche zéro du port A if (GPIOA-> IDR & GPIO_IDR_IDR0)
Si le port est configuré pour la sortie, son état peut être contrôlé à l'aide du registre de données de sortie du port ou GPIOx_ODR... La valeur que nous écrivons dans ce registre apparaîtra sur les broches correspondantes du port. Pour définir l'état du port, les 16 bits les moins significatifs sont alloués, les 16 bits les plus significatifs ne sont pas utilisés.


// si la sortie est en mode entrée, alors le pull-up vers l'alimentation est activé GPIOA-> ODR | = GPIO_ODR_ODR0; // ou à la masse GPIOA-> ODR & = ~ GPIO_ODR_ODR0; // si dans le mode de sortie, alors le niveau logique correspondant sera défini dessus // par exemple, de cette façon, vous pouvez définir toutes les sorties de port sur 1 GPIOA-> ODR = 0xFFFF;
Dans STM32, il est possible de contrôler de manière atomique les bits individuels d'un port à l'aide de registres GPIOx_BSRR(Port Bit Set / Reset Register) et GPIOx_BRR(Registre de réinitialisation de bit de port).
Pour définir manuellement un bit de port séparé, vous devez lire la valeur du port, modifier le bit souhaité à l'aide d'un masque et renvoyer le résultat à GPIOx_ODR... Puisqu'il y a trois actions, l'interruption qui se produit entre elles peut gâcher les données. En utilisant les registres décrits ci-dessus, cela se fait en une seule étape.
Pour effacer un peu, vous devez définir le bit zéro GPIOx_BRRécrire un, tandis que dans le bit zéro GPIOx_ODR 0 sera écrit, les 16 bits inférieurs sont alloués pour cette opération, les 16 bits supérieurs ne sont pas utilisés.


// réinitialiser le bit zéro du port A GPIOA-> BRR = GPIO_BRR_BR0;
AVEC GPIOx_BSRR tout est un peu plus intéressant, les 16 bits inférieurs sont responsables du réglage 1, les 16 bits supérieurs pour la remise à 0. Pour régler 1 au bit zéro, vous devez définir le bit zéro GPIOx_BSRRécriture 1. Pour définir 0 à zéro bit, définissez 1 à 16 bits.


// réinitialiser le bit zéro GPIOA-> BSRR = GPIO_BSRR_BR0; // définit le bit zéro GPIOA-> BSRR = GPIO_BSRR_BS0;
STM32 a la capacité de protéger la configuration du port contre les changements, un registre est alloué pour cela GPIOx_LCKR... Les 16 bits les moins significatifs sont utilisés pour sélectionner la broche que l'on veut bloquer (la sélection d'un bit se fait en définissant un), puis une séquence spéciale d'enregistrements 16 bits ( LCKK), le blocage est effectué.


La séquence est la suivante : écrire 1 dans LCKK, écrire 0, écrire 1, puis lire 0 dans le registre LCKR, lire 1. La dernière lecture indique que la sortie est bloquée. La sortie ne sera déverrouillée qu'après le redémarrage du contrôleur.
#include "stm32f10x.h" uint32_t temp; int main (void) (// activer la synchronisation du port RCC-> APB2ENR | = RCC_APB2ENR_IOPAEN; // le configurer en sortie push-pull GPIOA-> CRL & = ~ GPIO_CRL_CNF0; // avec fréquence maximale 50MHz GPIOA-> CRL | = GPIO_CRL_MODE0; // sélectionnez la sortie que nous voulons verrouiller GPIOA-> LCKR | = GPIO_LCKR_LCK0; // écrire 1 GPIOA-> LCKR | = GPIO_LCKR_LCKK; // écrire 0 GPIOA-> LCKR & = ~ GPIO_LCKR_LCKK; // écrire 1 GPIOA-> LCKR | = GPIO_LCKR_LCKK; // lit 2 fois temp = GPIOA-> LCKR; temp = GPIOA-> LCKR; )
Pour plus d'informations, vous pouvez contacter Manuel de référence RM0008, à la section E/S à usage général et à fonction alternative (GPIO et AFIO).

Lorsqu'un événement se produit, le contrôleur d'interruption interrompt automatiquement l'exécution du programme principal et appelle la fonction de gestion d'interruption correspondante. Après avoir quitté la fonction de gestionnaire d'interruption, le programme continue l'exécution à partir de l'endroit où l'interruption s'est produite. Tout se passe automatiquement (quand réglage correct NVIC, mais plus à ce sujet ci-dessous).

Comme son nom l'indique, le contrôleur NVIC prend en charge l'imbrication et la priorité des interruptions. Chaque interruption se voit attribuer une priorité lorsque le NVIC est configuré. Si une interruption de haute priorité se produit lors du traitement d'une interruption de basse priorité, elle interrompra à son tour le gestionnaire d'interruption de basse priorité.

Comment ça fonctionne?

Cet article n'a pas vocation à être complet, je vous conseille d'étudier la section des interruptions dans le Cortex™ -M3 Technical Reference Manual. Cette partie du noyau n'ayant pas changé, sa description est donnée dans la première révision r1p1 du noyau Cortex-M3.
Entrée et sortie d'une interruption
Lorsqu'une interruption est déclenchée, le NVIC bascule le cœur en mode de gestion des interruptions. Après être entré en mode de gestion des interruptions, les registres du noyau sont poussés sur la pile. Immédiatement lors de l'écriture des valeurs des registres dans la pile, l'adresse initiale de la fonction de gestion des interruptions est récupérée.

Le registre d'état du programme est déplacé sur la pile ( Registre d'état du programme (PSR)), compteur de programme ( Compteur de programme (PC)) et registre de liens ( Registre de liens (LR)). Voir le Cortex-M4 Generic User Guide pour une description des registres principaux. Pour cette raison, l'état dans lequel se trouvait le noyau avant d'entrer dans le mode de traitement des interruptions est mémorisé.

Les registres R0 - R3 et R12 sont également sauvegardés. Ces registres sont utilisés dans des instructions pour passer des paramètres, par conséquent, les pousser sur la pile permet de les utiliser dans une fonction de gestion des interruptions, et R12 agit souvent comme le registre de travail du programme.

À la fin du traitement de l'interruption, toutes les actions seront effectuées dans l'ordre inverse : le contenu de la pile est extrait et, en parallèle, l'adresse de retour est récupérée.

A partir du moment où l'interruption est initiée jusqu'à l'exécution de la première commande du gestionnaire d'interruption, 12 cycles d'horloge s'écoulent, le même temps est nécessaire pour reprendre le programme principal après le traitement de l'interruption.

Interrompre l'imbrication
Comme mentionné ci-dessus, NVIC prend en charge les interruptions avec des priorités différentes, qui peuvent s'interrompre les unes les autres. Dans ce cas, différentes situations peuvent se présenter, dont le traitement est optimisé de différentes manières.

1. Suspension d'une interruption de faible priorité
Dans cette situation, le traitement de l'interruption de faible priorité est terminé. Pour les 12 cycles suivants, le nouvel ensemble de données est poussé sur la pile et l'interruption de haute priorité est déclenchée. Après son traitement, le contenu de la pile est automatiquement extrait et le traitement de l'interruption de faible priorité reprend.
Il n'y a pas de grande différence à interrompre le programme principal.

2. Traitement continu des interruptions
Cette situation peut se présenter dans deux cas : si deux interruptions ont priorité égale et se produisent simultanément si une interruption de faible priorité se produit pendant que l'interruption de haute priorité est en cours de traitement.
Dans ce cas, aucune opération de pile intermédiaire n'est effectuée. Il ne charge que l'adresse du gestionnaire d'interruption de faible priorité et passe à son exécution. L'abandon des opérations de pile permet d'économiser 6 cycles d'horloge. La transition vers l'interruption suivante ne se produit pas en 12 cycles d'horloge, mais en seulement 6.

3. Latence d'interruption de haute priorité
La situation se présente si une interruption de haute priorité se produit pendant la transition vers le traitement d'une interruption de faible priorité (dans les mêmes 12 cycles d'horloge). Dans ce cas, la transition vers une interruption de haute priorité se produira au moins 6 cycles d'horloge à partir du moment de son apparition (le temps nécessaire pour charger l'adresse du gestionnaire d'interruption et y accéder). Le retour en basse priorité a déjà été décrit plus haut.

Priorités d'interruption
En plus de simplement définir la priorité des interruptions, NVIC implémente la possibilité de regrouper les priorités.
Les interruptions dans un groupe de priorité plus élevée peuvent interrompre les gestionnaires d'interruptions de groupe de priorité plus faible. les interruptions du même groupe, mais avec des priorités différentes au sein du groupe, ne peuvent pas s'interrompre. La priorité au sein du groupe ne détermine l'ordre dans lequel le gestionnaire est appelé lorsque les deux événements ont été déclenchés.

La valeur de priorité d'interruption est définie dans les registres Registres de priorité d'interruption(voir le Guide de l'utilisateur générique du Cortex-M4). Dans ce cas, certains bits sont responsables de la priorité du groupe dans lequel se trouve l'interruption, et certains - de la priorité au sein du groupe.
Le réglage de l'affectation des bits à la priorité du groupe ou à la priorité au sein du groupe s'effectue à l'aide du registre Registre de contrôle d'interruption et de réinitialisation d'application(ATTENTION !!! voir Cortex-M4 Generic User Guide).

Comme vous l'avez peut-être remarqué, le Guide de l'utilisateur générique Cortex-M4 indique que la définition des priorités et le regroupement des priorités sont spécifiques à la mise en œuvre. mise en œuvre définie.
Mais plus loin n'est pas une chose très agréable. Dans MK STM32F407, il n'y a presque aucune information sur NVIC. Mais il y a un lien vers un document séparé. Afin de comprendre l'implémentation de NVIC dans STM32, vous devrez lire un autre document -. D'une manière générale, je vous conseille d'étudier attentivement ce document et sur toutes les autres questions, il décrit le travail du noyau plus en détail que dans la documentation d'ARM.
On y trouve déjà :

Un niveau de priorité programmable de 0 à 15 pour chaque interruption. Un niveau supérieur correspond à un
priorité inférieure, donc le niveau 0 est la priorité d'interruption la plus élevée

Sur les 8 bits de priorité possibles, seuls 4 sont utilisés, mais c'est largement suffisant pour la plupart des tâches.
Masquage des interruptions
Supposons que nous soyons confrontés à la tâche de lancer le lanceur en appuyant sur le bouton rouge, mais uniquement à condition que la clé soit tournée.
Il ne sert absolument à rien de générer une interruption de tour de clé. Mais nous avons besoin d'une interruption pour appuyer sur le bouton rouge. Afin d'activer / désactiver divers vecteurs d'interruption, il existe un masquage d'interruption.
Le masquage d'interruption se fait à l'aide de registres Registres d'activation d'interruption .
Si l'interruption est masquée, cela ne veut pas dire que le périphérique ne génère pas d'événements ! C'est juste que NVIC n'appelle pas le gestionnaire pour cet événement.
Tableau vectoriel d'interruption
Toutes les interruptions possibles prises en charge par le NVIC sont enregistrées dans la table des vecteurs d'interruption. En substance, la table des vecteurs d'interruption n'est rien de plus qu'une liste d'adresses de fonctions de gestionnaire d'interruption. Le numéro dans la liste correspond au numéro d'interruption.
Créez un tableau vectoriel et placez-le au bon endroit
Pour que la table des vecteurs avec les adresses correctes des fonctions du gestionnaire d'interruption se trouve au début de la mémoire flash du MK, créez et connectez le fichier startup.c au projet.

Contenu du fichier

// Active les extensions IAR pour ce fichier source. #pragma language = extended #pragma segment = "CSTACK" // Déclaration en amont des gestionnaires d'erreurs par défaut. void ResetISR (void); vide statique NmiSR (vide); vide statique FaultISR (vide); static void IntDefaultHandler (void); // Le point d'entrée du code de démarrage de l'application. extern void __iar_program_start (void); extern void EXTI_Line0_IntHandler (void); extern void EXTI_Line6_IntHandler (void); // Une union qui décrit les entrées de la table vectorielle. L'union est nécessaire // car la première entrée est le pointeur de pile et les autres // sont des pointeurs de fonction. typedef union (void (* pfnHandler) (void); void * ulPtr;) uVectorEntry; // La table vectorielle. Notez que les constructions appropriées doivent être placées dessus pour // s'assurer qu'il se termine à l'adresse physique 0x0000.0000. __root const uVectorEntry __vector_table @ ".intvec" = ((.ulPtr = __sfe ("CSTACK")), // Le pointeur de pile initial ResetISR, // Le gestionnaire de réinitialisation NmiSR, // Le gestionnaire NMI FaultISR, // La panne matérielle gestionnaire IntDefaultHandler, // MPU Fault Handler IntDefaultHandler, // Bus Fault Handler IntDefaultHandler, // Usage Fault Handler IntDefaultHandler, // Réservé IntDefaultHandler, // Réservé IntDefaultHandler, // Réservé IntDefaultHandler // Réservé, // IntDefaultHandler // Réservé IntDefaultHandler Debug Monitor Handler IntDefaultHandler, // Réservé IntDefaultHandler, // PendSV Handler IntDefaultHandler, // SysTick Handler // Interruptions externes IntDefaultHandler, // Window WatchDog IntDefaultHandler, // PVD via la détection de ligne EXTI IntDefaultHandler, // via la ligne EXTI Tamper et TimeStamp IntDefaultHandler, // RTC Wakeup via la ligne EXTI IntDefaultHandler, // FLASH IntDefaultHandler, // RCC EXTI_Line0_IntHandler, // EXTI Line0 IntDef aultHandler, // EXTI Line1 IntDefaultHandler, // EXTI Line2 IntDefaultHandler, // EXTI Line3 IntDefaultHandler, // EXTI Line4 IntDefaultHandler, // DMA1 Stream 0 IntDefaultHandler, // DMA1 Stream 1 IntDefaultHandler, // DMA1 Stream1 et HDMA 2 Int Stream 2 IntDefaultHandler Stream 3 IntDefaultHandler, // DMA1 Stream 4 IntDefaultHandler, // DMA1 Stream 5 IntDefaultHandler, // DMA1 Stream 6 IntDefaultHandler, // ADC1, ADC2 et ADC3s IntDefaultHandler, // CAN1 TX IntH et IntDefaultHandler CAN1 , // CAN1 SCE EXTI_Line6_IntHandler, // Lignes externes IntDefaultHandler, // TIM1 Break et TIM9 IntDefaultHandler, // Mise à jour TIM1 et TIM10 IntDefaultHandler, // TIM1 Trigger and Commutation et TIM11 IntDefaultHandler, // TIM1 IntDefaultHandler IntDefault Capture, // TIM1 et IntDefaultHandler , // TIM4 IntDefaultHandler, // I2C1 Event IntDefaultHandler, // Erreur I2C1 IntDefaultHandler, // I2C2 Event IntDefaultHandler, // Erreur I2C2 IntDefaultHandler, // SPI1 IntDefaultHandler, // SPI2 IntDefaultHandler, // USART1 IntDefaultHandler, // USART2 IntDefaultHandler, // USART3 IntDefaultHandler, // Lignes externes IntDefaultHandler, // Alarme RTC (IntDefaultHandler ) via EXTI , // USB OTG FS Wakeup via la ligne EXTI IntDefaultHandler, // TIM8 Break et TIM12 IntDefaultHandler, // TIM8 Update et TIM13 IntDefaultHandler, // TIM8 Trigger and Commutation et TIM14 IntDefaultHandler, // TIM8 Capture Compare IntDefaultHandler, // DMA1 Stream7 IntDefaultHandler , // FSMC IntDefaultHandler, // SDIO IntDefaultHandler, // TIM5 IntDefaultHandler, // SPI3 IntDefaultHandler, // UART4 IntDefaultHandler, // UART5 IntDefaultHandler, // TIM6 underrun et DAC1 & 2 erreurs IntDefault2 IntDefaultHandler, // DMA2 Flux 1 IntDefaultHandler, // DMA2 Flux 2 IntDefaultHandler, // DMA2 Flux 3 IntDefaultHandler, // DMA2 Flux 4 IntDefaultH andler, // Ethernet IntDefaultHandler, // Ethernet Wakeup via la ligne EXTI IntDefaultHandler, // CAN2 TX IntDefaultHandler, // CAN2 RX0 IntDefaultHandler, // CAN2 RX1 IntDefaultHandler, // CAN2 SCE IntDefaultHandler, // USB OTG FS IntDefault2Handler, // Stream 5 IntDefaultHandler, // DMA2 Stream 6 IntDefaultHandler, // DMA2 Stream 7 IntDefaultHandler, // USART6 IntDefaultHandler, // événement I2C3 IntDefaultHandler, // Erreur I2C3 IntDefaultHandler, // USB OTG HS End Point 1 Out IntDefault, // USB Point final HS 1 dans IntDefaultHandler, // USB OTG HS Wakeup via EXTI IntDefaultHandler, // USB OTG HS IntDefaultHandler, // DCMI IntDefaultHandler, // CRYP crypto IntDefaultHandler, // Hash et Rng IntDefaultHandler, // FPU); // Il s'agit du code qui est appelé lorsque le processeur démarre pour la première fois l'exécution // après un événement de réinitialisation. Seul l'ensemble absolument nécessaire est effectué, // après quoi la routine d'entrée () fournie par l'application est appelée. Toutes les // actions sophistiquées (telles que la prise de décisions basées sur le registre de cause de réinitialisation et // la réinitialisation des bits dans ce registre) sont laissées uniquement entre les mains de // l'applicateur.

Usage
@ ".intvec" Place la __vector_table au début de la section déclarée dans le fichier de l'éditeur de liens. Le fichier lui-même peut être consulté ici :

La section elle-même est définie au début de la mémoire ROM. Les adresses peuvent être consultées (le document qui décrit l'adressage de la mémoire flash STM32) :

Combinaison de la directive IAR et de la fonction spéciale IAR :
#pragma segment = "CSTACK" __sfe ("CSTACK") Écrit un pointeur en haut de la pile au début du flash.

La table elle-même est remplie d'adresses de fonctions qui implémentent une boucle éternelle. Une exception est faite uniquement pour les fonctions qui nous intéressent :
extern void EXTI_Line0_IntHandler (void); extern void EXTI_Line6_IntHandler (void);
Dans la fonction appelée au démarrage, il va simplement à
extern void __iar_program_start (void); Cette fonction est principale (). Le symbole lui-même peut être redéfini si vous le souhaitez :

Aller au fichier principal
Tout d'abord, notons et redéfinissons toutes les adresses et champs de bits dont nous avons besoin.

Référencement

// Définitions pour le registre SCB_AIRCR #define SCB_AIRCR (* (unsigned volatile long *) 0xE000ED0C) // accès à SCB_AIRCR #define SCB_AIRCR_GROUP22 0x05FA0500 // change les données de priorité // Définitions pour RCC_AHB1_ENR_ register #define RCC (0x40023830)) RCC_AHB1ENR reg #define RCC_AHB1_ENR_GPIOA 0x1 // Champ de bits GPIOA #define RCC_AHB1_ENR_GPIOC 0x4 // Champ de bits GPIOC #define RCC_AHB1_ENR8GPI Définition ) (0x40023844)) // accès au RCC_00023844)) // accès à 0 RCC_APBdef2ENFGD_Définition RCC_APBSYSR GPIOA_MODER (* (unsigned volatile long *) (0x40020000)) (0x40020000)) // accès à GPOCERGERO (* (unsigned volatile long *) (0x40020800)) // accès à GPIOC_MODER reg #define GPIOD_MODER (* (unsigned volatile long * ) (0x40020C00)) // accès au reg GPIOD_MODER // Définition du registre GPIO ODR #define GPIOD_ODR (* (volat non signé ile long *) (0x40020C14)) // accès à GPIOD_MODER reg #define GPIO_ODR_13PIN 0x2000 #define GPIO_ODR_14PIN 0x4000 // Définitions des champs de bits #define GPIO_MODER_0BITS 0x3 // Broche 0 bits de mode #define_ GPIN0 bits_GPIN0 mode 6_MOD03 input #define 0x000 // Mode d'entrée de la broche 6 #define GPIO_MODER_13BITS 0xC000000 // Bits de mode de la broche 13 #define GPIO_MODER_13OUT 0x4000000 // Mode de sortie de la broche 13 #define GPIO_MODER_14BITS 0x30000000de // GPIO_MODER_14BITS 0x30000000de // Mode 14 registres // Mode de sortie de la broche 14 // Définition de GPIOC_PUP #define GPIOC_PUPDR (* (unsigned volatil long *) (0x4002080C)) // accès au reg GPIOC_PUPDR #define GPIOC_PUPDR_6BITS 0x3000 // Champ de bits PC6 #define GPIOC_PxDR1_6PU // pull enable // SYSCFG_EXTIx définitions de registres volatils1_EXTIx #define EXTIC *) 0x40013808) // SYSCFG_EXTICR1 acces #define SYSCFG_EXTICR1_0BITS 0xF // EXTI 0 bits #define SYSCRFG0 port CFG_EXTICR2 (* (non signé volatile long *) 0x4001380C) // SYSCFG_EXTICR2 acces #define SYSCFG_EXTICR2_6BITS 0xF00 // EXTI 6 bits #define SYSCFG_EXTICR2_6PC 0x200 // EXTI 6 définitions - port C // EXTIed longdef _40013 0x1 // LINE 0 definition #define EXTI_LINE6 0x40 // LINE 6 definition #define EXTI_RTSR (* (unsigned volatile long *) 0x40013C08) // EXTI_RTSR reg ac_ces #define * EXTI volatile long *) 0x40013C0C) // EXTI_FTSR reg acces #define EXTI_PR (* (unsigned volatile long *) 0x40013C14) // EXTI_PR reg acces // NVIC registres et définitions des bits #define NVIC_ISER0_REG (* (unsigned volatile long *) 0xE000E100) / NVIC_ISER0 reg acces #define NVIC_ISER0_6VECT 0 definition define NVIC_ISER0_23VECT 0x800000 // vect 30 definition #define NVIC_IPR0_ADD (0xE000E400) #define NVIC_IPR23_REG (char * () unsigned volatile_REG (* () unsigned volatile_REG) (unsigned volatile char *) (NVIC_IPR0)_ADD + 6)

Veuillez noter que les valeurs des registres spéciaux MK sont déclarées comme volatil... Cela est nécessaire pour que le compilateur n'essaye pas d'optimiser les opérations d'accès, car ce ne sont pas que des morceaux de mémoire et leurs valeurs peuvent changer sans la participation du noyau.

Configuration du regroupement prioritaire
Tout d'abord, il convient de configurer le regroupement des priorités d'interruption : SCB_AIRCR = SCB_AIRCR_GROUP22 ; Cette action ne doit être effectuée qu'une seule fois. Dans les projets complexes utilisant des bibliothèques tierces, cela vaut la peine de vérifier ce fait. La modification de la division des priorités en groupes peut entraîner un fonctionnement incorrect du firmware.
Activation de la synchronisation des périphériques utilisés
Permettez-moi de vous rappeler qu'avant de commencer à travailler avec des unités périphériques, vous devez activer leur synchronisation :
// Activer SYSCFG, la synchronisation des ports GPIO A et D RCC_AHB1_ENR | = RCC_AHB1_ENR_GPIOA | RCC_AHB1_ENR_GPIOC | RCC_AHB1_ENR_GPIOD; RCC_APB2_ENR | = RCC_APB2_ENR_SYSCFG ;
Vous ne pouvez pas travailler avec SYSCFG à la fois, vous devez attendre plusieurs cycles d'horloge. Mais nous ne le ferons pas. Commençons par initialiser le GPIO.
Initialisation GPIO
Les LED sont initialisées de la même manière que la dernière fois :
// Initialisation LED3 et LED5 GPIOD_MODER = (GPIOD_MODER & (~ GPIO_MODER_13BITS)) | GPIO_MODER_13OUT ; GPIOD_MODER = (GPIOD_MODER & (~ GPIO_MODER_14BITS)) | GPIO_MODER_14OUT ;
Le bouton PA0 et le contact PC7 sont initialisés en entrée :
// Initialisation des broches PA0 et PC6 GPIOA_MODER = (GPIOA_MODER & (~ GPIO_MODER_0BITS)) | GPIO_MODER_0IN; GPIOC_MODER = (GPIOC_MODER & (~ GPIO_MODER_6BITS)) | GPIO_MODER_6IN ;
C'est juste pour la broche PC6, vous devez activer le pull-up d'alimentation. Le pull-up est activé à l'aide du registre GPIOC_PUPDR :
// Activer le pull-up PC6 GPIOC_PUPDR = (GPIOC_PUPDR & (~ GPIOC_PUPDR_7BITS)) | GPIOC_PUPDR_6PU ;
Réglage EXTI
Et donc, vous devez configurer les paramètres suivants - activer les interruptions pour les lignes 0 et 6, pour la ligne 0 une interruption sur front montant, pour la ligne 6 - une interruption sur front descendant :
// Configurer EXTI EXTI_RTSR | = EXTI_LINE0; EXTI_FTSR | = EXTI_LINE6; EXTI_IMR = EXTI_LINE0 | EXTI_LINE6 ;
Il reste à configurer les broches dont les ports sont connectés à la ligne EXTI (une solution étrange, par exemple, MC stellaris peut générer une interruption pour n'importe quelle combinaison de broches, pour STM32 c'est plus difficile) :
// Connexion EXTI au port SYSCFG_EXTICR1 = (SYSCFG_EXTICR1 & (~ SYSCFG_EXTICR1_0BITS)) | SYSCFG_EXTICR1_0PA ; SYSCFG_EXTICR2 = (SYSCFG_EXTICR2 & (~ SYSCFG_EXTICR2_6BITS)) | SYSCFG_EXTICR2_6PC ;
Configuration du NVIC
Il reste à configurer les priorités des interruptions et à les masquer pour lancer le traitement. A noter que les registres NVIC_IPR sont disponibles pour l'accès aux octets, ce qui simplifie grandement l'accès uniquement aux octets prioritaires nécessaires des vecteurs d'interruption individuels. Il suffit d'effectuer un décalage de la valeur du numéro du vecteur d'interruption (voir la liste des définitions). Rappelons encore une fois que la ligne EXTI 0 a le numéro 6 dans la table vectorielle, et la ligne EXTI 5_9 a le numéro 23. Dans STM32, seuls les 4 bits de priorité les plus significatifs ont une signification :
// Définir la priorité des interruptions NVIC_IPR6_REG = 0xF0; NVIC_IPR23_REG = 0x0 ;
Pour démonstration, les priorités sont fixées différemment.
Vous pouvez maintenant activer les interruptions :
// Activer les interruptions NVIC_ISER0_REG | = NVIC_ISER0_6VECT | NVIC_ISER0_23VECT;
A partir de ce moment, l'appui sur le bouton et les courts-circuits de PC6 et GND conduiront à un appel aux fonctions de gestion d'interruption EXTI_Line0_IntHandler et EXTI_Line6_IntHandler, respectivement.
Gestion des interruptions
Dans les fonctions de gestion des interruptions, la première chose à faire est d'effacer l'interruption, après quoi les LED peuvent s'allumer. Pour démontrer les priorités des interruptions, une boucle éternelle a été ajoutée à l'un des gestionnaires. Si la priorité d'une interruption avec une boucle éternelle est inférieure à la priorité de la seconde, alors elle ne peut pas être appelée. Sinon, il peut interrompre le premier. Je vous suggère d'essayer vous-même différentes valeurs de priorités d'interruption et de voir clairement à quoi cela conduit ( ATTENTION - n'oubliez pas les groupes d'interruption !).
void EXTI_Line0_IntHandler (void) (// Effacer l'interruption EXTI_PR = EXTI_LINE0; // Allumez la LED 3 GPIOD_ODR | = GPIO_ODR_13PIN;) void EXTI_Line6_IntHandler (void) (// Effacez l'interruption EXTI_PR = EXTI_LINEPI; // Allumez la LED4 GP4_LINEPI); )

Au lieu d'une conclusion

Juste au cas où, je donnerai une liste complète du programme résultant.

Référencement

// Définitions pour le registre SCB_AIRCR #define SCB_AIRCR (* (unsigned volatile long *) 0xE000ED0C) // accès à SCB_AIRCR #define SCB_AIRCR_GROUP22 0x05FA0500 // change les données de priorité // Définitions pour RCC_AHB1_ENR_registerAHdefine RCC (0x40023830))reg acc EN_ #define RCC_AHB1_ENR_GPIOA 0x1 // Champ de bits GPIOA #define RCC_AHB1_ENR_GPIOC 0x4 // Champ de bits GPIOC #define RCC_AHB1_ENR8GPI ) (0x40023844)) // accès au registre RCC_APB2ENR reg #FGSYSC #define 0 RCC_RdefSYS2 (unsigned volatile long *) (0x40020000)) (0x40020000)) // accès à GPOCERGERO (* (unsigned volatile long *) (0x40020800)) // accès à GPIOC_MODER reg #define GPIOD_MODER (* (unsigned volatile long *) (0x40020C00 )) // accès au reg GPIOD_MODER // Définition du registre GPIO ODR #define GPIOD_ODR (* (volat non signé ile long *) (0x40020C14)) // accès à GPIOD_MODER reg #define GPIO_ODR_13PIN 0x2000 #define GPIO_ODR_14PIN 0x4000 // Définitions des champs de bits #define GPIO_MODER_0BITS 0x3 // Bits de mode Pin 0 #define_ GPIN0 mode_MOD0x3MO bits input_MOD0x3MO 0x000 // Mode d'entrée de la broche 6 #define GPIO_MODER_13BITS 0xC000000 // Bits de mode de la broche 13 #define GPIO_MODER_13OUT 0x4000000 // Mode de sortie de la broche 13 #define GPIO_MODER_14BITS 0x30000000de // GPIO_MODER_14BITS 0x30000000de // Mode 14 registres // Mode de sortie de la broche 14 // Définition de GPIOC_PUP #define GPIOC_PUPDR (* (non signé volatile long *) (0x4002080C)) // accès au reg GPIOC_PUPDR #define GPIOC_PUPDR_6BITS 0x3000 // Champ de bits PC6 #define GPIOC_PUPDR1_6PU // pull enable // SYSCFG_EXTIx définitions de registres volatils1_EXTIx #define EXTIC *) 0x40013808) // SYSCFG_EXTICR1 acces #define SYSCFG_EXTICR1_0BITS 0xF // EXTI 0 bits #define SYSCRFG0 port CFG_EXTICR2 (* (non signé volatile long *) 0x4001380C) // SYSCFG_EXTICR2 acces #define SYSCFG_EXTICR2_6BITS 0xF00 // EXTI 6 bits #define SYSCFG_EXTICR2_6PC 0x200 // EXTI 6 définitions - port C // EXTIed longdef _40013 0x1 // LINE 0 definition #define EXTI_LINE6 0x40 // LINE 6 definition #define EXTI_RTSR (* (unsigned volatile long *) 0x40013C08) // EXTI_RTSR reg ac_ces #define * EXTI volatile long *) 0x40013C0C) // EXTI_FTSR reg acces #define EXTI_PR (* (unsigned volatile long *) 0x40013C14) // EXTI_PR reg acces // NVIC registres et définitions des bits #define NVIC_ISER0_REG (* (unsigned volatile long *) 0xE000E100) / NVIC_ISER0 reg acces #define NVIC_ISER0_6VECT 0 definition define NVIC_ISER0_23VECT 0x800000 // vect 30 definition #define NVIC_IPR0_ADD (0xE000E400) #define NVIC_IPR23_REG (char * (unsigned) volatile_REG (* () unsigned volatile_REG) (unsigned volatile char *) (NVIC_IPR0)_ADD + 6) EXTI_Line0_IntHandler (vide); void EXTI_Line6_IntHandler (void); void main () (// NVIC SCB_AIRCR = SCB_AIRCR_GROUP22 ; // Activer SYSCFG, GPIO port A, C et D cadencement RCC_AHB1_ENR | = RCC_AHB1_ENR_GPIOA | RCC_AHB1_ENR_GPIOC | RCC_AHB1_ENR_GPIOD ; RCC_APB2_ENR | = RCC_APB2_ENR_SYSCFG ; // Initialisation LED3 et LED5 GPIOD_MODER = (GPIOD_MODER & (~ GPIO_MODER_13BITS)) | GPIO_MODER_13OUT ; GPIOD_MODER = (GPIOD_MODER & (~ GPIO_MODER_14BITS)) | GPIO_MODER_14OUT ; // Initialisation des broches PA0 et PC6 GPIOA_MODER = (GPIOA_MODER & (~ GPIO_MODER_0BITS)) | GPIO_MODER_0IN; GPIOC_MODER = (GPIOC_MODER & (~ GPIO_MODER_6BITS)) | GPIO_MODER_6IN ; // Activer le pull-up PC7 GPIOC_PUPDR = (GPIOC_PUPDR & (~ GPIOC_PUPDR_6BITS)) | GPIOC_PUPDR_6PU ; // Configurer EXTI EXTI_RTSR | = EXTI_LINE0; EXTI_FTSR | = EXTI_LINE6; EXTI_IMR = EXTI_LINE0 | EXTI_LINE6 ; // Connexion EXTI au port SYSCFG_EXTICR1 = (SYSCFG_EXTICR1 & (~ SYSCFG_EXTICR1_0BITS)) | SYSCFG_EXTICR1_0PA ; SYSCFG_EXTICR2 = (SYSCFG_EXTICR2 & (~ SYSCFG_EXTICR2_6BITS)) | SYSCFG_EXTICR2_6PC ; // Définir la priorité des interruptions NVIC_IPR6_REG = 0xF0; NVIC_IPR23_REG = 0x00 ; // Activer les interruptions NVIC_ISER0_REG | = NVIC_ISER0_6VECT | NVIC_ISER0_23VECT; while (1) ()) void EXTI_Line0_IntHandler (void) (// Effacer l'interruption EXTI_PR = EXTI_LINE0; // Allumer la LED 3 GPIOD_ODR | = GPIO_ODR_13PIN;) void EXTI_Line6_IntHandler (void) (// Effacer l'interruption EXTI_PR = EXTIOD_LINEO | GPIO_ODR_14PIN ; tandis que (1) ;)


Pour tester l'impact des priorités d'interruption et des priorités de groupe d'interruption, essayez de changer les priorités et observez ce qui se passe (deux bits - la priorité au sein du groupe, 2 bits - la priorité du groupe).

Mots clés:

  • STM32
  • Cortex-M
  • BRAS
  • microcontrôleurs
Ajouter des balises

Dans cet article, nous allons passer à l'analyse des étapes de gestion des ports d'E/S. Dans la documentation technique de la carte Discovery avec puce STM32f407VG il est indiqué que les leds sont connectées au port GPIOD et aux broches 12, 13, 14, 15. On regarde dans le Manuel de référence du RM0090, et on voit que les registres de contrôle du port D (GPIOD) occupent de l'espace en mémoire, à partir de adresse 0x4002 0C00. Plus loin dans la documentation, il y a une description des registres de contrôle GPIOx (où "x" est le nom du port : A, B, C ...). Première inscription - GPIOx_MODER, l'adresse de décalage est zéro, c'est-à-dire qu'elle est la première du bloc et se situe à 0x40020C00. Il est responsable de la direction du courant : vers l'entrée, la sortie ou le mode de la fonction alternative (c'est-à-dire que différentes unités intérieures/interfaces pour transmettre/recevoir des données peuvent être connectées aux pieds, et pas seulement utilisées pour / arrêt + 3V). Puisque nous devons allumer la LED, nous définissons le mode de sortie des jambes 12,13,14,15. Nous écrivons "01" dans chaque paire de bits des segments requis et écrivons le nombre résultant dans un pointeur vers le registre du moder GPIOD :

* ((uint32_t *) 0x40020C00) = 0x5500000;

On regarde plus loin, le prochain registre GPIOxOTYPER, peut être laissé par défaut. GPIOxVITESSE, la vitesse de lecture de l'état de la jambe, nous pouvons la laisser à la valeur par défaut. GPIOxPUPDR-register vous permet de "presser" la jambe à la valeur de courant positive ou négative.
GPIOx_IDR- Un registre en lecture seule, indique une valeur booléenne sur la jambe correspondante. Vous pouvez l'utiliser, par exemple, si vous voulez savoir si un bouton a été enfoncé, ou si un signal est venu à la jambe.
GPIOx_ODR- s'inscrire à la lecture et à l'écriture. L'écriture de "1" sur le bit souhaité enverra une unité logique au segment correspondant. Écrire "0" - effacera le bit et définira un zéro logique sur la jambe.
GPIOx_BSRR- Le registre met le zéro ou un logique sur les pattes. contrairement au registre GPIOx_ODR, dont la valeur doit être entièrement réécrite afin de définir un bit séparé, GPIOx_BSRR - vous pouvez définir les bits correspondants du registre GPIOx_ODR sans écraser toute sa valeur. La définition des bits 0 à 15 définira les bits 0 à 15 sur GPIOx_ODR. La définition de "1" sur les bits 16-31 définira "0" sur les bits 0-15 dans GPIOx_ODR. écrire "0" dans le registre GPIOx_BSRR n'a aucun effet.
GPIOx_LCKR- registre qui définit le verrou pour modifier les paramètres des registres de port.
GPIOx_AFRL- Registre des fonctions alternatives des jambes 0-7.
GPIOx_AFRH- Registre des fonctions alternatives des jambes 7-15.

Adresses de registre GPIO

#define RCC_GPIO * ((uint32_t *) 0x40023830) // .................
#define GPIOA_MODER * ((uint32_t *) 0x40020000)
#define GPIOA_OTYPER * ((uint32_t *) 0x40020004)
#define GPIOA_OSPEEDR * ((uint32_t *) 0x40020008)
#définir GPIOA_IDR * ((uint32_t *) 0x40020010)
#define GPIOA_PUPDR * ((uint32_t *) 0x4002000C)
#définir GPIOA_ODR * ((uint32_t *) 0x0x40020014)
#définir GPIOA_BSRR * ((uint32_t *) 0x40020018)
#define GPIOA_AFRL * ((uint32_t *) 0x40020020)
#define GPIOA_AFRH * ((uint32_t *) 0x40020024)
//.................
#define GPIOB_MODER * ((uint32_t *) 0x40020400)
#define GPIOB_OTYPER * ((uint32_t *) 0x40020404)
#define GPIOB_OSPEEDR * ((uint32_t *) 0x40020408)
#définir GPIOB_IDR * ((uint32_t *) 0x40020410)
#define GPIOB_PUPDR * ((uint32_t *) 0x4002040C)
#définir GPIOB_ODR * ((uint32_t *) 0x40020414)
#définir GPIOB_BSRR * ((uint32_t *) 0x40020418)
#définir GPIOB_AFRL * ((uint32_t *) 0x40020420)
#définir GPIOB_AFRH * ((uint32_t *) 0x40020424)
//................
#définir GPIOC_MODER * ((uint32_t *) 0x40020800)
#define GPIOC_OTYPER * ((uint32_t *) 0x40020804)
#define GPIOC_OSPEEDR * ((uint32_t *) 0x40020808)
#définir GPIOC_IDR * ((uint32_t *) 0x40020810)
#définir GPIOC_PUPDR * ((uint32_t *) 0x4002080C)
#définir GPIOC_ODR * ((uint32_t *) 0x40020814)
#définir GPIOC_BSRR * ((uint32_t *) 0x40020818)
#définir GPIOC_AFRL * ((uint32_t *) 0x40020820)
#définir GPIOC_AFRH * ((uint32_t *) 0x40020824)
//.................
#define GPIOD_MODER * ((uint32_t *) 0x40020C00)
#define GPIOD_OTYPER * ((uint32_t *) 0x40020C04)
#define GPIOD_OSPEEDR * ((uint32_t *) 0x40020C08)
#définir GPIOD_IDR * ((uint32_t *) 0x40020C10)
#define GPIOD_PUPDR * ((uint32_t *) 0x40020C0C)
#définir GPIOD_ODR * ((uint32_t *) 0x40020C14)
#définir GPIOD_BSRR * ((uint32_t *) 0x40020C18)
#définir GPIOD_AFRL * ((uint32_t *) 0x40020C20)
#définir GPIOD_AFRH * ((uint32_t *) 0x40020C24)
//................
#define GPIOE_MODER * ((uint32_t *) 0x400210000)
#define GPIOE_OTYPER * ((uint32_t *) 0x40021004)
#define GPIOE_OSPEEDR * ((uint32_t *) 0x40021008)
#définir GPIOE_IDR * ((uint32_t *) 0x40021010)
#define GPIOE_PUPDR * ((uint32_t *) 0x4002100C)
#définir GPIOE_ODR * ((uint32_t *) 0x40021014)
#définir GPIOE_BSRR * ((uint32_t *) 0x40021018)
#define GPIOE_AFRL * ((uint32_t *) 0x40021020)
#define GPIOE_AFRH * ((uint32_t *) 0x40021024)

Registres principaux du port I/O du microcontrôleur STM32

Un port fait référence à un ensemble nommé spécifique de jambes de microcontrôleur. Dans les microcontrôleurs STM, ils sont appelés GPIOA, GPIOB, GPIOC, etc. Les ports d'E / S des microcontrôleurs STM32 ont, en règle générale, 16 lignes (jambes). Une ligne désigne l'une ou l'autre branche du microcontrôleur. Chaque ligne de port peut être configurée de manière spécifique et remplir les fonctions suivantes :

  • entrée numérique;
  • sortie numérique;
  • entrée d'interruption externe ;
  • Fonction E/S d'autres modules de microcontrôleur.

Vous pouvez configurer le port pour le mode de fonctionnement souhaité à l'aide des registres du microcontrôleur. Ces registres sont directement accessibles et des méthodes spéciales de la bibliothèque périphérique peuvent être utilisées.

Jetons un coup d'œil aux principaux registres nécessaires pour travailler avec les ports d'E/S.

Registres responsables de la configuration des ports

Avant de pouvoir commencer à travailler avec un port de sortie, vous devez le configurer selon vos besoins.

Les registres de configuration sont chargés de définir ou de configurer un port. Dans les microcontrôleurs des familles STM32F302xx, STM32F303xx et STM32F313xx, ce sont les registres suivants :

  • GPIOx_MODER;
  • GPIOx_OTYPER;
  • GPIOx_OSPEEDR ;
  • GPIOx_PUPDR.

Registre GPIOx_MODER (où x = A ... F)

C'est 32 bits le registre est responsable du mode de fonctionnement de la ligne. Nécessite 2 bits pour configurer une ligne prédéfinie. Les combinaisons suivantes sont possibles :

  • 00 - la ligne est configurée pour entrer;
  • 01 - pour sortir ;
  • 10- mode de fonctionnement alternatif;
  • 11 - mode analogique.

Registre GPIOx_TYPER (où x = A ... F)


Ce registre permet de configurer le type de fonctionnement de la ligne, il utilise 16 bits en fonctionnement, les 16 restants sont réservés. Il prend les valeurs suivantes :

  • 0 - mode push-pull;
  • 1 - drain ouvert

Registre GPIOx_PUPDR (où x = A ... F)


Ce registre est responsable du pullup. Il prend les valeurs suivantes :

  • 00 - pas d'accolade
  • 01 - serrage au plus de la nourriture
  • 10 - tirer au sol

Registre GPIOx_SPEEDR (où x = A ... F)


Registre de réglage de la vitesse de ligne.

  • 00 - 2 MHz ;
  • 01 - 10 MHz ;
  • 10 - 50MHz.

Registre de sortie (registre de sortie) GPIOx_ODR (où x = A ... F) - registre de données de sortie


Ce registre est utilisé pour sortir des données vers le port. Lors de l'écriture de certaines valeurs dans ce registre, une valeur similaire est définie sur la ligne (jambe). Comme nous avons 16 lignes et que le registre a 32 bits, seuls les 16 premiers bits (les moins significatifs) sont utilisés.

Registre d'entrée (état du port ou registre de données d'entrée) GPIOx_IDR (où x = A ... F) - registre de données d'entrée


Ce registre est en lecture seule. Une sorte d'indicateur d'état du port. PINx analogique dans les microcontrôleurs AVR.

Registre de réglage au niveau du bit du port de sortie GPIOx_BSRR


Ce registre définit l'état de la broche dans le port de sortie bit par bit.

Plus de détails sur tous les registres d'un microcontrôleur spécifique peuvent être trouvés dans le referens manual.pdf qui peut être téléchargé à partir du site officiel www.st.com

Configuration du port du microcontrôleur à l'aide de la bibliothèque

De plus, le port peut être configuré à l'aide d'une bibliothèque spéciale, qui contient diverses méthodes pour travailler avec les registres d'E/S, ainsi que pour déclarer des variables spéciales. Cette bibliothèque libère le programmeur de la nécessité de calculer "manuellement" quelle valeur doit être écrite dans un registre particulier.

Regardons un exemple de code qui allume une LED

#include "stm32f30x.h" // En-tête de périphérique #include "stm32f30x_rcc.h" #include "stm32f30x_gpio.h" void init_led (void) (RCC_APB2PeriphClockCmd (GPIOE, ENABLE); GPIO_Mode_Init_Type_Def vitesse maximale GPIO_ModeDef; sortie GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; // Spécifie la branche à laquelle la LED GPIO_Init est connectée (GPIOE, GPIO_InitStructure); // Initialisation de la structure GPIOE, GPIO_Pin_8); // Définit l'état haut sur la branche tandis que (1) ( _NOP ();))

Afin d'économiser de l'énergie, tous les périphériques des microcontrôleurs sont désactivés. Pour "activer" telle ou telle périphérie, vous devez d'abord lui envoyer des signaux d'horloge.

Nous travaillons avec le port GPIOE, nous devons donc activer la synchronisation en utilisant la méthode

RCC_APB2PeriphClockCmd (uint32_t RCC_APB2Periph, FunctionalState NewState);

Ce qui prend deux valeurs, la première est le port lui-même que nous devons activer, et la seconde est l'état de ce port, activé ou désactivé.

Vous avez aimé l'article ? A partager avec des amis :