本帖最后由 lilijin1995 于 2023-8-13 21:09 编辑
引言: 其实个人觉得本贴子也没啥创新的地方,只是做了一些移植工作,关于一些经验分享,如果无兴趣阅读,请直接跳过,勿网暴!
背景:
这次使用的是某品牌的51单片机,系统时钟频率12Mhz,SPI最高可以系统的2分频,得到6Mhz的SPI时钟频率;基本能满足WS2812的时序要求;
- void InitSPI(void)
- {
- FCTR &= ~ SPIFTR;
- SPICFG |= 0x50;
- SPICTL = 0x00;
- // f SCK= SYSCLK / 2*( SPICKR +1 ) SPI ʱÖÓ 12M/2*(0+1)=6Mhz
- SPISCR = 0;
- SPI_EN();
- }
正文接下来我们看一个WS2812彩虹轮盘的实现代码:
- void ws281x_rainbowCycle(void) {
- u16 i, j;
- for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
- for(i=0; i< PIXEL_NUM; i++) {
- ws281x_setPixelColor(i,ws281x_wheel(((i * 256 / PIXEL_NUM) + j) & 255));
- }
- ws281x_show();
- DelayMS(10);
- }
- }
大家应该发现了,基本不变,还是同样的配方,同样的味道
- void ws281x_setPixelColor(u16 n ,u32 GRBcolor)
- {
- u8 i;
- if(n < PIXEL_NUM)
- {
- for(i = 0; i < 24; ++i)
- {
- pixelBuffer[n][i] = (((GRBcolor << i) & 0X800000) ? WS_HIGH : WS_LOW);
- }
- }
- }
另外的ws281x_wheel就是渐变算法的实现;
- unsigned long ws281x_color(u8 red, u8 green, u8 blue)
- {
- return ((unsigned long)green << 16) | ((unsigned long)red << 8) | blue;
- }
- // Input a value 0 to 255 to get a color value.
- // The colours are a transition r - g - b - back to r.
- u32 ws281x_wheel(u8 wheelPos) {
- wheelPos = 255 - wheelPos;
- if(wheelPos < 85) {
- return ws281x_color(255 - wheelPos * 3, 0, wheelPos * 3);
- }
- if(wheelPos < 170) {
- wheelPos -= 85;
- return ws281x_color(0, wheelPos * 3, 255 - wheelPos * 3);
- }
- wheelPos -= 170;
- return ws281x_color(wheelPos * 3, 255 - wheelPos * 3, 0);
- }
这里需要注意以下,ws281x_color,这里面一定要强制转换先;一开始我是这样写的,如下:
- uint32_t WS281x_Color(uint8_t red, uint8_t green, uint8_t blue) { return green << 16 | red<< 8 | blue; }
但是C51里面不行,这样的效果是只有红蓝色,丢失了绿色;
在这里我直接chatGPT,直接问了C51怎么实现uint32_t WS281x_Color(uint8_t red, uint8_t green, uint8_t blue) { return green << 16 | red<< 8 | blue; }这个代码,最后采得到
((unsigned long)green << 16) | ((unsigned long)red << 8) | blue;
下载验证与预期效果一致;
在这里还需要注意一些时序问题,其实WS2812只要高电平的时序满足了,基本可以实现驱动点亮RGB了,因为我的时序是这样设置的,如下:
- #define PIXEL_NUM 16
- #define WS_HIGH 0XF8
- #define WS_LOW 0XC0
- u8 xdata pixelBuffer[PIXEL_NUM][24] ;
正常的话,我应该是
#define WS_HIGH 0XF0
#define WS_LOW 0XC0
但我用逻辑分析仪抓到的高电平时间并不满足要求,所以这里设置成了0xF8;毕竟没有DMA,而我们的低电平时间更加不满足了;
大家可以看我的数据刷新代码:ws281x_show
- void ws281x_show(void)
- {
- u8 n,i;
- for(n = 0; n < PIXEL_NUM; n++)
- {
- for(i = 0; i < 24; ++i)
- {
- SPIDAT = pixelBuffer[n][i];
- //SPIDAT=WS_LOW;
- while (!SPIF);
-
- SPIF = 0;
- }
- }
-
- }
因为51单片机主频很低的,加上等待SPI发送完成,以及一次循环的完成时间,故而低电平时间肯定比预期长又长的,但是感觉WS2812就是只判定高电平时序而已;
结尾:
|