开源一个可以调RGB三色的小灯棒子

news/2025/2/24 21:34:10

 开源一个可以调灯的小灯棒子。

主控用的STC8G1K08A-SOP8,RGB三色灯是WS2812B。

开源到立创开源广场了,可以直接进入下方链接,那边可以直接查看原理图和PCB。

一个可调RGB三色的小灯棒子 - 立创开源硬件平台一个可调RGB三色的小灯棒子https://oshwhub.com/zctnb/diao-guang-deng

通过观察板子下面三个灯来调整板子上两排的灯的颜色,下面三个灯分别表示RGB,并且也只亮对应的颜色,比如说最左边的灯只有红色的亮度,蓝和绿的亮度都是0。

这边可以稍微讲解一下WS2812B是通过调整RGB三色的亮度来完成任意颜色的表示的。

关于WS2812B如何控制的可以看看我之前的文章,不过里面的代码恐怕没法直接使用,因为我最近发现了时钟频率好像当时没调对,不过理论部分都是正确的,严格说起来代码也是没问题的,只不过烧录程序的时候频率要选成11.0592。

今天我们不点LED,我们点WS2812B_为什么用点灯blinker调ws2812第一灯闪-CSDN博客文章浏览阅读1.1k次,点赞10次,收藏22次。这也难不倒我,经过我一顿操作和计算,STC8G1K08A的主频为24MHz,一个_nop_()大概耗时是63+ns,其实我计算的结果应该是44ns,因为1/24 000 000 约等于是40ns,但是我拿着40一个_nop_()的结果去写代码,发现好像不对劲,最后定位在了一个_nop_()大概耗时是60+ns。1码和0码差不多,高低电平是顺序一样,都是先高电平后低电平,不一样的是持续时间,持续时间其实也差不多,就是高低电平的时间反过来,所以我们1码的高电平时间定为0.6us,低电平时间定为0.3us。_为什么用点灯blinker调ws2812第一灯闪 https://blog.csdn.net/m0_63235356/article/details/144155464?spm=1001.2014.3001.5501

一共有三个按钮,从左到右按照顺序,我姑且叫做A、B、C。

当按下A之后会切换模式,模式一共是五种,按照切换顺序是灯全灭(初始模式),调节红灯,调节绿灯,调节蓝灯,灭调节灯。

调节红灯的时候上面两排主灯亮,下面三个灯只亮最左边一个灯,并且只亮红光,这时候按下B可以增加红光的值,按下C可以减少红光的值,如果是双击的话,那么就是增加(减少)10,满数值是255。调节的结果会实时反应在两排主灯上。

调节绿灯蓝灯也是一样的道理,灭调节灯模式就是三个调节灯灭,两排主灯亮,此时按下B和C没有效果。

原理图画了两版,咱就聊聊第二版吧,因为第一版确实没设计好,三个按钮光加上拉电阻没加电容,然后烧录留的接触点第一版原理图里也没有(我记得放了,但是没有,不过PCB是没问题的)

主控用的STC8G1K08A-SOP8,外围电路仅需在VCC和GND之间接俩电容就行。

按键我接了P32、P33、P55,分别是外部中断0、1、3。

P54接WS2812B,并且是可以串联多个的。

充电管理芯片用的TP4056,买的国产高端平替,一个一毛多,一下子屯多了,大家可以自行更换成自己手上有的芯片。

Type-C只是用来充电的,所以也是可以自己换成别的型号的。

代码如下,注释都已经写好了,也不难,应该是可以看的懂的。

#include "STC8G_H_Delay.h"
#include "STC8G_H_Exti.h"
#include "STC8G_H_GPIO.h"
#include "STC8G_H_NVIC.h"
#include "STC8G_H_Timer.h"
// RGB灯和按键的引脚
#define WS2812B_IN  P54
#define KEY1_GPIO   P32
#define KEY2_GPIO   P33
#define KEY3_GPIO   P55

