打印
[活动专区]

【赛元95F】三色流水灯的制作

[复制链接]
1169|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 北斗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的闪烁顺序。整个代码见附件。

      

SinOne_WS2812.rar

130.64 KB

源码

使用特权

评论回复

相关帖子

沙发
北斗stone|  楼主 | 2020-4-7 20:57 | 只看该作者
一颗灯珠的电流在12mA,130颗灯珠全亮的话,电流在1.5A左右,USB肯定是带不动的,所以视频中只能下完程序拔掉下载器,接上实验室电源跑。

使用特权

评论回复
板凳
CS801380| | 2020-5-7 17:23 | 只看该作者
学习。。。。。。。。。。。。。。。

使用特权

评论回复
地板
heimaojingzhang| | 2020-5-13 16:30 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
5
keaibukelian| | 2020-5-13 16:31 | 只看该作者
非常详细啊

使用特权

评论回复
6
labasi| | 2020-5-13 16:31 | 只看该作者
楼主辛苦了

使用特权

评论回复
7
paotangsan| | 2020-5-13 16:31 | 只看该作者
做出来肯定很好看

使用特权

评论回复
8
jacktimto| | 2020-5-28 11:03 | 只看该作者

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

31

主题

338

帖子

6

粉丝