Blue Pill - USART

Table des matières

TL;DR

Mise en place d'un lien série pour du debug.

Le matériel

Un adaptateur série/usb.

Le port série

"Le port série, c'est la vie", tout administrateur Unix pourra le confirmer. S'il permet de reprendre la main sur du (gros) matériel à la configuration rendue bancale, il s'agit ici principalement de debug, histoire de confirmer que "le plan se déroule sans accroc". C'est pourquoi je ne m'occupe que de l'émission par la carte. Je ne vais pas non plus faire le foufou avec les configurations exotiques, 8N1 rulez.

Les ports série

Le tableau suivant récapitule les ports série disponibles:
NomBridgeRxTx
USART1APB2PA10PA9
USART2APB1PA3PA2
USART3APB1PB11PB10
USART1
remap
APB2PB7PB6
Va se poser un premier problème: les "bridges" APB2 et APB1 n'ont pas forcément la même vitesse. Et la vitesse est justement une des caractéristiques principales d'un lien série. Si 8 MHz est suffisant pour du 9 600 bauds, il faudra augmenter la cadence pour atteindre 115 200 bauds.

USART2 à 9 600 bauds

La configuration en émission d'un lien série est détaillée page 792 du manuel de référence qu'on peut résumer en:
  1. activer le composant USART
  2. calculer la valeur à placer dans son registre USART_BRR (Baud Rate Register)
  3. passer à 1 le bit TE (Transmission Enable) du registre USART_CR1 (Configuration Register 1)
  4. placer le caractère à envoyer dans le registre USART_DR (Data Register)
  5. attendre que le bit TC (Transmission Complete) passe à 1
La formule du calcul de la valeur de BRR: vitesse du "bridge" / vitesse en baud souhaitée. La vitesse du "bridge" correspond à SystemCoreClock / HPRE / (PPRE1 ou PPRE2 suivant l'USART). Le code suivant configure USART2 à 9 600 bauds:
#define USART_USART       USART2
#define USART_RCC_APB1EN  RCC_APB1ENR_USART2EN                    // usart 2 @APB1
#define USART_RCC_APB2EN  RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN // gpio & alternate function @APB2
#define USART_GPIO        GPIOA
#define USART_PIN_TX      2
#define USART_BAUD_RATE   9600

RCC->APB1ENR |= USART_RCC_APB1EN;
RCC->APB2ENR |= USART_RCC_APB2EN;
gpio_pin_init(USART_GPIO, USART_PIN_TX, O02_AF_PP); // max speed 02 MHz

// without config HPRE == PPRE1 == PPRE2 == 0 == not divided
// @APB1 == SystemCoreClock
uint16_t usart_div_value = SystemCoreClock / USART_BAUD_RATE;
USART_USART->BRR  = ( ((usart_div_value / 16) << 4) | ((usart_div_value % 16) << 0) );
USART_USART->CR1 |= (USART_CR1_TE | USART_CR1_UE); // enable Transmission & Usart
Je ne vais pas m'étendre sur le tableau d'erreur de la page 799 ni sur les calculs à base de mantisse et de fraction. La fonction suivante attend que le registre de données soit vide et y place le caractère à envoyer:
void
usart_putc(char c) {
    while (!(USART_USART->SR & USART_SR_TXE)); // wait for Transmit data register Empty
    USART_USART->DR = c;
}
Pour envoyer une chaine et les "\r", "\n" kivonbien:
void
usart_puts(const char * const buf) {
    const char *c = buf;

    while (*c) {
        usart_putc(*c++);
    }
    usart_putc('\r');
    usart_putc('\n');
}
Le code complet: https://gitlab.com/dsx/blue-pill/-/tree/master/stm32f103c8t6/02_usart_usart

Ce qui peut ne pas plaire

J'ai volontairement choisi USART2 pour ce premier exemple mais ce n'est pas le choix le plus pertinent: comme dans tous les cas il faut activer APB2 pour les GPIOs autant utiliser USART1. Pour du debug, 9 600 bauds est bien assez rapide pour envoyer quelques centaines de caractère toutes les secondes mais en augmentant cette vitesse on s'assure que la carte puisse avoir plus de temps pour faire des choses "utiles". 8N1 est une configuration "standard" et surtout celle par défaut, pas la peine de se compliquer la vie. Je configure toujours les GPIOs à la vitesse minimale (2 MHz) mais je ne pense pas que cela reste valable pour des hauts débits.

A suivre

Pouvoir utiliser n'importe quel USART.

A retenir

La vitesse du lien série est dépendante de la vitesse du "bridge" sur lequel est connecté l'USART.