C51学习笔记05:STC8H1K08T TSSOP20 WS2812 炫彩LED灯驱动(STC8H1K17T-33I-SOP20)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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_HighWS2812_LOW 来手动控制 WS2812 的时序。根据 24MHz 的时钟频率以及 _nop_() 的使用,这样的控制方法在较小规模的灯珠数量下应该是可以工作的。

改进与优化建议

  1. 时序的精确性
    WS2812 需要非常严格的时序(例如高电平和低电平持续时间),当前的做法通过 _nop_() 来延时。如果需要更高精度,可以通过定时器来精确控制时序,而不是仅仅依赖 _nop_()

    你可以使用定时器来生成精确的脉冲宽度,比如设置定时器在 24MHz 的系统时钟下产生 1.25μs 或者 0.4μs 的延时,从而精确控制高电平和低电平的持续时间。

  2. 外部信号干扰时序
    在发送 WS2812 数据的过程中,外部中断或其他任务可能打乱时序,导致灯珠颜色显示异常。可以考虑在发送灯珠数据期间禁用所有中断,确保时序的完整性。

    EA = 0; // 关闭全局中断
    WS2812_SetColor(red, green, blue, num); // 发送数据
    EA = 1; // 开启全局中断
    
  3. 简化延时处理
    当前的 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);  // 低电平长时间
    }
    

    这样不仅代码更加清晰,还可以灵活调整延时时间,以适应不同的时钟频率或不同的时序需求。

  4. 代码效率优化
    可以将 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;
            }
        }
    }
    
  5. 级联灯珠控制逻辑
    您已经实现了对不同灯珠颜色的控制逻辑,特别是设定某些灯珠关闭,其它灯珠点亮。如果需要更复杂的动画效果,可以使用数组来存储每个灯珠的 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 灯带,同时避免外部信号干扰时序问题。

总结

  1. WS2812灯珠时序要求非常严格,程序中设置了中断会导致灯珠有闪烁,需要在发送灯珠时序的地方关闭中断,时序发送完毕后打开中断
  2. 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Naiva

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值