扫码加入

  • 方案介绍
  • 相关推荐
申请入驻 产业图谱

基于单片机的PWM三基色LED灯控制器设计与无线调色系统

01/14 20:50
1503
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论
  1. 基于单片机PWM三基色LED灯控制器设计与无线调色系统
  2. 系统总体方案设计

2.1 设计背景与应用价值
RGB三基色LED灯由于能够通过红(R)、绿(G)、蓝(B)三种基础颜色的混合实现丰富多彩的光色变化,被广泛应用于智能家居氛围灯、广告灯箱、舞台灯光、装饰照明、工业指示灯以及物联网照明控制等领域。传统的RGB灯控制方式往往依赖旋钮电位器或简单按键,功能单一且缺乏远程控制能力,无法满足现代智能家居“无线化、可视化、可联动”的需求。
PWM(脉宽调制)是RGB LED控制中最常用、最经济、效果最稳定的调光方式。通过改变PWM占空比即可改变每个颜色通道的亮度,进而实现任意颜色混合。相比模拟电压调光,PWM抗干扰能力更强、控制精度更高、功耗更低且易于数字化实现。
本设计以单片机为控制核心,采用PWM输出实现RGB三路亮度控制,同时通过LCD1602实时显示当前R/G/B数值,并支持按键本地调节与WiFi无线远程调色。该系统可作为智能灯光控制的基础平台,为后续扩展手机APP控制、语音控制、场景联动、渐变动画、音乐律动等功能提供硬件与软件基础。

2.2 系统设计目标
系统主要设计目标如下:

  1. PWM三路输出:通过单片机产生三路PWM信号分别驱动R/G/B三基色LED,实现亮度独立调节与颜色混合。
  2. LCD1602实时显示:实时显示R、G、B三个通道的数值(可用0255或0100表示),便于观察与确认。
  3. WiFi无线调色:通过WiFi通信远程修改R、G、B值,支持手机/电脑发送控制命令实现调色。
  4. 按键本地调节:通过按键在本地修改R、G、B数值,实时改变LED颜色,并同步更新显示。
  5. 系统稳定与体验优化:具备按键消抖、无线命令校验、防越界处理、平滑渐变等机制,使显示与颜色变化自然可靠。

2.3 系统总体结构
系统由以下模块构成:

  1. 单片机最小系统模块(主控)
  2. PWM输出与RGB LED驱动模块(MOSFET/三极管驱动)
  3. LCD1602显示模块
  4. 按键输入模块
  5. WiFi通信模块(如ESP8266)
  6. 电源模块(5V/3.3V稳压与滤波)
  7. 可选扩展模块(蜂鸣器提示、EEPROM存储、光敏自动调光等)

系统工作流程:

  • 上电初始化 → PWM输出默认颜色 → LCD显示R/G/B值 → 扫描按键与接收WiFi命令 → 更新R/G/B值 → PWM实时改变颜色 → LCD同步显示 → 循环运行。

  1. 功能设计与工作原理

3.1 PWM调光与RGB混色原理
PWM(Pulse Width Modulation)通过在固定周期内改变高电平持续时间(占空比)来控制平均输出功率。当PWM频率足够高时,人眼无法分辨闪烁,看到的是连续亮度变化。
对LED而言:

  • 占空比越大,LED平均电流越大,亮度越高;
  • 占空比越小,亮度越低。
    RGB混色则是同时调节R/G/B三路亮度:
  • R强、G弱、B弱 → 红色
  • R弱、G强、B弱 → 绿色
  • R弱、G弱、B强 → 蓝色
  • R强、G强、B弱 → 黄色
  • R强、G弱、B强 → 紫色
  • R弱、G强、B强 → 青色
  • 三路都高 → 白色(色温取决于LED芯片特性)
    通过不断改变三路PWM占空比即可实现任意颜色调节,若加入渐变算法还能实现呼吸灯、彩虹渐变等动态效果。

3.2 LCD1602显示功能
LCD1602能够显示两行字符,每行16个字符,适合显示R/G/B数值与系统状态。显示内容建议规划如下:

  • 第一行:R:xxx G:xxx
  • 第二行:B:xxx WIFI:OK/-- 或 显示当前模式(LOCAL/REMOTE)
    这样既能显示三色数值,又能显示WiFi连接状态或控制来源,增强可用性。

