Hmmm, en revoyant les timings du protocole, c'est assez court...
Datasheet :
http://www.adafruit.com/datasheets/WS2812.pdf
Tout d'abord faut voir comment envoyer plusieurs couleurs si tu as plusieurs LED en cascade.
Sur la page 5 du datasheet, il y a une image qui montre comment tu contrôles des LED en cascade.
Celui ci montre un exemple pour 3 LED.
Ton microcontrolleur envoi donc 3 fois 24 bits et un reset.
Led D1 prends les données du "first 24 bits" et puis envoie les données entrante suivantes aux autres LEDs. C'est comme si, une fois qu'il a lu sa couleur, il fait un lien entre DI (data in) et DO (data out) et tout se que le microcontrolleur envoi est directement passé aux autres LEDs.
Là, D2 prends les données du "second 24 bit" et puis fait passer le reste des données au LED suivant.
Ainsi de suite, jusqu'au reset.
Pour envoyer une couleur RGB, tu dois suivre leur protocole.
Pour un bit de valeur 0, tu dois mettre DI 'high' pendant 350ns (+/- 150ns) puis 'low' pendant 800ns.
Pour un bit de valeur 1, tu dois mettre DI 'high' pendant 700ns puis 'low' pendant 600ns.
Un reset consiste à mettre DI 'low' pendant plus de 50us.
Pour faire se signal, à mon avis, utiliser des delays et set/clear un I/O n'est pas la bonne solution.
Le mieux serait d'utiliser un PWM ou encore un timer interrupt, voir même les deux.
Le PWM serait le plus simple je pense. Là tu n'as qu'a dire quelle fréquence utiliser et modifier le TON au fur et à mesure.
Pour faire simple on va dire que le signal d'un bit, fait 1.2µs.
T0H = 400ns
T0L = 800ns
T1H = 700ns
T1L = 500ns
Se qui te donne une fréquence de (à peu près) 833kHz.
A toi de voir comment tu gères la couleur de tes LEDs, disons que tu as une variable couleurLED qui contient 24 bits. Tu y stocke la couleur d'un LED avec le format GRB.
Le but est de faire en sorte de parcourir les bits de cette variable (de MSB à LSB), et de modifier le TON du PWM en conséquences.
Donc, si ton bit X de la variable couleurLED est à 1, tu dois changer TON pour qu'il soit égal à 700ns. Si ton bit X est à 0, tu changes TON à 400ns.
Une fois que tu as fini d'envoyer 24 bits, tu passes a la couleur du prochain LED, et ainsi de suite jusqu'à faire tout les LEDs.
Une fois fini, tu dois reset le tout. Pour ça tu pourrais, par exemple, désactiver le PWM et mettre sa sortie à 0 pendant plus de 50µs.
Si tu veux le faire via le timer interrupt, tu dois le configurer afin qu'il s'exécute tout les 100ns (par ex.).
Dans ton interrupt, tu n'as qu'à mettre un compteur. Celui-ci incrémente de 1, tout les 100ns.
Se compteur est remit à 0 à chaque fois que tu finis de transmettre un bit.
Dans ton main tu va parcourir chaque bit de ta variable couleurLED. Si la valeur est a 1, tu dois mettre un I/O à 1, puis attendre que ton compteur soit égal ou plus grand que 700ns, puis a 0 pendant 600ns.
Si la valeur est a 0, tu mets un I/O à 1 pendant 400ns puis a 0 pendant 800ns.
Voilà se que ça donne en pseudo code:
Code: Select all
// Dans cette exemple, DI est branché au pin 0 du PORTB
long compteur; // variable globale
void interrupt()
{
compteur++; // compteur incrémente toutes les 100ns
}
void main()
{
long couleurLED = 0x0000FF; // format de couleur GRB -> 0xGGRRBB
int bit;
// initialiser interrupt avec les bon paramètres
compteur = 0;
for (bit = 0; bit < 24; bit++) // boucle pour test les 24 bits dans une variable qui contient la couleur d'un LED.
{
// simple AND de la variable couleurLED et un 'mask', pour voir si un bit est à 1 ou 0
if ( couleurLED & ( 1 << ( 24 - 1 - bit ) ) ) // si la condition est 'vrai'
{
if ( compteur <= 7) // si le compteur est en dessous des 700ns (= T1H)
PORTB = 1; // mettre le I/O à 1
else if (compteur <= 13) // sinon, si le compteur est en dessous des 1300ns (= T1H + T1L)
PORTB = 0; // mettre le I/O à 0
else // sinon, si le compteur est plus grand que 13
compteur = 0;
}
else // si la condition est fausse / le bit est 0
{
if ( compteur <= 4) // si le compteur est en dessous des 400ns (= T0H)
PORTB = 1; // mettre le I/O à 1
else if (compteur <= 12) // sinon, si le compteur est en dessous des 1200ns (= T0H + T0L)
PORTB = 0; // mettre le I/O à 0
else // sinon, si le compteur est plus grand que 12
compteur = 0;
}
}
}
Voilà, j'espère que c'est assez clair, si t'as des questions n'hésite pas.
- Nicolas