Vous êtes sur la page 1sur 24

Microcontrôleurs II - S6

La programmation C pour l’embarqué


Principes, types de données et interruptions
Application : liaison série et systick

Frédéric Rousseau

IESE3 S6

2022–2023
C embarqué Fonctionnement et programmation Interruptions

Pourquoi du C pour l’embarqué ?


Quelles différences entre la programmation d’un
ordinateur et d’un système embarqué ?
I Le langage C a été conçu puis standardisé pour une
utilisation sur des ordinateurs. Il n’est pas toujours bien
adapté à la programmation des systèmes embarqués ... sa
principale utilisation aujourd’hui ...
I Pour assurer la portabilité des programmes, il faut s’assurer
que les types de données ne dépendent pas de
l’architecture du processeur (exemple du type int)
I Pour améliorer les performances (temps de réaction, temps
de traitement, consommation d’énergie, ...), la
programmation des systèmes embarqués doit être
événementielle (par interruption). Le C ne prévoit pas de
traitement par interruption ...
I La programmation des registres du microcontroleur ou des
périphériques suppose un contrôle total des adresses par
le programmeur (ou le compilateur - éditeur de liens).
Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023
C embarqué Fonctionnement et programmation Interruptions

Objectifs de la séance
I Types de données pour l’embarqué
I Opérations arithmétiques et logiques pour l’embarqué
I Principes de fonctionnement et de programmation
I Interruptions
I Une petite démo

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

Les types de données à utiliser


Le fichier de bibliothèque <stdint.h> contient les types
entiers pour l’embarqué

#include <stdint.h>
. . .
int8_t ...; /∗ équivalent à char ∗/
uint8_t ...; /∗ équivalent à unsigned char ∗/
int16_t ...;
uint16_t ...;
int32_t ...; /∗ Valeur entre et -2 147 483 648 et 2 147 483 647 ∗/
uint32_t ...; /∗ Valeur entre 0 et 4 294 967 295 ∗/
int64_t ...;
uint64_t ...;

/∗ exemple ∗/
int32_t var1 = 12;
uint16_t var2;

/∗ Déclaration d’un prototype de fonction ∗/


void SysTick_init(uint32_t freq);

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

Opérateurs de décalage en C

En C, on peut décaler de plusieurs bits à la fois.


I Décalage à gauche : res = valeur << nb_bits
I Décalage à gauche sur place : variable <<= nb_bits;
I Décalage à droite : res = valeur >> nb_bits
I Décalage à droite sur place : variable >>= nb_bits;
I décalage logique si
unsigned uint8_t, uint16_t, uint32_t, uint64_t
I décalage arithmétique si
signed int8_t, int16_t, int32_t, int64_t

{ uint8_t x,y;
x = 0xa5;
y = x >> 6;
x = x << y; } /∗ équivalent à: x <<= y; ∗/

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

Opérateurs bit à bit en C

Les opérateurs C bit à bit effectuent une opération logique sur


chacun des n (couples de) bits d’un ou deux mots de n bits.
I NON logique bit à bit (complément à 1) : res = ~valeur;
I ET bit à bit : res = valeur1 & valeur2;
I ET bit à bit sur place : variable &= valeur;
I OU bit à bit : res = valeur1 | valeur2;
I OU bit à bit sur place : variable |= valeur;
I OU exclusif bit à bit : res = valeur1 ^ valeur2;
I OU exclusif bit à bit sur place : variable ^= valeur;

Attention :
I Ne pas confondre avec les opérateur booléens ! && ||

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

Masques et modification de bits

I Masque OU
I mise à 1 des bits 1 du masque
I xxxxxxxx OU 00000110 = xxxxx11x
I en langage C : x |= 0x06;
I Masque ET
I mise à 0 des bits 0 du masque
I xxxxxxxx ET 10101111 = x0x0xxxx
I en langage C : x &= 0xaf;
I plus couramment : x &= ~0x50;
I Masque OUex
I inversion des bits 1 du masque
I xxxxxxxx OUex 00101000 = xxx̄xx̄xxx
I en langage C : x ^= 0x28;

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

Test de bits
I Utilisation dans les tests :
I On utilise un masque ET pour mettre à 0 les bits non testés
I On peut éventuellement décaler les bits à tester
Exemple : la fonction _putchar de la carte STM32
char _putchar(uint8_t caractere) {
/∗ Wait until bit TXE of USART = 1 (TDR not busy) ∗/
while ((USART2.SR & 0x80) == 0)
;
/∗ Write data in USART Data Reg. ∗/
USART2.DR = caractere & 0x7F;
}

Alternative avec décalage :


while (((USART2.SR >> 7) & 0x01) == 0)
;

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

Modification d’une plage de bits