// G、R、B
uint16 color[3] = {10, 10, 10};
uint8 timer0_open = 0, timer0_over = 0;     // 定时器0开启 结束标志
uint8 timer1_open = 0, timer1_over = 0;     // 定时器1开始 结束标志
uint8 key1_down = 0, key2_down = 0, key3_down = 0;      // 三个按键是否按下的标志
uint8 key1_count = 0, key2_count = 0, key3_count = 0;   // 三个按键按下次数
// 0全关,1调R,2调G,3调B,4关调色,
uint8 mode = 0;

void Timer0_ISR_Handler (void) interrupt TMR0_VECTOR{
    static uint8 i = 0;
    if(++i >= 5){           // 定时10秒消除抖动
        ET0 = 0;            // 关闭定时器0
        timer0_open = 0;    // 修改标志
        timer0_over = 1;
        i = 0;
    }
}

void Timer1_ISR_Handler (void) interrupt TMR1_VECTOR{
    static count = 0;
    if(++count >= 250){     // 定时500ms来记录期间按下按键的次数
	    ET1 = 0;            // 关闭定时器1
	    timer1_open = 0;    // 修改标志
        timer1_over = 1;
        count = 0;
    }
}

// 复位
void WS2812B_SendReset(void) {
    unsigned char data i, j;
    WS2812B_IN = 0;  // 拉低80us
    i = 2;
    j = 219;
    do {
        while (--j)
            ;
    } while (--i);
}

// 发送1码
void WS2812B_SendOne(void) {
    WS2812B_IN = 1;  // 拉高延时0.6us
    _nop_();_nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();_nop_();_nop_();
    WS2812B_IN = 0;  // 拉低延时0.3us
    _nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();
}

// 发送0码
void WS2812B_SendZero(void) {
    WS2812B_IN = 1;  // 拉高延时0.3us
    _nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();
   
    WS2812B_IN = 0;  // 拉低延时0.6us
    _nop_();_nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();_nop_();_nop_();
}

// 发送自定义RGB颜色
void WS2812B_SendColor(void) {
    uint8 i, j;
    for (i = 0; i < 3; ++i) {
        for (j = 0; j < 8; ++j) {   
            if (color[i] & (0x80 >> j))     // 高位在前
                WS2812B_SendOne();
            else
                WS2812B_SendZero();
        }
    }
}

// 初始化GPIO
void GPIO_Init(void) {
    P5_MODE_OUT_PP(GPIO_Pin_4);
    P3_MODE_IN_HIZ(GPIO_Pin_3);
    P3_PULL_UP_ENABLE(GPIO_Pin_3);
    P3_MODE_IN_HIZ(GPIO_Pin_2);
    P3_PULL_UP_ENABLE(GPIO_Pin_2);
    P5_MODE_IN_HIZ(GPIO_Pin_5);
    P5_PULL_UP_ENABLE(GPIO_Pin_5);
}

// 中断初始化
void Exti_Init(void) {
    EXTI_InitTypeDef Exti_InitStructure;

    Exti_InitStructure.EXTI_Mode = EXT_MODE_Fall;
    Ext_Inilize(EXT_INT0, &Exti_InitStructure);
    NVIC_INT0_Init(ENABLE, Priority_1);
    Exti_InitStructure.EXTI_Mode = EXT_MODE_Fall;
    Ext_Inilize(EXT_INT1, &Exti_InitStructure);
    NVIC_INT1_Init(ENABLE, Priority_1);
    Exti_InitStructure.EXTI_Mode = EXT_MODE_Fall;
    Ext_Inilize(EXT_INT3, &Exti_InitStructure);
    NVIC_INT3_Init(ENABLE, Priority_1);
}

// 定时器初始化
void Timer_Init(void){

    TIM_InitTypeDef initer;
    initer.TIM_Mode = TIM_16BitAutoReload;          // 模式0,16位自动重装载寄存器.
    initer.TIM_ClkOut = DISABLE;                    // 失能可编程时钟输出
    initer.TIM_ClkSource = TIM_CLOCK_1T;            // 1T工作模式
    initer.TIM_Value = 17536;                       // 延时2ms                       
    initer.TIM_Run = ENABLE;                        
 
    Timer_Inilize(Timer0, &initer);
    Timer_Inilize(Timer1, &initer);
    NVIC_Timer0_Init(DISABLE, Priority_2);
    NVIC_Timer1_Init(DISABLE, Priority_2);
}

