Vous êtes sur la page 1sur 15

OS temps-rel sur STM32

J.-M Friedt, 18 janvier 2016

1 Premiers pas avec le STM32


Le STM32 est un processeur ARM de la classe Cortex-M3 [1]. Ce cur se dcline de la gamme faible cot aux performances
modestes, jusquau haut de gamme comportant des interfaces ethernet et USB.
Bien que FreeRTOS [2] fournisse un certain nombre de niveaux dabstraction [3] pour fournir des mthodes sapparentant
au multitche, les fonctions de bas niveau telles que la communication asynchrone (RS23) ou synchrone (SPI/I2 C) restent la
charge du dveloppeur, quitte utiliser les fonctionnalits fournies par une bibliothque annexe (libstm32 ou libopencm3).
Nous nous engageons donc dans un premier temps sur un petit exercice de communication sur bus asynchrone afin de nous
familiariser avec le microcontrleur et en particulier une de ses subtilits qui tient dans le routage des horloges sans lesquelles
un priphrique ne peut pas fonctionner. Larchitecture de la carte est rsume dans la Fig. 1 : on y trouvera la rfrence du port
de communicatin asynchrone (USART), LEDs et convertisseur analogique-numrique (ADC) accessibles.

PC
(openocd,
stflash)

STLINK

STM32VL discovery
PC8 PC9 ADC1 USART1

F IGURE 1 Connections du STM32VL Discovery vers le monde extrieur.

2 Premiers pas : compiler et flasher


Lexemple que nous nous proposons dans un premier temps dexprimenter est le classique clignotement de diode et envoi
de trames de texte sur le port srie. En particulier, lobjectif est dillustrer linitialisation des ports (entre/sortie, fonction GPIO
ou tendue) et surtout la slection des horloges cadenant chaque priphrique (code 1). Dans cet exemple, nous utilisons les
deux bibliothques simplifiant laccs aux ressources du STM32 (en vitant dans un premier temps de consulter les 1093 pages
du manuel) : libopencm3 1 et libstm32. Nous verrons plus tard que FreeRTOS favorise la bibliothque de ST Microelectronics
libstm32.
Afin de rutiliser un maximum de code entre le programme C autonome et FreeRTOS, nous exploitons la compilation spare
pour le code dinitialisation (rutilisable) et la charge utile (spcifique chaque application).
Les codes dinitialisation sont fournis en annexe A.
BLinitialisation des liaisons asynchrones en autre chose que N (ie O ou E) ncessite de dfinir des mots de 9 bits et non de 8 !
Une fois ces initialisations effectues, envoyer un message sur USART et faire clignoter une diode sobtient en libopencm3
par
1 #include <libopencm3/cm3/common. h> // BEGIN_DECL,
#include <libopencm3/stm32/ f1 / rcc . h>
3 #include <libopencm3/stm32/ f1 / gpio . h>
#include <libopencm3/stm32/ usart . h>
5
//# define avec_newlib
7
# i f d e f avec_newlib
9 #include <errno . h>
#include < s t d i o . h>
11 #include <unistd . h>

added 150116

1. http://libopencm3.org/wiki/Main_Page mais le plus simple est de slectionner cette bibliothque lors de la compilation de sa toolchain par
https://github.com/vedderb/summon-arm-toolchain/

i n t _write ( i n t f i l e , char * ptr , i n t len ) ;


13 # endif
15 void clock_setup ( void ) ;
void usart_setup ( void ) ;
17 void gpio_setup ( void ) ;
19 i n t main( void )
{ int i , c = 0;
21
clock_setup ( ) ;
gpio_setup ( ) ;
23
usart_setup ( ) ;
while ( 1 ) {
25
gpio_toggle (GPIOC, GPIO9) ;
gpio_toggle (GPIOC, GPIO8) ;
27
c = ( c == 9) ? 0 : c + 1 ; // c y c l i c increment c
# i f n d e f avec_newlib
29
usart_send_blocking (USART1, c + 0 ) ; // USART1 : send byte
usart_send_blocking (USART1, \ r ) ;
31
usart_send_blocking (USART1, \n ) ;
#else
33
p r i n t f ( "%d\ r \n" , ( i n t ) c ) ;
# endif
35
f o r ( i = 0 ; i < 800000; i ++) __asm__ ( "NOP" ) ;
}
37
return 0 ;
}

Listing 1 Exemple en C exploitant libopencm3


ou par libstm32 au moyen de
#include " s t d i n t . h"
2 #include <sys / cdefs . h>
//#include " stm32f10x . h" // obsolete
4 #include "stm32/ gpio . h"
#include "stm32/ usart . h"
6
// Function Declarations
8 i n t main( void ) ;
void Led_Init ( void ) ;
10 void Usart1 _Ini t ( void ) ;
void uart_putc ( char c ) ;
12 void uart_puts ( char * c ) ;
14 i n t main( void )
{ i n t i , c = 0 , k =0;
16 // clock_setup ( ) ;
Led_Init ( ) ;
18
Usart1_Init ( ) ; // usart_setup ( ) ;
while ( 1 ) {
20
i f ( k==1) GPIO_SetBits (GPIOC, GPIO_Pin_9 ) ;
else
GPIO_ResetBits (GPIOC, GPIO_Pin_9 ) ;
22
k=1k ;
c = ( c == 9) ? 0 : c + 1 ; // c y c l i c increment c
24
uart_putc ( c + 0 ) ; // USART1 : send byte
uart_putc ( \ r ) ;
26
uart_putc ( \n ) ;
f o r ( i = 0 ; i < 80000; i ++) __asm__ ( "NOP" ) ;
28
}
return 0 ;
30 }

Listing 2 Exemple en C exploitant libstm32


Ayant valid la compilation de ces programmes de base et le lien avec les bibliothques appropries au moyen du Makefile
a l l : usart_stm32 . bin
2 # a l l : usart_cm3 . bin usart_stm32 . bin pwm_cm3. bin
4 usart_cm3 . bin : main_cm3 . c . . /common/usart_opencm3 . c

6
8
10
12
14
16
18
20

armnoneeabigcc Wall Wextra Wimplicitfunctiondeclaration \


Wredundantdecls Wmissingprototypes Wstrictprototypes \
Wundef Wshadow I ~/ s a t / bin / . . / armnoneeabi / include \
fnocommon mthumb mcpu=cortexm3 msoftf l o a t MD DSTM32F1 \
o usart_cm3 . o c . . /common/usart_opencm3 . c
armnoneeabigcc Wall Wextra Wimplicitfunctiondeclaration \
Wredundantdecls Wmissingprototypes Wstrictprototypes \
Wundef Wshadow I ~/ s a t / bin / . . / armnoneeabi / include \
fnocommon mthumb mcpu=cortexm3 msoftf l o a t MD DSTM32F1 \
o main_cm3 . o c main_cm3 . c
armnoneeabigcc o usart_cm3 . e l f usart_cm3 . o main_cm3 . o lopencm3_stm32f1 s t a t i c \
Wl, s t a r t group l c l g c c lnosys Wl,endgroup \
L~/ s a t /armnoneeabi / l i b /thumb/ cortexm3 L~/ s a t / bin / . . / armnoneabi / l i b \
T . / stm32vldiscovery . ld n o s t a r t f i l e s Wl,gcsections mthumb \
mcpu=cortexm3 msoftf l o a t mfixcortexm3ldrd \
L~/ s a t /armnoneeabi / l i b /thumb/ cortexm3
armnoneeabiobjcopy O binary usart_cm3 . e l f usart_cm3 . bin

22
24
26
28
30
32
34
36
38
40

pwm_cm3. bin : pwm_cm3. c


armnoneeabigcc Wall Wextra Wimplicitfunctiondeclaration \
Wredundantdecls Wmissingprototypes Wstrictprototypes \
Wundef Wshadow I ~/ s a t / bin / . . / armnoneeabi / include \
fnocommon mthumb mcpu=cortexm3 msoftf l o a t MD DSTM32F1 \
o usart_cm3 . o c . . /common/usart_opencm3 . c
armnoneeabigcc Wall Wextra Wimplicitfunctiondeclaration \
Wredundantdecls Wmissingprototypes Wstrictprototypes \
Wundef Wshadow I ~/ s a t / bin / . . / armnoneeabi / include \
fnocommon mthumb mcpu=cortexm3 msoftf l o a t MD DSTM32F1 \
o pwm_cm3. o c pwm_cm3. c
armnoneeabigcc o pwm_cm3. e l f pwm_cm3. o usart_cm3 . o lopencm3_stm32f1 s t a t i c \
Wl, s t a r t group l c l g c c lnosys Wl,endgroup \
L~/ s a t /armnoneeabi / l i b /thumb/ cortexm3 L~/ s a t / bin / . . / armnoneabi / l i b \
T . / stm32vldiscovery . ld n o s t a r t f i l e s Wl,gcsections mthumb \
mcpu=cortexm3 msoftf l o a t mfixcortexm3ldrd \
L~/ s a t /armnoneeabi / l i b /thumb/ cortexm3
armnoneeabiobjcopy O binary pwm_cm3. e l f pwm_cm3. bin

42
LDSCRIPT= ld / stm32_flash . ld
44 #LDSCRIPT= . / stm32vldiscovery . ld
46 usart_stm32 . bin : main_stm32 . c . . /common/ usart_stm32 . c system_stm32f10x . c
armnoneeabigcc DSTM32F10X_LD_VL I . / c fnocommon O0 g3 I /home/ j m f r i e d t / s a t /armnoneeabi / include / stm32f1 / I /
home/ j m f r i e d t / s a t /armnoneeabi / include / Wall Wextra mcpu=cortexm3 mthumb I /home/ j m f r i e d t / s a t /armnoneeabi /
include / include fnocommon mthumb msoftf l o a t
c o usart_stm32 . o . . /common/ usart_stm32 . c
48
armnoneeabigcc DSTM32F10X_LD_VL I . / c fnocommon O0 g3 I /home/ j m f r i e d t / s a t /armnoneeabi / include / stm32f1 / I /
home/ j m f r i e d t / s a t /armnoneeabi / include / Wall Wextra mcpu=cortexm3 mthumb I /home/ j m f r i e d t / s a t /armnoneeabi /
include / include fnocommon mthumb msoftf l o a t
c o main_stm32 . o main_stm32 . c
armnoneeabigcc fnocommon O0 g3 Wall Wextra mcpu=cortexm3 mthumb mthumb msoftf l o a t Wl,gcsections T$ (
LDSCRIPT) n o s t a r t f i l e s L~/ s a t /armnoneeabi / l i b /thumb/ cortexm3 o usart_stm32 . e l f main_stm32 . o usart_stm32 . o
lstm32 lopencm3_stm32f1
50
armnoneeabiobjcopy O binary usart_stm32 . e l f usart_stm32 . bin
52 clean :
rm * . o * . bin * . d * . e l f

Listing 3 Makefile associ pour libopencm3 et libstm32


Techniquement, il est possible de directement charger et excuter le logiciel depuis le gestionnaire de lien JTAG 2 openocd
au moyen de

openocd -s /usr/local/share/openocd/scripts/ -f board/stm32vldiscovery.cfg \


-c "init" -c "reset init" -c "stm32f1x mass_erase 0" \
-c "flash write_image `pwd`/output/main.elf" -c "reset"
2. vrifier que la carte Discovery nest pas reconnue comme un priphrique de stockage de mass dmesg doit indiquer usb-storage 1-1:1.0: device
ignored. Il est envisageable que openocd ncessite les droits dadministration pour accder au port USB. Ne pas hsiter re-essayer plusieurs fois la connexion
en cas dchec.

Cependant, cette approche a le mauvais got de faire planter le bootloader aprs cette transaction et de ncessiter de dbrancher/rebrancher le cble USB afin de relancer le Discovery Board.
Une alternative consiste passer par gdb, qui en plus nous donne accs aux outils de dverminage associs (affichage de la
pile, des valeurs des variables ...). Dans un premier terminal nous lanons
openocd -s /usr/local/share/openocd/scripts/ -f board/stm32vldiscovery.cfg qui lance le serveur. Dans un second terminal, le client gdb pour la cible approprie est lanc avec pour argument le programme (au format ELF, idalement
avec les symboles de dverminage compils par -g) et le programme est charg par

