ws2812.h
#ifndef __WS2812_H__
#define __WS2812_H__
#include "config.h"
#include "GPIO.h"
#include "delay.h"
#define WS2812_H {P35=1;}
#define WS2812_L {P35=0;}
#define PIXEL_NUM 22
void WS2812_LOW(void);
void WS2812_High(void);
void WS2812_WriteByte(unsigned char red, unsigned char green, unsigned char blue);
void WS2812_SetColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char num);
void WS2812_CloseAll(void);
void WS2812_RGB_Waterflow(uint8_t red, uint8_t green, uint8_t blue, uint8_t delaytime);
#endif
ws2812.c
//#include <STC8G.H>
//#include <intrins.h>
#include "ws2812.h"
void WS2812_High(void)
{
WS2812_H;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
WS2812_L;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
void WS2812_LOW(void)
{
WS2812_H;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
WS2812_L;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
/**
* @brief 设置单个灯珠颜色
* @param red 红色的亮度 范围0~255
* @param green 绿色的亮度 范围0~255
* @param blue 蓝色的亮度 范围0~255
* @retval 无
*/
void WS2812_WriteByte(uint8_t red, uint8_t green, uint8_t blue)
{
uint8_t i, j;
uint8_t color[3]; // WS2812 的顺序是 GRB
color[0] = green;
color[1] = red;
color[2] = blue;
for (j = 0; j < 3; j++) {
for (i = 0; i < 8; i++) {
if (color[j] & 0x80) {
WS2812_High();
} else {
WS2812_LOW();
}
color[j] <<= 1;
}
}
}
/**
* @brief 设置多个灯珠颜色
* @param red 红色的亮度 范围0~255
* @param green 绿色的亮度 范围0~255
* @param blue 蓝色的亮度 范围0~255
* @param num 灯珠数量 范围0~255
* @retval 无
*/
级联灯珠亮
//void WS2812_SetColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char num)
//{
// unsigned char i;
// for (i = 1; i <= num; i++) {
// WS2812_WriteByte(red, green, blue);
// }
//}
// 测试级联灯珠收尾亮,中间灯珠灭
void WS2812_SetColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char num)
{
unsigned char i;
for (i = 1; i <= num; i++) {
if (i == 6 || i == 7 || i == 8 || i == 9 || i == 10 || i == 11) {
WS2812_WriteByte(0, 0, 0);
} else {
WS2812_WriteByte(red, green, blue);
}
}
编号为偶数的灯珠熄灭
// for (i = 1; i <= num; i++) {
// if (i%2) {
// WS2812_WriteByte(0, 0, 0);
// } else {
// WS2812_WriteByte(red, green, blue);
// }
// }
全亮
// for (i = 1; i <= num; i++) {
// WS2812_WriteByte(red, green, blue);
// }
}
/**
* @brief 清除多个灯珠颜色
* @param
* @retval 无
*/
void WS2812_CloseAll(void)
{
uint8_t i;
for (i = 0; i < PIXEL_NUM; ++i) {
WS2812_WriteByte(0, 0, 0);
}
}
/**
* @brief RGB流水灯,流水顺序:G-灭-R-灭-B-灭
* @param red 红色的亮度 范围0~255
* @param green
* @param blue
* @param delaytime
* @retval 无
*/
void WS2812_RGB_Waterflow(uint8_t red, uint8_t green, uint8_t blue, uint8_t delaytime)
{
uint8_t i;
EA = 0;
// WS2812_CloseAll();
for (i = 1; i <= PIXEL_NUM; ++i) {
WS2812_SetColor(0, green, 0, i);
delay_ms(delaytime);
}
// WS2812_CloseAll();
for (i = 1; i <= PIXEL_NUM; ++i) {
WS2812_SetColor(red, 0, 0, i);
delay_ms(delaytime);
}
// WS2812_CloseAll();
for (i = 1; i <= PIXEL_NUM; ++i) {
WS2812_SetColor(0, 0, blue, i);
delay_ms(delaytime);
}
// WS2812_CloseAll();
EA = 1;
}
main.c
#include "config.h"
#include "timer.h"
#include "GPIO.h"
#include "delay.h"
#include "STC8XXXX.H"
#include "ws2812.h"
uint8_t i = 0;
//========================================================================
// 函数: void delay_ms(unsigned char ms)
// 描述: 主函数
// 参数: none.
// 返回: none.
// 版本:
// 日期:
// 备注: 外部信号干扰时序
//========================================================================
void main(void)
{
GPIO_config();
Timer_config();
EA = 1; // 定时器中断会对WS2812时序有影响
LED_WaterFlow_OFF();
while (1) {
Single_Scan();
Single_Control();
WS2812_CloseAll();
// // GRB 全亮
// EA = 0;
// WS2812_SetColor(0, 0x1e, 0, PIXEL_NUM);
// EA = 1;
// delay_ms(255);
// EA = 0;
// WS2812_SetColor(0x1e,0, 0, PIXEL_NUM);
// EA = 1;
// delay_ms(-255);
// EA = 0;
// WS2812_SetColor(0, 0, 0x1e, PIXEL_NUM);
// WS2812_CloseAll();
// EA = 1;
// delay_ms(255);
WS2812_RGB_Waterflow(20,20,20,25);
}
}
从代码中可以看出,您正在使用 STC8H 系列单片机通过 IO 口直接驱动 WS2812 全彩 LED 灯带,并且自定义了时序函数 WS2812_High
和 WS2812_LOW
来手动控制 WS2812 的时序。根据 24MHz 的时钟频率以及 _nop_()
的使用,这样的控制方法在较小规模的灯珠数量下应该是可以工作的。
改进与优化建议
-
时序的精确性:
WS2812 需要非常严格的时序(例如高电平和低电平持续时间),当前的做法通过_nop_()
来延时。如果需要更高精度,可以通过定时器来精确控制时序,而不是仅仅依赖_nop_()
。你可以使用定时器来生成精确的脉冲宽度,比如设置定时器在 24MHz 的系统时钟下产生 1.25μs 或者 0.4μs 的延时,从而精确控制高电平和低电平的持续时间。
-
外部信号干扰时序:
在发送 WS2812 数据的过程中,外部中断或其他任务可能打乱时序,导致灯珠颜色显示异常。可以考虑在发送灯珠数据期间禁用所有中断,确保时序的完整性。EA = 0; // 关闭全局中断 WS2812_SetColor(red, green, blue, num); // 发送数据 EA = 1; // 开启全局中断
-
简化延时处理:
当前的WS2812_High()
和WS2812_LOW()
函数中使用了大量的_nop_()
延时,这会使得代码看起来不够简洁且不易调节。可以将延时函数封装成一个独立函数,通过参数控制延时时间:void DelayNops(uint8_t n) { while (n--) { _nop_(); } } void WS2812_High(void) { WS2812_H; DelayNops(18); // 根据时钟频率调节这个数值 WS2812_L; DelayNops(14); // 调节低电平延时 } void WS2812_LOW(void) { WS2812_H; DelayNops(7); // 高电平短时间 WS2812_L; DelayNops(18); // 低电平长时间 }
这样不仅代码更加清晰,还可以灵活调整延时时间,以适应不同的时钟频率或不同的时序需求。
-
代码效率优化:
可以将WS2812_WriteByte()
中的三个 for 循环合并成一个循环,依次发送 RGB 数据,减少不必要的重复代码:void WS2812_WriteByte(uint8_t red, uint8_t green, uint8_t blue) { uint8_t i; uint8_t color[3] = {green, red, blue}; // WS2812 的顺序是 GRB for (int j = 0; j < 3; j++) { for (i = 0; i < 8; i++) { if (color[j] & 0x80) { WS2812_High(); } else { WS2812_LOW(); } color[j] <<= 1; } } }
-
级联灯珠控制逻辑:
您已经实现了对不同灯珠颜色的控制逻辑,特别是设定某些灯珠关闭,其它灯珠点亮。如果需要更复杂的动画效果,可以使用数组来存储每个灯珠的 RGB 值,并根据外部信号或定时器事件来更新这些颜色。uint8_t led_colors[NUM_LEDS][3]; // 定义一个数组存储每个灯珠的RGB颜色 void update_led_colors(uint8_t red, uint8_t green, uint8_t blue) { for (int i = 0; i < NUM_LEDS; i++) { // 更新颜色逻辑,可以根据需要修改 led_colors[i][0] = green; led_colors[i][1] = red; led_colors[i][2] = blue; } } void WS2812_SetAll(void) { for (int i = 0; i < NUM_LEDS; i++) { WS2812_WriteByte(led_colors[i][1], led_colors[i][0], led_colors[i][2]); } }
总结
通过使用定时器、优化中断控制、合并 RGB 循环、封装延时函数等方法,你可以在 STC8H 单片机中更精确和高效地驱动 WS2812 灯带,同时避免外部信号干扰时序问题。
总结
- WS2812灯珠时序要求非常严格,程序中设置了中断会导致灯珠有闪烁,需要在发送灯珠时序的地方关闭中断,时序发送完毕后打开中断;
- WS2812 24bit 数据结构,高位先发,按照 GRB 的顺序发送数据(G7→G6→………B0),如果按照RGB的顺序发送,在发送R的数据的时候,第一颗灯珠会闪烁或者颜色叠加绿色分量,导致颜色偏黄
20241127
#include "ws2812.h"
void WS2812_High(void)
{
WS2812_H;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
WS2812_L;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
void WS2812_LOW(void)
{
WS2812_H;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
WS2812_L;
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
/**
* @brief 设置单个灯珠颜色
* @param red 红色的亮度 范围0~255
* @param green 绿色的亮度 范围0~255
* @param blue 蓝色的亮度 范围0~255
* @retval 无
*/
void WS2812_WriteByte(uint8_t red, uint8_t green, uint8_t blue)
{
uint8_t i, j;
uint8_t color[3]; // WS2812 的顺序是 GRB
color[0] = green;
color[1] = red;
color[2] = blue;
EA = 0;
for (j = 0; j < 3; j++) {
for (i = 0; i < 8; i++) {
if (color[j] & 0x80) {
WS2812_High();
} else {
WS2812_LOW();
}
color[j] <<= 1;
}
}
EA = 1;
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
//uint32_t WS2812_Wheel(uint8_t wheelPos)
//{
// wheelPos = 255 - wheelPos;
// if (wheelPos < 85) {
// return WS2812_Color(255 - wheelPos * 3, 0, wheelPos * 3);
// }
// if (wheelPos < 170) {
// wheelPos -= 85;
// return WS2812_Color(0, wheelPos * 3, 255 - wheelPos * 3);
// }
// wheelPos -= 170;
// return WS2812_Color(wheelPos * 3, 255 - wheelPos * 3, 0);
//}
uint32_t WS2812_Wheel(uint8_t wheelPos) {
wheelPos = 255 - wheelPos; // 反转 wheelPos
if (wheelPos < 85) {
// 第一段:从绿色到红色
return WS2812_Color(255 - wheelPos * 3, wheelPos * 3, 0); // 绿色减少,红色增加
} else if (wheelPos < 170) {
// 第二段:从红色到蓝色
wheelPos -= 85;
return WS2812_Color(255 - wheelPos * 3, 0, wheelPos * 3); // 红色减少,蓝色增加
} else {
// 第三段:从蓝色回到绿色
wheelPos -= 170;
return WS2812_Color(0, wheelPos * 3, 255 - wheelPos * 3); // 蓝色减少,绿色增加
}
}
uint32_t WS2812_Color(uint8_t red, uint8_t green, uint8_t blue)
{
return green << 16 | red << 8 | blue;
}
void WS2812_SetPixelColor(uint16_t n, uint32_t GRBcolor)
{
// uint8_t i;
// 提取绿色、红色和蓝色分量
uint8_t greenValue = (GRBcolor >> 16) & 0xFF;
uint8_t redValue = (GRBcolor >> 8) & 0xFF;
uint8_t blueValue = GRBcolor & 0xFF;
if (n < PIXEL_NUM) {
// 存储到 pixelBuffer
pixelBuffer[n+1][0] = greenValue;
pixelBuffer[n+1][1] = redValue;
pixelBuffer[n+1][2] = blueValue;
}
}
//void WS2812_Show(void)
//{
// uint16_t i;
// uint8_t j;
// EA = 0;
// for (i = 0; i < PIXEL_NUM; i++) {
// // 发送每个灯珠的颜色数据
// for (j = 0; j < 24; j++) {
// if (pixelBuffer[i][j / 8] & (0x80 >> (j % 8))) {
// WS2812_High(); // 发送高电平
// } else {
// WS2812_LOW(); // 发送低电平
// }
// }
// }
// EA = 1;
//}
void WS2812_Show(void)
{
uint16_t i;
uint8_t j;
EA = 0;
for (i = PIXEL_NUM; i > 0; i--) {
// 发送每个灯珠的颜色数据
for (j = 0; j < 24; j++) {
if (pixelBuffer[i][j / 8] & (0x80 >> (j % 8))) {
WS2812_High(); // 发送高电平
} else {
WS2812_LOW(); // 发送低电平
}
}
}
EA = 1;
}
// 正常工作时的幻彩函数
void WS2812_RainbowCycle(uint8_t wait)
{
uint16_t i, j;
// 1个周期
for (j = 0; j < 256 * 1; j++) {
for (i = 0; i < PIXEL_NUM; i++) {
WS2812_SetPixelColor(i, WS2812_Wheel(((i * 256 / PIXEL_NUM) + j) & 255));
}
WS2812_Show();
if (RGB_Light_Signal == 0) {
RGB_Light_Flag = 0;
break;
}
delay_ms(wait);
}
}
// 开机动画幻彩函数
void WS2812_RainbowCycle_Run(uint8_t wait)
{
uint16_t i, j;
// 1个周期
for (j = 0; j < 256 * 1; j++) {
for (i = 0; i < PIXEL_NUM; i++) {
WS2812_SetPixelColor(i, WS2812_Wheel(((i * 256 / PIXEL_NUM) + j) & 255));
}
WS2812_Show();
delay_ms(wait);
}
}
//void WS2812_RainbowCycle(uint8_t wait)
//{
// uint16_t i, j;
// // 1个周期
// for (j = 0; j < 256 * 1; j++) {
// for (i = 0; i < PIXEL_NUM; i++) {
// WS2812_SetPixelColor(i, WS2812_Wheel(((i * 256 / PIXEL_NUM) + j) & 255));
// }
// WS2812_Show();
// delay_ms(wait);
// }
//}
// 级联灯珠亮
void WS2812_SetColor(unsigned char green, unsigned char red, unsigned char blue, unsigned char num)
{
unsigned char i;
for (i = 1; i <= num; i++) {
WS2812_WriteByte(red, green, blue);
}
delay_ms(1);
}
/**
* @brief 清除多个灯珠颜色
* @param
* @retval 无
*/
void WS2812_CloseAll(void)
{
uint8_t i;
for (i = 0; i < PIXEL_NUM; ++i) {
WS2812_WriteByte(0, 0, 0);
}
}
/**
* @brief RGB流水灯,流水顺序:G-灭-R-灭-B-灭
* @param red 红色的亮度 范围0~255
* @param green
* @param blue
* @param delaytime
* @retval 无
*/
void WS2812_RGB_Waterflow(uint8_t red, uint8_t green, uint8_t blue, uint8_t delaytime)
{
uint8_t i;
// WS2812_CloseAll();
for (i = 1; i <= PIXEL_NUM; ++i) {
WS2812_SetColor(0, green, 0, i);
delay_ms(delaytime);
}
// WS2812_CloseAll();
for (i = 1; i <= PIXEL_NUM; ++i) {
WS2812_SetColor(red, 0, 0, i);
delay_ms(delaytime);
}
// WS2812_CloseAll();
for (i = 1; i <= PIXEL_NUM; ++i) {
WS2812_SetColor(0, 0, blue, i);
delay_ms(delaytime);
}
// WS2812_CloseAll();
}
// 呼吸灯
void WS2812_BreathLight(uint8_t delaytime)
{
static uint8_t PWM1_Duty = 0;
static uint8_t PWM1_Flag = 0;
// static uint8_t red = 10;
// static uint8_t green = 180;
// static uint8_t blue = 10;
if (!PWM1_Flag) {
PWM1_Duty++;
if (PWM1_Duty >= 180) {
PWM1_Flag = 1;
}
} else {
PWM1_Duty--;
if (PWM1_Duty <= 10) {
PWM1_Flag = 0;
}
}
// WS2812_SetColor(PWM1_Duty, 0, 0, 22);
WS2812_WriteByte(PWM1_Duty, 0, 0);
delay_ms(delaytime);
}
void WS2812_WhiteLight()
{
uint8_t i;
for (i = 0; i < PIXEL_NUM; i++) {
WS2812_SetPixelColor(i, WS2812_Color(200,200,200));
}
WS2812_Show();
delay_ms(1);
}
#ifndef __WS2812_H__
#define __WS2812_H__
#include "config.h"
#include "GPIO.h"
#include "delay.h"
#include "math.h"
//#define WS2812_H {P35=1;}
//#define WS2812_L {P35=0;}
#define WS2812_H {P11=1;}
#define WS2812_L {P11=0;}
#define PIXEL_NUM 26
extern uint8_t pixelBuffer[PIXEL_NUM][24] ;
void WS2812_LOW(void);
void WS2812_High(void);
uint32_t WS2812_Color(uint8_t red, uint8_t green, uint8_t blue);
void WS2812_SetPixelColor(uint16_t n ,uint32_t GRBcolor);
uint32_t WS2812_Wheel(uint8_t wheelPos);
void WS2812_RainbowCycle(uint8_t wait);
void WS2812_RainbowCycle_Run(uint8_t wait);
void WS2812_Show(void);
void WS2812_WriteByte(unsigned char red, unsigned char green, unsigned char blue);
void WS2812_SetColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char num);
void WS2812_CloseAll(void);
void WS2812_RGB_Waterflow(uint8_t red, uint8_t green, uint8_t blue, uint8_t delaytime);
void HSV_to_RGB(uint16_t hue, uint8_t *r, uint8_t *g, uint8_t *b);
void WS2812_RainbowFlow(uint8_t delaytime);
void WS2812_SequentialColors(uint8_t delaytime);
void WS2812_BreathLight(uint8_t delaytime);
void WS2812_WhiteLight();
#endif