Blue Pill - Introduction

Table des matières

TL;DR

Traduction à l'aide du manuel de référence des 3 lignes du code suivant qui éteint la LED d'une carte Blue Pill:
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
GPIOC->CRH   |= GPIO_CRH_MODE13_1;
GPIOC->BSRR   = GPIO_BSRR_BS13;
La manipulation de bits en C est un pré-requis qui ne sera pas traité.

A suivre

Exercice de base

Les ressources concernant la programmation d'une carte Blue Pill ne manquent pas (la puce animant cette carte datant de 2007) et c'est plus pour mettre mes idées au clair que je partage mon expérience de ce monde fabuleux qu'est l'embarqué. A l'heure de l'informatique nuagique, des stockages toujours plus volumineux, des vitesses toujours plus rapides, il est utile de parfois prendre son temps et de revenir à quelque chose de plus "physique" (et abordable (financièrement)).

Pour appréhender une carte, l'exercice de base consiste à faire clignoter une LED (ce qui est d'autant plus facile lorsque ladite LED est intégrée à la carte). Mais avant de songer à la faire clignoter, il serait utile de savoir comment l'allumer ou l'éteindre. Une fois le schéma de la carte sous les yeux (rechercher "stm32f103 schematic bluepill"):

on peut constater pourquoi la LED verte (D2) est allumée par défaut: on part de l'alimentation (VCC3V3), puis la LED, sa résistance (R5) et enfin la broche PC13. Cette broche est inactive par défaut (donc reliée à la masse), le courant circule, la LED s'allume. Passer cette broche à l'état actif va la relier à l'alimentation, le courant ne circule plus, la LED s'éteint.
La LED rouge D1 étant directement connectée à la masse (GND) elle est allumée en permanence.

Les broches (ou PINs à partir de maintenant) d'un micro-contrôleur sont référencées par un nombre et regroupées dans des PORTs (aussi appelés GPIOs) nommés par une lettre. Les PORTs vont de 'A' à 'G' (sur une Blue Pill seuls les PORTs A, B et C sont disponibles) et les PINs vont de 0 à 15. La LED est reliée à la 13ème PIN du PORT C et cette PIN est notée "PC13" (la 6ème PIN du PORT B est notée "PB6", la 10ème du GPIO A "PA10", etc). Le code suivant passe PC13 à l'état haut/actif et éteint la LED:

     1	#include "stm32f1xx.h"
     2	
     3	int
     4	main (void) {
     5	    RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
     6	    GPIOC->CRH   |= GPIO_CRH_MODE13_1;
     7	    GPIOC->BSRR   = GPIO_BSRR_BS13;
     8	    return 0;
     9	}
On distingue assez facilement: Ces 3 lignes de code (lignes 5 à 7): Il n'est pas possible d'aller plus loin sans avoir la bible sous les yeux: Cet imposant document de 1136 pages peut sembler effrayant mais la carte offrant de très nombreuses possibilités, il est normal que son manuel de référence soit conséquent. Avant de se plonger dans sa lecture, il est nécessaire de préciser que: Qu'il soir bien clair qu'à ce stade il est parfaitement inutile de lire ce manuel, que ce soit dans l'ordre ou dans son entièreté. Il va pour l'instant servir de support pour se familiariser avec les concepts et le "jargon" STM32. On peut maintenant se rendre directement à la page 47, figure 1:

où l'on peut voir que:

La première ligne de code:
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
se compose des expressions: et se traduit par "dans le registre d'activation du bridge APB2 (APB2ENR) passer à 1 le bit correspondant à l'activation du GPIO C (RCC_APB2ENR_IOPCEN)". On peut en déduire que s'il fallait activer le deuxième bus I2C, on écrirait (page 115):
RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;
APB1ENR correspond à APB1 ENable Register (page 115) et I2C2EN à I2C2 ENable (page 116). Toutes les expressions du code proviennent du fichier d'entête "stm32f1xx.h" qui va, par le jeu de définition de macros, finir par inclure "stm32f103xb.h". Ce fichier décrit tous les composants, leurs registres et les principales valeurs à utiliser pour notre carte (pour plus de détails: compilation et chargement de la carte). La deuxième ligne de code :
GPIOC->CRH |= GPIO_CRH_MODE13_1;
se traduit par: "dans le registre de configuration supérieur (Configuration Register High, page 172) du composant GPIO C passer à 1 le bit numéro 1 des bits du mode de la PIN 13 (GPIO_CRH_MODE13_1)". Pour activer les bits numéro 0 et 1 des bits du mode de la PIN 6 du PORT B (page 171) on écrira:
GPIOB->CRL |= GPIO_CRL_MODE6_0 | GPIO_CRL_MODE6_1;
Petite subtilité: les PINs 0 à 7 sont contrôlées par le registre Configuration Register Low, les PINs de 8 à 15 par le CRH déjà vu. Une PIN ayant un mode et une configuration, pour activer par exemple le bit numéro 0 des bits de configuration de la PIN 10 du GPIOA:
GPIOA->CRH |= GPIO_CRH_CNF10_0;
Reste la dernière ligne de code:
GPIOC->BSRR = GPIO_BSRR_BS13;
"dans le registre Bit Set Reset Register (page 173) du composant GPIO C passer à 1 le bit qui active la PIN 13".
Petite subtilité: le registre BSRR est en écriture seule, il n'est donc pas correct d'utiliser l'opérateur |=. Il existe un autre registre de remise à zéro: Bit Reset Register (page 174). On pourra donc écrire à la place du code précédent:
GPIOB->BRR = GPIO_BRR_BR6;
On peut maintenant commenter le code initial:
     1	#include "stm32f1xx.h"
     2	
     3	int
     4	main (void) {
     5	    // Passe à 1 le bit numéro 4 du registre APB2 == activation du GPIO C
     6	    RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
     7	
     8	    // mode de la PIN 13 de GPIO C à 0b10 == Output mode, max speed 2 MHz
     9	    GPIOC->CRH |= GPIO_CRH_MODE13_1;
    10	
    11	    // Passe à 1 le bit numéro 13 du registre BSRR du GPIO C == passage à l'étact haut/actif de la PIN 13 du GPIO C
    12	    GPIOC->BSRR = GPIO_BSRR_BS13;
    13	
    14	    // La LED est désormais éteinte
    15	    return 0;
    16	}
qui est une version que je considère plus lisible (bien que trop commentée) mais bien moins courante que
     1	#include "stm32f1xx.h"
     2	
     3	int
     4	main (void) {
     5	    RCC->APB2ENR |= (1 <<  4);
     6	    GPIOC->CRH   |= (1 << 23);
     7	    GPIOC->BSRR   = (1 << 13);
     8	    return 0;
     9	}
où les valeurs utilisées ne sont pas particulièrement intuitives. Comme je commence à devenir bilingue en STM32, je vais tenter de respecter ce style de code:
     1	#include "stm32f1xx.h"
     2	
     3	int
     4	main (void) {
     5	    RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; // Enable GPIOC 
     6	    GPIOC->CRH   |= GPIO_CRH_MODE13_1;  // GPIOC PIN 13 mode 0b10: Output mode, max speed 2 MHz
     7	    GPIOC->BSRR   = GPIO_BSRR_BS13;     // GPIOC PIN 13 state high
     8	
     9	    // LED is off
    10	    return 0;
    11	}
Le code complet: https://gitlab.com/dsx/blue-pill/-/tree/master/stm32f103c8t6/00_gpio

Reste à compiler et charger le code dans la carte ou passer directement à la LED qui clignote (ou compter le temps qui passe).

A retenir

Un composant est activé depuis son "bridge", lui-même contrôlé par RCC. Pour activer un composant "COMP" relié au bridge "BRI" on écrira :
RCC->BRIENR |= RCC_BRIENR_COMPEN;
Un composant possède un (ou plusieurs) Configuration Register.