arm-none-eabi-gdb output/main.elf
Une fois gdb lanc, nous le connectons au client et chargeons le programme par
target remote localhost:3333
load
continue

En particulier, lintrt de gdb est de permettre dinterrompre lexcution du programme pour observer ltat de la pile (bt),
afficher une variable (print c) ou placer des points darrt (break fonction, par exemple break uart_putc).
En cas derreur de liaison, vrifier que la carte de dveloppement nest pas considre comme un priphrique de stockage
de masse : dmesg doit indiquer usb-storage : device ignored. Dans le cas contraire, ajouter une condition au chargement du module usb-storage en crant le fichier /etc/modprobe.d/usb-storage.conf contenant options usb-storage

quirks=483:3744:i

1. Faire clignoter alternativement les deux LEDs (PC8 et PC9). Varier la vitesse de clignotement.
2. Afficher la chane de caractres Hello World au moyen dune fonction prenant en entre un tableau de caractres.
Nous profitons de cet exemple pour sensibiliser sur limpact dutiliser inconsidrment une bibliothque. Avec lutilisation
de newlib pour une fonctionnalit telle que printf, la taille du code rsultant est

-rwxr-xr-x 1 jmfriedt jmfriedt

29136 Aug 10 13:00 usart.bin

Nous obtenons exactement le mme rsultat en crivant la main les fonctions de gestion de laffichage de chanes de
caractres, et en vitant newlib nous rduisons la taille

-rwxr-xr-x 1 jmfriedt jmfriedt

1620 Aug 10 13:00 usart.bin

Lutilisation de FreeRTOS naffranchit donc pas de lire une datasheet et sapproprier les mthodes daccs aux priphriques. Les exemples fournis dans https://github.com/libopencm3/libopencm3-examples permettent daborder sereinement cette architecture.

3 Outils dmulation et de dverminage


Nous avons vu que gdb permet de sonder les ressources du microcontrleur pendant lexcution du programme. Afin de
tester du logiciel sur du matriel inexistant ou en cours de dveloppement, une alternative aux cartes de dveloppement consiste
excuter le programme sur un mulateur. Celle fonctionnalit est par exemple fournie par qemu. Par dfaut, qemu supporte
larchitecture ARM mais pas le STM32. Une configuration de qemu pour STM32 est propose https://github.com/beckus/
qemu_stm32 qui se compile par ./configure --enable-debug --target-list="arm-softmmu" && make
La configuration ainsi propose de qemu ne supporte quun port de communication srie asynchrone (UART), et il sagit de
UART2. Nous ajoutons donc le support pour UART1 en modifiant le fichier hw/arm/stm32_p103.c pour y ajouter

DeviceState *uart1 = DEVICE(object_resolve_path("/machine/stm32/uart[1]", NULL));


assert(uart1);
stm32_uart_connect((Stm32Uart *)uart1,serial_hds[0],STM32_USART1_NO_REMAP);
Dans sa version la plus simple, qemu excute le programme. Nous pouvons demander ce que les deux ports srie soient
redirigs vers la sortie standard :

qemu-system-arm -M stm32-p103 -serial stdio -serial stdio -kernel main.bin


mais plus subtil, qemu peut se comporter comme serveur gdb. Avec loption -s, qemu attend une connexion gdb sur le port

1234 :

qemu-system-arm -M stm32-p103 -s -S -serial stdio -kernel main.bin


suivi dans un second terminal du lancement de arm-none-eabi-gdb main.elf qui se configure par

target remote localhost:1234


continue
4

4 Du C FreeRTOS
FreeRTOS (www.freertos.org) amne un niveau dabstraction additionnel par rapport la programmation en C par lajout
dun scheduler et de la notion de tches avec des priorits. Il sagit dun environnement excutif, donc une mthode de travail
qui donne limpression du point de vue du dveloppeur davoir accs des mthodes fournies par un systme dexploitation,
mais avec gnration dun binaire monolithique et donc dterministe qui ne peut pas charger dynamiquement des excutables
ou des bibliothques. FreeRTOS est un OS temps rel, signifiant quil borne les latences daccs un processus.
La multitude des processeurs dclins autour de larchitecture STM32 impose de prendre soin de bien dfinir les capacits du
processeur et les priphriques disponibles. En particulier dans le Makefile, dfinir la nature du processeur (-DSTM32F10X_LD_VL
pour les processeurs les plus petits, -DSTM32F10X_MD pour les composants de densit moyenne MD) est un point fondamental
sans lequel le programme ne peut sexcuter faute dune initialisation approprie.
En cas dchec de lexcution sur la carte STM32-Discovery, vrfier que la ligne -DSTM32F10X_LD_VL est active.

4.1 Premier exemple


Le premier exemple reproduit le rsultat du programme en C ci-dessus, mais dans le contexte de FreeRTOS, afin dapprendre
crer une tche et dy associer des actions sur GPIO et communication sur port srie asynchrone (RS232).
La principale fonction noter est la cration de tches, xTaskCreate() [2, p.6], qui prend en argument la fonction appeler,
le nom (jamais utilis), la taille de la pile alloue la tche, les arguments, la priorit de la tche, et un pointeur de retour de la
structure reprsentant la tche.
La fonction appele par xTaskCreate() ne doit jamais sarrter (boucle infinie).
1 #include " stm32f10x . h"
3 #include "FreeRTOS . h"
#include " task . h"
5 #include "queue . h"
#include " croutine . h"
7
#include <stm32/ gpio . h>
9
i n t main( void ) ;
11 void Led_Init ( void ) ;
void U sart1_Init ( void ) ;
13 void uart_putc ( char c ) ;
void uart_puts ( char * c ) ;
15 void vLedsFloat ( void * dummy) ;
void vLedsFlash ( void * dummy) ;
17 void vPrintUart ( void * dummy) ;
19 i n t main( void ) {
volatile int i ;
21 Led_Init ( ) ;
Usart1_Init ( ) ;
23
i f ( ! ( pdPASS == xTaskCreate ( vLedsFloat , ( signed char * ) " LedFloat " ,100 ,NULL, 1 0 ,NULL ) ) ) goto h e l l ;
25 i f ( ! ( pdPASS == xTaskCreate ( vLedsFlash , ( signed char * ) " LedFlash " ,100 ,NULL, 1 0 ,NULL ) ) ) goto h e l l ;
i f ( ! ( pdPASS == xTaskCreate ( vPrintUart , ( signed char * ) " Uart " ,
100 ,NULL, 1 0 ,NULL ) ) ) goto h e l l ;
27
vTaskStartScheduler ( ) ;
29 h e l l :
// should never be reached
while ( 1 ) ;
31
return 0 ;
}
33
void vLedsFloat ( void * dummy)
35 { while ( 1 ) {
GPIO_SetBits (GPIOC, GPIO_Pin_2 ) ;
37
GPIO_SetBits (GPIOC, GPIO_Pin_9 ) ;
vTaskDelay (120/portTICK_RATE_MS ) ;
39
GPIO_ResetBits (GPIOC, GPIO_Pin_2 ) ;
GPIO_ResetBits (GPIOC, GPIO_Pin_9 ) ;
41
vTaskDelay (120/portTICK_RATE_MS ) ;
}
43 }

