Bare-Metal STM32: Communication asynchrone universelle avec UART

L’une des interfaces de communication les plus basiques et les plus polyvalentes sur un MCU est l’UART, ou récepteur / émetteur asynchrone universel. Habituellement trouvé sous la forme d’un UART ou d’un USART, le premier permet une communication série asynchrone pure, tandis que le second ajoute un contrôle de flux. Lorsque vous travaillez avec des MCU, ils constituent également l’un des moyens les plus courants de générer des informations de débogage.

Bien qu’un peu plus délicate à configurer et à utiliser qu’un périphérique GPIO, la famille U (S) ART des familles STM32 de ST est assez simple à utiliser et fournit immédiatement un moyen facile de communiquer de manière bidirectionnelle avec un périphérique. Dans cet article, nous verrons ce qu’il faut pour démarrer avec la communication UART de base sur les microcontrôleurs STM32.

Choisissez-en un, n’importe lequel

Le choix du périphérique USART à utiliser semble assez facile, surtout lorsque les microcontrôleurs STM32F0 bas de gamme n’ont que deux USART. Autrement dit, jusqu’à ce que vous vous rendiez compte qu’ils ne sont pas tous identiques. Bien que tous prennent en charge les fonctionnalités UART de base, certains ajoutent le support USART, certains ont le mode IrDA et Smartcard.

Fonctionnalités USART sur le MCU STM32F051.

Cette liste de la fiche technique STM32F051 montre que ces deux périphériques sont assez distincts, avec de nombreuses fonctionnalités avancées uniquement sur le premier périphérique, le second étant plutôt barebones en comparaison. C’est quelque chose que nous voyons beaucoup sur les périphériques STM32, non seulement pour les USART, mais aussi pour les minuteries en particulier. Choisir le bon périphérique pour ses besoins peut être essentiel, donc savoir de quelles fonctionnalités vous avez besoin est une compétence importante.

Pour nos besoins (UART de base sans DMA), à peu près tout est permis, mais si nous voulions ajouter une fonction de lecteur de carte à puce plus tard, ou décidions que la détection automatique de la vitesse de transmission serait pratique, nous voudrions garder cela à l’esprit. Surtout lors de la conception de matériel personnalisé, choisir un ensemble de périphériques et des broches associées pour eux peut se retourner terriblement si vous devez changer cela à un stade ultérieur, ou rendre le développement ultérieur d’un produit beaucoup plus compliqué.

Allume ça

Comme pour les autres périphériques, au démarrage, le périphérique USART n’est pas alimenté. Pour changer cela, nous devons basculer un peu dans le registre RCC (Reset & Clock Control) approprié pour le bus sur lequel le périphérique est activé. Par exemple, si nous voulons utiliser USART1 sur un MCU STM32F0, nous pouvons voir qu’il se trouve sur le bus APB2 lorsque nous regardons les registres d’activation d’horloge RCC dans le manuel de référence (RM) pour le MCU en question, dans ce case RCC_APB2ENR:

RCC_APB2ENR sur STM32F042 (RM0091 6.4.7).

En écrivant un ‘1’ sur le bit 14, le domaine d’horloge du périphérique USART1 sera activé et nous pourrons utiliser ses registres. À ce stade, cependant, le périphérique n’est pas configuré et n’est pas encore activé (actif). Pour ce faire, nous devons passer par quelques étapes supplémentaires:

  • Activez la banque GPIO dont nous voulons utiliser les broches pour la communication avec le monde extérieur.
  • Définissez la vitesse de transmission dans l’USART_BRR.
  • Activez le périphérique USART en utilisant son registre d’horloge interne.
  • Configurez éventuellement les interruptions.

Alternance entre les fonctions

Les périphériques d’E / S à usage général (GPIO) n’ont pas qu’une seule fonction. Dans le cadre de leur désignation «  polyvalente  », ils sont câblés pour permettre non seulement les E / S numériques, mais également pour se connecter au contrôleur d’interruption (EXTI) et à d’autres périphériques, y compris les USART, les ADC, les DAC et ainsi de suite:

Structure des broches du port d’E / S de base sur les microcontrôleurs F0. (RM0091 8,3).

