Quero compartilhar a experiência de criar um display LED de 8x8 pixels, 262k combinações de cores (18 bits), uma taxa de quadros de 180 FPS e uma conexão USB. Também estou pronto para ouvir sugestões de otimização e refinamento. No futuro, pretendo usar as melhores práticas para criar uma exibição da estação meteorológica doméstica.Prefácio
Tudo começou com o esquema de controle mais simples para uma linha de 8 LEDs através de uma porta LPT. A versão seguinte era uma placa 5x8 de LEDs de três cores, que também era conectada ao LPT e, de fato, era uma matriz de quinze buffers de 8 bits com um decodificador para o endereçamento. Mais tarde, depois de conhecer os microcontroladores, decidi criar uma tela semelhante, mas com uma conexão USB. Inicialmente esperado usar apenas 8 cores. Posteriormente, ele encontrou uma maneira de controlar o brilho de cada diodo usando um temporizador por analogia com o PWM e, após finalizar a parte do software, o dispositivo atual foi desligado. Teoricamente, você pode trabalhar com 16 milhões de cores, mas os LEDs convencionais não são adequados para esse modo em termos de reprodução e repetibilidade de cores. Além disso, problemas com a cor de diferentes diodos já são visíveis na configuração atual.Descrição do trabalho
O dispositivo é baseado no microcontrolador PIC18F4550, operando a uma frequência de 48 MHz. Um controlador USB embutido e uma biblioteca pronta para trabalhar com ele são usados, Timer0 no modo de 8 bits, que implementa a indicação dinâmica. Para armazenar três cores em uma coluna, três gatilhos de 8 bits no 74F374 foram usados. O uso desse buffer permite reduzir o tempo de exibição de um quadro em 3 vezes. Nota: Quando selecionei o buffer 74F374, não prestei atenção à fiação de suas pernas, mas entendi isso apenas no suporte de montagem, portanto tive que complicar significativamente a placa. É melhor usar análogos mais convenientes. Por exemplo, 74HC574. Os LEDs são conectados através das teclas ULN2803 e UDN2982. As resistências limitadoras de corrente estão apenas no canal vermelho, porque a tensão de alimentação está abaixo de azul e verde. Para azul e verde, a resistência não está instalada, porque queda de tensão suficiente nas teclas. Nota: Para uma reprodução de cores mais precisa, é melhor selecionar resistências limitadoras de corrente mais precisas em cada canal. O microcontrolador em um ciclo interminável pesquisa o status do USB e, quando um pacote de dados chega, dependendo do comando, inicia / para a exibição ou prepara os dados para exibição. Devido à limitação do tamanho de um pacote para 64 bytes, os dados de cada cor são transmitidos em um pacote separado de 48 bytes - 6 bytes para cada uma das 8 colunas, codificando o brilho de cada LED na coluna. Após receber cada pacote, ele é copiado da memória USB para uma matriz de sua cor. Após o recebimento do comando de indicação, o MK ativa o timer no modo de 8 bits e um divisor em 128. O timer usa a frequência de operação do microcontrolador como pulsos de clock. Um aumento no contador do timer ocorre a cada 4 compassos. O período mínimo do timer é de 10,6 μs (1/48 * 4 * 128), que é aproximadamente 2,8 vezes o tempo de processamento da interrupção (46 operações, contra 128 amostras do timer). Quando o temporizador transborda, uma alta interrupção do vetor é realizada. O manipulador de interrupção desativa a indicação, atualiza os dados nos buffers, transferindo 1 byte de cada matriz de cores de acordo com o cursor e, em seguida, ativa a indicação. Insere um novo valor no timer a partir do buffer temporário, diminui o cursor, muda o buffer temporário para o timer. Se o buffer do temporizador excedeu o valor máximo, ou seja, deslocado mais de 5 vezes, o buffer do timer é redefinido para o valor mínimo e o ponteiro da coluna selecionada é deslocado.Como resultado, é obtido o seguinte algoritmo de indicação dinâmica:- Pegamos o primeiro grupo de 3 bytes de três matrizes e colocamos os buffers de cada cor na coluna.
- Ativamos o timer com um tempo de atraso mínimo de 128 ticks.
- Pegamos o próximo grupo de 3 bytes de três matrizes e colocamos os buffers de cada cor na coluna.
- Ativamos o timer com um atraso duplo em relação ao passo anterior.
- Repita a amostra mais 4 vezes e duplique o tempo de atraso de cada vez.
- Redefinimos o cronômetro e começamos a processar a próxima coluna da etapa 1.
Assim, podemos definir 2 ^ 6 = 64 opções de brilho para cada diodo na coluna. Combinando o brilho de cada uma das três cores básicas, obtemos 64 * 64 * 64 = 262144 cores. O tempo de processamento para uma coluna é (2 ^ 6-1) * 10,6 μs = 672 μs. O tempo por quadro de 8 colunas é 672 * 8 = 5.4ms, o que corresponde aproximadamente a 186 quadros por segundo.Componentes Utilizados
- PIC18F4550 - Microcontrolador
- 74F374 - Disparador para manter os valores atuais da coluna
- ULN2803 - Chave para controle de cátodo
- UDN2982 - Chave para controle de ânodos
- LEDs RGB de 4 pinos com um cátodo comum (qualquer LED pode ser usado)
Esquema
Esquema no formato dsn - downloadTaxa
Lay6 desenhos - baixarFirmware
Origens e HEX montado no MPLABX X IDE v2.30 - downloadCódigo principal#ifndef MAIN_C
#define MAIN_C
#include "config.h"
#include "usb.h"
#include "HardwareProfile.h"
#include "usb_function_hid.h"
#include "genericHID.h"
#define UdnOn LATA&=0b11111110
#define UdnOff LATA|=0b00000001
#define UlnOn LATD
#define UlnOff LATD =0b00000000
#define LineBufer LATB
#define WriteR LATE|=0b00000001
#define WriteG LATE|=0b00000010
#define WriteB LATE|=0b00000100
#define WriteRst LATE =0b00000000
#define Columns 8
#define BrightLevels 6
#define BlockSize (Columns*BrightLevels)
#define MinBright 0b11111111
unsigned char cursor;
unsigned char bright;
unsigned char column;
unsigned char dataR[BlockSize];
unsigned char dataG[BlockSize];
unsigned char dataB[BlockSize];
void ProcessIO(void) {
unsigned char temp = BlockSize + 1;
if ((USBDeviceState < CONFIGURED_STATE) || (USBSuspendControl == 1)) return;
if (!HIDRxHandleBusy(USBOutHandle))
{
switch (ReceivedDataBuffer[0])
{
case 0x80:
while (--temp) dataR[temp-1] = ReceivedDataBuffer[temp];
break;
case 0x81:
while (--temp) dataG[temp-1] = ReceivedDataBuffer[temp];
break;
case 0x82:
while (--temp) dataB[temp-1] = ReceivedDataBuffer[temp];
break;
case 0x90:
column = 0b00000001;
cursor = BlockSize;
bright = MinBright;
TMR0ON = 1;
SWDTEN = 0;
break;
case 0x91:
UdnOff;
UlnOff;
TMR0ON = 0;
SWDTEN = 0;
break;
case 0x92:
UdnOff;
UlnOff;
TMR0ON = 0;
SWDTEN = 0;
SLEEP();
break;
}
USBOutHandle = HIDRxPacket(HID_EP, (BYTE*) & ReceivedDataBuffer, 64);
}
}
void main(void)
{
PCFG3 = 1;
PORTA = 0b00000000;
PORTB = 0b00000000;
PORTC = 0b00000000;
PORTD = 0b00000000;
PORTE = 0b00000000;
TRISA = 0b00000000;
TRISB = 0b00000000;
TRISC = 0b00000000;
TRISD = 0b00000000;
TRISE = 0b00000000;
INTCON = 0b10100000;
T0CON = 0b01000110;
USBDeviceInit();
while(1)
{
USBDeviceTasks();
ProcessIO();
};
}
void interrupt tc_int()
{
UdnOff;
UlnOff;
LineBufer = dataR[cursor-1]; WriteR;
LineBufer = dataG[cursor-1]; WriteG;
LineBufer = dataB[cursor-1]; WriteB;
UdnOn;
UlnOn = column;
WriteRst;
TMR0L = bright;
if (!--cursor) cursor = BlockSize;
bright <<= 1;
asm("BTFSS _bright, 5, 0"); asm("RLNCF _column, 1, 0");
asm("BTFSS _bright, 5, 0"); bright = MinBright;
TMR0IF = 0;
}
#endif
Dispositivo em operação
Para controle, uso um reprodutor de rádio da Internet escrito em C, baseado na biblioteca BASS.DLL. Uma demonstração de gradiente em toda a paleta de cores disponível funciona durante uma pausa, a taxa de atualização de quadros (pacotes transmitidos para o dispositivo) é de 20 Hz. Ao tocar música, um visualizador funciona usando a matriz FFT obtida pelo BASS.DLL, a taxa de atualização de quadros (pacotes transmitidos para o dispositivo) nesse modo é 29 Hz.Visualizer
: Tape Five — Soulsalicious
: ( ) ( ). .. , .- ( UDN)
- USB
- smd
- 74F374 74HC574,
- 74F374
- 74HC138,
- 3 ULN, UDN