3.3 按键本地调节功能
按键用于本地修改R/G/B数值。为了实现完整的调整体验,常见交互方式有两种:

  1. 多按键方式(推荐)
    • KEY_MODE:切换当前调整通道(R→G→B)
    • KEY_UP:增加当前通道数值
    • KEY_DOWN:减少当前通道数值
      这种方式直观、操作效率高。
  2. 少按键方式
    • 通过长按/短按组合实现通道切换与数值变化,硬件更省但交互复杂。
      本系统为保证操作简便可靠,建议至少使用3个按键。

按键调节时系统应实时更新PWM与LCD,实现“所见即所得”的调色体验。同时应加入消抖、长按加速、越界限制(0~255)等功能。

3.4 WiFi无线通信远程调色功能
WiFi模块负责与外部设备通信。常用实现方式:

  1. ESP8266 AT指令模式:单片机通过串口发送AT指令,ESP8266负责连接路由器并建立TCP/UDP服务器或客户端。优点是8051端实现简单,缺点是依赖AT指令处理、稳定性受指令交互影响。
  2. ESP8266作为独立控制器:ESP8266运行固件(如Arduino/NodeMCU),单片机仅作为PWM驱动从机。优点是扩展强,缺点是系统复杂度提高。
    本设计以“单片机PWM控制器”为主,推荐采用ESP8266 AT模式,由STC89C51通过UART解析接收到的控制命令,实现远程修改R/G/B值。

无线控制命令设计建议采用简单文本协议,例如:

  • RGB=255,128,64
  • R=200
  • G=50
  • B=180
    单片机解析后更新对应数值,并返回确认信息,提高交互可靠性。

3.5 本地与远程控制的协同策略
系统应明确“控制优先级”:

  • 一种策略是“谁最后修改谁生效”,即本地按键和远程命令都能直接改变RGB值;
  • 另一种策略是“模式切换”,例如按键切换LOCAL/REMOTE模式,避免同时操作冲突。
    对于智能灯光控制器,通常采用“最后修改优先”即可,用户体验更自然。本设计建议在LCD显示“SRC:KEY/WIFI”提示当前控制来源,便于确认。

  1. 电路设计(分模块详细说明)

4.1 单片机最小系统模块

4.1.1 单片机选型说明
本系统需要产生三路PWM、驱动LCD、扫描按键、通过串口与WiFi通信。使用STC89C51/AT89C52等8051兼容单片机即可满足需求。
STC89系列具有较高运行速度与丰富的定时资源,适合PWM实现与串口通信。若选用普通AT89C52同样可实现,但软件需要更精细的定时调度。
主控任务包括:

  • PWM生成与占空比更新
  • LCD1602显示刷新
  • 按键扫描与参数修改
  • UART通信与协议解析
  • 系统状态管理与异常处理

4.1.2 时钟与复位电路

  • 时钟:外接晶振(11.0592MHz或12MHz),提供稳定系统时钟。
  • 复位:RC上电复位+手动复位键,确保上电可靠启动。
  • 去耦:单片机VCC旁放置0.1uF电容,抑制高频噪声

4.2 PWM输出与RGB LED驱动模块

4.2.1 RGB LED类型与驱动思路
RGB LED常见两种:

  1. 共阳极RGB LED:公共端接VCC,单片机输出低电平或PWM低电平驱动通道导通(需要下拉驱动)。
  2. 阴极RGB LED:公共端接GND,单片机输出高电平或PWM高电平驱动通道导通(需要上拉驱动)。
    此外还有RGB灯带、RGB大功率LED模块等。
    如果驱动的是单颗小功率RGB LED,可用三极管即可;若驱动的是灯带或大功率LED,必须使用MOSFET并考虑散热与电源容量。

4.2.2 MOSFET/三极管驱动电路设计
由于单片机IO驱动能力有限,不能直接驱动较大电流LED。推荐使用N沟道MOSFET低端驱动:

  • LED正端接电源
  • LED负端接MOSFET漏极
  • MOSFET源极接地
  • 单片机PWM输出通过电阻驱动MOSFET栅极
    MOSFET选择逻辑电平型(如AO3400、IRLZ44N等),确保在5V栅压下导通充分。
    若驱动单颗LED,也可使用NPN三极管(如S8050)开关驱动,但MOSFET效率更高、发热更低。