Exemple : écrire les bits yyy à partit du bit 4 d’un octet xxxxxxxx
I On le fait en trois étapes :
I On utilise un masque ET NON pour mettre à 0
l’emplacement d’écriture
I (xxxxxxxx ET (NON 01110000)) = x000xxxx
I On décale les bits à écrire à la bonne position
I (00000yyy << 4) = 0yyy0000
I On ajoute les bits (masque OU) à l’emplacement d’arrivée
I (x000xxxx OU 0yyy0000) = xyyyxxxx
I Au final en C : x = ((x & ~0x70) | (y << 4))
I Spécial parano : remplacer y par (y & 0x07)
void led_init() {
/∗ Configure PA4-PA10 as outputs, push-pull, high speed ∗/
GPIOA.MODER = (GPIOA.MODER & ~(0x3FFF<<8)) | (0x1555<<8);
GPIOA.OTYPER &= ~(0x7F<<4);
GPIOA.OSPEEDR |= (0x3FFF<<8);
}

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

Quelques rappels sur les structures


Une structure en C :
I Collection indexée de variables de types différents
I Stockage contigu en mémoire (plus alignement ...)
I Chaque variable est appelée membre de la structure
I Un membre est désigné par un nom
Définition d’une structure :
struct MaStructure {
int32_t a;
int64_t c;
uint8_t b;
}; /∗ ne pas oublier le ’;’ ∗/

Ce code définit le type « struct MaStructure ».


struct MaStructure ms;
/∗ Définit une nouvelle variable ’ms’, structure possédant ∗/
/∗ un entier 32 bits, un entier long 64 bits et un caractère ∗/

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

Accès aux membres - initialisation - copie

Accès aux membres de struct MaStructure ms;

ms.a = 0;
ms.b = ’r’;

/∗ initialisation ∗/
struct MaStructure ms2 = {1, 3000000, ’z’};

/∗ copie ms dans ms2∗/


ms2 = ms;

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

Utilisation pour l’accès aux périphériques


Le fichier devices.h contient une structure par périphérique,
avec comme membres la liste des registres accessibles en
lecture ou en écriture, ainsi que les déclarations des variables.
Par exemple, pour l’USART et le systick :
struct USART_registers {
uint32_t SR;
struct STK_registers {
uint32_t DR;
uint32_t CTRL;
uint32_t BRR;
uint32_t LOAD;
uint32_t CR1;
uint32_t VAL;
uint32_t CR2;
uint32_t CALIB;
uint32_t CR3;
};
uint32_t GTPR;
};

. . .
extern volatile struct NVIC_registers NVIC;
extern volatile struct STK_registers SysTick;
extern volatile struct GPIO_registers GPIOA;
extern volatile struct USART_registers USART2;
. . .
Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023
C embarqué Fonctionnement et programmation Interruptions

Mapping en mémoire
Le fichier /link/stm32f446.ld contient les adresses associées à
chaque périphérique
. . .
NVIC = 0xE000E100;
SysTick = 0xE000E010;
. . .
GPIOA = 0x40020000;
. . .
USART2 = 0x40004400;
. . .

Ce qui permet d’utiliser la variable USART2 pour accéder aux


registres.
char _putchar(uint8_t caractere) {
/∗ Wait until bit TXE of USART = 1 (TDR not busy) ∗/
while ((USART2.SR & 0x80) == 0)
;
/∗ Write data in USART Data Reg. ∗/
USART2.DR = caractere & 0x7F;
}

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

Flot de compilation - make load


Le flot de compilation permet de compiler votre programme, de
le charger sur la carte STM32 et de lancer son exécution.

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

Flot de debug - make debug


Le flot de deboggage permet de compiler votre programme, de
le charger sur la carte STM32 et de lancer son exécution à
travers un outil de deboggage.

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

La gestion des interruptions en C - STM32

Le langage C n’a pas été prévu pour la gestion des


interruptions. Il faut donc trouver une solution pour déclarer et
définir les fonctions (routines, handler) d’interruptions, mais
aussi permettre les échanges de données entre fonction
d’interruption et programme principal.
Pour mettre en oeuvre les interruptions
I Valider les interruptions sur le périphérique, le processeur,
éventuellement le controleur NVIC
I Insérer la bonne fonction d’interruption
I Définir le contenu de la fonction d’interruption
I Utiliser une ou des variables globales pour échanger des
données avec le reste du programme

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

La validation des interruptions en C - STM32


La validation est propre à chaque périphérique.
Par exemple, 3 registres à programmer pour le systick
void SysTick_init(uint32_t freq) {
uint32_t period=get_SYSCLK()/freq;
SysTick.LOAD=((period-1)&0x00ffffff);
SysTick.VAL=0;
SysTick.CTRL|=0x7;
}

Rappel de systick_init /∗ Clear STK_VAL ∗/


systick_init: ldr r0, =#0
push (r0, r1, lr) str r0, [r1, 0x08]
ldr r1, =Systick
/∗ STK_CTRL = 0x07 ∗/
/∗ STK_LOAD = 15999 ∗/ ldr r0, =#0x07
/∗ 1ms with CLK = 16MHz ∗/ str r0, [r1]
ldr r0, =#15999
str r0, [r1, 0x04] pop {r0, r1, pc}

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

