Now we can use VoCore2 control WS2812B based LED belt 🙂 Hope this helps you make a beautiful house.
This is a “big” project for me, it contains three parts.
- Linux Driver
I am using I2S interface, because the best timer on VoCore2 is 1us… WS2812 requests 800KHz, and its bit0, bit1 is special: 800ns high then 400ns low, it is its bit1, 400ns high then 800ns low it is its bit0. We can not use timer to output that.
But There is a super good channel, it is I2S. I2S speed is up to 240MHz. I find a way to use three I2S bits to simulate one WS2812 bit, set I2S speed to 2400KHz. 🙂
PS: I spend some time rewrite dma driver, so data is transfer from dma and VoCore2 is able to control 1024 LEDs at 30fps using less than 1% cpu. - API for user space
I2S interface is not designed for such “common” usage, it is used to transfer audio data, so it is align with double word(4 byte), and have its special byte order, we can not send “abc” to I2S directly, because I2S real output will be “c?ab”. To use I2S interface, an API convert data order is necessary. - Hardware design.
VoCore2 signal output voltage is 3.3V, WS2812B min signal voltage is 0.7 x Vcc = 3.5V, not match well. We’d better to use a buffer gate to convert the signal voltage for the accurate situation. This is option, without gate it still work, just sometime one or two LEDs are naughty. Connection is simple, I2S_SDO to WS2812B DATAIN.
“There is no secret in the source code.”
First is Linux Driver Code: ws2812b.c, put it into Linux kernel and add it to Makefile and Kconfig, also enable gdma at VOCORE2.dts, compile. If it works normal, it will create a system file at /sys/devices/10000000.palmbus/10002800.gdma/update
You can try “echo aaaa > /sys/devices/10000000.palmbus/10002800.gdma/update”, I2S_SDO will output wave like this:
PS: actually I2S received data is “aaaa\r”
Then API Source Code: ws2812_api
ws2812_reset(): call this to turn all LEDs off RGB(0,0,0).
ws2812_set(int *color, int size): set leds colors. example: you have 16 WS2812B in a line, you want to set them to different color, code should be like this (include ws2812.h)
int color[] = {0x080000, 0x000800, 0x000008, 0x080000, 0x000800, 0x000008, 0x080000, 0x000800, 0x000008, 0x080000, 0x000800, 0x000008, 0x080000, 0x000800, 0x000008, 0x080000, }; ws2812_set(color, sizeof(color) / sizeof(int));
For more details, check unit test code at the end of ws2812.c.
The head photo is from this code 🙂 For me everything works well.