Attiny15, PWM, led qui pulse, Sinus et GNU bc
Le microcontrôleur AVR Attiny15 offre une fonctionnalité pour faire de la PWM (Pulse-width modulation) ou de modulation de largeur d'impulsions en bon français. Ceci permet, par exemple, de faire varier l'intensité lumineuse d'une Led ou la vitesse d'un moteur. Pour cela, on utilise dans le code de l'Atiny15 une valeur entre 0 et 255. Pour obtenir une fluctuation d'intensité qui donne l'impression que la Led pulse (comme sur les Mac en veille) il faut utiliser une série de valeurs sinusoïdale. C'est là que GNU bc intervient et se révèle sous sa vrai nature : bien plus qu'une calculette en ligne de commande, un véritable langage.
Nous avons besoin de 256 valeurs hexadécimales entre 0 et 255 qui soient répartie à la façon d'un sinus. Hors de question d'utiliser un simple compteur de 255 à 0 puis de 0 à 255. Nous n'obtiendrons pas une pulsation ainsi. Il nous fait un sinus. Ca, bc, nous le donne de bon coeur avec la fonction s(x). Il nous faut ensuite décaler le sinus sur x de pi/2 (1) et sur y de 1 pour ne pas avoir de valeurs négatives. Nous calculons donc :
((s(3.14159*(0.5+((2/256)*a)))+1)/2)*256)
Aaaaah ! Du calme, découpons :
- a : c'est notre valeur de base entre 1 et 256
- Nous avons besoins de deux sommets du sinus. Le premier est à pi/2 et le suivant à pi fois 2.5. A ces deux points sur x nous avons 1 sur y. Nous avons donc 2 fois pi sur x entre les deux sommets. 2 que nous découpons en 256 tranches. Le premier point est pi fois 0.5 plus 2/256 fois 1. Le dernier est pi fois 0.5 plus 2/256 fois 256. Donc : 3.14159*(0.5+((2/256)*a))
- Nous n'oublions pas d'ajouter 1 au résultat pour "remonter" le sinus variant alors entre 0 et 1 en non plus -1 et 1.
- Notre sinus fait 2 "de haut" on "ratatine" en divisant par 2
- Enfin pour obtenir un sinus entre 0 et 255 on multiplie par 256 (l'arrondis se chargera du reste)
L'arrondis, justement. Il n'y a pas d'arrondis dans bc (enfin pas exactement). Nous pouvons jouer sur la variable scale permettant de définir le nombre de décimales significatives. Si nous utilisons scale=0 notre 3.14159 devient 3 et le calcul est complètement faussé. Donc....
Première démonstration de la puissance de bc. On créé simplement la fonction qui nous manque :
define i(x) {
auto s
s = scale
scale = 0
x /= 1
scale = s
return (x)
}
Fonction très simple prenant en argument une valeur et retournant la même divisée par 1. On change au passage la valeur de scale ce qui élimine les décimales. Résultat, après avoir enregistré cette fonction dans un fichier toto.bc :
$ echo "i(10/3)" | bc -l toto.bc 3
Bingo ! Et là vous vous dites, "il ne reste plus qu'à mettre une bonne couche de shell et" :
for ((i=1;i<=256;i+=1));
do echo "obase=16;i(((s(3.14159*(0.5+((2/256)*$i)))+1)/2)*256)" | \
bc -l toto.bc;
done
Rappelons que obase permet de spécifier la base en sortie (16 = hexa).
Et bien non ! Si bc permet à l'utilisateur de créer des fonctions il doit bien être capable de supporter des boucles. Nous ajoutons donc, dans notre toto.bc :
obase=16
auto a
for(a=1;a<=256;a=a+1) {
i(((s(3.14159*(0.5+((2/256)*a)))+1)/2)*256)
}
quit
Et voilà. Il ne nous reste plus qu'à faire :
$ bc -l toto.bc FF FF FF FF FF FE FE FD FC FC FB FA [...] FB FC FC FD FE FE FF FF FF FF FF FF
Voilà notre beau sinus pour notre Attiny15.
Note sur la PWM : la modulation de largeur d'impulsions est une technique pour faire varier l'intensité (Led) ou la vitesse (moteur) d'un composant qui ne peut pas être contrôlé en variant la tension ou le courant. Dans le cas d'une Led, elle a besoin de 10mA pour fonctionner (selon le modèle) et fait apparaître une tension à ses bornes (+/- 2 Volts selon le modèle). Pour faire varier l'intensité lumineuse, il faut la faire clignoter très rapidement, notre oeil fera le reste. Avec la PWM on fait clignoter la Led à une fréquence fixe et on joue sur le rapport cyclique. 100% d'intensité ce sont des "clignotements" toujours allumés. 0%, toujours éteint. 50% un "clignotement" sur deux est allumé. 10% c'est un sur 10, etc. La Led ne clignote jamais plus ou moins vite, on passe simplement des "tranches" à "éteint". Avec un Attiny15 on règle le rapport cyclique avec une valeur entre 0 et 255 dans le registre OCR1A. Ce sont donc les valeurs de ce registre que nous avons calculé avec bc.