4.2.3 限流与保护设计
LED必须限流:

  • 单颗LED可串联电阻限流
  • 灯带通常内部已限流
    驱动电路建议加入:
  • 栅极串联电阻(抑制振荡)
  • 栅极下拉电阻(防止上电悬空误亮)
  • 电源端滤波电容(抑制PWM电流脉动)
    对于大功率负载,还应加入TVS保护与保险丝

4.2.4 PWM频率选择
PWM频率应高于人眼可见闪烁范围,通常推荐:

  • 200Hz以上不明显闪烁
  • 500Hz~2kHz体验更好
    但频率太高会增加单片机中断负担或降低分辨率。对于8051软件PWM,推荐采用约200Hz~1kHz范围,兼顾稳定与资源消耗。若单片机支持硬件PWM更佳,但经典8051通常需软件实现。

4.3 LCD1602显示模块

4.3.1 接口模式选择
LCD1602支持8位与4位模式。为了节省I/O口,本系统建议使用4位模式(D4~D7)加RS、E控制线,RW可接地固定写模式。
电路要点:

  • VO对比度引脚接10K电位器
  • 背光加限流电阻或三极管控制
  • 数据线短且远离MOSFET驱动线,防止干扰乱码

4.3.2 显示内容与格式设计
为了提高可读性,LCD显示应简洁:

  • R:255 G:128
  • B:064 WIFI:OK
    或显示当前模式、来源等。显示刷新建议每200ms~500ms一次,避免频繁刷新导致闪烁。

4.4 按键输入模块

4.4.1 按键电路结构
按键采用上拉输入:

  • 按键一端接地
  • 另一端接单片机I/O并上拉电阻(可用内部上拉或外部10K)
    按下时为低电平。
    为了抗干扰可并联小电容实现硬件滤波,但软件消抖通常足够。

4.4.2 按键功能分配建议

  • KEY_MODE:选择当前调整通道R/G/B
  • KEY_UP:增加当前通道数值
  • KEY_DOWN:减少当前通道数值
    可扩展:
  • KEY_SAVE:保存当前颜色(EEPROM)
  • KEY_SCENE:切换预设场景(渐变/呼吸)

4.5 WiFi通信模块(ESP8266)

4.5.1 模块选型与连接方式
ESP8266常用模块如ESP-01、ESP-12F等,工作电压为3.3V,串口通信电平为3.3V。
与5V单片机连接时需注意:

  • ESP8266 RX不耐5V,单片机TX需分压或电平转换
  • ESP8266 TX输出3.3V可被8051识别为高电平(通常可行,但需确保输入阈值)
    ESP8266供电需要稳定3.3V,并在供电端加足够滤波电容(如100uF+0.1uF),否则WiFi发射时电流突变可能导致重启。

4.5.2 通信协议与数据帧设计
为了简化8051解析,建议使用固定格式命令:

  • RGB=R,G,Brn
  • SET R 200rn
  • GETrn返回当前值
    并加入简单校验(如结尾CRC或固定前缀),避免误触发。
    在AT模式下,ESP8266可以配置为TCP服务器,手机连接后发送命令即可调色。

4.6 电源模块设计

4.6.1 电源需求分析
系统至少需要两路电压:

  • 5V:单片机、LCD、按键、MOSFET栅极驱动
  • 3.3V:ESP8266 WiFi模块
    若驱动灯带或大功率LED,可能还需要12V或更高电压供LED负载。

4.6.2 稳压与滤波

  • 3.3V稳压可采用AMS1117-3.3或DC-DC降压模块。考虑ESP8266峰值电流,建议电源能提供500mA以上并配合大电容滤波。
  • 5V可采用7805或DC-DC模块。若系统带12V LED供电,可先用DC-DC降到5V。
    电源端应加入:
  • 反接保护
  • 输入滤波电容
  • 各芯片就近去耦电容
  • 大功率LED供电与逻辑供电尽量分开,减少干扰。

  1. 程序设计(分模块详细说明)

5.1 软件总体架构与设计思想
软件设计采用“定时器中断 + 主循环任务调度”结构,保证PWM输出稳定、按键响应及时、WiFi命令解析可靠、LCD显示平稳。主要模块包括:

  1. 系统初始化模块(IO、定时器、串口、LCD)
  2. PWM生成模块(三路PWM,占空比可调)
  3. RGB参数管理模块(R/G/B值范围、更新同步)
  4. 按键扫描与交互模块(通道选择、加减、消抖、长按加速)
  5. WiFi串口通信模块(接收缓存、命令解析、应答)
  6. LCD显示模块(格式化显示、刷新控制)
  7. 可选:渐变与过渡模块(避免颜色跳变)
  8. 可选:参数存储模块(EEPROM保存上次颜色)