45 void vLedsFlash ( void * dummy)


{ while ( 1 ) {
47
GPIO_SetBits (GPIOC, GPIO_Pin_1 ) ;
GPIO_SetBits (GPIOC, GPIO_Pin_8 ) ;
49
vTaskDelay (301/portTICK_RATE_MS ) ;
GPIO_ResetBits (GPIOC, GPIO_Pin_1 ) ;
51
GPIO_ResetBits (GPIOC, GPIO_Pin_8 ) ;
vTaskDelay (301/portTICK_RATE_MS ) ;
53 }
}
55
/ * Writes each 500 ms * /
57 void vPrintUart ( void * dummy)
{ portTickType last_wakeup_time ;
59 last_wakeup_time = xTaskGetTickCount ( ) ;
while ( 1 ) { uart_puts ( " Hello World\ r \n" ) ;
61
vTaskDelayUntil(&last_wakeup_time , 500/portTICK_RATE_MS ) ;
}
63 }

1basic/src/main.c

4.2 change de messages entre tches


Des tches ne sauraient vivre indpendamment les unes des autres dans le contexte dun programme manipulant des donnes communes.
1 #include " stm32f10x . h"
3 #include "FreeRTOS . h"
#include " task . h"
5 #include "queue . h"
#include " croutine . h"
7
#include <stm32/ gpio . h>
9
i n t main( void ) ;
11 void Led_Init ( void ) ;
void U sart1_Init ( void ) ;
13 void uart_putc ( char c ) ;
void uart_puts ( char * c ) ;
15 void vLedsFloat ( void * dummy) ;
void vLedsFlash ( void * dummy) ;
17 void vPrintUart ( void * dummy) ;
19
void vLedsFloat ( void * dummy)
21 { while ( 1 ) {
GPIO_SetBits (GPIOC, GPIO_Pin_2 ) ;
23
GPIO_SetBits (GPIOC, GPIO_Pin_9 ) ;
vTaskDelay (120/portTICK_RATE_MS ) ;
25
GPIO_ResetBits (GPIOC, GPIO_Pin_2 ) ;
GPIO_ResetBits (GPIOC, GPIO_Pin_9 ) ;
27
vTaskDelay (120/portTICK_RATE_MS ) ;
}
29 }
31 void vLedsFlash ( void * dummy)
{ while ( 1 ) {
33
GPIO_SetBits (GPIOC, GPIO_Pin_1 ) ;
GPIO_SetBits (GPIOC, GPIO_Pin_8 ) ;
35
vTaskDelay (301/portTICK_RATE_MS ) ;
GPIO_ResetBits (GPIOC, GPIO_Pin_1 ) ;
37
GPIO_ResetBits (GPIOC, GPIO_Pin_8 ) ;
vTaskDelay (301/portTICK_RATE_MS ) ;
39
}
}
41

/ * Writes each 500 ms * /


43 void vPrintUart ( void * dummy)
{ portTickType last_wakeup_time ;
45 last_wakeup_time = xTaskGetTickCount ( ) ;
while ( 1 ) { uart_puts ( " Hello World\ r \n" ) ;
47
vTaskDelayUntil(&last_wakeup_time , 500/portTICK_RATE_MS ) ;
}
49 }
51 xQueueHandle qh = 0 ;
53 void t a s k _ t x ( void * p)
{ i n t myInt = 0 ;
55
while ( 1 )
{ myInt++;
57
i f ( ! xQueueSend (qh , &myInt , 500) )
{ uart_puts ( " Failed to send item to queue within 500ms" ) ;
59
}
vTaskDelay (1000) ;
61
}
}
63
void t a s k _ r x ( void * p)
65 { char c [ 1 0 ] ;
i n t myInt = 0 ;
67 while ( 1 )
{ i f ( ! xQueueReceive (qh , &myInt , 1000) )
69
{ uart_puts ( " Failed to receive item within 1000 ms" ) ;
}
71
e l s e { c [ 0 ] = 0 +myInt ; c [ 1 ] = 0 ;
uart_puts ( " Received : " ) ; uart_puts ( c ) ; uart_puts ( " \ r \n" ) ;
73
}
}
75 }
77 i n t main ( )
{ Led_Init ( ) ;
79 Usart1_Init ( ) ;
81

qh = xQueueCreate ( 1 , s i z e o f ( i n t ) ) ;

83 //
85

a c t i v e r ces fonctions f a i t a t t e i n d r e l e timeout de t r a n s f e r t de donnees dans l a queue


i f ( ! ( pdPASS == xTaskCreate ( vLedsFloat , ( signed char * ) " LedFloat " , 100 , NULL, 2 , NULL ) ) ) goto h e l l ;
i f ( ! ( pdPASS == xTaskCreate ( vLedsFlash , ( signed char * ) " LedFlash " , 100 , NULL, 2 , NULL ) ) ) goto h e l l ;
i f ( ! ( pdPASS == xTaskCreate ( vPrintUart , ( signed char * ) " Uart " ,
100 , NULL, 2 , NULL ) ) ) goto h e l l ;

87
i f ( ! ( pdPASS == xTaskCreate ( task_tx , ( signed char * ) " t1 " , 100 , 0 , 2 , 0) ) ) goto h e l l ;
i f ( ! ( pdPASS == xTaskCreate ( task_rx , ( signed char * ) " t2 " , 100 , 0 , 2 , 0) ) ) goto h e l l ;
vTaskStartScheduler ( ) ;
91 h e l l : while ( 1 ) ;
return 0 ;
93 }