// 当进入外部中断之后的处理逻辑
void when_key_down(uint8 what_key){
    WakeUpSource = 0;
    if(timer0_open == 0){   // 如果定时器0没有打开,那么进入处理逻辑;否则判定为抖动,不予理会
        timer0_open = 1;    
        ET0 = 1;            // 打开定时器0
        // 记录按下的按键
        if(what_key == 1)   key1_down = 1;
        else if(what_key == 2)   key2_down = 1;
        else if(what_key == 3)   key3_down = 1;

        if(timer1_open == 0){   // 如果定时器1没打开,那么打开
            timer1_open = 1;
            ET1 = 1;
        }
    }
}

// 切换模式
void change_mode(void){
    uint16 i = 0;
    uint8 j = 0;
    if(mode == 0){          // 所有灯灭 给所有灯发送0x00 0x00 0x00
        WS2812B_SendReset();
        for(i = 0; i < 360; ++i) WS2812B_SendZero();
    }else if(mode == 1){    // 调R,前三个灯只亮第一个且只亮红灯
        WS2812B_SendReset();
        for(j = 0; j < 8; ++j)  WS2812B_SendZero();
        for(j = 0; j < 8; ++j){
            if (color[1] & (0x80 >> j)) WS2812B_SendOne();
            else    WS2812B_SendZero();
        }
        for(j = 0; j < 56; ++j) WS2812B_SendZero();
    }else if(mode == 2){    // 调G,前三个灯只亮第二个且只亮绿灯
        WS2812B_SendReset();
        for(j = 0; j < 24; ++j) WS2812B_SendZero();
        for(j = 0; j < 8; ++j){
            if (color[0] & (0x80 >> j)) WS2812B_SendOne();
            else    WS2812B_SendZero();
        }
        for(j = 0; j < 40; ++j) WS2812B_SendZero();
    }else if(mode == 3){    // 调B,前三个灯只亮第三个且只亮蓝灯
        WS2812B_SendReset();
        for(j = 0; j < 64; ++j) WS2812B_SendZero();
        for(j = 0; j < 8; ++j){
            if (color[2] & (0x80 >> j)) WS2812B_SendOne();
            else    WS2812B_SendZero();
        }

    }else if(mode == 4){    // 前三个灯全灭
        WS2812B_SendReset();
        for(i = 0; i < 72; ++i) WS2812B_SendZero();
    }
    if(mode != 0){  // 当模式不为全灭时,需要更新后面的RGB灯
        for(i = 0; i < 12; ++i){
            WS2812B_SendColor();
        }
    }
}

void main(void) {
    EAXSFR();  // 扩展SFR(XFR)访问使能

    GPIO_Init();
    Exti_Init();
    Timer_Init();

    EA = 1;             // 开启中断

    while (1) {
        if(WakeUpSource == 1){
            when_key_down(1);
        }else if(WakeUpSource == 2){
            when_key_down(2);
        }else if(WakeUpSource == 4){
            when_key_down(3);
        }
        // 当定时器0结束运行
        if(timer0_over == 1){
            timer0_over = 0;
            // 增加按键按下次数
            if(key1_down == 1){
                key1_down = 0;
                if(KEY1_GPIO == 0)  key1_count++;
            }else if(key2_down == 1){
                key2_down = 0;
                if(KEY2_GPIO == 0)  key2_count++;
            }else if(key3_down == 1){
                key3_down = 0;
                if(KEY3_GPIO == 0)  key3_count++;
            }
        }
        // 当定时器1结束运行
        if(timer1_over == 1){
            timer1_over = 0;
            // 根据按键按下的次数分别对RGB的数值进行处理
            if(key1_count != 0){
                if(++mode > 4) mode = 0;
            }else if(key2_count == 1){
                if(mode == 1) color[1]++;
                else if(mode == 2) color[0]++;
                else if(mode == 3) color[2]++;
            }else if(key3_count == 1){
                if(mode == 1) color[1]--;
                else if(mode == 2) color[0]--;
                else if(mode == 3) color[2]--;
            }else if(key2_count >= 2){
                if(mode == 1) color[1] += 10;
                else if(mode == 2) color[0] += 10;
                else if(mode == 3) color[2] += 10;
            }else if(key3_count >= 2){
                if(mode == 1) color[1] -= 10;
                else if(mode == 2) color[0] -= 10;
                else if(mode == 3) color[2] -= 10;
            }
            change_mode();
            key1_count = key2_count = key3_count = 0;   // 清空
        }
    }
}

