本帖最后由 北斗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的闪烁顺序。整个代码见附件。
|