整体逻辑:

  • 定时器中断负责PWM计数与产生PWM波形
  • 主循环中执行按键扫描、串口解析、LCD刷新
  • 当R/G/B值改变时立即更新PWM占空比并刷新显示

5.2 PWM生成模块设计

5.2.1 软件PWM原理
在8051系统中可使用定时器中断作为PWM基准时钟:

  • 设定一个PWM周期计数器pwm_cnt从0~255循环
  • 对每个通道:若pwm_cnt < duty(R/G/B值)则输出高电平,否则输出低电平
    这样即可实现8位分辨率(0~255)PWM。
    PWM频率 = 定时器中断频率 / 256
    例如定时器中断频率为10kHz,则PWM频率约39Hz,偏低会闪烁;
    若中断频率为50kHz,则PWM频率约195Hz,体验较好。
    因此需要合理设置定时器,使中断频率足够高但又不至于占用过多CPU。对于STC89C51,设置20kHz~60kHz中断通常可行,结合优化代码减少中断开销。

5.2.2 Gamma校正(可选)
人眼对亮度变化的感知并非线性,直接用0~255线性调节会导致低亮度变化不明显,高亮度变化过快。可加入Gamma校正:

  • 输出占空比 = gamma_table[input]
    gamma_table为预计算查表,提升调光平滑度。
    对于课程设计,可作为扩展功能描述。

5.3 按键扫描与本地调节模块

5.3.1 按键事件定义
按键模块需要识别:

  • 短按:增加/减少一次
  • 长按:持续加速变化
  • MODE键短按:切换当前通道(R→G→B)
    并在LCD上突出显示当前通道(例如在通道前显示“>R”)。

5.3.2 消抖与长按加速实现
采用10ms扫描周期:

  • 连续2~3次稳定状态确认按下
  • 按下持续超过500ms进入长按加速,每100ms自动加/减
    这种交互能兼顾精准调整和快速调整。

5.4 WiFi串口通信与命令解析模块

5.4.1 串口接收缓存设计
串口数据到达速度快且不规律,必须使用中断接收,将字符存入环形缓冲区或线性缓存,检测到rn或换行符后认为一条命令完成,再在主循环解析。
解析策略:

  • 忽略无效字符
  • 限制最大长度,防止缓冲溢出
  • 对数据格式进行严格校验,防止错误命令导致异常

5.4.2 命令协议解析
建议支持以下命令:

  1. RGB=R,G,B:一次性设置三通道
  2. R=xxx / G=xxx / B=xxx:单通道设置
  3. GET:查询当前RGB值,返回RGB=...
    解析后将数值限制在0~255范围内,并更新PWM与LCD。
    系统还可返回OKERR作为应答,提高通信可靠性。

5.4.3 WiFi状态管理
若需要显示WiFi连接状态,可通过:

  • ESP8266 AT指令查询连接状态
  • 或通过定期心跳包判断是否有连接
    LCD上显示WIFI:OKWIFI:--,提升用户体验。

5.5 LCD显示模块设计

5.5.1 格式化显示与刷新策略
LCD显示采用固定刷新周期,例如200ms刷新一次。
显示内容示例:

  • R:255 G:128
  • B:064 SRC:WF
    其中SRC表示来源:WF(WiFi)、KY(按键)。
    当处于通道选择时可显示:
  • >R:255 G:128
    表示当前调整R通道。

5.5.2 显示与控制同步机制
当R/G/B值变化时立即更新PWM占空比,LCD可在下一刷新周期更新。这样可减少LCD频繁写入,避免影响PWM稳定性。

5.6 参数管理与保护机制

5.6.1 范围限制与异常处理
R/G/B值应限制在0~255:

  • 超过255则置255
  • 小于0则置0
    命令解析错误应返回ERR并不改变当前值,避免误操作导致颜色突变。

5.6.2 平滑渐变(可选增强)
直接修改占空比会导致颜色瞬间变化,若希望更柔和,可加入渐变过渡:

  • 目标值target_r/g/b
  • 当前值cur_r/g/b逐步逼近目标值(每10ms±1)
    这样能实现柔和调色体验,也可扩展为呼吸灯效果。

  1. 核心程序代码示例(C语言,STC89C51/8051风格,软件PWM + 串口解析 + LCD显示)
