【原创】AVR混编驱动WS2812测试
本帖最后由 t3486784401 于 2015-11-17 12:26 编辑【背景】
前段时间跟着双十一的节奏,剁手收获了一大批乱七八糟的零件模块,WS2812灯串就是这其中之一。
本来想要买四脚版本的WS2812S,但是店里偏偏只有六脚版的WS2812搞活动,无奈买来几个玩玩。
相对于普通LED,这玩意真心不便宜,一块8灯串的模块就要¥10大洋,总之买来是这样的(背面焊了插座):
网上关于WS2812的驱动,通篇都是 Arduino/ARM/FPGA,既然这样说明 AVR 驱动这玩意还是有望的。
至于 Arduino 怎么做我也没去研究,只是觉着这个事情应该不至于困难到举步维艰的地步。
【C级分析】
看了手册后发现,WS2812的传输码率约为800kbps,直接拿AVR做C语言级别的循环,基本没戏,原因如下:
1. 我板子是 7.3728M 主频的,C转一圈几十个时钟周期松松的(移位、循环判断),直接分频至不到700k;
2. 写驱动就要有通用性,如果都用直接指令写死了(减少移位等运算),那这驱动也就没意义了;
3. AVR没有锁相环诶(PLL),这个在ARM里用烂了的东西,这里不能用了,而且AVR最快也就16M不能瞎搞了。
上述分析,被我第一版纯C的程序证实了:即使数据循环逻辑都对,比特间也有us级的延时,LED失控瞎JB亮。
【汇编分析】
本来就已放弃,打算某日换STM32再战,不想无意中算了下周期,发现很有趣的事情:
1. WS2812规定:逻辑1:H/L=0.85us/0.4us,逻辑0:H/L=0.4us/0.85us,时间比接近 2:1 和 1:2;
2. 对于 7.3728M 主频而言,800kbps 约为其 1/9,即需要每 9T 时间内,完成一个比特的传输;
3. 9T 按照 2:1 和 1:2 分配,居然可以整除!(瞬间RP爆棚了)
上述三条分析的结果是,如果可以实现 H/L=6T/3T、H/L=3T/6T 这样IO代码,就可以正确驱动!
想到这里,我已经开始去翻 AVR 的指令集了(手册上居然有),找到这么几个有希望的指令:
a) sbiP, b // 2T, P|= (1<<b)
b) cbiP, b // 2T, P&= ~(1<<b)
c) outP, Rr // 1T, P = Rr
d) ld Rd, X+ // 2T, Rd= *(X++)
由于需要间接寻址(内存中颜色数据影响到比特内容),所以 ld 这条指令基本必不可少了(2T);
访问端口的话,sbi/cbi虽然很好用,但一则需要 2T 太浪费,二则需要控制分支,最终选择用out,
置位、复位操作的数值用两个寄存器事先准备好,直接out就可以在 1T 时间内完成输出;
为了给循环控制挤出时间,将9T拆分为3*3T,前段确定为H输出,后段确定为L输出,中段为数据。
每段的最终输出使用out指令,因而每段当中还有2T空闲,可以用作加载、循环控制
【混编实现】
有了上述分析,就可以着手开始工作了。整个程序框架由C生成,在关键的 WS2812_Write 当中,
通过内嵌汇编的方式,确保 out 指令间隔严格为3T,实现WS2812驱动逻辑。
这个源码我在M64/M8上都跑过,除了头文件和MCU设置,其他直接移植就可用,
说明内嵌的汇编仅涉及AVR内核,在MEGA(甚至是TINY)系列当中还是可以复用的。
实测加载8个灯约5ms,这个性能将来刷动画是妥妥有希望的。
直接上源码(ICCAVR v7.22):
附带个手册(六脚WS2812):
【运行效果】
我只写了几个简单的颜色进去,稍后再改动画吧:
【后记】
不知不觉中感觉AVR也就那样了,毕竟有些年头了再加上ATMEL的态度,难怪新品都投奔ARM去了。
不过在稳定的平台上,做出些稳定的东西,终究是有人要的,Arduino就是很好的例子。
-----------------------------------------------------------------------------
不写了,[吃饭去鸟].....(((((ヾ( o=^•ェ•)o ┏━┓ 技术贴,值得学习! 不错,楼主时间好多啊。AVR用起来还是不错的,虽然已经不敌M0 用硬件SPI驱动WS2812这类的芯片还是很开心的 shuiluo2 发表于 2015-11-18 09:07
不错,楼主时间好多啊。AVR用起来还是不错的,虽然已经不敌M0
其实是没有细去思考 5-3.3 之间的转换,才拿AVR来偷懒的:) zqf441775525 发表于 2015-11-18 09:17
用硬件SPI驱动WS2812这类的芯片还是很开心的
也是个好办法啊,不过AVR的SPI不带DMA也不带FIFO,数据发送连不上是个头痛的事情
这么一想还是投奔ARM吧 t3486784401 发表于 2015-11-18 10:39
也是个好办法啊,不过AVR的SPI不带DMA也不带FIFO,数据发送连不上是个头痛的事情
这么一想还是投奔ARM吧 ...
嗯,很对,得要带DMA的SPI,我就是这么驱动的,而且它的1和0的高电平时间和低电平时间我实际测试过没必要那么精准,误差30-40%都是可以的。 zqf441775525 发表于 2015-11-18 10:41
嗯,很对,得要带DMA的SPI,我就是这么驱动的,而且它的1和0的高电平时间和低电平时间我实际测试过没必要 ...
不过感觉还是略有浪费,SPI 开了后好些引脚就占用了(SCK/MISO)....
用UART还好,不过UART静态是高电平状态,又需要反相器了.... t3486784401 发表于 2015-11-18 11:15
不过感觉还是略有浪费,SPI 开了后好些引脚就占用了(SCK/MISO)....
用UART还好,不过UART静态是高电平 ...
STM32的SPI好像有单输出模式,只占用两个口,一个SCK,一个MOSI。 zqf441775525 发表于 2015-11-18 11:26
STM32的SPI好像有单输出模式,只占用两个口,一个SCK,一个MOSI。
这样很好嘛
不过既然用DMA了,不妨直接 MEM 到 IO 试试看,就是需要额外一个定时器咯 谢谢分享 好东西,我来上传个WS2812的PDF吧
GCC内联汇编吗?前段时间刚用,感觉不熟悉。 dma加pwm的路过 wrmike 发表于 2015-12-4 12:56
GCC内联汇编吗?前段时间刚用,感觉不熟悉。
ICC内联汇编,需要效率就只能这么做了,换控制器那是另一条路 用LGT驱动过TM1812,2812还没研究过。 彩灯?多少位色深 HYLG 发表于 2015-12-6 00:30
用LGT驱动过TM1812,2812还没研究过。
2812最难的地方在于高速,比特流一旦开始传,800kbps 就不能断 huangqi412 发表于 2015-12-6 18:26
彩灯?多少位色深
24bit真彩的,实际 LED 在比较亮的时候基本就看不太清楚了 Arduino,attiny85,8个引脚,也能驱动这个2812。 读了文章后,估计也是汇编编写~~ zbjzxc 发表于 2015-12-28 00:10
Arduino,attiny85,8个引脚,也能驱动这个2812。 读了文章后,估计也是汇编编写~~ ...
是的,这个对内存消耗不大(如果灯带不是很长的话),关键在于 AVR 内核级的 IO 模拟,需要很快的速度才能实现 NRZ 编码 好文!
AVR 可以跑到 20MHz.
前段时间也用 WS2812B 做了一个项目(图案跑的是 Conway's Game of Life):
驱动用的是 http://fastled.io/
一篇驱动的相关文章
Light_WS2812 library V2.0 – Part I: Understanding the WS2812
https://cpldcpu.wordpress.com/2014/01/14/light_ws2812-library-v2-0-part-i-understanding-the-ws2812/ 3*8也就是11个端口,即使每个灯条搞一个stm32f030f4也差不多10元,不知道我算的对不对,然后uart,iic,spi想定义什么方法用什么方法通信。16k可以写很多样式出来吧。楼主考虑一下啊,我很早想搞着玩,一直没动手,用空一起来整。 buck 发表于 2015-12-28 04:27
这个要gamma曲线校正的,亮度和电压是指数关系
2812 据手册讲是自带 GAMMA 校正的,实际用起来仅仅一致性可以,但裸眼亮度需要拿纸罩住才有比较好的线性 atommann 发表于 2015-12-28 05:49
好文!
AVR 可以跑到 20MHz.
感觉上 WS2811 这个芯片可以更给力,每片带 3 个通道(要是更多也不错)
这样驱动白光的话,灰度屏有希望啊 gmyu 发表于 2015-12-28 09:32
3*8也就是11个端口,即使每个灯条搞一个stm32f030f4也差不多10元,不知道我算的对不对,然后uart,iic,spi ...
以前用 M64/128 做过类似的东西,所有 RGB 公用一个三通道 PWM 发生器(OC1A/B/C),每个 LED 单独一个 IO 进行选通扫描。
最后一片 M64 带了 30 只全彩 LED,分两组进行扫描(1/15占空比),通过 GAMMA 校正后每个颜色大约有 8 位灰度。
剩下就是怎么写效果了..... t3486784401 发表于 2015-12-28 10:34
以前用 M64/128 做过类似的东西,所有 RGB 公用一个三通道 PWM 发生器(OC1A/B/C),每个 LED 单独一个 I ...
效果如何? t3486784401 发表于 2015-12-28 10:34
以前用 M64/128 做过类似的东西,所有 RGB 公用一个三通道 PWM 发生器(OC1A/B/C),每个 LED 单独一个 I ...
以前PIC的代码,软件PWM,感觉效果不好就没玩了。
gmyu 发表于 2015-12-28 10:59
效果如何?
基本上就是仿老外那个 Aurora 项目的效果,不过 LED 没买到雾面的不爽。
直接板子的视频没录,这里有个套上了柚子皮(我女票太有才)的效果:
http://player.youku.com/player.php/sid/XMTM2MzMzMzc1Mg==/v.swf t3486784401 发表于 2015-12-28 12:11
基本上就是仿老外那个 Aurora 项目的效果,不过 LED 没买到雾面的不爽。
直接板子的视频没录,这里有个 ...
有才,单纯的呼吸变色,你几十个LED搞出啥花样了? gmyu 发表于 2015-12-28 12:19
有才,单纯的呼吸变色,你几十个LED搞出啥花样了?
这是焊接效果图,以及刚录制的视频:
http://player.youku.com/player.php/sid/XMTQyODM2ODc1Mg==/v.swf 我直接拿PIC的PIC12F1572做12个灯的WS2812B,不会用CLC CWG NCO,所以就直接循环发送,很笨,不过还是做出来了~有12个模式~回头发一下视频哈哈哈~ yoursnemo 发表于 2015-12-31 18:04
我直接拿PIC的PIC12F1572做12个灯的WS2812B,不会用CLC CWG NCO,所以就直接循环发送,很笨,不过还是做出来了~ ...
非常期待该作品!! zqf441775525 发表于 2015-11-18 09:17
用硬件SPI驱动WS2812这类的芯片还是很开心的
可以这样玩! 学习中,谢谢楼主分享! 本帖最后由 辣条 于 2017-7-27 15:53 编辑
楼主,用您提供的程序测试确实好用,另外我用的晶振是14.7456Mhz的,在汇编中加入几个延时就能用。但我用的是mega168,1kRAM,只能驱动40多个灯,不然内存不够用啦?(由于您代码中把一位变为一个字节表示,那么驱动一个灯就需要一个24字节的数组)能否可可以把汇编部分改为按位输出的呢?想用您的这个底层驱动更多灯数,有好的办法吗!?{:smile:} 辣条 发表于 2017-7-27 14:38
楼主,用您提供的程序测试确实好用,另外我用的晶振是14.7456Mhz的,在汇编中加入几个延时就能用。但我用的 ...
14.7456MHz 下,每个循环多出了 9T 的时间,好好安排指令的话,应该可以实现驱动。目前有这么个思路:
1. 建议循环还是一圈走一个字节,因为 LD 指令(对应 *(ptr++) 之类的操作)比较耗时。这样的话CPU负载比较稳定;
2. 使用 BST/BLD 指令,将字节的各个位逐一搬运到目标端口比特;
3. 这个需要排指令,建议用 EXCEL 好好规划一下指令,按时间要求(8*18T),逐一编排;
我用 7.3728MHz 的 m328p 曾经实现过这个需求,每 3B-RAM 对应一个灯。不过不巧手头找不着这个版本了。
页:
[1]