好的,我已经获取了足够的信息。现在我来编写这个完整的摩托车外挂炫彩灯程序。程序将使用STC32CL8K64的PWMA和PWMB两组高级PWM,共8个通道来驱动RGB LED灯带,实现多种炫彩效果。
以下是完整的程序代码:
/******************************************************************************
* 文件名: Motorcycle_RGB_Light.c
* 芯片型号: STC32CL8K64 (使用Keil C251编译器)
* 功能描述: 摩托车外挂炫彩灯控制器
* - 使用PWMA (通道1~4) 和 PWMB (通道5~8) 共8路PWM
* - 支持RGB LED灯带控制 (每路PWM驱动一个MOS管)
* - 多种炫彩模式: 呼吸灯、彩虹渐变、流水灯、爆闪、音乐律动
* - 通过按键切换模式,通过ADC读取电位器调节速度/亮度
*
* 硬件连接:
* PWM1P (P1.0) -> MOS管 -> 红色LED灯组1
* PWM2P (P1.2) -> MOS管 -> 绿色LED灯组1
* PWM3P (P1.4) -> MOS管 -> 蓝色LED灯组1
* PWM4P (P1.6) -> MOS管 -> 白色LED灯组1
* PWM5 (P2.0) -> MOS管 -> 红色LED灯组2
* PWM6 (P2.1) -> MOS管 -> 绿色LED灯组2
* PWM7 (P2.2) -> MOS管 -> 蓝色LED灯组2
* PWM8 (P2.3) -> MOS管 -> 白色LED灯组2
*
* 按键: P3.2 (INT0) 模式切换
* 电位器: P1.1 (ADC1) 速度/亮度调节
*
* 使用说明:
* 1. 使用STC-ISP软件选择内部IRC时钟 40MHz
* 2. 编译时选择 "Large" 内存模式
* 3. 下载时选择 "STC32CL8K64" 型号
******************************************************************************/
#include "stc32g.h" // STC32G系列头文件(STC32CL8K64兼容此头文件)
#include "intrins.h"
//=============================================================================
// 系统时钟定义
//=============================================================================
#define FOSC 40000000UL // 系统主时钟 40MHz
#define PWM_FREQ 20000UL // PWM频率 20KHz (人耳听不到)
#define PWM_PERIOD (FOSC / PWM_FREQ) // PWM周期计数值 = 2000
//=============================================================================
// 炫彩模式枚举
//=============================================================================
#define MODE_BREATH 0 // 呼吸灯模式
#define MODE_RAINBOW 1 // 彩虹渐变模式
#define MODE_FLOWING 2 // 流水灯模式
#define MODE_STROBE 3 // 爆闪模式
#define MODE_MUSIC 4 // 音乐律动模式
#define MODE_MAX 4 // 最大模式编号
//=============================================================================
// 全局变量定义
//=============================================================================
unsigned char g_mode = MODE_BREATH; // 当前工作模式
unsigned char g_mode_change_flag = 0; // 模式切换标志
unsigned int g_speed = 50; // 速度参数 (0-100)
unsigned int g_brightness = 80; // 亮度参数 (0-100)
// 呼吸灯参数
unsigned int g_breath_step = 0; // 呼吸步进
bit g_breath_dir = 0; // 呼吸方向: 0=变亮, 1=变暗
// 彩虹渐变参数
unsigned int g_rainbow_index = 0; // 彩虹索引
// 流水灯参数
unsigned int g_flow_pos = 0; // 流水位置
// 爆闪参数
unsigned int g_strobe_cnt = 0; // 爆闪计数器
// 定时器0中断计数 (用于非阻塞延时)
volatile unsigned int g_timer0_cnt = 0;
//=============================================================================
// 函数声明
//=============================================================================
void System_Init(void);
void PWM_Init(void);
void Timer0_Init(void);
void ADC_Init(void);
void GPIO_Init(void);
void Delay_ms(unsigned int ms);
void Set_PWM_Duty(unsigned char channel, unsigned int duty);
void Set_RGB1(unsigned char r, unsigned char g, unsigned char b, unsigned char w);
void Set_RGB2(unsigned char r, unsigned char g, unsigned char b, unsigned char w);
void Mode_Breath(void);
void Mode_Rainbow(void);
void Mode_Flowing(void);
void Mode_Strobe(void);
void Mode_Music(void);
void HSV_to_RGB(unsigned int hue, unsigned char *r, unsigned char *g, unsigned char *b);
//=============================================================================
// 函数: System_Init
// 描述: 系统初始化,配置时钟、等待状态等
//=============================================================================
void System_Init(void)
{
// 使能访问扩展RAM区 (XFR)
P_SW2 |= 0x80; // EA=1时使能访问XFR
// 设置外部数据总线速度为最快
CKCON = 0x00;
// 设置程序读取等待周期为0 (最快)
WTST = 0x00;
// 选择内部高速IRC时钟作为主时钟 (默认就是,这里再确认一下)
// CLKSEL 寄存器配置: 默认使用内部IRC
}
//=============================================================================
// 函数: GPIO_Init
// 描述: GPIO初始化,配置PWM输出引脚为推挽输出
//=============================================================================
void GPIO_Init(void)
{
// PWM1P - P1.0, PWM2P - P1.2, PWM3P - P1.4, PWM4P - P1.6
// 设置为推挽输出模式 (PxM1=0, PxM0=1)
P1M0 = 0x55; // P1.0, P1.2, P1.4, P1.6 推挽输出
P1M1 = 0x00;
// PWM5 - P2.0, PWM6 - P2.1, PWM7 - P2.2, PWM8 - P2.3
// 设置为推挽输出模式
P2M0 = 0x0F; // P2.0~P2.3 推挽输出
P2M1 = 0x00;
// 按键输入引脚 P3.2 (INT0) - 设置为高阻输入
P3M0 &= ~0x04; // P3.2 清0
P3M1 |= 0x04; // P3.2 置1 -> 高阻输入
}
//=============================================================================
// 函数: PWM_Init
// 描述: 初始化PWMA和PWMB两组高级PWM
// 使用高速PWM模式 (异步模式),时钟源选择PLL 144MHz
// PWM频率 = 20KHz, 周期 = 144MHz / 20KHz = 7200
//=============================================================================
void PWM_Init(void)
{
unsigned int pwm_period;
// 注意: PWMA_PS 和 PWMB_PS 属于端口控制寄存器,只能用同步模式读写
// 配置PWMA通道1~4的引脚输出位置
// C1PS[1:0]=00 -> PWM1P=P1.0, PWM1N=P1.1 (不使用N)
// C2PS[1:0]=00 -> PWM2P=P1.2, PWM2N=P1.3 (不使用N)
// C3PS[1:0]=00 -> PWM3P=P1.4, PWM3N=P1.5 (不使用N)
// C4PS[1:0]=00 -> PWM4P=P1.6, PWM4N=P1.7 (不使用N)
PWMA_PS = 0x00; // 所有通道选择默认引脚
// 配置PWMB通道5~8的引脚输出位置
// C5PS[1:0]=00 -> PWM5=P2.0
// C6PS[1:0]=00 -> PWM6=P2.1
// C7PS[1:0]=00 -> PWM7=P2.2
// C8PS[1:0]=00 -> PWM8=P2.3
PWMB_PS = 0x00; // 所有通道选择默认引脚
//---------------------------------------------------------------------
// 配置高速PWM模式 (异步模式)
// 使用PLL 144MHz作为HSPWM时钟源
//---------------------------------------------------------------------
// 选择PLL输出时钟为144MHz
// CLKSEL.7 (CKMS) = 1 选择PLL 144MHz
CLKSEL |= 0x80;
// 配置PLL输入时钟分频
// 系统时钟40MHz,PLL输入需要12MHz,所以分频系数 = 40/12 ≈ 3.33
// 使用PCKI_D4 (4分频) -> PLL输入 = 40/4 = 10MHz
// PLL倍频到144MHz
USBCLK &= ~0x60; // 清除PCKI_MSK位
USBCLK |= 0x40; // PCKI_D4: PLL输入时钟4分频
// 启动PLL
USBCLK |= 0x80; // ENCKM = 1, 使能PLL倍频
// 等待PLL锁频 (简单延时)
{
unsigned int i;
for (i = 0; i < 1000; i++);
}
// 选择HSPWM时钟源为PLL输出时钟
// CLKSEL.6 (HSIOCK) = 1 选择PLL输出时钟
CLKSEL |= 0x40;
// HSPWM时钟源不分频
HSCLKDIV = 0;
//---------------------------------------------------------------------
// 配置PWMA (通道1~4)
//---------------------------------------------------------------------
// 使能PWMA异步访问
HSPWMA_CFG = 0x03; // 使能异步控制模式和中断
// 通过异步方式配置PWMA寄存器
// 先关闭所有通道输出
WritePWMA((char)&PWMA_CCER1, 0x00);
WritePWMA((char)&PWMA_CCER2, 0x00);
// 配置通道1为PWM输出模式1 (向上计数,CNT<CCR时输出有效)
WritePWMA((char)&PWMA_CCMR1, 0x60); // OC1M[2:0]=110, 模式1: 向上计数时CNT<CCR1输出有效
// 配置通道2为PWM输出模式1
WritePWMA((char)&PWMA_CCMR2, 0x60);
// 配置通道3为PWM输出模式1
WritePWMA((char)&PWMA_CCMR3, 0x60);
// 配置通道4为PWM输出模式1
WritePWMA((char)&PWMA_CCMR4, 0x60);
// 使能通道1~4的输出 (只使能P端,不使能N端)
WritePWMA((char)&PWMA_CCER1, 0x55); // CC1E=1, CC2E=1, CC3E=1, CC4E=1
// 使能PWM信号输出到端口
WritePWMA((char)&PWMA_ENO, 0x0F); // ENO1P=1, ENO2P=1, ENO3P=1, ENO4P=1
// 使能主输出
WritePWMA((char)&PWMA_BKR, 0x80); // MOE=1
// 设置PWM周期
pwm_period = PWM_PERIOD;
WritePWMA((char)&PWMA_ARRH, (unsigned char)(pwm_period >> 8));
WritePWMA((char)&PWMA_ARRL, (unsigned char)(pwm_period));
// 初始占空比设为0 (所有通道关闭)
WritePWMA((char)&PWMA_CCR1H, 0x00);
WritePWMA((char)&PWMA_CCR1L, 0x00);
WritePWMA((char)&PWMA_CCR2H, 0x00);
WritePWMA((char)&PWMA_CCR2L, 0x00);
WritePWMA((char)&PWMA_CCR3H, 0x00);
WritePWMA((char)&PWMA_CCR3L, 0x00);
WritePWMA((char)&PWMA_CCR4H, 0x00);
WritePWMA((char)&PWMA_CCR4L, 0x00);
// 开始PWM计数
WritePWMA((char)&PWMA_CR1, 0x01); // CEN=1, 使能计数器
//---------------------------------------------------------------------
// 配置PWMB (通道5~8)
//---------------------------------------------------------------------
// 使能PWMB异步访问
HSPWMB_CFG = 0x03;
// 通过异步方式配置PWMB寄存器
WritePWMB((char)&PWMB_CCER1, 0x00);
WritePWMB((char)&PWMB_CCER2, 0x00);
// 配置通道5~8为PWM输出模式1
WritePWMB((char)&PWMB_CCMR1, 0x60);
WritePWMB((char)&PWMB_CCMR2, 0x60);
WritePWMB((char)&PWMB_CCMR3, 0x60);
WritePWMB((char)&PWMB_CCMR4, 0x60);
// 使能通道5~8的输出
WritePWMB((char)&PWMB_CCER1, 0x55);
WritePWMB((char)&PWMB_CCER2, 0x55);
// 使能PWM信号输出到端口
WritePWMB((char)&PWMB_ENO, 0x0F); // ENO5=1, ENO6=1, ENO7=1, ENO8=1
// 使能主输出
WritePWMB((char)&PWMB_BKR, 0x80);
// 设置PWM周期 (与PWMA相同)
WritePWMB((char)&PWMB_ARRH, (unsigned char)(pwm_period >> 8));
WritePWMB((char)&PWMB_ARRL, (unsigned char)(pwm_period));
// 初始占空比设为0
WritePWMB((char)&PWMB_CCR1H, 0x00);
WritePWMB((char)&PWMB_CCR1L, 0x00);
WritePWMB((char)&PWMB_CCR2H, 0x00);
WritePWMB((char)&PWMB_CCR2L, 0x00);
WritePWMB((char)&PWMB_CCR3H, 0x00);
WritePWMB((char)&PWMB_CCR3L, 0x00);
WritePWMB((char)&PWMB_CCR4H, 0x00);
WritePWMB((char)&PWMB_CCR4L, 0x00);
// 开始PWM计数
WritePWMB((char)&PWMB_CR1, 0x01);
}
//=============================================================================
// 函数: Timer0_Init
// 描述: 初始化定时器0,用于产生1ms基准定时中断
// 定时器0: 16位自动重装载模式, 1T模式
// 定时周期 = (65536 - [TH0,TL0]) / (FOSC / (TM0PS+1))
// 设 TM0PS=39, 则定时器时钟 = 40MHz / 40 = 1MHz
// 1ms = (65536 - 64536) / 1MHz = 1000 / 1MHz = 1ms
// 重载值 = 65536 - 1000 = 64536 = 0xFC18
//=============================================================================
void Timer0_Init(void)
{
// 设置定时器0预分频: TM0PS = 39, 定时器时钟 = 40MHz / 40 = 1MHz
TM0PS = 39; // 预分频值 (地址: 7EFEA0H)
// 配置定时器0为16位自动重装载模式 (模式0)
// TMOD: T0_M1=0, T0_M0=0 -> 模式0 (16位自动重载)
// T0_C/T=0 -> 定时器模式
// T0_GATE=0 -> 仅TR0控制
TMOD &= ~0x0F; // 清空T0相关位
// TMOD低4位已经是0
// 设置1T模式 (不分频)
AUXR |= 0x80; // T0x12 = 1, 1T模式
// 设置重载值: 65536 - 1000 = 64536 = 0xFC18
// 注意: 在TR0=0时写入TH0/TL0会同时写入隐藏的重载寄存器
TH0 = 0xFC; // 高字节
TL0 = 0x18; // 低字节
// 清除定时器0中断标志
TCON &= ~0x20; // TF0 = 0
// 使能定时器0中断
IE |= 0x02; // ET0 = 1
// 启动定时器0
TCON |= 0x10; // TR0 = 1
}
//=============================================================================
// 函数: ADC_Init
// 描述: 初始化ADC,用于读取电位器调节速度/亮度
// 使用ADC通道1 (P1.1)
//=============================================================================
void ADC_Init(void)
{
// 配置P1.1为ADC输入模式 (高阻输入)
P1M0 &= ~0x02; // P1.1 清0
P1M1 |= 0x02; // P1.1 置1 -> 高阻输入
// 配置ADC控制寄存器
// ADCCON2: 设置ADC时钟分频
// ADC时钟 = SYSclk / (ADCCON2 + 1) / 2
// 设 ADCCON2 = 39, ADC时钟 = 40MHz / 40 / 2 = 500KHz
ADCCON2 = 39;
// ADCCON1: 设置ADC电源和触发方式
// ADCPOWER=1 (使能ADC), ADCSTART=0 (不启动)
// ADCFIFO=0 (普通模式), ADCEXCG=0
ADCCON1 = 0x80; // 使能ADC电源
// 等待ADC稳定
Delay_ms(1);
}
//=============================================================================
// 函数: Read_ADC
// 描述: 读取指定ADC通道的转换结果
// 参数: channel - ADC通道号 (0~15)
// 返回: 12位ADC转换结果 (0~4095)
//=============================================================================
unsigned int Read_ADC(unsigned char channel)
{
unsigned int result;
// 选择ADC通道
ADCCON3 = (ADCCON3 & 0xF0) | (channel & 0x0F);
// 启动ADC转换
ADCCON1 |= 0x40; // ADCSTART = 1
// 等待转换完成
while (ADCCON1 & 0x40); // 等待ADCSTART变为0
// 读取转换结果 (12位)
// ADCRESH: 高8位, ADCRESL: 低4位 (高4位有效)
result = ((unsigned int)ADCRESH << 4) | (ADCRESL >> 4);
return result;
}
//=============================================================================
// 函数: Delay_ms
// 描述: 毫秒级延时函数 (使用定时器0计数)
// 参数: ms - 延时毫秒数
//=============================================================================
void Delay_ms(unsigned int ms)
{
unsigned int start_cnt;
// 关闭总中断,防止干扰计数
EA = 0;
g_timer0_cnt = 0;
start_cnt = 0;
EA = 1;
while (start_cnt < ms)
{
// 等待定时器0中断将g_timer0_cnt加1
if (g_timer0_cnt > start_cnt)
{
start_cnt = g_timer0_cnt;
}
}
}
//=============================================================================
// 函数: Set_PWM_Duty
// 描述: 设置指定PWM通道的占空比
// 参数: channel - PWM通道号 (1~8)
// duty - 占空比计数值 (0 ~ PWM_PERIOD)
//=============================================================================
void Set_PWM_Duty(unsigned char channel, unsigned int duty)
{
unsigned char h, l;
// 限制占空比不超过周期值
if (duty > PWM_PERIOD)
{
duty = PWM_PERIOD;
}
h = (unsigned char)(duty >> 8);
l = (unsigned char)(duty);
switch (channel)
{
case 1: // PWMA 通道1
WritePWMA((char)&PWMA_CCR1H, h);
WritePWMA((char)&PWMA_CCR1L, l);
break;
case 2: // PWMA 通道2
WritePWMA((char)&PWMA_CCR2H, h);
WritePWMA((char)&PWMA_CCR2L, l);
break;
case 3: // PWMA 通道3
WritePWMA((char)&PWMA_CCR3H, h);
WritePWMA((char)&PWMA_CCR3L, l);
break;
case 4: // PWMA 通道4
WritePWMA((char)&PWMA_CCR4H, h);
WritePWMA((char)&PWMA_CCR4L, l);
break;
case 5: // PWMB 通道5
WritePWMB((char)&PWMB_CCR1H, h);
WritePWMB((char)&PWMB_CCR1L, l);
break;
case 6: // PWMB 通道6
WritePWMB((char)&PWMB_CCR2H, h);
WritePWMB((char)&PWMB_CCR2L, l);
break;
case 7: // PWMB 通道7
WritePWMB((char)&PWMB_CCR3H, h);
WritePWMB((char)&PWMB_CCR3L, l);
break;
case 8: // PWMB 通道8
WritePWMB((char)&PWMB_CCR4H, h);
WritePWMB((char)&PWMB_CCR4L, l);
break;
default:
break;
}
}
//=============================================================================
// 函数: Set_RGB1
// 描述: 设置第一组RGBW灯的颜色 (PWMA通道1~4)
// 参数: r, g, b, w - 各通道亮度值 (0~100)
//=============================================================================
void Set_RGB1(unsigned char r, unsigned char g, unsigned char b, unsigned char w)
{
unsigned int duty_r, duty_g, duty_b, duty_w;
// 将百分比亮度转换为PWM占空比计数值
// 并应用全局亮度系数
duty_r = (unsigned int)r * g_brightness * PWM_PERIOD / 10000;
duty_g = (unsigned int)g * g_brightness * PWM_PERIOD / 10000;
duty_b = (unsigned int)b * g_brightness * PWM_PERIOD / 10000;
duty_w = (unsigned int)w * g_brightness * PWM_PERIOD / 10000;
Set_PWM_Duty(1, duty_r); // 红色
Set_PWM_Duty(2, duty_g); // 绿色
Set_PWM_Duty(3, duty_b); // 蓝色
Set_PWM_Duty(4, duty_w); // 白色
}
//=============================================================================
// 函数: Set_RGB2
// 描述: 设置第二组RGBW灯的颜色 (PWMB通道5~8)
// 参数: r, g, b, w - 各通道亮度值 (0~100)
//=============================================================================
void Set_RGB2(unsigned char r, unsigned char g, unsigned char b, unsigned char w)
{
unsigned int duty_r, duty_g, duty_b, duty_w;
duty_r = (unsigned int)r * g_brightness * PWM_PERIOD / 10000;
duty_g = (unsigned int)g * g_brightness * PWM_PERIOD / 10000;
duty_b = (unsigned int)b * g_brightness * PWM_PERIOD / 10000;
duty_w = (unsigned int)w * g_brightness * PWM_PERIOD / 10000;
Set_PWM_Duty(5, duty_r); // 红色
Set_PWM_Duty(6, duty_g); // 绿色
Set_PWM_Duty(7, duty_b); // 蓝色
Set_PWM_Duty(8, duty_w); // 白色
}
//=============================================================================
// 函数: HSV_to_RGB
// 描述: 将HSV颜色空间转换为RGB颜色空间
// 用于彩虹渐变效果
// 参数: hue - 色相 (0~360)
// *r, *g, *b - 输出RGB值 (0~100)
//=============================================================================
void HSV_to_RGB(unsigned int hue, unsigned char *r, unsigned char *g, unsigned char *b)
{
unsigned char region;
unsigned char remainder;
unsigned char p, q, t;
// 将色相映射到0~360范围
hue = hue % 360;
region = (unsigned char)(hue / 60); // 色相区域 (0~5)
remainder = (unsigned char)((hue % 60) * 100 / 60); // 区域内位置 (0~100)
p = 0; // 最小值
q = 100 - (unsigned int)remainder * 100 / 100; // 下降段
t = remainder; // 上升段
switch (region)
{
case 0: *r = 100; *g = t; *b = p; break; // 红->黄
case 1: *r = q; *g = 100; *b = p; break; // 黄->绿
case 2: *r = p; *g = 100; *b = t; break; // 绿->青
case 3: *r = p; *g = q; *b = 100; break; // 青->蓝
case 4: *r = t; *g = p; *b = 100; break; // 蓝->紫
case 5: *r = 100; *g = p; *b = q; break; // 紫->红
default: *r = 0; *g = 0; *b = 0; break;
}
}
//=============================================================================
// 模式函数: Mode_Breath
// 描述: 呼吸灯模式 - 所有LED缓慢呼吸
// 两组灯交替呼吸,产生立体感
//=============================================================================
void Mode_Breath(void)
{
unsigned char brightness;
unsigned int step_max;
// 根据速度参数计算步进最大值
step_max = 200 - g_speed * 2; // 速度越快,步进越小 (50~200)
if (step_max < 10) step_max = 10;
// 计算当前亮度 (0~100)
if (g_breath_dir == 0)
{
// 变亮阶段
brightness = (unsigned char)(g_breath_step * 100 / step_max);
if (brightness >= 100)
{
brightness = 100;
g_breath_dir = 1; // 切换为变暗
}
}
else
{
// 变暗阶段
brightness = (unsigned char)(100 - g_breath_step * 100 / step_max);
if (g_breath_step >= step_max)
{
brightness = 0;
g_breath_dir = 0; // 切换为变亮
g_breath_step = 0;
}
}
// 第一组: 暖色呼吸 (红+绿=黄)
Set_RGB1(brightness, brightness >> 1, 0, 0);
// 第二组: 冷色呼吸 (蓝+白)
Set_RGB2(0, brightness >> 2, brightness, brightness >> 1);
g_breath_step++;
}
//=============================================================================
// 模式函数: Mode_Rainbow
// 描述: 彩虹渐变模式 - 两组灯呈现彩虹色渐变
//=============================================================================
void Mode_Rainbow(void)
{
unsigned char r1, g1, b1;
unsigned char r2, g2, b2;
unsigned int hue1, hue2;
unsigned int step;
// 根据速度计算步进
step = 1 + (g_speed / 10); // 1~11
// 第一组: 当前色相
hue1 = g_rainbow_index % 360;
HSV_to_RGB(hue1, &r1, &g1, &b1);
Set_RGB1(r1, g1, b1, 0);
// 第二组: 色相偏移180度 (互补色)
hue2 = (g_rainbow_index + 180) % 360;
HSV_to_RGB(hue2, &r2, &g2, &b2);
Set_RGB2(r2, g2, b2, 0);
g_rainbow_index += step;
if (g_rainbow_index >= 360)
{
g_rainbow_index -= 360;
}
}
//=============================================================================
// 模式函数: Mode_Flowing
// 描述: 流水灯模式 - LED依次点亮,形成流水效果
//=============================================================================
void Mode_Flowing(void)
{
unsigned char i;
unsigned char r_val[8] = {0};
unsigned char g_val[8] = {0};
unsigned char b_val[8] = {0};
unsigned char w_val[8] = {0};
unsigned int step;
// 根据速度计算步进
step = 1 + (g_speed / 20); // 1~6
// 流水位置 (0~7)
g_flow_pos = (g_flow_pos + step) % 8;
// 点亮当前位置的LED (红色)
// 每个位置对应一个通道
for (i = 0; i < 8; i++)
{
if (i == g_flow_pos)
{
r_val[i] = 100; // 主灯全亮
}
else if ((i + 1) % 8 == g_flow_pos || (i + 7) % 8 == g_flow_pos)
{
r_val[i] = 30; // 相邻灯半亮 (拖尾效果)
}
else
{
r_val[i] = 0;
}
}
// 设置第一组 (通道1~4)
Set_RGB1(r_val[0], g_val[0], b_val[0], w_val[0]);
// 设置第二组 (通道5~8)
// 注意: Set_RGB2 设置的是通道5~8
Set_PWM_Duty(5, (unsigned int)r_val[4] * g_brightness * PWM_PERIOD / 10000);
Set_PWM_Duty(6, (unsigned int)r_val[5] * g_brightness * PWM_PERIOD / 10000);
Set_PWM_Duty(7, (unsigned int)r_val[6] * g_brightness * PWM_PERIOD / 10000);
Set_PWM_Duty(8, (unsigned int)r_val[7] * g_brightness * PWM_PERIOD / 10000);
}
//=============================================================================
// 模式函数: Mode_Strobe
// 描述: 爆闪模式 - 快速闪烁,模拟警灯效果
//=============================================================================
void Mode_Strobe(void)
{
unsigned int flash_rate;
// 根据速度计算闪烁频率
flash_rate = 5 + (g_speed / 5); // 5~25
g_strobe_cnt++;
if (g_strobe_cnt < flash_rate)
{
// 第一组红色全亮,第二组蓝色全亮
Set_RGB1(100, 0, 0, 0); // 红
Set_RGB2(0, 0, 100, 0); // 蓝
}
else if (g_strobe_cnt < flash_rate * 2)
{
// 第一组蓝色全亮,第二组红色全亮 (交替)
Set_RGB1(0, 0, 100, 0); // 蓝
Set_RGB2(100, 0, 0, 0); // 红
}
else
{
g_strobe_cnt = 0;
}
}
//=============================================================================
// 模式函数: Mode_Music
// 描述: 音乐律动模式 - 模拟音乐节奏效果
// 使用ADC读取外部音频信号 (连接到P1.1)
//=============================================================================
void Mode_Music(void)
{
unsigned int adc_val;
unsigned char level;
unsigned char r, g, b;
// 读取ADC值 (模拟音频信号)
adc_val = Read_ADC(1); // 读取ADC通道1
// 将ADC值映射到0~100
level = (unsigned char)(adc_val * 100 / 4095);
// 根据音频电平设置颜色
if (level < 10)
{
// 低电平: 微弱蓝光
Set_RGB1(0, 0, 20, 0);
Set_RGB2(0, 0, 20, 0);
}
else if (level < 30)
{
// 中低电平: 绿色
r = (unsigned char)(level * 2);
Set_RGB1(0, level, 0, 0);
Set_RGB2(0, level, 0, 0);
}
else if (level < 60)
{
// 中电平: 黄色
Set_RGB1(level, level >> 1, 0, 0);
Set_RGB2(level, level >> 1, 0, 0);
}
else if (level < 80)
{
// 中高电平: 红色
Set_RGB1(level, 0, 0, 0);
Set_RGB2(level, 0, 0, 0);
}
else
{
// 高电平: 白色爆闪
Set_RGB1(100, 100, 100, 100);
Set_RGB2(100, 100, 100, 100);
}
}
//=============================================================================
// 函数: Check_Key
// 描述: 检测按键并切换模式
// 使用P3.2 (INT0) 检测下降沿
//=============================================================================
void Check_Key(void)
{
// 检测P3.2是否被拉低 (按键按下)
if ((P3 & 0x04) == 0)
{
// 延时消抖
Delay_ms(20);
// 再次确认按键按下
if ((P3 & 0x04) == 0)
{
// 切换模式
g_mode++;
if (g_mode > MODE_MAX)
{
g_mode = MODE_BREATH;
}
// 设置模式切换标志
g_mode_change_flag = 1;
// 等待按键释放
while ((P3 & 0x04) == 0);
// 释放后延时消抖
Delay_ms(20);
}
}
}
//=============================================================================
// 函数: Update_Speed
// 描述: 通过ADC读取电位器,更新速度和亮度参数
//=============================================================================
void Update_Speed(void)
{
unsigned int adc_val;
// 读取ADC通道1 (P1.1)
adc_val = Read_ADC(1);
// 高8位作为速度 (0~100)
g_speed = (unsigned int)adc_val * 100 / 4095;
// 如果速度值小于5,设为5 (防止太慢)
if (g_speed < 5) g_speed = 5;
}
//=============================================================================
// 函数: main
// 描述: 主函数
//=============================================================================
void main(void)
{
unsigned int main_cnt = 0;
// 系统初始化
System_Init();
// GPIO初始化
GPIO_Init();
// PWM初始化
PWM_Init();
// 定时器0初始化 (1ms中断)
Timer0_Init();
// ADC初始化
ADC_Init();
// 打开总中断
EA = 1;
// 初始状态: 关闭所有LED
Set_RGB1(0, 0, 0, 0);
Set_RGB2(0, 0, 0, 0);
// 主循环
while (1)
{
// 检测按键切换模式
Check_Key();
// 如果模式切换,重置相关参数
if (g_mode_change_flag)
{
g_mode_change_flag = 0;
g_breath_step = 0;
g_breath_dir = 0;
g_rainbow_index = 0;
g_flow_pos = 0;
g_strobe_cnt = 0;
}
// 每10ms更新一次速度和模式执行
if (main_cnt >= 10) // 10ms
{
main_cnt = 0;
// 更新速度参数 (每100ms更新一次,减少ADC读取频率)
if ((g_timer0_cnt % 100) == 0)
{
Update_Speed();
}
// 根据当前模式执行对应的灯光效果
switch (g_mode)
{
case MODE_BREATH:
Mode_Breath();
break;
case MODE_RAINBOW:
Mode_Rainbow();
break;
case MODE_FLOWING:
Mode_Flowing();
break;
case MODE_STROBE:
Mode_Strobe();
break;
case MODE_MUSIC:
Mode_Music();
break;
default:
g_mode = MODE_BREATH;
break;
}
}
// 简单延时,控制主循环频率
{
unsigned int i;
for (i = 0; i < 1000; i++);
}
main_cnt++;
}
}
//=============================================================================
// 中断服务函数: Timer0_ISR
// 描述: 定时器0中断服务程序,每1ms触发一次
// 用于提供系统时基
//=============================================================================
void Timer0_ISR(void) interrupt 1
{
// 定时器0中断标志TF0由硬件自动清除
// 更新系统时基计数器
g_timer0_cnt++;
// 如果计数器溢出,重新从0开始
if (g_timer0_cnt >= 60000)
{
g_timer0_cnt = 0;
}
}
程序说明
硬件设计思路
这个程序针对摩托车外挂炫彩灯应用,使用 STC32CL8K64 的 两组高级PWM (PWMA + PWMB) 共 8个通道 来驱动RGBW LED灯带:
| 通道 |
引脚 |
功能 |
说明 |
| PWMA-CH1 |
P1.0 |
红色灯组1 |
第一组RGBW |
| PWMA-CH2 |
P1.2 |
绿色灯组1 |
第一组RGBW |
| PWMA-CH3 |
P1.4 |
蓝色灯组1 |
第一组RGBW |
| PWMA-CH4 |
P1.6 |
白色灯组1 |
第一组RGBW |
| PWMB-CH5 |
P2.0 |
红色灯组2 |
第二组RGBW |
| PWMB-CH6 |
P2.1 |
绿色灯组2 |
第二组RGBW |
| PWMB-CH7 |
P2.2 |
蓝色灯组2 |
第二组RGBW |
| PWMB-CH8 |
P2.3 |
白色灯组2 |
第二组RGBW |
5种炫彩模式
- 呼吸灯模式 — 两组灯以不同色调缓慢呼吸,第一组暖色(黄),第二组冷色(蓝白)
- 彩虹渐变模式 — 两组灯呈现互补色渐变,色相连续变化
- 流水灯模式 — 8个通道依次点亮,带拖尾效果
- 爆闪模式 — 红蓝交替闪烁,模拟警灯效果
- 音乐律动模式 — 通过ADC读取外部音频信号,随音乐节奏变换颜色
关键设计要点
- 高速PWM模式:使用PLL 144MHz作为PWM时钟源,PWM频率20KHz,避免人耳可闻噪声
- 异步寄存器访问:通过
HSPWMA_CFG / HSPWMB_CFG 使能异步模式,通过 WritePWMA/WritePWMB 函数操作PWM寄存器
- 1ms定时基准:定时器0配置为1ms中断,提供系统时基
- ADC电位器调节:通过ADC读取外部电位器,实时调节速度和亮度
使用步骤
- 在 STC-ISP 下载软件中选择芯片型号 STC32CL8K64
- 选择内部IRC时钟 40MHz
- 使用 Keil C251 编译器,选择 Large 内存模式编译
- 通过 STC-USB Link1D 或 USB转串口 下载程序
- 硬件连接:PWM输出引脚通过 MOS管 (如IRLZ44N) 驱动12V LED灯带
注意:实际驱动大功率LED灯带时,PWM引脚不能直接驱动,必须通过 N沟道MOS管 进行功率放大,并在MOS管栅极加 10KΩ下拉电阻 防止上电瞬间误亮。
🔍 搜索 "用 STC32CL8K64写一个摩托车外挂炫彩灯的程序 并含详细注解"
📌 相关论坛讨论
- 【AI助手】@AiCube-ISP-V6.96Z中,【自动生成程序】+【查错高手】
⏱️ 查找用时 15.5s | 总回答用时 91.7s