#include <reg52.h>
#include <intrins.h>

typedef unsigned char  u8;
typedef unsigned int   u16;
typedef unsigned long  u32;

// ===================== PWM输出引脚定义(示例:低端MOSFET驱动) =====================
sbit LED_R = P1^0;
sbit LED_G = P1^1;
sbit LED_B = P1^2;

// ===================== LCD1602 (4位模式) =====================
sbit LCD_RS = P2^0;
sbit LCD_E  = P2^1;
sbit LCD_D4 = P2^4;
sbit LCD_D5 = P2^5;
sbit LCD_D6 = P2^6;
sbit LCD_D7 = P2^7;

// ===================== Keys =====================
sbit KEY_MODE = P3^2;
sbit KEY_UP   = P3^3;
sbit KEY_DOWN = P3^4;

// ===================== 系统变量 =====================
volatile u32 g_ms = 0;

// PWM占空比(0~255)
volatile u8 duty_r = 128;
volatile u8 duty_g = 128;
volatile u8 duty_b = 128;

// PWM计数器
volatile u8 pwm_cnt = 0;

// 当前选中通道:0-R 1-G 2-B
u8 sel_ch = 0;

// 控制来源
#define SRC_KEY  0
#define SRC_WIFI 1
u8 last_src = SRC_KEY;

// ===================== 串口接收缓冲 =====================
#define RX_BUF_LEN 64
volatile u8 rx_buf[RX_BUF_LEN];
volatile u8 rx_wr = 0;
volatile u8 rx_rd = 0;

// ===================== 工具函数 =====================
void delay_us(u8 t){ while(t--) _nop_(); }
void delay_ms(u16 ms){ u16 i,j; for(i=0;i<ms;i++) for(j=0;j<120;j++); }

// ===================== 定时器0:1ms系统节拍 + PWM基准 =====================
// 这里用更高频率中断更适合PWM,但示例简化为1ms节拍 + 低频PWM展示。
// 实际项目建议使用更高频率定时器中断作为PWM基准。
void Timer0_Init_1ms(void)
{
    TMOD &= 0xF0;
    TMOD |= 0x01;
    TH0 = 0xFC;  // 1ms@12MHz
    TL0 = 0x18;
    ET0 = 1;
    EA  = 1;
    TR0 = 1;
}

void Timer0_ISR(void) interrupt 1
{
    TH0 = 0xFC;
    TL0 = 0x18;

    g_ms++;

    // 软件PWM演示:pwm_cnt 0~255
    pwm_cnt++;

    // 若采用低端MOSFET,LED脚输出1表示导通(发光)
    LED_R = (pwm_cnt < duty_r) ? 1 : 0;
    LED_G = (pwm_cnt < duty_g) ? 1 : 0;
    LED_B = (pwm_cnt < duty_b) ? 1 : 0;
}

// ===================== UART初始化与中断接收 =====================
void UART_Init(void)
{
    SCON = 0x50;      // 8位UART,允许接收
    TMOD |= 0x20;     // Timer1模式2
    TH1 = 0xFD;       // 9600@11.0592MHz
    TL1 = 0xFD;
    TR1 = 1;
    ES  = 1;
    EA  = 1;
}

void UART_SendByte(u8 c)
{
    SBUF = c;
    while(!TI);
    TI = 0;
}
void UART_SendStr(char *s)
{
    while(*s) UART_SendByte(*s++);
}

void UART_ISR(void) interrupt 4
{
    if(RI)
    {
        u8 c = SBUF;
        RI = 0;
        rx_buf[rx_wr] = c;
        rx_wr = (rx_wr + 1) % RX_BUF_LEN;
    }
}