// attention a l a t a i l l e de l a p i l e

89

2message_passing/src/main.c
1. ajouter les tches commentes et observer la consquence en terme de gestion du temps par lordonnanceur.

4.3 Protger les changes de donnes


Le danger de partager des donnes entre tches tient en la cohrence des informations. Si une premire tche est en train de
manipuler les donnes requises par une seconde tche, les valeurs contenues dans les variables peuvent devenir incohrente.
Une mthode pour viter ce problme est de garantir que un seul processus peut accder une variable un instant donn :
accs MUTually EXclusive ou mthode mutex. Chaque accs une variable est encadre par un mutex et lexcution de la tche
nest possible que si le mutex est dbloqu.
1 #include " stm32f10x . h"
3 #include "FreeRTOS . h"

#include
5 #include
#include
7 #include

" task . h"


"queue . h"
"semphr . h"
" croutine . h"

9 i n t global =0;
//xSemaphoreHandle xMutex ;
11
i n t main( void ) ;
13 void Led_Init ( void ) ;
void U sart1_Init ( void ) ;
15 void uart_putc ( char c ) ;
void uart_puts ( char * c ) ;
17 void vLedsFloat ( void * dummy) ;
void vLedsFlash ( void * dummy) ;
19 void vPrintUart ( void * dummy) ;
21 void t a s k _ r x ( void * p)
{
char a f f [ 1 0 ] ;
23
char * t =( char * ) p ;
i n t myInt = 0 ;
25
volatile int local ;
f o r ( myInt =0; myInt <8; myInt++)
27
{
//
xSemaphoreTake ( xMutex , portMAX_DELAY ) ;
29
l o c a l =global ;
l o c a l ++;
31
uart_puts ( t ) ;
// c e t t e operation nous f a i t perdre beaucoup de temps . . . i l y a toutes l e s
33 // chances pour que l a seconde tache se lance pendant cet i n t e r v a l l e de temps . . . mais l a seconde tache
// va cherche global qui n a pas encore ete incremente et l e r e s u l t a t sera errone puisque l e s 16 taches
35 // auront f a i t une somme de 8
global= l o c a l ;
37
//
xSemaphoreGive ( xMutex ) ;
39
vTaskDelay ( ( rand ( ) & 0x1 ) ) ; // essayer de deplacer l e delay sous c e t t e } pour ne pas a l t e r n e r
}
41
a f f [ 0 ] = ; a f f [ 1 ] = global+ 0 ; a f f [ 2 ] = ; a f f [ 3 ] = 0 ; uart_puts ( a f f ) ;
while ( 1 ) vTaskDelay ( ( rand ( ) & 0x5 ) ) ; // on n a jamais l e d r o i t de f i n i r toutes l e s taches
43 }
45 i n t main ( )
{
47
Led_Init ( ) ;
Usart1_Init ( ) ;
49
srand ( 567 ) ;
//
xMutex = xSemaphoreCreateMutex ( ) ;
51
xTaskCreate ( task_rx , ( signed char * ) " t1 " , (100) , " 1111111111111111111111111111111111111111111\ r \n\0 " , 1 , 0) ;
xTaskCreate ( task_rx , ( signed char * ) " t2 " , (100) , " 2222222222222222222222222222222222222222222\ r \n\0 " , 1 , 0) ;
53
vTaskStartScheduler ( ) ;
55 h e l l :
while ( 1 ) ;
57
return 0 ;
}

texte1/main_mutex.c
Lexemple ci-dessus considre une variable globale globale qui est manipule par deux tches. Chacune de ces tches
effectue un calcul long, illustr ici par un transfert sur le port srie (une tche trs longue lchelle du temps de calcul : rappeler
la dure dun transfert de 45 caractres au dbit de 115200 bauds selon un encodage 8N1 sur bus RS232).
Afin de pallier aux dficiences observes, nous utiliserons un mutex.
1. Dcommenter les lignes contenant la dclaration des mutex, et constater la diffrence de comportement.
Attention : nous devons informer FreeRTOS de notre intention dutiliser les mutex et donc de charger les fonctionnalits associes. Nous devons pour cela ajouter la configuration #define configUSE_MUTEXES 1 dans src/include/FreeRTOSConfig.h.

4.4 Squencer les tches par les smaphores


Finalement, lexcution des tches peut tre squence sur des conditions dexcution, ou smaphores. Si ce mcanisme
nest pas implment judicieusement, il peut donner lieu des bloquages dans lequel un processus de forte priorit attend
dtre dbloqu par un processus de faible priorit qui nest jamais appel : un deadlock survient.
#include " stm32f10x . h"
2
4 #include
#include
6 #include
#include
8 #include

"FreeRTOS . h"
" task . h"
"queue . h"
"semphr . h"
" croutine . h"

10 i n t globale =0;
xSemaphoreHandle event_signal ;
12
i n t main( void ) ;
14 void Usart1 _Ini t ( void ) ;
void uart_putc ( char c ) ;
16 void uart_puts ( char * c ) ;
void vPrintUart ( void * dummy) ;
18
void task1 ( void * p)
20 {
while ( 1 ) {
22 //
i f ( xSemaphoreTake ( event_signal ,500/portTICK_RATE_MS ) ==pdFALSE)
//
uart_puts ( " not a v a i l a b l e \ r \n\0") ;
24 //
else
//
uart_puts ( "sem take \ r \n\0") ;
26
while ( globale ==0) ; // vTaskDelay ( 1/portTICK_RATE_MS ) ;
28
globale =0;
uart_puts ( "sem take \ r \n\0 " ) ;
30 // i c i on demontre que l e semaphore e s t bien f a i t car i l rend l a main en cas de blocage ,
// contrairement au cas de l a v a r i a b l e globale qui , en l absence de Delay , va bloquer sur
32 // l a tache p r i o r i t a i r e qui ne rend pas l a main
}
34 }
36 void task2 ( void * p)
{
38
while ( 1 ) {
//
xSemaphoreGive ( event_signal ) ; // debloque tache 1
40
globale =1;
uart_puts ( "sem give \ r \n\0 " ) ;
42
vTaskDelay ( 700/portTICK_RATE_MS ) ; // remplacer 400 par 700 !
}
44 }
46 i n t main ( )
{
48 Usart1_Init ( ) ;
uart_puts ( " depart \ r \n\0 " ) ;
50 //
vSemaphoreCreateBinary ( event_signal ) ;
// Create the semaphore
//
xSemaphoreTake ( event_signal , 0) ;
// Take semaphore a f t e r creating i t .
52 xTaskCreate ( task1 , ( signed char * ) " t1 " , (100) , 0 , 2 , 0) ;
xTaskCreate ( task2 , ( signed char * ) " t2 " , (100) , 0 , 1 , 0) ;
54 vTaskStartScheduler ( ) ;
56 h e l l :
while ( 1 ) ;
58
return 0 ;
}