Puisque nous voulons connecter notre périphérique USART1 au monde extérieur, nous devons activer le mode de fonction alternative (AF) sur les broches que nous souhaitons utiliser. Pour cela, nous avons besoin de deux choses:

  • Quelles broches peuvent être ciblées par le MCU pour cette fonctionnalité.
  • Un moyen de définir ce mode AF sur la broche.

Pour les STM32F0, F4, F7 et les familles apparentées, c’est assez simple. Nous devons d’abord regarder le tableau avec les mappages de fonctions alternatives dans la fiche technique du MCU. Pour USART1 sur un MCU STM32F042, par exemple, nous regardons dans sa fiche technique (ici révision 5, à partir de 2017 depuis l’onglet Documentation) à la section 4 (« Brochages et descriptions des broches ») où dans le tableau 14, nous avons vu les modes AF suivants pour le port A:

Modes AF sur PA9 et PA10 pour MCU STM32042.

Les en-têtes lebeled AF[0..7] sont pour la fonction alternative 0 à 7. Nous voyons ici que nous pouvons utiliser les broches TX et RX de notre USART1 sur les broches 9 et 10 du port A en utilisant AF1. Nous avons maintenant juste besoin d’un moyen de définir cela, ce qui se fait à l’aide des registres AFRL et AFRH du périphérique GPIO. Cela signifie «  Registre des fonctions alternatives bas  » et «  Registre des fonctions alternatives haut  » respectivement, avec la moitié des 16 broches d’une banque GPIO réparties sur chacun de ces registres.

Puisque nous nous intéressons aux broches 9 et 10, nous voulons changer les valeurs dans AFSEL9 et AFSEL10 dans GPIO_AFRH en AF1 (0x1):

GPIO_AFRH sur STM32F042 avec des valeurs AF.

Une fois cela fait, nous sommes prêts à configurer le périphérique USART, juste après une note rapide sur la configuration STM32F1 AF.

Méfiez-vous du léopard

Bien qu’il y ait beaucoup de choses positives à dire à propos de la famille de microcontrôleurs STM32F1, leurs périphériques GPIO n’en font pas partie. La raison en est de nouveau apparente lorsque l’on regarde la configuration du mode AF sur une broche GPIO. Disons que nous souhaitons configurer le mode AF sur un MCU STM32F103, nous regardons d’abord dans son manuel de référence (RM0008) à la section 9.3 (‘Autre fonction E / S et configuration de débogage (AFIO)’) et passez à la section 9.3.8 (‘Remappage des fonctions alternatives USART’), choisissez notre table USART préférée (USART1, table 54) et obtenez:

Remappage USART STM32F103 (RM0008 9.3.8, tableau 54).

Pas de superposition de couches sur des couches de modes de fonction alternative ici, juste un bref «soit / ou» en raison de la structure de multiplexage limitée sur le STM32F103. Ça ne semble pas trop mal, non? La partie amusante ici est que la fonctionnalité AF n’est pas entièrement intégrée dans le périphérique GPIO, mais se trouve dans l’AFIO. Jetant un coup d’œil à la section 9.4.2, nous trouvons l’AFIO_MAPR (registre de remappage), dans lequel nous sommes censés basculer l’entrée correspondante (USART1_REMAP):

AFIO_MAPR sur STM32F103 (RM0008 9.4.2).

Bien que cela puisse ne pas sembler beaucoup plus complexe que l’approche STM32 moderne, le plus ennuyeux ici est que les modes AF sont associés au périphérique, au lieu de la broche GPIO. Au lieu de sélectionner un mode AF dans GPIO_AFRH ou GPIO_AFRL en utilisant le port, le numéro de broche et la cible AF souhaitée, sur les MCU F1, vous devez connaître le périphérique, ainsi que sa position d’entrée dans AFIO_MAPR et la broche associée à chacune de ces entrées. et périphériques.

Configuration USART

Pour récapituler, à ce stade, nous devons encore définir le débit en bauds pour l’UART et activer l’UART avant même de pouvoir envoyer un «Hello World». La configuration du débit en bauds n’est naturellement pas aussi simple que la définition du nombre souhaité dans le registre USART_BRR:

Mise en page STM32F0 USART_BRR. (RM0091 27.8.4)