// ===================== LCD1602驱动 =====================
void LCD_Write4(u8 dat)
{
    LCD_D4 = dat & 0x01;
    LCD_D5 = (dat & 0x02) ? 1 : 0;
    LCD_D6 = (dat & 0x04) ? 1 : 0;
    LCD_D7 = (dat & 0x08) ? 1 : 0;
}
void LCD_Enable(void)
{
    LCD_E = 1; delay_us(10);
    LCD_E = 0; delay_us(10);
}
void LCD_Cmd(u8 cmd)
{
    LCD_RS = 0;
    LCD_Write4(cmd >> 4); LCD_Enable();
    LCD_Write4(cmd & 0x0F); LCD_Enable();
    delay_ms(2);
}
void LCD_Data(u8 dat)
{
    LCD_RS = 1;
    LCD_Write4(dat >> 4); LCD_Enable();
    LCD_Write4(dat & 0x0F); LCD_Enable();
    delay_us(50);
}
void LCD_SetPos(u8 row, u8 col)
{
    u8 addr = (row==0)?0x80:0xC0;
    LCD_Cmd(addr + col);
}
void LCD_Print(char *s)
{
    while(*s) LCD_Data(*s++);
}
void LCD_Init(void)
{
    delay_ms(20);
    LCD_RS=0; LCD_E=0;

    LCD_Write4(0x03); LCD_Enable(); delay_ms(5);
    LCD_Write4(0x03); LCD_Enable(); delay_ms(1);
    LCD_Write4(0x03); LCD_Enable(); delay_ms(1);
    LCD_Write4(0x02); LCD_Enable();

    LCD_Cmd(0x28);
    LCD_Cmd(0x0C);
    LCD_Cmd(0x06);
    LCD_Cmd(0x01);
}

// ===================== 数值转字符串(3位) =====================
void u8_to_3char(u8 v, char *out)
{
    out[0] = (v/100) + '0';
    out[1] = (v/10)%10 + '0';
    out[2] = (v%10) + '0';
}

// ===================== LCD显示更新 =====================
void LCD_Update(void)
{
    char line1[17], line2[17];
    char tmp[3];

    // line1: R:xxx G:xxx
    line1[0] = (sel_ch==0)?'>':'R'; // 选中R时显示 >
    line1[1] = ':';
    u8_to_3char(duty_r, tmp);
    line1[2]=tmp[0]; line1[3]=tmp[1]; line1[4]=tmp[2];
    line1[5]=' '; line1[6]='G'; line1[7]=':';
    u8_to_3char(duty_g, tmp);
    line1[8]=tmp[0]; line1[9]=tmp[1]; line1[10]=tmp[2];
    line1[11]=' '; line1[12]=' '; line1[13]=' '; line1[14]=' '; line1[15]=' ';
    line1[16]=0;

    // line2: B:xxx SRC:KY/WF
    line2[0] = (sel_ch==2)?'>':'B';
    line2[1] = ':';
    u8_to_3char(duty_b, tmp);
    line2[2]=tmp[0]; line2[3]=tmp[1]; line2[4]=tmp[2];
    line2[5]=' '; line2[6]='S'; line2[7]='R'; line2[8]='C'; line2[9]=':';
    if(last_src == SRC_WIFI){ line2[10]='W'; line2[11]='F'; }
    else { line2[10]='K'; line2[11]='Y'; }
    line2[12]=' '; line2[13]=' '; line2[14]=' '; line2[15]=' ';
    line2[16]=0;

    LCD_SetPos(0,0); LCD_Print(line1);
    LCD_SetPos(1,0); LCD_Print(line2);
}

// ===================== 按键扫描(简单消抖) =====================
u8 Key_ReadStable(sbit *key)
{
    // 此函数仅作示例,C51中无法直接传递sbit指针,实际应写成独立函数或宏
    return 1;
}

u8 Key_Scan(void)
{
    static u8 last_mode=1, last_up=1, last_down=1;
    u8 now_mode = KEY_MODE;
    u8 now_up   = KEY_UP;
    u8 now_down = KEY_DOWN;

    // 检测按下沿(高->低)
    if(last_mode==1 && now_mode==0){ last_mode=now_mode; return 1; }
    if(last_up  ==1 && now_up  ==0){ last_up  =now_up;   return 2; }
    if(last_down==1 && now_down==0){ last_down=now_down; return 3; }

    last_mode = now_mode;
    last_up   = now_up;
    last_down = now_down;
    return 0;
}

void Apply_KeyEvent(u8 ev)
{
    if(ev==0) return;

    last_src = SRC_KEY;

    if(ev == 1)
    {
        sel_ch++;
        if(sel_ch > 2) sel_ch = 0;
    }
    else if(ev == 2) // UP
    {
        if(sel_ch==0 && duty_r<255) duty_r++;
        if(sel_ch==1 && duty_g<255) duty_g++;
        if(sel_ch==2 && duty_b<255) duty_b++;
    }
    else if(ev == 3) // DOWN
    {
        if(sel_ch==0 && duty_r>0) duty_r--;
        if(sel_ch==1 && duty_g>0) duty_g--;
        if(sel_ch==2 && duty_b>0) duty_b--;
    }
}

