我想分享创建8x8像素LED显示屏,262k颜色组合(18位),180 FPS的帧速率和USB连接的经验。我也准备倾听关于优化和完善的建议。将来,我计划使用最佳实践来创建家庭气象站的显示。前言
一切始于通过LPT端口对8个LED线进行最简单的控制。下一个版本是5x8的三色LED板,它也连接到LPT,实际上是由15个8位缓冲器组成的阵列,带有用于寻址的解码器。 后来,在见到微控制器之后,我着手创建一个类似的显示器,但带有USB连接。最初预期仅使用8种颜色。随后,他找到了一种使用计时器类似于PWM的方法来控制每个二极管的亮度的方法,并在完成软件部分的处理后,将当前的设备变成了现实。从理论上讲,您可以使用1600万种颜色,但是就颜色再现性和可重复性而言,常规LED不适合该模式。此外,不同二极管的颜色问题在当前配置中已经很明显。职位描述
该器件基于PIC18F4550微控制器,其工作频率为48 MHz。使用一个内置的USB控制器和一个可用的现成库,该定时器库为8位模式下的Timer0,可实现动态指示。为了在一列中存储三种颜色,在74F374上使用了三个8位触发器。使用这样的缓冲器允许将一帧的显示时间减少三倍。注意:当我选择74F374缓冲器时,我并没有注意其支脚的布线,但是我仅在安装支架上才了解这一点,因此必须使电路板非常复杂。最好使用更方便的类似物。例如74HC574。 LED通过ULN2803和UDN2982键连接。限流电阻仅在红色通道中,因为它们的电源电压低于蓝色和绿色。对于蓝色和绿色的电阻没有安装,因为按键上的压降足够大。注意:为了获得更准确的色彩还原,最好在每个通道中选择更准确的限流电阻。 微控制器以无休止的周期轮询USB状态,并在到达数据包时根据命令启动/停止显示或准备显示数据。由于一个数据包的大小限制为64字节,每种颜色的数据都在一个单独的48字节数据包中传输-8列中的每列6字节,对列中每个LED的亮度进行编码。收到每个包装后,会将其从USB存储器复制到其颜色的阵列中。 接收到指示启动命令后,MK会以8位模式激活计时器并将其除以128。该计时器将微控制器的工作频率用作时钟脉冲。定时器计数器每增加4小节就会增加一次。最小定时器周期为10.6μs(1/48 * 4 * 128),大约是中断处理时间的2.8倍(46个操作,需要128个定时器采样)。 当定时器溢出时,将执行高向量中断。中断处理程序禁用该指示,更新缓冲区中的数据,根据光标从每个颜色数组中传输1个字节,然后打开该指示。从临时缓冲区向计时器输入新值,减少光标,移动计时器的临时缓冲区。如果定时器缓冲区超过最大值,即 如果移位超过5次,计时器缓冲区将重置为最小值,并且所选列的指针将移位。结果,获得以下动态指示算法:- 我们从三个数组中取出第一组3字节,并放入列中每种颜色的缓冲区中。
- 我们以最少128个滴答的延迟时间激活计时器。
- 我们从三个数组中取出下一组3个字节,并将其放入列中每种颜色的缓冲区中。
- 相对于上一步,我们以两倍的延迟激活了计时器。
- 再重复采样4次,每次延迟时间加倍。
- 我们重置计时器,并开始处理步骤1中的下一列。
因此,我们可以为列中的每个二极管设置2 ^ 6 = 64个亮度选项。结合三种基本颜色的亮度,我们得到64 * 64 * 64 = 262144种颜色。一列的处理时间为(2 ^ 6-1)* 10.6μs= 672μs。8列的每帧时间为672 * 8 = 5.4ms,大约相当于每秒186帧。使用的组件
方案
DSN格式的方案- 下载手续费
Lay6图纸- 下载韧体
MPLABX X IDE v2.30中的源代码和已组装的HEX- 下载主要代号#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
设备正在运行
为了进行控制,我使用基于C编写的Internet广播播放器,该播放器基于BASS.DLL库。暂停期间,整个可用调色板上的渐变演示都将起作用,帧(传输到设备的数据包)的刷新率为20 Hz。播放音乐时,可视化器使用BASS.DLL获得的FFT数组工作,在此模式下帧(发送到设备的数据包)的刷新率是29 Hz。可视化器
: Tape Five — Soulsalicious
: ( ) ( ). .. , .- ( UDN)
- USB
- smd
- 74F374 74HC574,
- 74F374
- 74HC138,
- 3 ULN, UDN