Sans entrer dans trop de détails (pour cela voir par exemple 27.5.4 dans RM0091), la façon simple de remplir ce registre sans changer les paramètres par défaut (comme activer PLUS DE8 dans USART_CR1), consiste à diviser l’horloge centrale du système par le débit en bauds souhaité, puis en divisant celle-ci par 16 deux fois, d’abord pour obtenir la fraction entière, puis en utilisant une opération modulo pour obtenir le reste (appelée à tort la «  mantisse  » par ST).

Dans du code:


uint16_t uartdiv = SystemCoreClock / baudrate;
instance.regs->BRR = (((uartdiv / 16) << USART_BRR_DIV_MANTISSA_Pos) |	((uartdiv % 16) << USART_BRR_DIV_FRACTION_Pos));

Ici, le débit en bauds est le débit en bauds souhaité (par exemple 9600) et SystemCoreClock est la vitesse d’horloge système principale en Hz. En utilisant la position des positions «mantisse» et «fraction» dans USART_BRR, leurs valeurs sont décalées en bits en une valeur entière qui est ensuite écrite dans le registre.

Une fois tout le travail accompli, nous pouvons maintenant activer l’USART. Cela se fait dans le registre USART_CR1:

USART_CR1 sur STMF0. (RM0091 27.8.1)

Les bits pour passer à ‘1’ ici sont (Réception activée), TE (Activer la transmission), UE (Activer USART) et RXNEIE (Activer l’interruption RXNE). Le dernier permet la génération d’une interruption chaque fois que de nouvelles données arrivent et est facultatif pour le fonctionnement UART de base. À ce stade, nous devrions être en mesure d’envoyer et de recevoir des données, en écrivant ou en lisant à partir de GPIO_DR, respectivement.

Il est temps de dire «  bonjour  »

L’USART_SR (registre d’état) est au cœur de l’envoi et de la réception de données avec un UART:

STM32F0 Disposition du registre USART_SR. (RM0091 27.6.1)

Les éléments à rechercher ici sont:

  • TXE (Registre de transmission de données vide).
  • RXNE (Registre de réception des données non vide).

Le premier (TXE) est à vérifier à chaque fois que l’on souhaite envoyer un octet:


while (!(instance.regs->SR & USART_SR_TXE)) {};
instance.regs->DR = (uint8_t) ch;

Vice versa, pour lire il faut vérifier ce dernier (RXNE) pour savoir que les données sont disponibles pour la lecture:


if (instance.regs->SR & USART_SR_RXNE) {
    rxb = instance.regs->DR;
    instance.callback(rxb);
}

Ces extraits de code ont été extraits de la classe USART dans le framework Nodate.

Emballer

Être capable de recevoir et d’envoyer des octets uniques de cette manière n’est pas exactement la manière optimale d’utiliser un UART. Dans les prochains articles, nous examinerons l’ajout d’interruptions, les transferts DMA et le flux de contrôle (USART) et plus encore pour tirer pleinement parti de la polyvalence de ces périphériques USART.

Bien que d’une simplicité trompeuse, les USART peuvent être considérés comme un couteau suisse des périphériques de communication. Ils fonctionnent à peu près partout et peuvent être adaptés à une grande variété de tâches, qu’il s’agisse de connecter des capteurs, de fournir une interface utilisateur, de contrôler des équipements industriels ou quelque chose de plus exotique. Espérons que cet article a donné un premier aperçu de ces possibilités.

François Zipponi
Je suis François Zipponi, éditorialiste pour le site 10-raisons.fr. J'ai commencé ma carrière de journaliste en 2004, et j'ai travaillé pour plusieurs médias français, dont le Monde et Libération. En 2016, j'ai rejoint 10-raisons.fr, un site innovant proposant des articles sous la forme « 10 raisons de... ». En tant qu'éditorialiste, je me suis engagé à fournir un contenu original et pertinent, abordant des sujets variés tels que la politique, l'économie, les sciences, l'histoire, etc. Je m'efforce de toujours traiter les sujets de façon objective et impartiale. Mes articles sont régulièrement partagés sur les réseaux sociaux et j'interviens dans des conférences et des tables rondes autour des thèmes abordés sur 10-raisons.fr.