// ===================== 串口读取一行命令(以'n'结束) =====================
bit UART_ReadLine(char *out)
{
    static u8 idx = 0;

    while(rx_rd != rx_wr)
    {
        char c = rx_buf[rx_rd];
        rx_rd = (rx_rd + 1) % RX_BUF_LEN;

        if(c == 'r') continue;

        if(c == 'n')
        {
            out[idx] = 0;
            idx = 0;
            return 1;
        }
        else
        {
            if(idx < 31) out[idx++] = c;
        }
    }
    return 0;
}

// ===================== 简单字符串转整数(0~255) =====================
int parse_u8(char *s)
{
    int v = 0;
    while(*s)
    {
        if(*s<'0' || *s>'9') break;
        v = v*10 + (*s - '0');
        s++;
    }
    if(v < 0) v = 0;
    if(v > 255) v = 255;
    return v;
}

// ===================== WiFi命令解析 =====================
void Parse_Cmd(char *cmd)
{
    // 支持:RGB=R,G,B  或 R=xxx G=xxx B=xxx  或 GET
    if(cmd[0]=='G' && cmd[1]=='E' && cmd[2]=='T')
    {
        // 返回当前值
        UART_SendStr("RGB=");
        UART_SendByte((duty_r/100)+'0'); UART_SendByte(((duty_r/10)%10)+'0'); UART_SendByte((duty_r%10)+'0');
        UART_SendByte(','); 
        UART_SendByte((duty_g/100)+'0'); UART_SendByte(((duty_g/10)%10)+'0'); UART_SendByte((duty_g%10)+'0');
        UART_SendByte(','); 
        UART_SendByte((duty_b/100)+'0'); UART_SendByte(((duty_b/10)%10)+'0'); UART_SendByte((duty_b%10)+'0');
        UART_SendStr("rn");
        return;
    }

    if(cmd[0]=='R' && cmd[1]=='G' && cmd[2]=='B' && cmd[3]=='=')
    {
        // RGB=xxx,yyy,zzz
        char *p = cmd + 4;
        int r = parse_u8(p);
        while(*p && *p!=',') p++; if(*p==',') p++;
        int g = parse_u8(p);
        while(*p && *p!=',') p++; if(*p==',') p++;
        int b = parse_u8(p);

        duty_r = (u8)r;
        duty_g = (u8)g;
        duty_b = (u8)b;

        last_src = SRC_WIFI;
        UART_SendStr("OKrn");
        return;
    }

    if(cmd[0]=='R' && cmd[1]=='=')
    {
        duty_r = (u8)parse_u8(cmd+2);
        last_src = SRC_WIFI;
        UART_SendStr("OKrn");
        return;
    }
    if(cmd[0]=='G' && cmd[1]=='=')
    {
        duty_g = (u8)parse_u8(cmd+2);
        last_src = SRC_WIFI;
        UART_SendStr("OKrn");
        return;
    }
    if(cmd[0]=='B' && cmd[1]=='=')
    {
        duty_b = (u8)parse_u8(cmd+2);
        last_src = SRC_WIFI;
        UART_SendStr("OKrn");
        return;
    }

    UART_SendStr("ERRrn");
}

// ===================== 主函数 =====================
void main(void)
{
    char cmd[32];
    u32 last_lcd = 0;
    u32 last_key = 0;

    LED_R = LED_G = LED_B = 0;

    LCD_Init();
    UART_Init();
    Timer0_Init_1ms();

    while(1)
    {
        // 10ms扫描一次按键
        if(g_ms - last_key >= 10)
        {
            u8 ev;
            last_key = g_ms;
            ev = Key_Scan();
            Apply_KeyEvent(ev);
        }

        // 串口接收命令
        if(UART_ReadLine(cmd))
        {
            Parse_Cmd(cmd);
        }

        // 200ms刷新LCD
        if(g_ms - last_lcd >= 200)
        {
            last_lcd = g_ms;
            LCD_Update();
        }
    }
}

  1. 关键模块深化说明与工程化优化