La fonction d’interruption - STM32


Le fichier /boot/interrupts.c contient la liste des fonctions d’IT
. . .
void SysTick_Handler()
__attribute__ ((weak, alias ("Default_Handler")));
. . .
void EXTI9_5_Handler()
__attribute__ ((weak, alias ("Default_Handler")));
void EXTI15_10_Handler()
__attribute__ ((weak, alias ("Default_Handler")));
. . .

Extrait de la doc gcc :


I In GNU C, you can use function attributes to declare certain things
about functions called in your program which help the compiler optimize
calls and check your code more carefully.
I For example, many targets support attributes for defining interrupt
handler functions, which typically must follow special register usage and
return conventions.
I Many of these attributes are target-specific.
Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023
C embarqué Fonctionnement et programmation Interruptions

Le mapping des fonctions d’IT - STM32


Dans le fichier interrupts.c, un tableau permet de définir la liste des
fonctions d’interruption. Dans le fichier /link/stm32_generic.ld on
indique que ce tableau doit être placé en ROM.
const __attribute(( section("interrupt_vector") ))
isr __interrupt_vector[] = {
. . .
SysTick_Handler,
. . .
EXTI9_5_Handler,
EXTI15_10_Handler,
};

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

Interruption - utilisation du systick


Initialisation et utilisation
I Les fichiers systick.h et systick.c contiennent la fonction
SysTick_init()
I L’initialisation se fait simplement par l’appel à systick_init
avec la valeur de la fréquence souhaitée (en Hz)
I La fonction de handler est SysTick_Handler

Extrait du fichier main.c


void __attribute__((interrupt)) SysTick_Handler() {
. . .
}

int main() {
. . .
/∗ Enable Systick 1 ms (1000 Hz) ∗/
SysTick_init(1000);
. . .

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

IT générée par le bouton poussoir - RAPPEL

main:
push (lr)
Contenu du main /∗ enable GPIOC ∗/
I Valider les GPIOC (PC13) bl enable_GPIOC
I Valider le contrôleur /∗ Enable SYSCFG on APB2 ∗/
système SYSFIG sur le bl enable_SYSCFG
bus APB2
/∗ enable PC13 input from button ∗/
I Utiliser pour rediriger
bl enable_button2PC13
l’une des lignes d’entrée
extérieure vers les GPIO /∗ IRQ init from PC13 ∗/
bl init_IRQ_PC13
I Valider le bouton sur PC13
loop:
I Valider les interruptions sur b loop
PC13
pop {pc}

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023


C embarqué Fonctionnement et programmation Interruptions

Interruption - Bouton poussoir de la carte fille

Initialisation et utilisation
I Le bouton poussoir est sur PB8 - init de GPIOB8
I L’initialisation se fait simplement par
int main() {
enable_GPIOB();
enable_SYSCFG();

/∗ Enable button on PB8 ∗/


button_init();

/∗ Enable interrupts (user button PB8)∗/


button_irq_init();
. . .

I La table des vecteurs d’IT indique que les IT provenant de


EXTI8 sont gérées par la fonction d’interruption
EXTI9_5_Handler() ;
Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023
C embarqué Fonctionnement et programmation Interruptions

Interruption - Bouton poussoir de la carte fille


Extrait du fichier main.c
/∗ mode = 0 is not started - mode = 1 is the running mode ∗/
static volatile uint32_t mode = 0;

void __attribute__((interrupt)) EXTI9_5_Handler() {


EXTI.PR |= (1<<8); /∗ Clear pending register EXTIPR[8] ∗/
mode = mode ^ 1; /∗ switch the mode value 0 <-> 1 ∗/
}

int main() {
enable_GPIOB();
enable_SYSCFG();
/∗ Enable button on PB8 ∗/
button_init();
/∗ Enable interrupts (user button PB8)∗/
button_irq_init();
. . .
}

Extrait Wikipedia : Une variable volatile est une variable sur laquelle aucune
optimisation de compilation n’est appliquée. Le préfixe volatile est notamment
utilisé quand la variable d’un programme peut être modifiée par ailleurs.
Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023
C embarqué Fonctionnement et programmation Interruptions

Liaison série - disponibilité de printf

La liaison série via l’USART2 est initialisée par défaut (cf


/src/sys/init.c et /src/sys/serial_io.c) à 9600 bauds. Vous
disposez alors des fonctions d’E/S comme printf, putchar, puts
dans un terminal GTKTERM. Vous disposez aussi de la
fonction scanf.
int main() {
. . .
prinf("*** Welcome to Nucleo F446 ! ***\r\n");
puts("Hello\r");

char cr;
scanf("%c", &cr);
printf("%c\r\n", cr);
. . .
}

Microcontrôleurs II - S6 — F. Rousseau Cours 6 IESE3 S6 — 2022–2023

Vous aimerez peut-être aussi