num´erique J.-M Friedt
Electronique num´ ´ erique
J.-M Friedt
FEMTO-ST/d´epartement temps-fr´equence jmfriedt@femto-st.fr
transparents `ajmfriedt.free.fr
22 novembre 2020
1 / 21
Electronique´ num´erique
J.-M Friedt
Plan des interventions :
7 cours/TP d’introduction au STM32 enC baremetal:
1 Electronique num´´ erique et conception du circuit
L3 : aspects analogiques, consommation ´electrique, lecture de datasheet Survol des divers p´eriph´eriques qui seront abord´es (RS232, SPI, timer, ADC) repr´esentation des donn´ees (tailles/encodage), masques, architecture
Rappels sur Atmega32U4 (Makefile, compilation, masques ...)
2 Premiers pas sur le STM32, adresses des p´eriph´eriques, architecture
3 Fonctionnement de gccet optimisations :
pr´eprocess–compilateur–assembleur–linker, passage C `a assembleur, pointeurs 4 biblioth`eques et s´eparation algorithme/mat´eriel, simulateurs
libopencm3, newlib & stubs, ressources requises par les biblioth`eques 5 Bus de communications´erie, synchrone/asynchrone
6 arithm´etique sur syst`emes embarqu´es
entiers, flottants, convertir un algorithme exprim´e en nombres `a virgules vers des entiers, timers
7 interruptions et acquisition de donn´ees analogiques, fr´equence d’´echantillonnage
vecteurs d’interruption, gestion des horloges du STM32, ADC
2 / 21
num´erique
J.-M Friedt
Optimisations du code C
Taille des variables1
• (unsigned) char: 8 bits
• (unsigned) short: 16 bits
• (unsigned) long: 32 bits
• (unsigned long long): 64 bits
• virgule flottante simple pr´ecisionfloatet double pr´ecisiondouble Utiliser le type pr´esentant lemeilleur compromis espace/pr´ecision des donn´ees (analyse rationnelle)
Types d´eriv´es explicitent la taille et la nature de la variable
/usr/include/stdint.h :
/∗ISO C99 : 7 . 1 8 I n t e g e r t y p e s<s t d i n t . h>∗/
t y p e d e f s i g n e d c h a r i n t 8 t ; t y p e d e f s h o r t i n t i n t 1 6 t ;
t y p e d e f i n t i n t 3 2 t ;
# i f WORDSIZE == 64
t y p e d e f l o n g i n t i n t 6 4 t ;
# e l s e e x t e n s i o n
t y p e d e f l o n g l o n g i n t i n t 6 4 t ;
# e n d i f
#e n d i f
Types d´eriv´es explicitent la taille et la nature de la variable
/usr/lib/avr/include/stdint.h :
/∗ISO / IEC 9 8 9 9 : 1 9 9 9 7 . 1 8 I n t e g e r t y p e s∗/ /∗∗8−b i t s i g n e d t y p e . ∗/
t y p e d e f s i g n e d c h a r i n t 8 t ; /∗∗8−b i t u n s i g n e d t y p e . ∗/
t y p e d e f u n s i g n e d c h a r u i n t 8 t ; /∗∗16−b i t s i g n e d t y p e . ∗/
t y p e d e f s i g n e d i n t i n t 1 6 t ; /∗∗16−b i t u n s i g n e d t y p e . ∗/
t y p e d e f u n s i g n e d i n t u i n t 1 6 t ;
t y p e d e f s i g n e d l o n g i n t i n t 3 2 t ; // 32 s i g n e d
1. Atmel AVR4027,Tips and Tricks to Optimize Your C Code for 8-bit AVR
Microcontrollers, Rev. 8453A-AVR-11/11 3 / 21
Electronique´ num´erique
J.-M Friedt
Virgule fixe/virgule flottante
• Repr´esentation d’un nombre en virgule flottante :
• mantisse/exposant (mantisse∈[0,1−1[), similaire `a la notation scientifique 0,1234×103
• supporte une large gamme de valeurs, au d´etriment de la pr´ecision (codage de la position de la virgule)
• exposant 8 bits-mantisse 23 bits (float), 11-52 (double) qui code donc 2±1024'10±308 sur 15 d´ecimales (252'4×1015)
• ⇒arithm´etique complexe, addition passe par la d´enormalisation pour avoir le mˆeme exposant (donc des 0 en d´ebut de mantisse du nombre le plus petit), multiplication ais´ee
• impl´ementation mat´erielle sur processeurs haut de gamme (FPU)
• Repr´esentation en virgule fixe : homoth´etie pour se ramener `a des calculs sur des entiers.
• on note Qm.n pour repr´esenter la partie enti`ere sur m bits et la partie fractionnaire sur n bits2 (notation en compl´ement `a deux, donc inclut un bit de signe)
2. Maxim, Application Note 3722 : Developing FFT Applications with Low-Power Microcontrollers (2006)
4 / 21
num´erique
J.-M Friedt
Op´ erations sur les flottants
• flottant = mantisse·2exposant
• ⇒multiplication somme les exposants et multiplie les mantisses (probl`eme de pr´ecision)
• ⇒sommer n´ecessite d’aligner les mantisses en ajustant les exposants
• en l’absence de FPU, op´erations logicielles gourmandes en ressources
Exemple sur STM32F1 (pas de FPU)3:
Op´eration dur´ee (µs)
ADC →entier 1,73
ADC→flottant 3,36
entier×106/106 1,08 entier (<<20)(>>20) 0,83 IIR `a 2 coefficients flottants 30
IIR `a 2 coefficients entiers 3
3. donn´ees acquises par G. Matten, LMA, FEMTO-ST
5 / 21
Electronique´ num´erique
J.-M Friedt
Impr´ ecision de l’arithm´ etique ` a
virgule flottante
i n t main ( )
{v o l a t i l e f l o a t f 1 = 0 . 1 , f 2 = 1 0 . ; v o l a t i l e d o u b l e d1 = 0 . 1 , d2 = 1 0 . ; i n t k ;
f o r ( k =0; k<1000; k++) {f 2+=f 1 ;
d2+=d1 ; }
p r i n t f (" % f % lf % 0 . 9 lf \ n ", f 2 , d2 , (d o u b l e) ( d2−f 2 ) ) ; }
Alignement des exposants : un nombre flottant n’est pas exact et perd en pr´ecision lors d’op´erations sur des ordres de grandeur tr`es diff´erents.
R´eponse : 109.998894 110.000000 0.001106262
6 / 21
num´erique
J.-M Friedt
Cas de la fonction affine
1 Un microcontrˆoleur manipule une donn´ee dans sa repr´esentation : une fr´equence de DDS est comprise entre 0 etfCK, repr´esent´ee par [0..2N−1],
2 un humain veut connaˆıtre une fr´eqeunce en Hz fHz =mot
2N ·fCK 3 application num´erique,fCK = 70 MHz etN= 28
⇒70·106/228= 0.260770320892334
4 GNU/Octave (Matlab) :rats(0.260770320892334,5)=6/23et rats(0.260770320892334,8)=914/3505
5 mot∈[0..228]⇒mot×914 sur 38 bits
6 mais si la bande de fr´equence est connue (e.g. 8-10 MHz), bits de poids fort peuvent ˆetre ´elimin´es
(mot&0x003fffff)*914/3505sera exacte, tandis que
0x00400000*914/3505 est pr´ecalcul´e (bits de poids forts connus)
7 si la fr´equence est impr´ecise, bits de poids faible peuvent ˆetre
´
elimin´es : (mot>>8)*914/3505perd les 100 derniers Hz
7 / 21
Electronique´ num´erique
J.-M Friedt
Exemple de capteur
• soit une mesure de fr´equencef issue d’un capteur oscillant, dont la fr´equence de vibration change avec la temp´eratureT
• sachant quef '1 MHz avec une pr´ecision de 10 Hz, on veut obtenir la temp´erature `a 0,1 K pr`es :
300000 400000 500000 600000 700000 800000 900000 1e+06
-200 -150 -100 -50 0 50 100 150
frequence (Hz)
temperature (degC)
-40 -20 0 20 40 60 80
700000 750000 800000 850000 900000 950000 1e+06
temperature (degC)
frequence (Hz)
f =−6,6667×T2+ 2000×T+ 916670 T=−150 +√
160000−0,15×f
Exprimer cette loi d’´etalonnage avec des coefficients entiers Rappel : la suitexn+1= 12
xn+xy
n
converge vers√ y
(m´ethode de Newton surx2−y= 0 avecxn+1 =xn−f/f0)
8 / 21
num´erique
J.-M Friedt
Application des coefficients
d’´ etalonnage
• la loi liant fr´equence et temp´erature est du type
f =αT2+βT+γ⇔αT2+βT+γ−f = 0===racine⇒T =A±√ B+Cf T =A+√
B+C·f Hypoth`eses :
• f '106(1 MHz)
• f `a 10 Hz pr`es
• A'100
• C '0,1
• T `a 0,1 K pr`es
9 / 21
Electronique´ num´erique
J.-M Friedt
Application des coefficients
d’´ etalonnage
T =A+√
B+C·f
Exemple de calcul en virgule fixe compte tenu de la pr´ecision recherch´ee :
1 (T−A)2=B+C×f donc4 2(T−A)dT−2(T−A)dA= dB+dC ×f ⇒ |dT|=
dB 2(T−A)
+
dC×f 2(T−A)
+|dA|
2 on doit donc travailler avec chacun de ces termes inf´erieurs `a 0,1.
3 Application num´erique : par conception,T −A≥100 etf ≤106 doncdB≤10,dA≤0,1 et dC ≤10−5
4 on va donc travailler surA×10 etC×105 pour respecter la r´esolution recherch´ee sur des calculs entiers
5 cependant,C '0,1 doncC×105×f '1010 qui n´ecessite 34 bits,
6 mais f `a 10 Hz donc on peut travailler sur C×105×(f/10)'109 qui ne n´ecessite que 30 bits
4. d´eriv´ees partielles selon les inconnuesA,B,C,Tpuisque la mesuref est parfaitement connue doncdf = 0
10 / 21
Electronique´ num´erique
J.-M Friedt
Application des coefficients
d’´ etalonnage
T =A+√
B+C·f
Exemple de calcul en virgule fixe compte tenu de la pr´ecision recherch´ee :
1 (T−A)2=B+C×f donc 2(T −A)dT−2(T−A)dA= dB+dC ×f ⇒ |dT|=
dB 2(T−A)
+
dC×f 2(T−A)
+|dA|
2 on doit donc travailler avec chacun de ces termes inf´erieurs `a 0,1.
Application num´erique : par conception,T −A≥100 etf ≤10 doncdB≤10,dA≤0,1 et dC ≤10−5
4 on va donc travailler surA×10 etC×105 pour respecter la r´esolution recherch´ee sur des calculs entiers
5 cependant,C '0,1 doncC×105×f '1010 qui n´ecessite 34 bits,
6 mais f `a 10 Hz donc on peut travailler sur C×105×(f/10)'109 qui ne n´ecessite que 30 bits
11 / 21
Electronique´ num´erique
J.-M Friedt
Application des coefficients
d’´ etalonnage
T =A+√
B+C·f
Exemple de calcul en virgule fixe compte tenu de la pr´ecision recherch´ee :
1 (T−A)2=B+C×f donc 2(T −A)dT−2(T−A)dA= dB+dC ×f ⇒ |dT|=
dB 2(T−A)
+
dC×f 2(T−A)
+|dA|
2 on doit donc travailler avec chacun de ces termes inf´erieurs `a 0,1.
3 Application num´erique : par conception,T −A≥100 etf ≤106 doncdB≤10,dA≤0,1 et dC ≤10−5
4 on va donc travailler surA×10 etC×105 pour respecter la r´esolution recherch´ee sur des calculs entiers
5 cependant,C '0,1 doncC×105×f '1010 qui n´ecessite 34 bits,
6 mais f `a 10 Hz donc on peut travailler sur C×105×(f/10)'109 qui ne n´ecessite que 30 bits
12 / 21
Electronique´ num´erique
J.-M Friedt
Application des coefficients
d’´ etalonnage
T =A+√
B+C·f
Exemple de calcul en virgule fixe compte tenu de la pr´ecision recherch´ee :
1 (T−A)2=B+C×f donc 2(T −A)dT−2(T−A)dA= dB+dC ×f ⇒ |dT|=
dB 2(T−A)
+
dC×f 2(T−A)
+|dA|
2 on doit donc travailler avec chacun de ces termes inf´erieurs `a 0,1.
3 Application num´erique : par conception,T −A≥100 etf ≤106 doncdB≤10,dA≤0,1 et dC ≤10−5
4 on va donc travailler surA×10 etC×105 pour respecter la r´esolution recherch´ee sur des calculs entiers
5 cependant,C '0,1 doncC×10 ×f '10 qui n´ecessite 34 bits,
6 mais f `a 10 Hz donc on peut travailler sur C×105×(f/10)'109 qui ne n´ecessite que 30 bits
13 / 21
Electronique´ num´erique
J.-M Friedt
Application des coefficients
d’´ etalonnage
T =A+√
B+C·f
Exemple de calcul en virgule fixe compte tenu de la pr´ecision recherch´ee :
1 (T−A)2=B+C×f donc 2(T −A)dT−2(T−A)dA= dB+dC ×f ⇒ |dT|=
dB 2(T−A)
+
dC×f 2(T−A)
+|dA|
2 on doit donc travailler avec chacun de ces termes inf´erieurs `a 0,1.
3 Application num´erique : par conception,T −A≥100 etf ≤106 doncdB≤10,dA≤0,1 et dC ≤10−5
4 on va donc travailler surA×10 etC×105 pour respecter la r´esolution recherch´ee sur des calculs entiers
5 cependant,C '0,1 doncC×105×f '1010 qui n´ecessite 34 bits,
6 mais f `a 10 Hz donc on peut travailler sur C×105×(f/10)'109 qui ne n´ecessite que 30 bits
14 / 21
num´erique
J.-M Friedt
Application des coefficients
d’´ etalonnage
T =A+√
B+C·f
Exemple de calcul en virgule fixe compte tenu de la pr´ecision recherch´ee :
1 (T−A)2=B+C×f donc 2(T −A)dT−2(T−A)dA= dB+dC ×f ⇒ |dT|=
dB 2(T−A)
+
dC×f 2(T−A)
+|dA|
2 on doit donc travailler avec chacun de ces termes inf´erieurs `a 0,1.
3 Application num´erique : par conception,T −A≥100 etf ≤106 doncdB≤10,dA≤0,1 et dC ≤10−5
4 on va donc travailler surA×10 etC×105 pour respecter la r´esolution recherch´ee sur des calculs entiers
5 cependant,C '0,1 doncC×105×f '1010 qui n´ecessite 34 bits,
6 maisf `a 10 Hz donc on peut travailler sur C×105×(f/10)'109 qui ne n´ecessite que 30 bits
15 / 21
Electronique´ num´erique
J.-M Friedt
Application des coefficients
d’´ etalonnage
T =A+√
B+C·f
Exemple de calcul en virgule fixe compte tenu de la pr´ecision recherch´ee :
1 (T−A)2=B+C×f donc 2(T −A)dT−2(T−A)dA= dB+dC ×f ⇒ |dT|=
dB 2(T−A)
+
dC×f 2(T−A)
+|dA|
2 on doit donc travailler avec chacun de ces termes inf´erieurs `a 0,1.
3 Application num´erique : par conception,T −A≥100 etf ≤106 doncdB≤10,dA≤0,1 et dC ≤10−5
4 on va donc travailler surA×10 etC×105 pour respecter la r´esolution recherch´ee sur des calculs entiers
5 cependant,C '0,1 doncC×105×f '1010 qui n´ecessite 34 bits,
6 maisf `a 10 Hz donc on peut travailler sur C×105×(f/10)'109 qui ne n´ecessite que 30 bits
10×T = 10×A+ 10√
B+C·f 10×A+ 10√
B·104+C·f ·104/100 10×A+p
B×104+ (105C) (f/10)/10
Conclusion : 10·A, 104·B et 105·C pr´e- calcul´es 16 / 21
num´erique
J.-M Friedt
Application des coefficients
d’´ etalonnage
En exprimant le probl`eme
T =A+√
B+C·f comme
10×T = 10×A+p
B×104+ (105C) (f/10)/10
• tous les calculs se font sur des entiers
• calculer 10T garantit la r´esolution recherch´ee (0,1 K)
• l’exactitude du calcul est garanti
• pas d’impr´ecision du calcul flottant
• pas de d´epassement de capacit´e de stockage
17 / 21
Electronique´ num´erique
J.-M Friedt
Timers
https://www.st.com/resource/
en/datasheet/stm32f410cb.pdf 1 comptage par incr´ement ou
d´ecr´ement
2 fr´equence du processeur divis´e 3 remise `a z´ero quand la borne
du compteur est atteinte 4 Output Compare : changement
d’´etat de la broche lorsque le compteur atteint une valeur
#i n c l u d e<l i b o p e n c m 3 / stm32 / t i m e r . h>
r c c p e r i p h c l o c k e n a b l e ( RCC TIM1 ) ; // a p b 2 f r e q u e n c y =70 MHz, c f p . 4 0 de RM0401 g p i o s e t o u t p u t o p t i o n s ( GPIOA , GPIO OTYPE PP , GPIO OSPEED 50MHZ , GPIO8 ) ; g p i o m o d e s e t u p ( GPIOA , GPIO MODE AF , GPIO PUPD NONE , GPIO8 ) ;
g p i o s e t a f ( GPIOA , GPIO AF1 , GPIO8 ) ;
r c c p e r i p h r e s e t p u l s e ( RST TIM1 ) ; // t i m e r r e s e t ( TIM1 ) ; m o d i f 1 8 0 8 2 6 /w new l i b o p e n c m 3 v e r t i m e r s e t m o d e ( TIM1 , TIM CR1 CKD CK INT , TIM CR1 CMS EDGE , TIM CR1 DIR UP ) ;
// TIM CR1 CKD CK INT=c l o c k d i v i s i o n r a t i o // TIM CR1 CMS EDGE=c o u n t i n g mode // TIM CR1 DIR UP=c o u n t d i r e c t i o n
// h t t p s : / / g i t h u b . com/ l i b o p e n c m 3 / l i b o p e n c m 3 / b l o b / m a s t e r / l i b / stm32 /common/ t i m e r c o m m o n a l l . c t i m e r s e t o c m o d e ( TIM1 , TIM OC1 , TIM OCM PWM2) ;
t i m e r e n a b l e o c o u t p u t ( TIM1 , TIM OC1 ) ; t i m e r e n a b l e b r e a k m a i n o u t p u t ( TIM1 ) ; t i m e r s e t o c v a l u e ( TIM1 , TIM OC1 , 1 ) ;
t i m e r s e t p r e s c a l e r ( TIM1 , 0 ) ; // pp . 9 4 & 1 3 0 : p r e s c a l e r !=1 =>CK=2∗APB2 t i m e r s e t p e r i o d ( TIM1 , 1 ) ;
t i m e r e n a b l e c o u n t e r ( TIM1 ) ;
https://www.st.com/resource/en/reference_manual/dm00180366.pdfsection 14
18 / 21
num´erique
J.-M Friedt
Timers
https://www.st.com/resource/
en/datasheet/stm32f410cb.pdf 1 comptage par incr´ement ou
d´ecr´ement
2 fr´equence du processeur divis´e 3 remise `a z´ero quand la borne
du compteur est atteinte 4 Output Compare : changement
d’´etat de la broche lorsque le compteur atteint une valeur
#i n c l u d e<l i b o p e n c m 3 / stm32 / t i m e r . h>
r c c p e r i p h c l o c k e n a b l e ( RCC TIM1 ) ; // a p b 2 f r e q u e n c y =70 MHz, c f p . 4 0 de RM0401 g p i o s e t o u t p u t o p t i o n s ( GPIOA , GPIO OTYPE PP , GPIO OSPEED 50MHZ , GPIO8 ) ; g p i o m o d e s e t u p ( GPIOA , GPIO MODE AF , GPIO PUPD NONE , GPIO8 ) ;
g p i o s e t a f ( GPIOA , GPIO AF1 , GPIO8 ) ;
r c c p e r i p h r e s e t p u l s e ( RST TIM1 ) ; // t i m e r r e s e t ( TIM1 ) ; m o d i f 1 8 0 8 2 6 /w new l i b o p e n c m 3 v e r t i m e r s e t m o d e ( TIM1 , TIM CR1 CKD CK INT , TIM CR1 CMS EDGE , TIM CR1 DIR UP ) ;
// TIM CR1 CKD CK INT=c l o c k d i v i s i o n r a t i o // TIM CR1 CMS EDGE=c o u n t i n g mode // TIM CR1 DIR UP=c o u n t d i r e c t i o n
// h t t p s : / / g i t h u b . com/ l i b o p e n c m 3 / l i b o p e n c m 3 / b l o b / m a s t e r / l i b / stm32 /common/ t i m e r c o m m o n a l l . c t i m e r s e t o c m o d e ( TIM1 , TIM OC1 , TIM OCM PWM2) ;
t i m e r e n a b l e o c o u t p u t ( TIM1 , TIM OC1 ) ; t i m e r e n a b l e b r e a k m a i n o u t p u t ( TIM1 ) ; t i m e r s e t o c v a l u e ( TIM1 , TIM OC1 , 1 ) ;
t i m e r s e t p r e s c a l e r ( TIM1 , 0 ) ; // pp . 9 4 & 1 3 0 : p r e s c a l e r !=1 =>CK=2∗APB2 t i m e r s e t p e r i o d ( TIM1 , 1 ) ;
t i m e r e n a b l e c o u n t e r ( TIM1 ) ;
https://www.st.com/resource/en/reference_manual/dm00180366.pdfsection 14
19 / 21
Electronique´ num´erique
J.-M Friedt
Timers
https://www.st.com/resource/
en/datasheet/stm32f410cb.pdf 1 comptage par incr´ement ou
d´ecr´ement
2 fr´equence du processeur divis´e 3 remise `a z´ero quand la borne
du compteur est atteinte 4 Output Compare : changement
d’´etat de la broche lorsque le compteur atteint une valeur
#i n c l u d e<l i b o p e n c m 3 / stm32 / t i m e r . h>
r c c p e r i p h c l o c k e n a b l e ( RCC TIM1 ) ; // a p b 2 f r e q u e n c y =70 MHz, c f p . 4 0 de RM0401 g p i o s e t o u t p u t o p t i o n s ( GPIOA , GPIO OTYPE PP , GPIO OSPEED 50MHZ , GPIO8 ) ; g p i o m o d e s e t u p ( GPIOA , GPIO MODE AF , GPIO PUPD NONE , GPIO8 ) ;
g p i o s e t a f ( GPIOA , GPIO AF1 , GPIO8 ) ;
r c c p e r i p h r e s e t p u l s e ( RST TIM1 ) ; // t i m e r r e s e t ( TIM1 ) ; m o d i f 1 8 0 8 2 6 /w new l i b o p e n c m 3 v e r t i m e r s e t m o d e ( TIM1 , TIM CR1 CKD CK INT , TIM CR1 CMS EDGE , TIM CR1 DIR UP ) ;
// TIM CR1 CKD CK INT=c l o c k d i v i s i o n r a t i o // TIM CR1 CMS EDGE=c o u n t i n g mode // TIM CR1 DIR UP=c o u n t d i r e c t i o n
// h t t p s : / / g i t h u b . com/ l i b o p e n c m 3 / l i b o p e n c m 3 / b l o b / m a s t e r / l i b / stm32 /common/ t i m e r c o m m o n a l l . c t i m e r s e t o c m o d e ( TIM1 , TIM OC1 , TIM OCM PWM2) ;
t i m e r e n a b l e o c o u t p u t ( TIM1 , TIM OC1 ) ; t i m e r e n a b l e b r e a k m a i n o u t p u t ( TIM1 ) ; t i m e r s e t o c v a l u e ( TIM1 , TIM OC1 , 1 ) ;
t i m e r s e t p r e s c a l e r ( TIM1 , 0 ) ; // pp . 9 4 & 1 3 0 : p r e s c a l e r !=1 =>CK=2∗APB2 t i m e r s e t p e r i o d ( TIM1 , 1 ) ;
t i m e r e n a b l e c o u n t e r ( TIM1 ) ;
https://www.st.com/resource/en/reference_manual/dm00180366.pdfsection 14
20 / 21
num´erique
J.-M Friedt
Exercices
1 Configurer le timer (fr´equence, rapport cyclique) auquel est connect´ee la LED (lequel est-ce4?) pour commuter suffisamment lentement pour que le clignotement soit visible
2 impl´ementer la fonction racine pour ne calculerque sur des entiers et que le r´esultat soit garanti `a 4 d´ecimales exact
4. on pourra consulter la datasheet du STM32F410 –
https://www.st.com/resource/en/datasheet/stm32f410cb.pdf–en se rappelant que la LED bicolore est command´ee par PC1 – voir page 43
21 / 21