[活动专区] 【赛元95F】三色流水灯的制作

[复制链接]
1467|7
 楼主| 北斗stone 发表于 2020-4-7 20:49 | 显示全部楼层 |阅读模式
本帖最后由 北斗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芯片的工作模式:
      1.png
      一颗灯珠有RGB三色,每种颜色有8bit,共24bit。结合上图,笔者选用的时间为:T0H可选250ns,T0L可选750ns;T1H可选750ns,T1L可选250ns。所以一个bit耗费1us;24个bit即为24us,默认间隔为1us;则1ms时间可以点亮40个灯珠。10ms即可以点亮400颗串联型灯珠。笔者用到的灯珠约有140颗,因此10ms的定时器中断亮一帧140颗灯珠绰绰有余。
       定时器初始化如下:
  1. void Timer_Init(void)
  2. {
  3.         TMCON = 0X00;  
  4.         TMOD |= 0x01;
  5.         TL0 = (65536 - 32000)%256;
  6.         TH0 = (65536 - 32000)/256;
  7.         TR0 = 0;
  8.         ET0 = 1;
  9.         TR0 = 1;
  10.         EA = 1;        
  11. }
       结合视频分析一下自己的代码,请各位提出宝贵建议:
  1. void timer0() interrupt 1
  2. {
  3.         TL0 = (65536 - 32000)%256;
  4.         TH0 = (65536 - 32000)/256;
  5.         P02 = ~P02;        
  6.         Task_run(Task_Wait_Buffer,sizeof(Task_Wait_Buffer));
  7.         switch(Blink_Task_Num)
  8.         {
  9.         case 0:break;
  10.         case 1:Blink_Task_BeginToEnd();break;
  11.         case 2:Blink_Task_EndToBegin();break;
  12.         case 3:Blink_Task_MiddleToEdge();break;
  13.         case 4:Blink_Task_Breath();break;
  14.         default:break;
  15.         }
  16. }
  1. void Task_run(char Task_Buffer[],char len)
  2. {
  3.         char *p = Task_Buffer;
  4.         static char i=0;
  5.         if(Blink_Task_Num == 0)
  6.         {
  7.                 Blink_Task_Num = p[i];
  8.                 if(i<len-1)
  9.                 {
  10.                         i++;
  11.                 }
  12.         }
  13. }
  1. void Blink_Task_BeginToEnd()
  2. {
  3.         static short int j=0;
  4.         int i;
  5.         for(i=0;i<LED_NUM;i++)
  6.         {
  7.                 if(i<=j&&i>j-LED_Blink_Len)
  8.                         RGB_LED_Write_24Bits(0xffffff);
  9.                 else
  10.                         RGB_LED_Write_24Bits(0x00);
  11.         }
  12.         j++;
  13.         if(j>=LED_NUM+LED_Blink_Len)
  14.         {
  15.                 j=0;
  16.                 Blink_Task_Num = 0;
  17.         }
  18. }
  19. void Blink_Task_EndToBegin()
  20. {
  21.         static short int j=LED_NUM+LED_Blink_Len;
  22.         int i;
  23.         for(i=0;i<LED_NUM;i++)
  24.         {
  25.                 if(i<j&&i>=j-LED_Blink_Len)
  26.                         RGB_LED_Write_24Bits(0xffffff);
  27.                 else
  28.                         RGB_LED_Write_24Bits(0x00);
  29.         }
  30.         j--;
  31.         if(j<0)
  32.         {
  33.                 j=LED_NUM+LED_Blink_Len;
  34.                 Blink_Task_Num = 0;
  35.         }
  36. }
  37. void Blink_Task_MiddleToEdge()
  38. {
  39.         static short int j=0;
  40.         int i;
  41.         for(i=0;i<LED_NUM;i++)
  42.         {
  43.                 if((i<j&&i>=j-LED_Blink_Len/2)||(i>=LED_NUM-j&&i<LED_NUM-j+LED_Blink_Len/2))
  44.                         RGB_LED_Write_24Bits(0xffffff);
  45.                 else
  46.                         RGB_LED_Write_24Bits(0x00);
  47.         }
  48.         j++;
  49.         if(j>=(LED_NUM+LED_Blink_Len))
  50.         {
  51.                 j=0;
  52.                 Blink_Task_Num = 0;
  53.         }
  54. }
  55. void Blink_Task_Breath()
  56. {
  57.         static int j=0;
  58.         static char dir = 1;
  59.         int i;
  60.         for(i=0;i<LED_NUM;i++)
  61.         {
  62.                 RGB_LED_Write_24Bits((int)(j<<16|j<<8|j));
  63.         }
  64.         if(dir)
  65.                 j++;
  66.         else
  67.                 j--;
  68.         if(j>=255 && dir==1)
  69.         {
  70.                 dir = 0;
  71.         }
  72.         if(j<0 && dir == 0)
  73.         {
  74.                 j = 0;
  75.                 dir=1;
  76.                 Blink_Task_Num = 0;
  77.         }
  78. }

