quinta-feira, novembro 26, 2015

Investigando uma Fita de LED RGB "endereçável"

O meu amigo Oda está montando um dispositivo POV (Persistence of Vision) carinhosamente batizado de "Pau de Led". Consiste em um trecho de fita LED RGB girado a velocidades absurdas por um motor cujo tamanho aumenta a cada versão. Um dos problemas enfrentados é conseguir atualizar a imagem com a velocidade necessária, o que me levou a estudar o funcionamento da fita de LED.

Os LEDs são RGB, mas esta imagem só usa verde.

Existem vários tipos de "fita de LED". No caso mais simples, são apenas LEDs interconectados, que acendem todos juntos. A fita que estou examinando aqui é mais "high-end": cada posição da fita é um circuito integrado especial ligado a três LEDs nas cores vermelho, verde e azul. Com isto os LEDs podem ser controlados "individualmente" (são "endereçáveis").



Porque estou falando "endereçáveis" e "individualmente", com aspas? Porque na verdade as posições não tem um endereço nem podem ser controladas isoladamente (pelo menos nos modelos que vi até agora). Devem ser enviados os estados de todos os LEDs para o primeiro circuito da fita. Ele irá consumir o primeiro conjunto de estados e repassar os demais para o seguinte e assim por diante.

Existem vários modelos de circuito, que usam diferentes formas de comunicação. Um aspecto importante nesta comunicação é como indicar o início de um envio. A fita de LED que o Oda me emprestou utiliza o CI APA102. No momento ele está utilizando um Arduino Mega com a biblioteca FastLED.

A figura abaixo, extraída do datasheet, mostra o diagrama de blocos do APA102:



Além da alimentação, devem ser fornecidos um sinal de dados (SDI) e um sinal de clock (CKI). Após extrair os seus dados, o APA102 fornece ao CI seguinte um novo par dado (SDO) e clock (CKO). O APA102 suporta altas velocidades de comunicação (o datasheet é omisso, mas na página da FastLED fala-se em uso de 24MHz sem problemas) e podemos usar uma interface SPI para gerar o dado e o clock.

Os dados são tratados como frames de 32 bits (quatro bytes do ponto de vista de uma interface SPI de 8 bits). Um frame contendo zeros marca o início de um envio. Um frame destinado a uma posição contem:
  • Um byte contendo três bits "1", seguido de uma "intensidade global" (que se aplica aos três LEDs) com cinco bits.
  • Um byte para cada cor, contendo a respectiva intensidade.
Após o frame da última posição devem ser enviados um ou mais frames com todos os bits em "1". Para ter tempo de analisar os bits recebidos, o clock de saída (CKO) é atrasado de meio ciclo em relação ao de entrada (CKI). A finalidade dos frames no final é fornecer ciclos de clock para propagar os dados que foram "ficando atrasados". É necessário um mínimo de 1 bit para cada duas posições na fita, portanto o envio de um frame é suficiente para fitas com até 64 LEDs.

Mais detalhes sobre o controle da intensidade podem ser vistos aqui (Tim's blog). O mesmo blog discute em detalhes o processo de comunicação (aqui).


Com estas informações, podemos agora examinar a questão do tempo necessário para atualizar todos os LEDs. Supondo que temos "n" LEDs (n menor ou igual a 64), são necessários enviar n+2 frames, ou seja 32n+64 bits.

Um Arduino Uno ou Mega trabalha com um cristal de 16MHz e o clock máximo para a SPI é metade disto (8MHz). Por exemplo, se forem 64 LEDs, a 8MHz o tempo para envio é 264 microsegundos. Isto desde que alimentemos a interface SPI suficientemente rápido. É aqui que temos alguns problemas:
  • A interface SPI do ATmega não tem buffer. Você só pode enviar o próximo byte a transmitir depois que o anterior foi enviado.
  • Para descobrir que o byte foi enviado é necessário ler o registrador de status e testar um bit. Isto leva algum tempo. Você pode usar também uma interrupção, mas isto é ainda mais lento (porém deixa você fazer alguma coisa enquanto o byte está sendo transmitido).
  • Como o clock da CPU e da SPI são próximos, são poucas as intruções que podem ser feitas durante a transmissão de um byte. Considerando que a maioria das instruções são executadas em um único ciclo, dá para executar 16 instruções enquanto são transmitidos os 8 bits a 8MHz.
O problema seguinte é o intervalo entre o envio de duas atualizações. A biblioteca fastLED usa um buffer que é manipulado pela aplicação e enviado via SPI quando se chama o método show. Portanto o tempo entre uma atualização e outra acaba sendo, no mínimo, o tempo para preparar o buffer.

Como podemos melhorar isto? Usando um microcontrolador mais rápido e com mais recursos na interface SPI. Um recurso muito útil é o DMA, onde bytes são movidos da memória para a interface sem passar pela CPU. Veja aqui o resultado de alguns testes da velocidade bruta do SPI, com e sem DMA (quando disponível), feito com diversas placas.

Em um post futuro mostro um exemplo de uso da fita LED com um Arduino Uno. Mais para frente pretendo fazer experiências com o Arduino Due e com a Launchpad Stellaris.

Nenhum comentário: