切换到宽版
  • 3198阅读
  • 22回复

[C51]STC15W204S驱动RGB灯带,实现流水灯效果(开源) [复制链接]

上一主题 下一主题
离线jjbboox
 

发帖
706
M币
3777
专家
10
粉丝
35
STC15W204是8PIN的宽电压版,驱动WS2812的时候单片机可以用5V电压,也可以用3.3V的电源。
RGB灯条的电源应该是4-7V之间,目前驱动的是8颗粒的灯条,原则上应该可以驱动更多颗粒的,不过手头没有那么长的,没试过,有兴趣的朋友可以自己试验,看看最多可以驱动多少个。按理论来讲驱动8颗和80颗应该是一样的。

硬件环境:
单片机:STC15W204S(SOP8)
灯带:WS2812(8颗粒)
非必需品:开关,USB转接板,面包板,跳线若干。
使用片内R/C振荡器,连外部晶振都不需要,工作频率为11.0592MHz
STC15W204S可以用其他其他STC 1T的MCU。

代码也非常简单,只用到1个IO口,不需要进行任何初始化。
如果使用其他工作频率时,请相应调整DELAY_LONG和DELAY_SHORT的宏定义,适当增减_nop()_的个数,直到能正确驱动灯条为止。
先通过视频看看实际运行的效果。重新做了个视频,欢迎点击观看。



以下是开源的代码,全部工程只有一个.c文件,新建工程后将该文件添加到工程中即可。
编译后也就在1.3K左右,剩余的ROM空间还可以扩展做一些动态效果什么的。
  1. #include <reg51.h>
  2. #include <intrins.h>
  3. // 使用的IO口,无需初始化
  4. sbit    DOUT = P3^3;
  5. #define    DELAY_LONG    {_nop_();_nop_();_nop_();}
  6. #define DELAY_SHORT    {_nop_();_nop_();}
  7. #define SEND_1    {DOUT=1;DELAY_LONG;DOUT=0;DELAY_SHORT;}
  8. #define SEND_0    {DOUT=1;DELAY_SHORT;DOUT=0;DELAY_LONG;}
  9. #define SEND_BIT(x) {if(x) {SEND_1;} else {SEND_0;}}
  10. // WS2812灯带颗粒数
  11. #define    PIXEL_CNT        (8)
  12. // RGB颗粒结构
  13. typedef struct {
  14.     unsigned int    next_pixel;        // 下一颗粒的index
  15.     unsigned char green;                // 绿色值
  16.     unsigned char red;                    // 红色值
  17.     unsigned char blue;                    // 蓝色值
  18.     unsigned char brightness;        // 亮度值
  19. } Pixel;
  20. void send_p(Pixel _p);                // 发送1个颗粒的值
  21. void send_pixels(Pixel *_p_list, unsigned int _start, unsigned int _cnt);        // 发送所有颗粒的值
  22. // 各颗粒值的初始值
  23. Pixel pixels[PIXEL_CNT] = {
  24. {1, 31, 255, 127, 255},
  25. {2, 31, 255, 127, 127},
  26. {3, 31, 255, 127, 31},
  27. {4, 31, 255, 127, 7},
  28. {5, 31, 255, 127, 1},
  29. {6, 31, 255, 127, 0},
  30. {7, 31, 255, 127, 0},
  31. {0, 31, 255, 127, 0},
  32. };
  33. // 通用计数器
  34. unsigned int cnt = 0;
  35. // 指定毫秒数Delay
  36. void delay_ms(unsigned int x)        //@11.0592MHz
  37. {
  38.     unsigned char i, j;
  39.     do {
  40.         _nop_();
  41.         _nop_();
  42.         _nop_();
  43.         i = 11;
  44.         j = 190;
  45.         do
  46.         {
  47.             while (--j);
  48.         } while (--i);
  49.     } while (--x);
  50. }
  51. // 主函数
  52. void main() {
  53.     // 初始化段
  54.     
  55.     // 循环处理段
  56.     while(1) {
  57.         // 等待50ms
  58.         delay_ms(50);
  59.         cnt %= 0x100;
  60.         send_pixels(pixels, cnt % PIXEL_CNT, PIXEL_CNT);
  61.         cnt++;
  62.     }    
  63. }
  64. void Delay50us()        //@11.0592MHz
  65. {
  66.     unsigned char i, j;
  67.     _nop_();
  68.     i = 1;
  69.     j = 134;
  70.     do
  71.     {
  72.         while (--j);
  73.     } while (--i);
  74. }
  75. // RESET信号
  76. void reset() {
  77.     DOUT = 0;
  78.     Delay50us();
  79. }
  80. void send_p(Pixel _p) {
  81.     unsigned char p_v;
  82.     p_v = (_p.green * _p.brightness) >> 8;
  83.     SEND_BIT(p_v & 0x80);
  84.     SEND_BIT(p_v & 0x40);
  85.     SEND_BIT(p_v & 0x20);
  86.     SEND_BIT(p_v & 0x10);
  87.     SEND_BIT(p_v & 0x08);
  88.     SEND_BIT(p_v & 0x04);
  89.     SEND_BIT(p_v & 0x02);
  90.     SEND_BIT(p_v & 0x01);
  91.     p_v = (_p.red * _p.brightness) >> 8;
  92.     SEND_BIT(p_v & 0x80);
  93.     SEND_BIT(p_v & 0x40);
  94.     SEND_BIT(p_v & 0x20);
  95.     SEND_BIT(p_v & 0x10);
  96.     SEND_BIT(p_v & 0x08);
  97.     SEND_BIT(p_v & 0x04);
  98.     SEND_BIT(p_v & 0x02);
  99.     SEND_BIT(p_v & 0x01);
  100.     p_v = (_p.blue * _p.brightness) >> 8;
  101.     SEND_BIT(p_v & 0x80);
  102.     SEND_BIT(p_v & 0x40);
  103.     SEND_BIT(p_v & 0x20);
  104.     SEND_BIT(p_v & 0x10);
  105.     SEND_BIT(p_v & 0x08);
  106.     SEND_BIT(p_v & 0x04);
  107.     SEND_BIT(p_v & 0x02);
  108.     SEND_BIT(p_v & 0x01);
  109. }
  110. void send_pixels(Pixel *_p_list, unsigned int _start, unsigned int _cnt) {
  111.     Pixel *_p = &_p_list[_start % _cnt];
  112.     reset();
  113.     while(_cnt--) {
  114.         send_p(*_p);
  115.         _p = &_p_list[_p->next_pixel];
  116.     }
  117.     reset();
  118. }




[ 此帖被jjbboox在2018-04-09 08:45重新编辑 ]
本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
本帖最近打赏记录:共1条打赏M币+8
jmkl128 M币 +8 優秀文章 04-08
离线elecfunwb

发帖
543
M币
2445
专家
5
粉丝
5
只看该作者 1楼 发表于: 04-07
那几个SEND_BIT可以用循环来简写
for (j=0; j<8; j++)
   SEND_BIT(p_v & (0x80>>j));
离线毁灭者e5

发帖
811
M币
221
专家
2
粉丝
27
只看该作者 2楼 发表于: 04-07
有什么好的灯效果程序没? 也在写这个灯,感觉用自加 自减 的呼吸灯效果不好 LED亮度和PWM不是线性的 还有三色LED工作电压 电流不同偏色,不知道有什么好的算法修正。。。 波浪效果实现程序也感觉没效率。
离线jjbboox

发帖
706
M币
3777
专家
10
粉丝
35
只看该作者 3楼 发表于: 04-07
回 elecfunwb 的帖子
elecfunwb:那几个SEND_BIT可以用循环来简写
for (j=0; j<8; j++)
   SEND_BIT(p_v & (0x80>>j)); (2018-04-07 14:07) 回 elecfunwb 的帖子

因为这里的时序控制非常严格,所以没有用循环来实现,也没试过,也许可以,也许循环了就不行了。有兴趣你可以试验一下。

另外需要注意的是,发送数据的时候不能被中断,特别是往WS2812输出驱动波形的时候。
如果有用到中断的,请在send_pixels函数的reset()之前关闭中断使能,等发送完毕后再将中断使能打开。
离线jjbboox