texte1/semaphore.c
Cet exemple donne lieu de nombreuses combinaisons possibles si on se contente dessayer dmuler manuellement un
smaphore par une variable globale :
9

1. Si

xTaskCreate(task1, (signed char*)"t1", STACK_BYTES(2048), 0, 2, 0);


xTaskCreate(task2, (signed char*)"t2", STACK_BYTES(2048), 0, 1, 0);
et while (globale==0) ; alors on bloque toute excution car le producteur ne peut pas sexcuter (task1, de priorit
forte 2, est prioritaire mais fait la boucle sans attente)
2. la tche est dbloque en mettant une attente dans task1 :

while (globale==0) vTaskDelay( 1/portTICK_RATE_MS ); qui parfois donne la main task2 (qui est de faible

priorit)
3. si les priorits sont inverses par

xTaskCreate(task1, (signed char*)"t1", STACK_BYTES(2048), 0, 1, 0);


xTaskCreate(task2, (signed char*)"t2", STACK_BYTES(2048), 0, 2, 0);
et while (globale==0) ;, seule task2 (la plus prioritaire) peut sexcuter (ou uart_puts() de task1 se fait craser
par celui de task2 ?)
Donc pour rsumer, dans le cas 1, le consommateur est le plus prioritaire et le producteur na jamais le temps de sexcuter,
donc de rcuperer les donnes et les fournir (task2 devrait par exemple charger des donnes sur ADC, et task1 devrait afficher).
Dans le cas 2, tout se passe bien car le consommateur rend la main au producteur en sendormant. Dans le cas 3, le producteur
est trop prioritaire donc la donne nest jamais consomme.
En passant de la variable globale au smaphore, lexcution consommateur/producteur est ordonnance sans devoir mettre
explicitement dattente pour rendre la main. Il arrive des cas de timeout mais au moins toutes les tches sont appeles.

5 Mise en pratique
Lexemple ci-dessous est une simple lecture priodique de temprature :
1 #include "stm32/ gpio . h"
#include "stm32/ usart . h"
3 #include <stm32/ rcc . h>
#include <cmsis /stm32 . h>
5 #include <stm32/ f l a s h . h>
#include <stm32/misc . h>
7 #include <stm32/adc . h>
#include <cmsis /stm32 . h>
9 #include < s t d l i b . h>
#include < s t r i n g . h>
11
uint16_t readADC( void ) ;
13 void initADC ( void ) ;
15 ADC_InitTypeDef ADC_InitStructure ;
17 unsigned short readADC1 ( unsigned char channel )
// jmf convert ADC1
{ ADC_RegularChannelConfig (ADC1, channel , 1 , ADC_SampleTime_239Cycles5 ) ;
19 ADC_SoftwareStartConvCmd (ADC1,ENABLE) ;
while ( ADC_GetFlagStatus (ADC1,ADC_FLAG_EOC) ==RESET) ; // EOC i s s e t @ end of cv
21 return ( ADC_GetConversionValue (ADC1) ) ;
}
23
void writeHEXc ( unsigned char ptr )
25 { unsigned char b ;
b = ( ( ptr&0xf0 ) >>4) ;
27 i f ( b<10) uart_putc (b+48) ; e l s e uart_putc (b+55) ;
b = ( ( ptr&0x0f ) ) ;
29 i f ( b<10) uart_putc (b+48) ; e l s e uart_putc (b+55) ;
}
31
void writeHEXi ( uint16_t v a l )
33 { writeHEXc ( val >>8) ;
writeHEXc ( v a l&0 x f f ) ;
35 }

10

37 i n t main( void )
{ i n t i =0;
39 unsigned short tempe ;
GPIO_InitTypeDef GPIO_InitStructure ;
41
Led_Init ( ) ;
43
Usart1_Init ( ) ;
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_AFIO | RCC_APB2Periph_ADC1 ,ENABLE) ;
45
/ * Configure USART1 RX (PA. 1 0 ) as input f l o a t i n g * /
47
GPIO_InitStructure . GPIO_Pin = GPIO_Pin_10 ;
GPIO_InitStructure . GPIO_Mode = GPIO_Mode_IN_FLOATING ;
49
GPIO_Init (GPIOA, &GPIO_InitStructure ) ;
51

// Configure ADC1
ADC_DeInit (ADC1) ;

53
ADC_InitStructure .ADC_Mode=ADC_Mode_Independent ;
ADC_InitStructure . ADC_ScanConvMode=DISABLE ;
ADC_InitStructure . ADC_ContinuousConvMode=DISABLE ;
ADC_InitStructure . ADC_ExternalTrigConv=ADC_ExternalTrigConv_None ;
ADC_InitStructure . ADC_DataAlign=ADC_DataAlign_Right ;
ADC_InitStructure . ADC_NbrOfChannel=1;

55
57
59
61

ADC_Init (ADC1, &ADC_InitStructure ) ;


ADC_Cmd(ADC1, ENABLE) ;

63
ADC_TempSensorVrefintCmd (ENABLE) ;
ADC_ResetCalibration (ADC1) ;
while ( ADC_GetResetCalibrationStatus (ADC1) ) ;
ADC_StartCalibration (ADC1) ;
while ( ADC_GetCalibrationStatus (ADC1) ) ;

65
67
69

while ( 1 )
{
tempe=readADC1 ( ADC_Channel_16 ) ;
writeHEXi ( tempe ) ; uart_putc ( ) ;
f o r ( i = 0 ; i < 80000; i ++) __asm__ ( "NOP" ) ;
}

71
73
75
}

temperature/main.c
avec initialisation des ADCs, des horloges associes ainsi que des broches.
La carte son se branche sur lADC canal 1 : remplacer la mesure de temprature par une mesure de tension.
Remplacer la lecture lente de temprature par une lecture de tension la plus rapide possible.

11

440 Hz, discovery


4500

4000

tension (bits)

3500

3000

2500

2000

1500

1000
0

50

100
echantillon (sans unite)

150

200

Pour afficher une squence de donnes stockes en format hexadcimal dans un fichier ASCII, on utilisera sous GNU/Octave :

f=fopen("fichier");d=fscanf(f,"%x",inf); plot(d);

Proposer une lecture priodique de temprature dans le contexte de FreeRTOS.

6 Problme des philosophes qui dnent


Le problme des philosophes qui dnent est un exemple classique de partage de ressources. Cinq philosophes sont assis
autour dune table ronde dans un restaurant asiatique et disposent de 5 baguettes, une entre chaque philosophe. Afin de pouvoir
manger son repas, chaque philosophe a besoin de deux baguettes : celle sa droite, et celle sa gauche. Lorsquun philosophe
peut accder simultanment aux deux baguettes qui lentourent, il sen saisit et mange. Sinon, le philosophe rflchit, et tentera
de manger plus tard.
Comment reprsenter chaque philosophe dans un programme sexcutant sous FreeRTOS ?
Comment exprimer le fait quune baguette ne peut tre utilise que par un philosophe la fois ?
#include "FreeRTOS . h"
2 #include " task . h"
#include "queue . h"
4 #include "semphr . h"
#include " croutine . h"
6
# define NB_PHILO 5
8
xSemaphoreHandle xMutex [NB_PHILO ] ;
10 i n t mange[NB_PHILO ] ;
12 i n t main( void ) ;
void Led_Init ( void ) ;
14 void Usart1 _Ini t ( void ) ;
void uart_putc ( char c ) ;
16
void func ( void * p)
18 { i n t numero= * ( i n t * ) p ;
while (mange[numero ] ! = 1 )
20
{ uart_putc ( a +numero) ; vTaskDelay (500 / portTICK_RATE_MS ) ;
i f ( xSemaphoreTake ( xMutex [numero] ,500/portTICK_RATE_MS ) ==pdFALSE)

12

22

{ uart_puts ( " not a v a i l a b l e \ r \n\0 " ) ;


xSemaphoreGive ( xMutex [numero] ) ;
}
xSemaphoreTake ( xMutex [ ( numero+1)%NB_PHILO] , portMAX_DELAY ) ;
uart_putc ( A +numero) ; vTaskDelay (500 / portTICK_RATE_MS ) ;
xSemaphoreGive ( xMutex [numero] ) ;
xSemaphoreGive ( xMutex [ ( numero+1)%NB_PHILO] ) ;
uart_putc ( 0 +numero) ;
mange[numero] = 1 ;

24
26
28
30

}
while ( 1 ) { vTaskDelay (100 / portTICK_RATE_MS ) ; } ; // on n a jamais l e d r o i t de f i n i r toutes l e s taches

32
}
34

i n t main ( )
36 {
//www. f r e e r t o s . org /FreeRTOS_Support_Forum_Archive/ February_2007 / freertos_Problems_with_passing_parameters_to_task_1666309 .
html
38
static int p[5]={0 ,1 ,2 ,3 ,4};
s t a t i c char * taskNames [ 5 ] = { "P0" , "P1" , "P2" , "P3" , "P4" } ;
40
int i ;
42
Led_Init ( ) ;
Usart1_Init ( ) ;
44
f o r ( i =0; i <NB_PHILO ; i ++) xMutex [ i ] = xSemaphoreCreateMutex ( ) ;
f o r ( i =0; i <NB_PHILO ; i ++)
46
{ xTaskCreate ( func , ( const signed char const * ) taskNames [ i ] , STACK_BYTES(256) , ( void * )&p [ i ] , 1 , 0 ) ; }
vTaskStartScheduler ( ) ;
48
while ( 1 ) ;
return 0 ;
50 }

philosophes.c
Ce programme renvoie la sortie juste eabcdEBnot available14DA03C2 puisque les 5 philosophes pensent, 1 et 4 mangent
puis reposent leurs baguettes alors que 3 se fait rejeter sa demande de nourriture, puis 3 et 0 mangent pour finalement laisser 2
salimenter
Le problme de deadlock apparat si une lattence existe entre la saisie de deux baguettes. Dans ce cas, chaque philosophe
peut avoir pris la baguette sa gauche et interdire son voisin dengager le repas : les ressources sont rserves et la condition de
dblocage ne peut tre rsolue. Cet exemple est illustr en dcommentant la constante deadlock dans lexemple de phthread :
la fonction usleep() autorise lordonnanceur passer la main une autre tche alors que seule une baguette a t prise, se
traduisant par un cas de blocage. Le solution consiste ce quau bout dun certain temps, le philosophe repose la baguette quil
a saisie et se remette penser, pour r-essayer de finir son repas plus tard. Cest le sens de la squence

if (xSemaphoreTake(xMutex[numero],500/portTICK_RATE_MS)==pdFALSE)
{uart_puts("not available\r\n\0");
xSemaphoreGive( xMutex[numero] );
qui ntait pas obligatoire si on suppose que les deux requtes de mutex se font simultanment et que lordonnanceur de peut
interrompre la tche entre les deux requtes.

Rfrences
[1] RM0008 Reference manual, STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx advanced ARMbased 32-bit MCUs, (May 2011), Doc ID 13902 Rev 13
[2] R. Barry, Using the FreeRTOS real time kernel A practical guide, (2009)
[3] J.J. Labrosse, MicroC OS II : The Real Time Kernel 2nd Ed., CRC Press (2002)

13

A Initialisation des priphriques


Linitialisation de priphriques et USART par libopencm3 sobtient par
#include <libopencm3/stm32/ f1 / rcc . h>
2 #include <libopencm3/stm32/ f1 / gpio . h>
#include <libopencm3/stm32/ usart . h>
4
//# define avec_newlib
6
# i f d e f avec_newlib
8 #include <errno . h>
#include < s t d i o . h>
10 #include <unistd . h>
i n t _write ( i n t f i l e , char * ptr , i n t len ) ;
12 # endif
14 void clock_setup ( void )
{ rcc_clock_setup_in_hse_8mhz_out_24mhz ( ) ;
16
rcc_peripheral_enable_clock (&RCC_APB2ENR, RCC_APB2ENR_IOPCEN) ;
rcc_peripheral_enable_clock (&RCC_APB2ENR, RCC_APB2ENR_IOPAEN) ;
18
rcc_peripheral_enable_clock (&RCC_APB2ENR, RCC_APB2ENR_USART1EN) ;
}
20
void usart_setup ( void )
22 { // Setup GPIO pin GPIO_USART1_TX/GPIO9 on GPIO port A f o r transmit . * /
gpio_set_mode (GPIOA, GPIO_MODE_OUTPUT_50_MHZ,
24
GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX) ;
26
28
30

usart_set_baudrate (USART1, 115200) ;


u s a r t _ s e t _ d a t a b i t s (USART1, 8) ;
u s a r t _ s e t _ s t o p b i t s (USART1, USART_STOPBITS_1) ;
usart_set_mode (USART1, USART_MODE_TX) ;
u s a r t _ s e t _ p a r i t y (USART1, USART_PARITY_NONE) ;
u sa rt_ se t_fl ow _ co ntro l (USART1, USART_FLOWCONTROL_NONE) ;

32
usart_enable (USART1) ;
34 }
36 void gpio_setup ( void )
{ gpio_set_mode (GPIOC,GPIO_MODE_OUTPUT_2_MHZ,GPIO_CNF_OUTPUT_PUSHPULL, GPIO9) ; }
38
// define newlib stub
40 # i f d e f avec_newlib
i n t _write ( i n t f i l e , char * ptr , i n t len )
42 { i n t i ;
i f ( f i l e == STDOUT_FILENO | | f i l e == STDERR_FILENO) {
44
f o r ( i = 0 ; i < len ; i ++) {
i f ( ptr [ i ] == \n ) { usart_send_blocking (USART1, \ r ) ; }
46
usart_send_blocking (USART1, ptr [ i ] ) ;
}
48
return i ;
}
50
errno = EIO ;
return 1;
52 }
# endif

Listing 4 Bibliothque libopencm3


tandis que les mmes fonctions sobtiennent par libstm32 au moyen de
1 #include " stm32f10x . h"
#include "stm32/ usart . h"
3 #include "stm32/ gpio . h"
#include "stm32/ rcc . h"
5
i n t main( void ) ;
7 void Led_Init ( void ) ;
void U sart1_Init ( void ) ;

14

9 void uart_putc ( char c ) ;


void uart_puts ( char * c ) ;
11
void U sart1_Init ( void ) // I n i t s USART1 ( s e r i a l l i n e )
13 { USART_InitTypeDef
usart_i ;
USART_ClockInitTypeDef usart_c ;
15 GPIO_InitTypeDef
gpio_i ;
17

GPIO_PinRemapConfig (GPIO_Remap_USART1, DISABLE) ;


/ * Enable needed clocks * /
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_AFIO ,
ENABLE) ;
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_USART1 , ENABLE) ;
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOA , ENABLE) ;

19
21
23
25

gpio_i . GPIO_Pin
gpio_i . GPIO_Speed
gpio_i . GPIO_Mode
GPIO_Init ( GPIOA,

= GPIO_Pin_9 ; // TX GPIO
= GPIO_Speed_50MHz ;
= GPIO_Mode_AF_PP ;
&gpio_i ) ;

gpio_i . GPIO_Pin
gpio_i . GPIO_Speed
gpio_i . GPIO_Mode
GPIO_Init ( GPIOA,

= GPIO_Pin_10 ; // RX GPIO
= GPIO_Speed_50MHz ;
= GPIO_Mode_IN_FLOATING ;
&gpio_i ) ;

27
29
31
33

u s a r t _ i . USART_BaudRate
= 115200; // Configure UART 1152008N1
u s a r t _ i . USART_HardwareFlowControl
= USART_HardwareFlowControl_None ;
u s a r t _ i .USART_Mode
= USART_Mode_Rx | USART_Mode_Tx ;
u s a r t _ i . USART_Parity
= USART_Parity_No ;
u s a r t _ i . USART_StopBits
= USART_StopBits_1 ;
u s a r t _ i . USART_WordLength
= USART_WordLength_8b ;

35
37
39

usart_c . USART_Clock
usart_c .USART_CPHA
usart_c .USART_CPOL
usart_c . USART_LastBit

41
43
45

= USART_Clock_Enable ;
= USART_CPHA_1Edge ;
= USART_CPOL_Low ;
= USART_LastBit_Disable ;

USART_ClockInit (USART1, &usart_c ) ;


USART_Init (USART1, &u s a r t _ i ) ;
USART_Cmd(USART1,ENABLE) ;

47
}
49

51 void Led_Init ( void ) // Configure the LED GPIOs


{ GPIO_InitTypeDef GPIO_InitStructure ;
53
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_AFIO , ENABLE) ; // i n i t Clocks
55 RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOC , ENABLE) ;
57

GPIO_WriteBit (GPIOC, 0 x00000000 , Bit_SET ) ;


GPIO_InitStructure . GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_8 | GPIO_Pin_9 ;
GPIO_InitStructure . GPIO_Mode = GPIO_Mode_Out_PP ;
GPIO_InitStructure . GPIO_Speed = GPIO_Speed_50MHz ;
GPIO_Init (GPIOC, &GPIO_InitStructure ) ;

59
61
}
63

65 void uart_putc ( char c ) // Writes one character over the s e r i a l l i n e


{ while ( USART_GetFlagStatus (USART1, USART_FLAG_TXE) == RESET) ;
67 USART_SendData (USART1, c ) ;
}

Listing 5 Bibliothque libstm32

15