本帖最后由 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就是只判定高电平时序而已;
结尾:
|