quinta-feira, janeiro 28, 2016

Comandando com um Arduino uma Fita de LED com CI APA102

Retomando um assunto do final de novembro passado, vamos ver uma experiência simples de comandar uma fita de LED RGB que usa o circuito integrado APA102.


O funcionamento do APA102 está detalhado no post anterior. Resumindo, precisamos gerar dois sinais (dado e clock) para enviar frames de 32 bits. O primeiro frame é uma marca especial de início (com zeros), cada frame seguinte controla um LED RGB e um frame final (com uns) fornece os clocks para garantir o tratamento de todos os bits.

No caso vamos usar a interface SPI do Arduino para gerar os sinais. O código abaixo está organizado para ser mais didático que eficiente. Uma animação simples é usada para demonstrar que estamos controlando cada um dos LEDs.
// Teste simples de controle de fita de LEDs RGB com
// controlador APA102
//
// A fita de LED deve estar conectada aos pinos de SPI
//   UNO:   11 ou ISP-4 (MOSI) e 13 ou ISP-3 (SCK)
//   Mega:  51 ou ISP-4 (MOSI) e 52 ou ISP-3 (SCK)
//   Due:   ICSP-4 (MOSI) e ISP-3 (SCK)
//
// Daniel Quadros - Jan/16 - http://dqsoft.blogspot.com

#include <SPI.h>

// Número de LEDs na fita
const int NUM_LEDS = 10;

// Frames
byte frameStart[4] = { 0x00, 0x00, 0x00, 0x00 };
byte frameOff[4] = { 0xE8, 0x00, 0x00, 0x00 };
byte frameB[4] = { 0xE8, 0xFF, 0x00, 0x00 };
byte frameG[4] = { 0xE8, 0x00, 0xFF, 0x00 };
byte frameR[4] = { 0xE8, 0x00, 0x00, 0xFF };
byte frameEnd[4] = { 0xFF, 0xFF, 0xFF, 0xFF };

// Iniciação
void setup() {
  SPI.begin();
  acionaLED (0, NUM_LEDS, frameOff);
}

// Laço perpétuo
void loop() {
  int nLeds = 0;
  while (nLeds < NUM_LEDS) {
    nLeds++;
    anima (nLeds, frameR);
    anima (nLeds, frameG);
    anima (nLeds, frameB);
  }
}

// Faz a "animação" dos primeiros n LEDs
// usando a cor indicada
void anima (int n, byte *cor) {
  for (int i = 1; i <= n; i++) {
    acionaLED (i, i, cor);
  }
  for (int i = n; i > 1; i--) {
    acionaLED (i-1, i, cor);
  }
}

// Acende o LED iLed com cor e
// apaga os demais n-1 LEDs
void acionaLED (int iLed, int n, byte *cor) {
  sendFrame (frameStart);
  for (int i = 1; i <= n; i++) {
    sendFrame ((i == iLed) ? cor : frameOff);
  }
  sendFrame (frameEnd);
  delay(100);
}

// Envia um frame para a fita
void sendFrame (byte *frame) {
  for (int i = 0; i < 4; i++)
    SPI.transfer(*frame++);
}
O vídeo abaixo mostra o resultado.


Este código é suficiente para animações simples, com a fita de LED parada. A motivação inicial deste estudo foi gerar animações por persistência de visão, girando a fita a velocidades absurdas. Para isto o código é lento. O problema principal é que a execução fica parada durante SPI.transfer, esperando o byte atual ser transmitido. Como o ATmega possui apenas um byte de buffer e vamos querer trabalhar com um clock alto na SPI, usar interrupções não vai trazer um grande alívio. Por exemplo, colocando a SPI do Arduino na taxa máxima, podemos executar apenas 16 instruções enquanto transmitimos um byte. A simples chamada e retorno da interrupção devem gastar algo muito próximo disto.

No próximo post vamos melhorar isto usando um Arduino Due, que tem um clock mais elevado e dispõe do recurso de DMA.

Nenhum comentário: