本帖最后由 北斗stone 于 2020-4-7 20:51 编辑
最近公司有个子项目用到了流水灯,正好看到赛元有开发板的活动,于是申请来一块板子进行验证。需求:一个DO控制WS2812串行三色彩灯进行控制,实现各种LED的各种亮灭模式。一个DI进行模式的执行。演示效果:https://v.youku.com/v_show/id_XNDYyMjYyNjE3Mg==.html?spm=a2hbt.13141534.app.5~5!2~5!2~5~5~5!2~5~5!2~5!2~5!2~5~5~A
首先分析下WS2812芯片的工作模式:
一颗灯珠有RGB三色,每种颜色有8bit,共24bit。结合上图,笔者选用的时间为:T0H可选250ns,T0L可选750ns;T1H可选750ns,T1L可选250ns。所以一个bit耗费1us;24个bit即为24us,默认间隔为1us;则1ms时间可以点亮40个灯珠。10ms即可以点亮400颗串联型灯珠。笔者用到的灯珠约有140颗,因此10ms的定时器中断亮一帧140颗灯珠绰绰有余。 定时器初始化如下: void Timer_Init(void)
{
TMCON = 0X00;
TMOD |= 0x01;
TL0 = (65536 - 32000)%256;
TH0 = (65536 - 32000)/256;
TR0 = 0;
ET0 = 1;
TR0 = 1;
EA = 1;
}
结合视频分析一下自己的代码,请各位提出宝贵建议: void timer0() interrupt 1
{
TL0 = (65536 - 32000)%256;
TH0 = (65536 - 32000)/256;
P02 = ~P02;
Task_run(Task_Wait_Buffer,sizeof(Task_Wait_Buffer));
switch(Blink_Task_Num)
{
case 0:break;
case 1:Blink_Task_BeginToEnd();break;
case 2:Blink_Task_EndToBegin();break;
case 3:Blink_Task_MiddleToEdge();break;
case 4:Blink_Task_Breath();break;
default:break;
}
}
void Task_run(char Task_Buffer[],char len)
{
char *p = Task_Buffer;
static char i=0;
if(Blink_Task_Num == 0)
{
Blink_Task_Num = p[i];
if(i<len-1)
{
i++;
}
}
}
void Blink_Task_BeginToEnd()
{
static short int j=0;
int i;
for(i=0;i<LED_NUM;i++)
{
if(i<=j&&i>j-LED_Blink_Len)
RGB_LED_Write_24Bits(0xffffff);
else
RGB_LED_Write_24Bits(0x00);
}
j++;
if(j>=LED_NUM+LED_Blink_Len)
{
j=0;
Blink_Task_Num = 0;
}
}
void Blink_Task_EndToBegin()
{
static short int j=LED_NUM+LED_Blink_Len;
int i;
for(i=0;i<LED_NUM;i++)
{
if(i<j&&i>=j-LED_Blink_Len)
RGB_LED_Write_24Bits(0xffffff);
else
RGB_LED_Write_24Bits(0x00);
}
j--;
if(j<0)
{
j=LED_NUM+LED_Blink_Len;
Blink_Task_Num = 0;
}
}
void Blink_Task_MiddleToEdge()
{
static short int j=0;
int i;
for(i=0;i<LED_NUM;i++)
{
if((i<j&&i>=j-LED_Blink_Len/2)||(i>=LED_NUM-j&&i<LED_NUM-j+LED_Blink_Len/2))
RGB_LED_Write_24Bits(0xffffff);
else
RGB_LED_Write_24Bits(0x00);
}
j++;
if(j>=(LED_NUM+LED_Blink_Len))
{
j=0;
Blink_Task_Num = 0;
}
}
void Blink_Task_Breath()
{
static int j=0;
static char dir = 1;
int i;
for(i=0;i<LED_NUM;i++)
{
RGB_LED_Write_24Bits((int)(j<<16|j<<8|j));
}
if(dir)
j++;
else
j--;
if(j>=255 && dir==1)
{
dir = 0;
}
if(j<0 && dir == 0)
{
j = 0;
dir=1;
Blink_Task_Num = 0;
}
}
在定时器中断中,首先执行task_run,传入的buffer是流水灯的闪烁模式的顺序。每种模式的帧数取决于灯的数量,当前模式执行的帧数记录在静态变量中。然后根据全局变量获取当前是哪个task执行自己的task,task即为该灯的闪烁模式:从左到右,从右到左,中间到两边还是呼吸模式。
以下是底层函数,由于ns级定时8位机实在是没法搞,因此用了nop空操作。nop的数量是对着示波器测出来的。 void RGB_LED_Write0()
{
RGB_LED_HIGH;
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();
RGB_LED_LOW;
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();
}
void RGB_LED_Write1()
{
RGB_LED_HIGH;
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
RGB_LED_LOW;
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
}
void RGB_LED_Write_Byte(char byte)
{
char i;
for(i=0;i<8;i++)
{
if(byte&0x80)
{
RGB_LED_Write1();
}
else
{
RGB_LED_Write0();
}
byte <<= 1;
}
}
void RGB_LED_Write_24Bits(int RGB_Color)
{
RGB_LED_Write_Byte((RGB_Color>>16)&0xff);
RGB_LED_Write_Byte((RGB_Color>> 8)&0xff);
RGB_LED_Write_Byte((RGB_Color )&0xff);
}
加上一些头文件和常量的定义: #define LED_NUM 132
#define LED_Blink_Len 25
#define RGB_LED_HIGH (P00 = 0x01)
#define RGB_LED_LOW (P00 = 0x00)
char Blink_Task_Num = 0;
char Task_Wait_Buffer[] = {0,1,2,1,2,3,4,3,4,0};
char Task_Cut_Buffer[] = {0,1,2,1,2,4};
灯条长度132个灯珠,每次闪烁的灯数量为25个;高低电平的信号;记录当前task和两个task的闪烁顺序。整个代码见附件。
|