Étape 5: Bitbanging une vague d’impulsion sur un microcontrôleur ATMega328p
Étant donné que le Conseil de développement Arduino Uno R3 conserve un signal d’horloge externe 16MHz sur le ATMega328p à bord, le microcontrôleur exécute une instruction de cycle d’horloge 1 exactement 62.5ns (1/16 MHz = 62.5ns). Étant donné que nous pouvons savoir combien chaque instruction prend les cycles d’horloge, nous pouvons contrôler précisément les instructions combien nous devons génèrent notre signal.
Comme nous l’avons vu précédemment, afin de transmettre un 1 à la puce WS281X nous devons émettre un signal qui reste à une valeur maximale (haute) pour 0.8μs et puis reste à une valeur minimale de (faible) pour 0.45μs. Ainsi, nous voulons écrire une liste d’instructions qui :
-Mettre broche numérique à haute
-Attendre 0.8μs
-Définit la broche numérique à faible
-Attend 0.45μs
En langage assembleur, ceci peut être réalisé par le code suivant :
(volatile) ASM
Instruction horloge Description Phase Bit transmis
« sbi %0, %1\n\t » / / 2 broches haute (T = 2)
"rjmp. + 0\n\t" / / 2 nop nop (T = 4)
"rjmp. + 0\n\t" / / 2 nop nop (T = 6)
"rjmp. + 0\n\t" / / 2 nop nop (T = 8)
"rjmp. + 0\n\t" / / 2 nop nop (T = 10)
"rjmp. + 0\n\t" / / 2 nop nop (T = 12)
"nop\n\t" // 1 nop (T = 13)
« cbi %0, %1\n\t » / / 2 broches faible (T = 15)
"rjmp. + 0\n\t" / / 2 nop nop (T = 17)
"rjmp. + 0\n\t" / / 2 nop nop (T = 19)
"nop\n\t" // 1 nop (T = 20) 1
::
Opérandes d’entrée
« I » (_SFR_IO_ADDR(PORT)), //%0
"I" (PORT_PIN) //%1
);
Instruction
La première colonne contient les instructions d’assemblage, suivie d’un saut de ligne et onglet caractères, qui font de l’assembleur final listing généré par le compilateur plus lisible.
Horloge
La deuxième colonne indique le nombre de cycles d’horloge que prend de chaque instruction. Pour cet ensemble d’instructions simples il y a qu’une seule valeur possible, nous verrons plus tard comment quelques instructions (par exemple, conditionnel) peuvent avoir 1, 2 ou 3 valeurs possibles. N’oubliez pas que chaque cycle d’horloge sur les 16 MHz Arduino Uno prend 62.5ns.
Description
La troisième colonne montre une très brève description de ce que fait chaque opération.
Phase de
En utilisant le terme un peu lâche, nous l’utilisons pour indiquer la somme cumulée de cycles d’horloge prise par les instructions qui ont été exécutées jusqu'à présent.
Afin d’envoyer une seule valeur 255 — 11111111 en binaire — à la WS281X nous devons répéter ce jeu d’instructions 8 fois. En outre, si nous insérons un 50μs (ou plus) pause entre les transmissions de la séquence de 8 bits, la WS281X s’enclenche les données transmises à son registre de sortie. Une fois que les données sont verrouillées, la première LED (vert) de la WS281X devrait s’allumer à un niveau de luminosité maximale. Le sketch Arduino à l’intérieur de bitbang_255.zip illustre cette opération.
Pour envoyer un 0, il faut modifier le code qui produit un 1 en réduisant le temps pendant lequel le signal a une valeur élevée (maximum) et en augmentant le temps pendant lequel le signal est faible (minimale). En outre, on notera que les valeurs pour chaque LED doivent toujours être spécifiés à l’aide de 8 bits. Par exemple, si nous voulions envoyer une valeur de 105 — 1101001 en binaire, il faudrait envoyer les 8 bits 01101001 y compris le 0. Le code qui génère un 0 ressemble à :
(volatile) ASM
Instruction horloge Description Phase Bit transmis
« sbi %0, %1\n\t » / / 2 broches haute (T = 2)
"rjmp. + 0\n\t" / / 2 nop nop (T = 4)
"rjmp. + 0\n\t" / / 2 nop nop (T = 6)
« cbi %0, %1\n\t » / / 2 broches faible (T = 8)
"rjmp. + 0\n\t" / / 2 nop nop (T = 10)
"rjmp. + 0\n\t" / / 2 nop nop (T = 12)
"rjmp. + 0\n\t" / / 2 nop nop (T = 14)
"rjmp. + 0\n\t" / / 2 nop nop (T = 16)
"rjmp. + 0\n\t" / / 2 nop nop (T = 18)
"rjmp. + 0\n\t" / / 2 nop nop (T = 20) 0
::
Opérandes d’entrée
« I » (_SFR_IO_ADDR(PORT)), //%0
"I" (PORT_PIN) //%1
);
Nous pouvons utiliser le sketch Arduino à l’intérieur de la bitbang_105.zip pour générer le signal dont l’image peut être vu sur les captures d’écran d’oscilloscope attachés à cette étape.
Maintenant, pour la WS281X afficher la couleur blanchâtre, nous voulons, nous devons envoyer non pas une mais trois 255 valeurs — auquel cas notre signal se compose des 24 — avant d’attendre le 50μs pour les données de verrouillage. Nous pourrions faire cela par copier-coller les instructions d’onze assemblage au-dessus de 23 fois (vous pouvez lui donner un essai modifiant l’esquisse de bitbang_255.ino). Mais le code ne serait pas pratique pour l’envoi des valeurs à plus d’un WS281X puces. Une meilleure solution serait d’écrire une boucle qui pourrait parcourir les valeurs 8 bits jusqu'à ce que tous les trois d'entre eux ont été envoyés.
L’esquisse à l’intérieur de le bitbang_whitish.zip comprend une description claire des mesures prises pour atteindre le résultat souhaité. La section principale, écrite en Assemblée à la suite de la logique décrite plus haut, se présente comme suit :
(volatile) ASM
Phase de l’instruction horloge Description
"nextbit:\n\t" // - label (T = 0)
« sbi %0, %1\n\t » / / 2 signal haute (T = 2)
« sbrc %4, 7\n\t » / / 1-2 si la valeur de MSB (T =?)
"mov %6, %3\n\t" / / 0-1 tmp définirai signal haute (T = 4)
« dec %5\n\t » / / 1 diminution bitcount (T = 5)
« nop\n\t » / / 1 nop (cycle de 1 horloge inactif) (T = 6)
"st % a2, %6\n\t" / / 2 PORT pour tmp (T = 8)
"mov %6, %7\n\t" / / 1 reset tmp à bas (par défaut) (T = 9)
« breq nextbyte\n\t » / / 1-2 si bitcount == 0 -> nextbyte (T =?)
« rol %4\n\t » / / 1 shift MSB vers la gauche (T = 11)
"rjmp. + 0\n\t" / / 2 nop nop (T = 13)
« cbi %0, %1\n\t » / / 2 signal faible (T = 15)
"rjmp. + 0\n\t" / / 2 nop nop (T = 17)
"nop\n\t" // 1 nop (T = 18)
« rjmp nextbit\n\t » / / 2 bitcount! = 0 -> nextbit (T = 20)
"nextbyte:\n\t" // - label -
« ldi 5 %, 8\n\t » / / 1 reset bitcount (T = 11)
"ld %4, %a8+\n\t" // 2 val = *p++ (T = 13)
« cbi %0, %1\n\t » / / 2 signal faible (T = 15)
"rjmp. + 0\n\t" / / 2 nop nop (T = 17)
"nop\n\t" // 1 nop (T = 18)
« dec %9\n\t » / / 1 diminution bytecount (T = 19)
« brne nextbit\n\t » / / 2 si bytecount! = 0 -> nextbit (T = 20)
::
);
La meilleure façon de comprendre le fonctionnement de cette section est d’examiner différents scénarios et suivre le code assembleur en ligne. Par exemple, nous savons que pour envoyer une valeur de 255, nous devons envoyer 8 bits avec un calendrier qui correspond à un 1. En d’autres termes, la broche numérique connecté à le WS281X devrait rester élevée pour 13 cycles (0.8125μs) et faible pour 7 (0.4375μs). Le code ci-dessus parvenir ? Nous allons voir ce qui arrive quand on commence tout d’abord à transmettre :
(volatile) ASM
"nextbit:\n\t" / / il s’agit d’une étiquette uniquement pour diriger les sauts ci-dessous.
« sbi %0, %1\n\t "/ / le signal est défini sur Elevé, instruction utilise 2 cycles.
« sbrc %4, 7\n\t » / / True. Envoi de 255 implique MSB actuel est « set » (= 1).
"mov %6, %3\n\t » / / ceci est exécuté. « tmp » est réglé sur haut.
« dec %5\n\t » / / Bit est transmis, décrémenter compteur de bit.
« nop\n\t » / / besoin de tourner au ralenti pour obtenir les cycles de 13 horloge.
"st % a2, %6\n\t" / / écrire la valeur de « tmp » dans le PORT (broche encore élevé).
"mov %6, %7\n\t » / / Set « tmp » au plus bas pour le prochain passage dans la boucle.
« breq nextbyte\n\t » / / False. Compteur de bit n’est pas 0, utiliser 1 cycle et continuer.
« rol %4\n\t » / / décalage de la valeur d’octet MSB vers la gauche.
"rjmp. + 0\n\t" / / inactifs pendant 2 cycles d’horloge. Phase atteinte T = 13.
« cbi %0, %1\n\t » / / la valeur signal faible.
"rjmp. + 0\n\t" / / inactifs pendant 2 cycles d’horloge.
« nop\n\t » / / ralenti pendant le cycle de 1 horloge.
« rjmp nextbit\n\t » / / compteur bits n’a pas été 0 alors sauter à bit suivant. T = 20.
);
Si les instructions qui effectivement exécutées de générer un signal sur la broche de données qui est 13 cycles haute (0.8125μs) et 7 LOW (0.4375μs), envoyant ainsi un peu avec la valeur 1 à la WS281X. Si nous continuons à étudier ce que le code fait quand le reste des bits sont envoyés et ce qu’il fait, lorsqu’on utilise des valeurs autres que 255, nous aurons une meilleure compréhension de cette implémentation particulière de bitbanging.
Personnellement, j’espère que vous trouverez ce tutoriel utile pour débuter avec bitbanging protocoles de votre propre communication chaque fois qu’il est nécessaire !