Comme je l’ai dit lors de mon introduction pour ces tutoriels, la programmation de microcontrôleur nécessite de connaitre les opérations arithmétiques binaires. Celles-ci ne se comportent pas comme les opérations usuels d’addition et de soustraction.
Les opérations booléennes
Vous le savez déjà, le binaire est une base où chaque chiffre ne peut avoir que deux valeurs : 0 ou 1. Nous allons nous intéresser aux opérations que l’on peut effectuer sur un bit. Cette partie des mathématiques s’appelle l’algèbre de Boole.
La conjonction : le «et» logique
La conjonction est l’opération qui prend pour valeur la vérification de ses deux opérandes. En C, on utilise deux esperluettes [ccie]&&[/ccie] comme opérateur. Pour vous aider à mieux comprendre, voici la liste des 4 valeurs que peut prendre cette opération :
- [ccie]0 && 0 = 0[/ccie]
- [ccie]0 && 1 = 0[/ccie]
- [ccie]1 && 0 = 0[/ccie]
- [ccie]1 && 1 = 1[/ccie]
La disjonction : le «ou» logique
La disjonction se vérifie si au moins un de ses deux opérandes se vérifie. Le symbole que l’on utilisera ici est le tuyau, deux fois [cci]||[/cci] que l’on obtient en pressant [cci]Alt Gr[/cci] avec la touche [cci]6[/cci] sur un clavier français. Voici les valeurs :
- [cci]0 || 0=0[/cci]
- [cci]0 || 1=1[/cci]
- [cci]1 || 0=1[/cci]
- [cci]1 || 1=1[/cci]
La négation
La négation est une opération qui prend la valeur opposée à son unique opérande. En C, on utilise le point d’exclamation [cci]![/cci], qui va précéder l’opérande. En toute logique, ses valeurs possibles sont :
- [cci]!0 = 1[/cci]
- [cci]!1 = 0[/cci]
Ces 3 opérations sont la base de toute l’algèbre de Boole. Elles cumulent diverses propriétés mathématiques (idempotence, commutativité, distributivité, etc.). Pour plus d’informations, je vous suggère la page Wikipédia de l’algèbre de Boole.
Les opérations bit à bit
À part pour gérer des conditions, il est rare d’utiliser le booléen sous sa forme brute. On utilisera plutôt des entiers, de 8 ou 16 bits, auxquels on appliquera des opérations logiques dédiées.
L’affectation directe
Plutôt que d’écrire des opérations compliquées, il est parfois plus simple d’initialiser un entier avec sa valeur binaire ou hexadécimale. En C, on fera comme ceci :
- [cci]int i = 0xFF; // les 8 premiers bits à 1[/cci]
- [cci]int i = 0x0F; // les 4 premiers bits à 1[/cci]
- [cci]int i = 0b10101010; // un bit sur deux[/cci]
- [cci]int i = 0b10000001; // seulement le premier et le dernier bits[/cci]
La négation
L’opérateur de négation bit à bit n’est pas un point d’exclamation, mais le tilde [cci]~[/cci]. La négation bit à bit se contente de changer chaque valeur de bit par l’opposée. Le résultat final dépendra de la taille de l’entier (8 ou 16 bits). On ne travaille pour le moment qu’en 8 bits. Aussi, on aura les valeurs suivantes :
- [cci] ~(0x0F) = 0xF0 [/cci]
- [cci] ~(0b00000111) = 0xb11111000 [/cci]
- [cci] ~(0b0000000) = 0xb11111111 = 0xFF = 255 [/cci]
Cette opération est parfois utilisée pour basculer une série de valeurs.
Le «et» bit à bit
On parcours deux nombres en même temps, bit par bit, et on réalise l’opération «et» logique vue plus haut. On utilise comme opérateur l’esperluette simple [ccie]&[/ccie]. Exemples :
- [ccie] 0xF8 & 0x1F = 0b11111000 & 0b00011111 = 0b00011000 = 0x18 [/ccie]
- [ccie] 0x3F & 0xAA = 0b00111111 & 0b10101010 = 0b00101010 = 0x2A [/ccie]
- [ccie] 0x47 & 0x2E = 0b01000111 & 0b00101110 = 0b00000110 = 0x06 [/ccie]
Il faut s’habituer à ce genre d’opérations, car elles sont courantes.
Le «ou» bit à bit
Comme pour l’opération précédente, sauf qu’à chaque pair de bits, on utilise l’opération «ou» logique. L’opérateur ici est le tuyau simple [cci]|[/cci]. Reprenons les mêmes exemples :
- [cci] 0xF8 | 0x1F = 0b11111000 | 0b00011111 = 0b11111111 = 0xFF [/cci]
- [cci] 0x3F | 0xAA = 0b00111111 | 0b10101010 = 0b10111111 = 0xBF [/cci]
- [cci] 0x47 | 0x2E = 0b01000111 | 0b00101110 = 0b01101111 = 0x6F [/cci]
Le «ou» exclusif bit à bit
L’opération fonctionne de la même manière, sauf qu’ici, si dans une pair, les deux bits sont à 1, alors le résultat sera 0. L’opérateur utilisé est l’accent circonflexe [cci]^[/cci].
- [cci] 0xF8 ^ 0x1F = 0b11111000 ^ 0b00011111 = 0b11100111 = 0xE7 [/cci]
- [cci] 0x3F ^ 0xAA = 0b00111111 ^ 0b10101010 = 0b10010101 = 0x95 [/cci]
- [cci] 0x47 ^ 0x2E = 0b01000111 ^ 0b00101110 = 0b01101001 = 0x69 [/cci]
Le décalage à gauche
Cette opération consiste à décaler l’ensemble des bits vers la gauche. Ce qui entre par la droite à pour valeur 0. Elle est très souvent utilisé. Ici on doit préciser un opérande, et un nombre de bits à décaler. L’opérateur sera le double chevron vers la gauche (signe inférieur) [ccie]<<[/ccie]. Tout ce qui dépasse la taille de l’entier est perdu (au delà de 8 bits ici).
- [ccie] 0xF8 << 1 = 0b11111000 << 1 = 0b11110000 = 0xF0 [/ccie]
- [ccie] 0x3F << 2 = 0b00111111 << 2 = 0b11111100 = 0xFC [/ccie]
- [ccie] 0x47 << 2 = 0b01000111 << 2 = 0b00011100 = 0x1C [/ccie]
Le décalage à droite
Cette opération fonctionne de façon similaire à celle-ci dessus, mais elle est beaucoup moins utilisée. On utilise le double chevron vers la droite (signe supérieur) [ccie]>>[/ccie].
- [ccie] 0xF8 >> 1 = 0b11111000 >> 1 = 0b00111100 = 0x3C [/ccie]
- [ccie] 0x3F >> 2 = 0b00111111 >> 2 = 0b00001111 = 0x0F [/ccie]
- [ccie] 0x47 >> 2 = 0b01000111 >> 2 = 0b00010001 = 0x11 [/ccie]
Leur utilisation au travers du C
Maintenant que vous connaissez ces opérations, voici comment les utiliser et surtout pourquoi les utiliser.
Dans mes tutoriels AVR, vous trouverez souvent ces opérations sous forme abrégée ; voici les équivalence :
- [cci]a ^= b[/cci] équivaut à [cci]a = a ^ b[/cci]
- [ccie]a &= b[/ccie] équivaut à [ccie]a = a & b[/ccie]
- [cci]a |= b[/cci] équivaut à [cci]a = a | b[/cci]
- [ccie]a <<= b[/ccie] équivaut à [ccie]a = a << b[/ccie]
- [ccie]a >>= b[/ccie] équivaut à [ccie]a = a >> b[/ccie]
Vous pourrez maintenant vous familiarisez à ce genre d’opérations :
- [ccie]PINB & (1<<PINB1)[/ccie] : on vérifie que le registre entrée ait bien le bit numéro PINB1 d’actif. Cela équivaut à ne chercher que le bit qui nous intéresse dans le registre.
- [ccie]PORTB ^= (1<<PORTB0)[/ccie] : on bascule l’état de la broche 0 du port B
Il vous faudra surement de l’entrainement pour réussir à déchiffrer le code. Mais une fois que vous aurez compris, vous ne pourrez plus vous passer de ce genre d’astuces !
Merci pour cet article ma foi bien intéressant.
Entre ça et ta suite d’articles sur les AVR, je n’ai plus vraiment d’excuses pour ne pas m’y mettre.
En revanche cette ligne :
0xF8 >> 1 = 0b11111000 >> 1 = 0b00111100 = 0x3C
Ne devrait pas plutôt ressembler à ça ? :
0xF8 >> 1 = 0b11111000 >> 1 = 0b01111100 = 0x7C