microPython驱动ws2812的方法

用microPython给esp8266写代码之后,越来越喜欢这种脚本语言,相对于arduino来写,节省了大量的编译时间,随手就能看到结果。尤其是使用arduino来写esp8266/esp32,编译时间比uno长了无数倍。
但是,用microPython来写,代码也是运行在单片机上,调试的手段也是用print打印出变量的状态,相对来说有点低效。
最近在给ws2812的矩阵写特效玩,所以我又换了一个思路。如果能用python计算出灯带颜色值,然后输出给microPython,那就可以在本机调试代码,打断点也非常方便了。等到算法都调试好,移植到microPython上面,基本上就是复制粘贴的事情了。

microPython驱动ws2812一般是使用neopixel库,调用其中的fill或者write方法即可。这个库的核心代码其实就是一句话:

machine.bitstream(machine.Pin(4, machine.Pin.OUT), 0, (400, 850, 800, 450),b'\x00,\x2e,\x00')

bitstream的用法

具体的参数不用太操心,主要就是第一个参数和第四个参数,第一个参数是输出的pin,第四个参数是输出的数组,也就是每个灯珠的rgb,格式为bytearray,约等于c语言的byte数组,每个元素为0-255的数字。
第三个参数(400, 850, 800, 450)代表的是高低电平时间,如果数据是800kHz,那么每位数据的时间就是1250ns,再根据neopixel的高低电平规定,定义出0的高电平是400ns,低电平是850ns,1的高电平是800ns,低电平是450ns。如果是用400kHz的码率通讯,根据neopixel.py的定义 (800, 1700, 1600, 900)要重新定义上述时序,但是没有实际测试过,按手头资料来看,ws2812只是对高电平时间敏感,低电平的时间多拉长一些也没有关系。
bitstream的定义时序方式,是否可以模拟出arduino的shiftout功能,也可以试试看。

python计算ws2812的值然后输出

将灯带的颜色值用python数组存储,然后转成bytearay,再转成microPython的rpel命令。

import serial
import time
ser=serial.Serial("COM8",115200,timeout=5)#打开串口
result=ser.write("\r\n".encode())#写入命令,要使用encode编码
time.sleep(0.1)#两次串口
result=ser.write("import machine\r\n".encode())
time.sleep(0.05)
buf=bytearray([0,0,0]*64)#灯带颜色数组
buf[3]=20
buf[0]=10
time.sleep(0.1)
cmdStr2="machine.bitstream(machine.Pin(4, machine.Pin.OUT), 0, (400, 850, 800, 450),"+str(buf)+")\r\n"
result=ser.write(cmdStr2.encode())
ser.close()

坑在哪里

坑就在time.sleep这里,一开始我没想到这点,结果发送串口命令总是失败,一直在折腾编码的问题,后来用串口助手软件接收自己发送的数据,终于发现,原来分次发送的命令,因为中间没有间隔时间,串口是当做一行来接收的。测试了一下,间隔0.05s发送的是比较稳妥的,但是这样也就意味着灯带特效,两次颜色刷新的间隔至少0.05秒。看来要想快速刷新,需要在microPython那边想办法,不使用repl的uart0,使用uart1来自己解析字符串,然后再刷新灯带,这样应该是最快的方案了。