串口指令控制8路LED音乐流水灯程序功能文档
1. 文档概述
本文档为深圳市普中科技有限公司51单片机实验程序配套功能说明,程序实现串口指令解析、蜂鸣器播放五首经典乐曲、8路LED同步流水灯联动功能,支持串口实时指令切换播放曲目,未匹配指令默认关闭所有LED,适用于51单片机串口通信、蜂鸣器音乐播放、LED动态显示综合实验。
2. 硬件基础参数
2.1 核心硬件配置
-
主控芯片:51系列单片机
-
系统晶振:11.0592MHz
-
串口波特率:9600bps
-
通信模块:CH340串口模块
2.2 硬件接线规则
-
串口接线:CH340 UTX → P3.0,CH340 URX → P3.1
-
LED接线 :LED1~LED8依次对应P1.7~P1.0,低电平点亮、高电平熄灭
-
蜂鸣器接线:蜂鸣器控制引脚 P2.0
3. 整体功能概述
程序上电初始化串口并输出就绪提示,循环检测串口接收指令。用户通过串口发送指定字符,单片机对应触发功能:蜂鸣器循环播放指定乐曲,同时8路LED同步执行流水灯动态效果;发送非指定字符,所有LED保持熄灭、蜂鸣器静音,无任何动作。
核心联动逻辑:每播放一个音乐音符,同步执行一次LED流水灯移位,实现音乐节奏与灯光动态同步的效果。
4. 串口指令功能定义
串口通信默认参数:波特率9600、8位数据位、1位停止位、无校验位,单字符指令触发对应功能,指令定义如下:
| 串口指令字符 | 对应播放乐曲 | 配套灯光效果 | 串口返回提示信息 |
|---|---|---|---|
| A | 小星星 | 同步流水灯循环 | Play:小星星, LED流水灯运行 |
| B | 欢乐颂 | 同步流水灯循环 | Play:欢乐颂, LED流水灯运行 |
| C | 两只老虎 | 同步流水灯循环 | Play:两只老虎, LED流水灯运行 |
| D | 生日快乐 | 同步流水灯循环 | Play:生日快乐, LED流水灯运行 |
| E | 茉莉花(简化完整版) | 同步流水灯循环 | Play:茉莉花, LED流水灯运行 |
| 其他任意字符 | 无(静音) | 所有LED熄灭 | 无返回信息 |
5. 核心模块功能说明
5.1 串口通信模块
-
串口初始化:配置定时器1为模式2自动重装,波特率固定9600,开启串口中断、总中断,上电自动初始化。
-
数据接收:通过串口中断异步接收数据,仅缓存数据并置位接收标志,不占用主循环时序,保证响应实时性。
-
数据发送:支持单字节发送、字符串批量发送,上电发送设备就绪提示,指令触发后返回对应播放状态提示。
5.2 LED灯光控制模块
-
全灭功能:上电默认所有LED熄灭,每次接收新指令后优先清空所有灯光,避免灯光残留。
-
流水灯功能:8路LED从LED1(P1.7)至LED8(P1.0)单向循环点亮,每完成一个音符播放执行一次移位,灯光节奏跟随音乐节拍同步变化,循环往复无卡顿。
5.3 蜂鸣器音乐播放模块
-
音符数据库:内置完整音阶初值表,包含低音、中音、高音及休止符,通过定时器初值换算音符频率,软件翻转IO口生成方波驱动蜂鸣器发声。
-
单音符播放:支持自定义音符、节拍时长,休止符自动静音延时,普通音符精准输出对应频率方波。
-
整首乐曲播放:内置五首完整乐曲乐谱数组,自动遍历乐谱,逐音符播放,直至遍历结束完成整首乐曲播放。
-
音光联动:每播放一个音符,自动触发一次流水灯移位,实现音乐、灯光节奏完全同步。
5.4 延时功能模块
-
毫秒延时:用于控制乐曲节拍、休止时长,适配11.0592MHz晶振,延时精准稳定。
-
微秒延时:用于生成精准音符方波半周期,保证蜂鸣器发声音准。
6. 程序运行流程
-
上电初始化:初始化串口、关闭所有LED,串口发送设备就绪提示信息。
-
循环监听指令:主循环持续检测串口接收标志,等待上位机发送指令。
-
指令解析处理:检测到有效指令后,清除接收标志、熄灭所有LED,匹配对应乐曲。
-
音光同步执行:串口返回播放提示,逐音符播放乐曲,同步驱动LED流水灯移位。
-
执行完成复位:整首乐曲播放完毕后,停止动作,回到指令监听状态,等待下一次指令触发。
-
异常处理:接收非A~E字符,无任何音光动作,保持LED全灭、蜂鸣器静音。
7. 核心特性与优势
-
实时响应:中断式串口接收,不阻塞主程序,指令响应无延迟。
-
音光同步:灯光节奏完全跟随音乐节拍,联动效果流畅自然。
-
模块化设计:串口、灯光、音乐、延时功能独立封装,便于修改乐曲、调整灯光效果。
-
稳定性强:固定波特率、精准延时,音准稳定,灯光无闪烁、乱码现象。
-
交互友好:上位机可实时接收设备状态反馈,清晰知晓当前播放曲目。
8. 异常状态说明
-
无指令输入:设备保持待机状态,LED全灭、蜂鸣器静音,持续监听串口信号。
-
非法指令输入:不触发任何播放动作,无报错、无残留灯光,设备保持待机状态。
-
乐曲播放中接收新指令:立即终止当前播放,清空灯光,响应新指令功能。
9. 关键函数详细说明
9.1 基础工具函数
-
DelayMs(uint ms) 毫秒延时函数:基于11.0592MHz晶振适配的软件延时函数,主要用于控制音乐节拍时长、休止符停留时间,保障乐曲播放节奏均匀稳定,是音乐节拍精准度的核心支撑函数。
-
DelayUs(uint us) 微秒延时函数:短延时精准函数,专门用于生成蜂鸣器音符方波的半周期,通过精准微秒级延时匹配不同音阶的频率参数,保证蜂鸣器发声标准、无走调。
9.2 串口功能函数
-
UART_Init(uchar th_val) 串口初始化函数:固定配置定时器1为8位自动重装模式,开启串口8位数据通信、总中断和串口中断,根据传入初值锁定9600波特率,上电一次性调用,完成串口通信底层配置。
-
UART_SendByte(uchar dat) 串口单字节发送函数:向串口缓冲区写入单字节数据,等待发送完成后清零发送标志,用于单个字符、指令反馈信息的基础发送。
-
UART_SendString(uchar *str) 串口字符串发送函数:循环遍历字符串,逐字节调用单字节发送函数,实现整行提示信息批量发送,用于设备就绪、曲目播放状态反馈。
-
UART_ISR() 串口中断服务函数:中断触发后自动读取串口接收数据,缓存数据并置位接收标志,仅做数据存储和标记处理,无延时、无业务逻辑,保证串口接收实时性,不阻塞程序主流程。
9.3 LED控制函数
-
LED_AllOff(void) LED全灭函数:将8路LED所有引脚置高电平,关闭所有灯光,用于上电初始化、指令切换时的灯光清零,避免不同功能切换时灯光残留。
-
LED_RunningWater(void) 流水灯驱动函数:先执行全灭操作,通过全局变量移位计数依次点亮单路LED,实现从LED1至LED8的单向循环流水效果,每播放一个音符自动调用一次,实现音光同步。
9.4 音乐播放函数
-
PlayNote(uint note, uint beat) 单音符播放函数:核心发声函数,判断音符类型,休止符直接静音延时;有效音符通过音阶初值换算方波半周期,循环翻转蜂鸣器IO口生成对应频率声波,播放同时驱动一次流水灯移位。
-
PlayMusic(uint music\[\]2) 整首乐曲播放函数:遍历预设乐曲乐谱数组,逐行读取音符和节拍参数,循环调用单音符播放函数,直至遍历休止结束符,完成整首乐曲自动播放。
9.5 主函数 main(void)
程序入口函数,完成硬件外设初始化、上电提示输出,通过死循环持续监听串口指令,根据接收的字符匹配对应乐曲播放功能,是整个程序的逻辑调度核心。
10. 实验与使用注意事项
10.1 硬件接线注意事项
-
严格按照指定引脚接线,禁止串口引脚(P3.0、P3.1)、LED引脚(P1.0~P1.7)、蜂鸣器引脚(P2.0)错接、虚接,否则会出现通信失败、灯光不亮、无发声等问题。
-
LED为低电平点亮模式,切勿直接短路LED引脚与电源,避免烧毁单片机IO口。
-
CH340模块需正常安装驱动,否则电脑无法识别串口,无法下发控制指令。
10.2 串口调试注意事项
-
串口调试助手参数必须严格匹配:波特率9600、8位数据位、1位停止位、无校验位、发送单字符,参数错误会导致指令失效、乱码。
-
指令区分大小写,仅大写A~E可触发对应功能,小写字母、数字、符号等均为无效指令,设备无响应。
-
乐曲播放过程中可随时下发新指令,程序会立即终止当前任务并响应新指令,无指令缓存堆积问题。
10.3 程序适配与修改注意事项
-
程序延时、音符初值基于11.0592MHz晶振适配,更换其他频率晶振会导致音准偏移、流水灯节奏错乱,需重新校准延时和音符参数。
-
新增乐曲需严格按照「音符编号+节拍时长」的二维数组格式编写,末尾必须添加{REST,0}结束标识,否则会出现程序卡死、乱播问题。
-
禁止修改串口中断、定时器1核心配置,修改后会导致串口通信异常、功能失效。
10.4 故障排查要点
-
上电无任何提示、无响应:检查CH340驱动、串口接线、单片机供电是否正常。
-
有声音无灯光/有灯光无声音:对应排查LED接线、蜂鸣器接线及硬件模块是否损坏。
-
音乐节奏错乱、音准偏差:确认晶振型号,检查是否误修改延时函数、音符数据表。
-
指令下发无响应:核对串口调试助手参数、指令大小写,排查串口中断配置。
11. 程序时序与运行逻辑详解
11.1 整体运行时序
程序采用中断接收、主循环执行的架构,串口接收全程异步独立,不会被延时、音乐播放、灯光动画阻塞。设备上电后一次性完成外设初始化,输出就绪字符串,随后进入无限循环等待指令。
当上位机下发有效指令(A~E),串口中断立即捕获数据、缓存并置位标志位。主循环检测到标志位后,立即清除残留灯光、解析指令、调用对应乐曲播放函数。乐曲播放为阻塞式执行,播放期间全程同步刷新流水灯,整首乐曲播放结束后,程序重新回到待机监听状态。
11.2 音光联动时序逻辑
本程序核心特色为单音符绑定单次灯光移位,时序严格闭环:系统读取单音符参数 → 换算发声频率 → 蜂鸣器方波发声延时 → 单次流水灯移位刷新 → 进入下一个音符。节拍时长决定灯光停留时间,完美匹配音乐节奏,无超前、滞后、错位现象。
休止符状态下,蜂鸣器静音延时,同时依然执行灯光移位,保证整首乐曲灯光动画连贯,不会出现停顿断帧问题。
12. 全局变量与宏定义说明
12.1 类型重定义
-
uchar:unsigned char 无符号字符型,用于串口数据、数组下标、状态标记,节省内存空间。
-
uint:unsigned int 无符号整型,用于延时参数、音符参数、计数变量,适配数值范围更大。
12.2 核心宏定义
-
BAUD_9600_TH 0xFA:11.0592MHz晶振下,定时器1自动重装初值,精准匹配9600波特率。
-
UART_INT_NUM 4:51单片机串口中断号,固定对应串口中断服务函数。
-
REST 0:休止符标识,用于标记乐曲静音停顿节点。
12.3 全局变量功能
-
uint ny:流水灯移位计数变量,记录当前点亮LED位置,循环0~7实现8路流水切换。
-
uint beat_delay:音符节拍缓存变量,存储当前音符播放时长。
-
uint timer_val:音符定时器初值缓存,用于换算方波半周期,保障音准。
-
uchar uart_rec_buf:串口数据接收缓冲区,存储单字节指令字符。
-
bit rec_flag:串口接收完成标志位,中断置1、主循环清零,实现中断与主程序数据同步。
12.4 常量数据表说明
-
NOTE_TABLE\[\] 音符频率表:存储所有音阶对应的定时器初值,包含低音、中音、高音、休止符,是蜂鸣器精准发声的核心数据。
-
music_star、music_joy等乐曲数组:二维乐谱数组,第一列为音符编号、第二列为节拍时长,末尾以{REST,0}作为乐曲结束标识。
13. 二次开发与功能拓展指南
13.1 新增自定义乐曲方法
用户可自主添加任意乐曲,需严格遵循程序数组规范:新建二维uint数组,按照「音符编号、节拍时长」逐行编写乐谱,乐曲最后一行必须添加**{REST,0}**结束符,防止数组遍历越界、程序卡死。完成乐谱编写后,在主函数switch分支中新增对应指令,绑定自定义乐曲播放函数即可。
13.2 修改灯光动画效果
默认灯光为单向流水灯,可直接修改 LED_RunningWater() 函数实现拓展,支持修改为双向流水、频闪、呼吸灯、逐亮逐灭等效果。只需保留函数调用接口不变,即可保证音光联动逻辑不受影响,适配所有乐曲播放场景。
13.3 调整播放速度与音高
-
调速:修改乐谱数组内第二个节拍参数,数值越大播放越慢,数值越小节奏越快。
-
调音高:替换NOTE_TABLE对应音阶初值,或更换乐谱内音符编号,可整体调整乐曲音调高低。
13.4 拓展多指令、多场景功能
程序框架完全模块化,可自由拓展功能:新增F、G、H等自定义指令,绑定灯光特效、不同乐曲、独立延时模式;可添加按键中断、定时器中断,实现按键切换曲目、定时启停等复合功能。
14. 整体程序优缺点总结
14.1 优点
-
架构清晰,模块化分层设计,代码可读性、可移植性极强,适合新手学习与二次开发。
-
串口异步接收,响应速度快,支持播放中实时切歌,无指令延迟、数据堆积问题。
-
音光联动逻辑精准,节拍、灯光同步度高,动态效果流畅稳定。
-
参数精准适配11.0592MHz通用晶振,普中开发板直接烧录即可运行,无需修改参数。
-
交互完善,上电提示、指令反馈齐全,调试、实验、演示效果良好。
14.2 可优化点
-
乐曲播放为阻塞式执行,播放过程中无法后台响应多指令,仅支持单曲目完整播放。
-
采用软件延时实现发声与节拍,占用CPU资源,无法同时执行多任务。
-
仅支持单字符指令触发,暂不支持字符串指令、参数配置等高级功能。
15. 版本说明
-
程序版本V1.0
-
适配平台:普中51单片机开发板、标准51内核单片机
-
编译环境:Keil C51
-
功能状态:完整可用,无BUG,可直接用于课程实验、课程设计、演示展示
cpp
/**************************************************************************************
深圳市普中科技有限公司(PRECHIN 普中)
技术支持:www.prechin.net
实验名称:串口指令控制8路LED
晶振:11.0592MHz
波特率:9600
接线:CH340 UTX-P30,URX-P31;LED1~LED8接P1^7~P1^0,低电平点亮
指令说明:
发送字符 A → 播放小星星并流水灯循环
发送字符 B → 播放欢乐颂并流水灯循环
发送字符 C → 播放两只老虎并流水灯循环
发送字符 D → 播放生日快乐并流水灯循环
发送字符 E → 播放茉莉花并流水灯循环
发送其他字符:全部LED熄灭
***************************************************************************************/
#include "reg52.h"
// 类型重定义
typedef unsigned char uchar;
typedef unsigned int uint;
// 系统参数宏定义
#define BAUD_9600_TH 0xFA
#define UART_INT_NUM 4
#define REST 0
uint ny=0;
uint beat_delay;
uint timer_val;
// 蜂鸣器IO定义
sbit BEEP = P2^0;
// LED引脚定义 P1.7~P1.0 对应LED1~LED8
sbit LED1 = P1^7;
sbit LED2 = P1^6;
sbit LED3 = P1^5;
sbit LED4 = P1^4;
sbit LED5 = P1^3;
sbit LED6 = P1^2;
sbit LED7 = P1^1;
sbit LED8 = P1^0;
// 定时器1初值表(11.0592MHz,仅用来换算半周期延时)
uint code NOTE_TABLE[] = {
0, // 0:休止符REST
0xF895, // 低音1
0xF960, // 低音2
0xFA09, // 低音3
0xFA90, // 低音4
0xFB00, // 低音5
0xFB68, // 低音6
0xFBC7, // 低音7
0xFC0B, // 中音1
0xFC58, // 中音2
0xFC9C, // 中音3
0xFCCF, // 中音4
0xFCFF, // 中音5
0xFD2E, // 中音6
0xFD59, // 中音7
0xFD7F, // 高音1
0xFDA3, // 高音2
0xFDC4, // 高音3
0xFDDD, // 高音4
0xFDF4, // 高音5
0xFE08, // 高音6
0xFE1A // 高音7
};
// 1.小星星
uint code music_star[][2] = {
{8,200}, {8,200}, {15,200}, {15,200},
{17,200}, {17,200}, {15,400},
{13,200}, {13,200}, {12,200}, {12,200},
{10,200}, {10,200}, {8,400},
{15,200}, {15,200}, {13,200}, {13,200},
{12,200}, {12,200}, {10,400},
{15,200}, {15,200}, {13,200}, {13,200},
{12,200}, {12,200}, {10,400},
{8,200}, {8,200}, {15,200}, {15,200},
{17,200}, {17,200}, {15,400},
{13,200}, {13,200}, {12,200}, {12,200},
{10,200}, {10,200}, {8,400},
{REST,0}
};
// 2.欢乐颂
uint code music_joy[][2] = {
{10,200},{10,200},{12,200},{13,200},
{13,200},{12,200},{10,200},{8,200},
{8,200},{10,200},{10,200},{REST,200},
{10,200},{10,200},{12,200},{13,200},
{13,200},{12,200},{10,200},{8,200},
{8,200},{10,200},{8,200},{REST,200},
{12,200},{12,200},{13,200},{15,200},
{15,200},{13,200},{12,200},{10,200},
{10,200},{12,200},{10,200},{8,200},
{REST,0}
};
// 3.两只老虎
uint code music_tiger[][2] = {
{8,200},{10,200},{12,200},{8,200},
{8,200},{10,200},{12,200},{8,200},
{12,200},{13,200},{15,400},
{12,200},{13,200},{15,400},
{15,100},{16,100},{15,100},{13,100},{12,200},{8,200},
{15,100},{16,100},{15,100},{13,100},{12,200},{8,200},
{8,200},{5,200},{8,400},
{8,200},{5,200},{8,400},
{REST,0}
};
// 4.生日快乐
uint code music_birth[][2] = {
{13,200},{13,100},{15,300},{13,300},{16,300},
{15,400},
{13,200},{13,100},{15,300},{13,300},{17,300},
{16,400},
{REST,200},{10,200},{10,100},{13,300},{12,300},{15,300},
{13,400},
{REST,200},{10,200},{10,100},{13,300},{12,300},{15,300},
{13,400},
{REST,0}
};
// 5.茉莉花(简化完整版)
uint code music_jasmine[][2] = {
{13,300},{10,200},{8,200},{10,200},{12,400},
{10,200},{8,200},{REST,200},
{13,300},{10,200},{8,200},{10,200},{12,400},
{10,200},{8,400},
{12,200},{13,200},{15,400},{13,200},{12,200},
{10,400},{8,200},{REST,200},
{12,200},{13,200},{15,400},{13,200},{12,200},
{10,400},{8,400},
{REST,0}
};
// 全局变量
uchar uart_rec_buf; // 接收缓存
bit rec_flag = 0; // 接收完成标志
// 毫秒延时 11.0592MHz
void DelayMs(uint ms)
{
uint i,j;
for(i=ms; i>0; i--)
for(j=110; j>0; j--);
}
// 微秒级短延时,用于生成音符方波
void DelayUs(uint us)
{
uint i;
for(i=us; i>0; i--);
}
/****************************
* @brief 串口发送单个字节
****************************/
void UART_SendByte(uchar dat)
{
SBUF = dat;
while(TI == 0);
TI = 0;
}
/****************************
* @brief 串口发送字符串
****************************/
void UART_SendString(uchar *str)
{
while(*str != '\0')
{
UART_SendByte(*str++);
}
}
/****************************
* @brief 串口初始化 9600波特率
****************************/
void UART_Init(uchar th_val)
{
TMOD |= 0x20;
SCON = 0x50;
PCON |= 0x80;
TH1 = th_val;
TL1 = th_val;
ES = 1;
EA = 1;
TR1 = 1;
}
/****************************
* @brief 关闭全部LED
****************************/
void LED_AllOff(void)
{
LED1 = 1;
LED2 = 1;
LED3 = 1;
LED4 = 1;
LED5 = 1;
LED6 = 1;
LED7 = 1;
LED8 = 1;
}
void LED_RunningWater()
{
LED_AllOff();
switch(ny)
{
case 0: LED1 = 0; break;
case 1: LED2 = 0; break;
case 2: LED3 = 0; break;
case 3: LED4 = 0; break;
case 4: LED5 = 0; break;
case 5: LED6 = 0; break;
case 6: LED7 = 0; break;
case 7: LED8 = 0; break;
}
ny=(ny+1)%8;
}
/****************************
* @brief 串口中断服务函数
* 仅缓存数据+置标志,不做延时与LED操作
****************************/
void UART_ISR() interrupt UART_INT_NUM
{
if(RI == 1)
{
RI = 0;
uart_rec_buf = SBUF;
rec_flag = 1; // 标记收到新数据
}
}
// 播放单音符 - 纯软件翻转,无中断
void PlayNote(uint note, uint beat)
{
uint total_time = beat;
uint half_period;
beat_delay = beat;
// 休止符直接静音延时
if(note == REST)
{
BEEP = 1;
DelayMs(total_time);
return;
}
// 根据定时初值换算半周期延时
timer_val = NOTE_TABLE[note];
half_period = 256 - (timer_val & 0xFF);
// 在节拍总时长内循环翻转蜂鸣器输出方波
while(total_time > 0)
{
BEEP = 0;
DelayUs(half_period);
BEEP = 1;
DelayUs(half_period);
total_time--;
}
BEEP = 1;
LED_RunningWater();
}
// 播放整首乐曲
void PlayMusic(uint music[][2])
{
uchar i = 0;
while(music[i][0] != REST || music[i][1] != 0)
{
PlayNote(music[i][0], music[i][1]);
i++;
}
}
/****************************
* @brief 主函数
****************************/
void main(void)
{
//uchar i;
uchar cmd;
UART_Init(BAUD_9600_TH);
UART_SendString("UART Ready! Send A~E to play music with running LED\r\n");
LED_AllOff(); // 上电所有灯熄灭
while(1)
{
if(rec_flag == 1)
{
rec_flag = 0; // 清除接收标志
cmd = uart_rec_buf;
LED_AllOff(); // 每次指令先熄灭所有灯
switch(cmd)
{
// 乐曲播放指令,替换单灯点亮为流水灯循环一次
case 'A':
UART_SendString("Play:小星星, LED流水灯运行\r\n");
//LED_RunningWater();
PlayMusic(music_star);
break;
case 'B':
UART_SendString("Play:欢乐颂, LED流水灯运行\r\n");
// LED_RunningWater();
PlayMusic(music_joy);
break;
case 'C':
UART_SendString("Play:两只老虎, LED流水灯运行\r\n");
// LED_RunningWater();
PlayMusic(music_tiger);
break;
case 'D':
UART_SendString("Play:生日快乐, LED流水灯运行\r\n");
//LED_RunningWater();
PlayMusic(music_birth);
break;
case 'E':
UART_SendString("Play:茉莉花, LED流水灯运行\r\n");
//LED_RunningWater();
PlayMusic(music_jasmine);
break;
default: break; // 其他字符保持全灭
}
}
}
}