在定时器中断中,首先执行task_run,传入的buffer是流水灯的闪烁模式的顺序。每种模式的帧数取决于灯的数量,当前模式执行的帧数记录在静态变量中。然后根据全局变量获取当前是哪个task执行自己的task,task即为该灯的闪烁模式:从左到右,从右到左,中间到两边还是呼吸模式。

以下是底层函数,由于ns级定时8位机实在是没法搞,因此用了nop空操作。nop的数量是对着示波器测出来的。
  1. void RGB_LED_Write0()
  2. {
  3.         RGB_LED_HIGH;
  4.         _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
  5.         _nop_();
  6.         RGB_LED_LOW;
  7.         _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
  8.         _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
  9.         _nop_();_nop_();_nop_();
  10. }
  11. void RGB_LED_Write1()
  12. {
  13.         RGB_LED_HIGH;
  14.         _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
  15.         _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
  16.         _nop_();_nop_();_nop_();_nop_();_nop_();
  17.         RGB_LED_LOW;
  18.         _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
  19. }
  20. void RGB_LED_Write_Byte(char byte)
  21. {
  22.         char i;
  23.         for(i=0;i<8;i++)
  24.         {
  25.                 if(byte&0x80)
  26.                 {
  27.                         RGB_LED_Write1();
  28.                 }
  29.                 else
  30.                 {
  31.                         RGB_LED_Write0();
  32.                 }
  33.                 byte <<= 1;
  34.         }
  35. }
  36. void RGB_LED_Write_24Bits(int RGB_Color)
  37. {
  38.         RGB_LED_Write_Byte((RGB_Color>>16)&0xff);
  39.         RGB_LED_Write_Byte((RGB_Color>> 8)&0xff);
  40.         RGB_LED_Write_Byte((RGB_Color    )&0xff);
  41. }

加上一些头文件和常量的定义:
  1. #define         LED_NUM                        132
  2. #define         LED_Blink_Len        25
  3. #define                RGB_LED_HIGH        (P00 = 0x01)
  4. #define         RGB_LED_LOW                (P00 = 0x00)

  5. char Blink_Task_Num = 0;
  6. char Task_Wait_Buffer[]        = {0,1,2,1,2,3,4,3,4,0};
  7. char Task_Cut_Buffer[]         = {0,1,2,1,2,4};
灯条长度132个灯珠,每次闪烁的灯数量为25个;高低电平的信号;记录当前task和两个task的闪烁顺序。整个代码见附件。

      

SinOne_WS2812.rar

130.64 KB, 下载次数: 39

源码

 楼主| 北斗stone 发表于 2020-4-7 20:57 | 显示全部楼层
一颗灯珠的电流在12mA,130颗灯珠全亮的话,电流在1.5A左右,USB肯定是带不动的,所以视频中只能下完程序拔掉下载器,接上实验室电源跑。
CS801380 发表于 2020-5-7 17:23 | 显示全部楼层
学习。。。。。。。。。。。。。。。
heimaojingzhang 发表于 2020-5-13 16:30 | 显示全部楼层
非常感谢楼主分享
keaibukelian 发表于 2020-5-13 16:31 | 显示全部楼层
非常详细啊
labasi 发表于 2020-5-13 16:31 | 显示全部楼层
楼主辛苦了
paotangsan 发表于 2020-5-13 16:31 | 显示全部楼层
做出来肯定很好看
jacktimto 发表于 2020-5-28 11:03 | 显示全部楼层
您需要登录后才可以回帖 登录 | 注册

本版积分规则

31

主题

338

帖子

6

粉丝
快速回复 在线客服 返回列表 返回顶部