发帖
706
M币
3777
专家
10
粉丝
35
只看该作者 4楼 发表于: 04-07
回 毁灭者e5 的帖子
毁灭者e5:有什么好的灯效果程序没? 也在写这个灯,感觉用自加 自减 的呼吸灯效果不好 LED亮度和PWM不是线性的 还有三色LED工作电压 电流不同偏色,不知道有什么好的算法修正。。。 波浪效果实现程序也感觉没效率。[表情] (2018-04-07 14:12) 回 毁灭者e5 的帖子

以前用STM32做过几个效果
流水灯,呼吸灯,呼吸流水灯,乒乓球效果,七彩循环色彩渐变。
再弄个蓝牙串口控制速度,模式,亮度什么的。

目前这个STC的U,ROM和内存都太小了,做不出太复杂的效果。优点就是小巧,而且外围电路超级简单,准备搞个单颗2812的,做一个放在车载烟灰缸里用用。

你说的三色LED发光效率不同的问题,我感觉实际没那么严重,驱动电压确实有不同,但是亮度应该还是基本一致的,毕竟WS2812本身就是芯片,内部应该已经有补偿了。毕竟三色合成的白色光还是挺正的。
这个也无需PWM控制的,直接把三原色的亮度值串行写入灯带就行了。

离线毁灭者e5

发帖
811
M币
221
专家
2
粉丝
27
只看该作者 5楼 发表于: 04-07
回 jjbboox 的帖子
jjbboox:以前用STM32做过几个效果
流水灯,呼吸灯,呼吸流水灯,乒乓球效果,七彩循环色彩渐变。
再弄个蓝牙串口控制速度,模式,亮度什么的。
....... (2018-04-07 14:34) 回 jjbboox 的帖子

发个程序研究下呗,我用stm8做 软件延时根本不能用 24位的位移取位加循环太耗时了,根本跟不上时序,改用SPI驱动还不错,现在要让出SPI 改用PWM驱动, LED低亮度时严重色不对,远点看还好 关键是呼吸灯感觉和专业的渐变过程差很多
离线jjbboox

发帖
706
M币
3777
专家
10
粉丝
35
只看该作者 6楼 发表于: 04-07
回 毁灭者e5 的帖子
毁灭者e5:发个程序研究下呗,我用stm8做 软件延时根本不能用 24位的位移取位加循环太耗时了,根本跟不上时序,改用SPI驱动还不错,现在要让出SPI 改用PWM驱动, LED低亮度时严重色不对,远点看还好 关键是呼吸灯感觉和专业的渐变过程差很多[表情] (2018-04-07 14:51) 回 毁灭者e5 的帖子

STM32我也是用SPI+DMA驱动的,PWM没试过,不知道咋玩!
离线szryg

发帖
60
M币
9
专家
1
粉丝
5
只看该作者 7楼 发表于: 04-07
回 jjbboox 的帖子
jjbboox:因为这里的时序控制非常严格,所以没有用循环来实现,也没试过,也许可以,也许循环了就不行了。有兴趣你可以试验一下。
另外需要注意的是,发送数据的时候不能被中断,特别是往WS2812输出驱动波形的时候。
如果有用到中断的,请在send_pixels函数的reset()之前关闭中断使能,等 .. (2018-04-07 14:28) 回 jjbboox 的帖子

确实,我以前做过这个程序,一开始就是用移位的,结果根本不行,可能频率调高点能ok,但我觉得楼主这个方法简单粗暴
离线jjbboox

发帖
706
M币
3777
专家
10
粉丝
35
只看该作者 8楼 发表于: 04-08
回 szryg 的帖子
szryg:确实,我以前做过这个程序,一开始就是用移位的,结果根本不行,可能频率调高点能ok,但我觉得楼主这个方法简单粗暴 (2018-04-07 16:53) 回 szryg 的帖子

简单粗暴,但有效。
这么低的主频,时序的最小间隔也只有2,3个nop,一切以速度为优先考量,有效的方法才是好方法,这可不是考校编程技巧的时候,哈哈
离线qvchat

发帖
8
M币
296
专家
0
粉丝
2
只看该作者 9楼 发表于: 04-08
SPI+DMA驱动我也用过
快速回复
限80 字节
“新手上路”发帖需审核后才能显示(请认真发帖),达到数码9级后取消此限制
 
上一个 下一个