我用的是STC的库函数,不懂使用的小伙伴可以看看我往期的文章,有教学系列文章。

https://blog.csdn.net/m0_63235356/category_12853526.html?spm=1001.2014.3001.5482https://blog.csdn.net/m0_63235356/category_12853526.html?spm=1001.2014.3001.5482


http://www.niftyadmin.cn/n/5864818.html

相关文章

申请SSL证书,如何完成域名验证

一、前言 给大家分享一下Lets Encrypt 证书申请时&#xff0c;如何完成域名验证这一步操作的方法。 二、为什么要进行域名验证 申请SSL证书时进行域名验证的主要原因是确保证书只颁发给有权控制特定域名的实体。这是为了保证互联网的安全性和信任&#xff0c;防止恶意方获取不…

算法1-2 排序(快排)

题目描述 将读入的 N 个数从小到大排序后输出。 输入格式 第一行为一个正整数 N。 第二行包含 N 个空格隔开的正整数 ai​&#xff0c;为你需要进行排序的数。 输出格式 将给定的 N 个数从小到大输出&#xff0c;数之间空格隔开&#xff0c;行末换行且无空格。 输入输出…

Maven最新版安装教程

一、Maven下载 1.前往官网下载 点击前往官网 2.进去之后点击Download 如果是Windows用户使用Maven则选择apache-maven-x.x.x-bin.zip即可。Liunx和MacOS用户则选择apache-maven-x.x.x-bin.tar.zip。 由于服务器在国外下载可能会很慢或者失败&#xff0c;大家可以去网盘获取 …

1-19 .gitignore的作用

Keil编译时会生成很多中间文件&#xff0c;我们并不需要对这些文件进行版本控制&#xff0c;否则容易出现提交一次代码&#xff0c;结果出现两百多个文件变更 在本地仓库目录下打开git bash&#xff0c;输入以下命令&#xff1a; touch .gitignore 随后用vscode打开创建的.gi…

(面试经典问题之连接池篇)连接池构成、作用及其基本原理详解

一、什么是连接池 连接池一般指的是数据库连接池&#xff08;connection pooling&#xff09;&#xff0c;是指程序启动时建立足够的数据库连接&#xff0c;并将这些连接组成一个连接池&#xff0c;由程序动态的对池中的连接进行申请&#xff0c;使用&#xff0c;释放&#xf…

《离线唤醒+离线Vosk识别+DeepSeek+离线合成,你的第二大脑》

在快节奏的现代生活中&#xff0c;我们渴望一种能随时随地助力的智慧伙伴。现在&#xff0c;一款融合离线听写、唤醒功能、大模型上下文携带以及合成技术的智能工具横空出世&#xff0c;宛如成为你的第二大脑。 离线听写功能&#xff0c;让你摆脱网络束缚。无论是在静谧的图书…

ip归属地和手机卡有关系吗?详细探析

在数字化浪潮席卷全球的今天&#xff0c;互联网已成为连接世界的桥梁。IP地址&#xff0c;作为网络世界中每个设备的“身份证”&#xff0c;承载着设备的位置信息和通信功能。而手机卡&#xff0c;则是我们移动设备接入互联网的钥匙&#xff0c;它让随时随地的在线交流成为可能…

Ubuntu中部署deepseek

deepseek-r1:1.5b版本可以在CPU上运行 1.安装ollama 1.1更新系统包 sudo apt update sudo apt upgrade -y1.2 安装依赖 sudo apt install -y curl git1.3 下载并安装 Ollama Ollama 通常通过脚本安装。你可以使用以下命令下载并运行安装脚本&#xff1a; curl -fsSL http…