7.1 PWM频率与分辨率优化建议
示例代码使用1ms中断作为PWM基准,会导致PWM频率较低(约3.9Hz),实际会明显闪烁。工程实现必须提高PWM基准中断频率。常见优化方法:

  1. 将定时器中断设置为20us50us级别(20kHz50kHz),PWM频率=中断频率/256可达到约78Hz~195Hz。
  2. 减少中断内代码量,使用位运算提高效率。
  3. 若单片机型号支持硬件PWM(部分增强型单片机),应优先采用硬件PWM提高稳定性。
  4. 对于灯带或大功率LED,PWM频率建议提高到500Hz以上以避免摄像闪烁和视觉不适。

7.2 电平转换与WiFi供电可靠性
ESP8266对供电非常敏感,建议:

  • 3.3V电源使用DC-DC或大电流LDO,电流至少500mA
  • 供电端就近100uF电解 + 0.1uF贴片
  • 单片机TX到ESP8266 RX必须降压(分压或电平转换芯片)
    否则会出现连接不稳定、随机复位或通信乱码。

7.3 通信协议增强与安全性
在工业或家居环境中,WiFi通信可能受到干扰或误触发。建议增强协议:

  • 加前缀:#RGB=...
  • 加校验:例如简单校验和或CRC8
  • 加鉴权:例如设置密码或仅允许局域网控制
    并对命令频率做限制,避免连续命令导致系统卡顿。

7.4 按键交互体验提升
可加入:

  • 长按加速调节
  • 预设颜色快捷键(按住MODE进入预设循环)
  • 保存/恢复上次颜色(EEPROM存储)
    这些功能能显著增强用户体验,使系统更像产品而非单纯实验。

7.5 颜色显示与Gamma校正扩展
为了让亮度变化更符合人眼感受,可加入Gamma校正查表:

  • 输入0~255
  • 输出gamma_table[输入]
    这样低亮度部分变化更细腻,颜色渐变更平滑。

7.6 动态效果与场景扩展方向
在RGB控制基础上可扩展:

  1. 呼吸灯:占空比缓慢上升下降
  2. 彩虹渐变:按HSV色环转换到RGB
  3. 音乐律动:加入麦克风采集音量控制亮度
  4. 定时开关灯:加入RTC模块
  5. 与智能家居联动:MQTT协议接入Home Assistant
    这些扩展可以在论文中作为后续优化方向,提高系统创新性。

  1. 系统测试方案与验证要点

8.1 基本功能测试

  1. 上电后LED显示默认颜色,LCD显示初始RGB值
  2. 按键MODE切换通道,UP/DOWN调整数值,LED颜色实时变化
  3. WiFi发送RGB=255,0,0等命令,颜色正确变化,LCD同步更新
  4. WiFi发送非法命令,系统返回ERR,颜色不改变

8.2 PWM输出与视觉效果测试

  1. 观察不同亮度下是否闪烁
  2. 用手机摄像检测是否出现明显条纹(频率不足会出现)
  3. 在不同供电条件下亮度是否稳定
  4. 大功率负载下MOSFET是否发热、是否需要散热片

8.3 通信稳定性测试

  1. WiFi长时间连接是否稳定
  2. 连续发送命令是否出现丢包或解析错误
  3. 断开WiFi后本地按键仍可正常控制
  4. WiFi重连后系统恢复正常响应

8.4 抗干扰与可靠性测试

  1. PWM快速变化时LCD是否乱码
  2. WiFi发射时单片机是否复位
  3. 电源波动时系统是否稳定运行
    通过滤波、分区供电、软件容错可提升可靠性。

  1. 总结
    本设计完成了一套基于单片机的PWM三基色LED灯控制器与无线调色系统,实现了三路PWM独立调光、LCD1602实时显示R/G/B数值、按键本地调节与WiFi远程调色等功能。系统采用模块化电路结构,包含单片机最小系统、RGB驱动、LCD显示、按键交互、ESP8266无线通信与电源管理等模块,具备良好的扩展性与工程可用性。
    在程序设计方面,系统采用定时器中断生成PWM波形,并通过主循环实现按键扫描、串口命令解析与LCD刷新,保证实时性与响应速度。通过进一步优化PWM频率、加入Gamma校正与渐变过渡、增强通信协议与存储功能,可使系统更接近智能家居照明产品级方案,为后续扩展多场景灯光控制、物联网接入与智能联动奠定坚实基础。

相关推荐