STM32F103 蜂鸣器音乐播放系统

发布时间:2026/7/16 20:26:22
STM32F103 蜂鸣器音乐播放系统 一、项目概述1.1 项目功能基于 STM32F10x 标准库开发仅保留PB8 有源蜂鸣器一套发声驱动搭配 PB0~PB7 八路 LED 流水灯、USART1 串口指令控制系统。内置 5 首乐谱小星星、欢乐颂、两只老虎、生日快乐、茉莉花串口发送大写 A~E 对应播放不同曲目播放时 LED 同步流水跑动SysTick 提供精准微秒 / 毫秒延时生成不同频率方波驱动蜂鸣器发声串口中断接收指令非阻塞主循环检测播放曲目串口回显提示。1.2 硬件资源分配表格外设引脚功能说明LED 阵列PB0~PB7推挽输出低电平点亮播放音乐同步流水有源蜂鸣器PB8标准库 GPIO 驱动高低电平翻转输出方波串口 USART1PA9(TX)/PA10(RX)9600 波特中断接收控制指令系统定时器SysTick提供 us/ms 延时音符频率控制1.3 系统时钟系统主频 72MHzSysTick 初始化配置 72 分频延时函数时序精准匹配音符周期。二、宏与全局变量说明2.1 类型重定义c运行typedef unsigned char uchar; typedef unsigned int uint;简化无符号字符、无符号整型书写适配单片机常用变量。2.2 功能宏c运行#define REST 0 // 休止符标记数组下标0代表静音 // IO端口宏 #define LED_RCC RCC_APB2Periph_GPIOB #define LED_PORT GPIOB #define BEEP8_PIN GPIO_Pin_8 #define BEEP8_PORT GPIOB #define BEEP8_RCC RCC_APB2Periph_GPIOB统一管理硬件引脚修改硬件仅需改动宏定义移植性强。2.3 全局变量uint ny0流水灯索引0~7 循环切换点亮 LEDuint beat_delay缓存音符持续时长uint timer_val缓存音符定时参数用于计算方波半周期uchar uart_rec_buf串口单字节接收缓存u8 rec_flag 0串口接收完成标志位1 代表收到有效指令。2.4 音符定时表NOTE_TABLE[]数组下标对应音符编号数组存储定时寄存器初值用于计算方波半周期下标 0休止符1~7低音 1~78~14中音 1~715~21高音 1~7计算公式half_period 256 - (timer_val 0xFF)半周期延时后翻转 IO 生成音调。2.5 乐谱数组格式所有乐曲统一二维数组uint music_xxx[][2]第一列音符编号REST0 为休止第二列音符持续延时时长末尾固定{REST,0}作为乐曲结束判断标记。三、函数模块详细说明3.1 硬件初始化模块3.1.1void LED_BEEP_Init(void)功能初始化 PB0~PB7 LED、PB8 蜂鸣器 GPIO开启 GPIOB 外设时钟PB0~PB7 配置 50MHz 推挽输出上电全部置高熄灭PB8 配置 50MHz 推挽输出上电置高蜂鸣器初始静音。3.1.2SysTick_Init(72)/void Delay_us(uint16_t us)SysTick 滴答定时器初始化与微秒延时函数装载 72 倍分频计数值匹配 72MHz 主频计数完成自动置标志位实现精准微秒延时用于音符方波生成。3.1.3USART1_Init(9600)串口 1 初始化开启接收中断配置 9600 8N1 串口参数。3.2 LED 流水灯模块3.2.1void LED_AllOff(void)将 PB0~PB7 全部置高熄灭所有 LED。3.2.2void LED_RunningWater(void)同步流水灯逻辑每次音符循环调用先熄灭全部 LED根据全局变量ny点亮对应单个 LEDny (ny1)%8实现 0~7 循环流水滚动效果。3.3 蜂鸣器音乐播放模块3.3.1void PlayNote(uint note, uint beat)播放单个音符核心函数判断休止符蜂鸣器置高静音直接延时对应时长读取音符定时参数计算方波半周期循环 beat 次拉低蜂鸣器延时半周期→拉高延时半周期循环中同步刷新流水灯音符播放完毕蜂鸣器置高静音。3.3.2void PlayMusic(uint music[][2])整首乐曲播放函数遍历乐谱二维数组逐字节调用 PlayNote检测{REST,0}终止循环结束播放。3.4 串口通信模块3.4.1void USART1_IRQHandler(void)串口 1 中断服务函数检测接收中断标志位 RXNE读取接收字节存入缓存置位接收标志rec_flag1主循环处理指令。3.4.2void UART_SendString(char *str)串口发送字符串函数循环发送每个字符等待发送完成标志后发送下一字节。3.5 主函数 main (void)程序入口系统运行主逻辑初始化 SysTick、LED 与蜂鸣器、串口串口打印开机提示信息熄灭全部 LED无限 while 循环轮询串口接收标志收到指令后清除标志通过 switch 匹配字符A小星星、B欢乐颂、C两只老虎、D生日快乐、E茉莉花匹配成功串口打印播放曲目调用对应乐曲播放函数其他无效字符直接跳过。四、软件运行流程上电初始化所有外设串口输出就绪提示主循环持续等待串口输入串口发送 A~E 任一字符触发中断缓存数据并置接收标志主循环检测到标志清空标志并解析指令调用对应乐曲播放函数逐音符输出方波驱动蜂鸣器同步流水灯跑动整首乐曲播放完成后返回主循环等待下一条串口指令。五、关键技术说明方波发声原理通过精准 us 延时翻转 IO 高低电平输出固定频率方波驱动有源蜂鸣器不同延时对应不同音调流水同步机制每次方波循环内部调用流水灯函数保证发声与灯光同步串口非阻塞设计采用中断接收主循环仅做判断播放乐曲时阻塞空闲时持续监听串口休止符处理音符编号为 0 时直接静音延时不翻转 IO实现乐曲停顿效果。六、使用操作说串口工具波特率设置 9600数据位 8停止位 1无校验发送大写字符控制播放A小星星B欢乐颂C两只老虎D生日快乐E茉莉花七、代码1. main.c#include stm32f10x.h #include SysTick.h #include usart.h typedef unsigned char uchar; typedef unsigned int uint; #define REST 0 uint ny0; uint beat_delay; uint timer_val; void LED_BEEP_Init(void); void LED_AllOff(void); void LED_RunningWater(void); void PlayNote(uint note, uint beat); void PlayMusic(uint music[][2]); void UART_SendString(char *str); // IO宏定义 #define LED_RCC RCC_APB2Periph_GPIOB #define LED_PORT GPIOB #define BEEP8_PIN GPIO_Pin_8 #define BEEP8_PORT GPIOB #define BEEP8_RCC RCC_APB2Periph_GPIOB // 音符定时表 uint NOTE_TABLE[] { 0, // 0休止符 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 }; // 小星星 uint 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} }; // 欢乐颂 uint 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} }; // 两只老虎 uint 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} }; // 生日快乐 uint 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} }; // 茉莉花 uint 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; u8 rec_flag 0; // LED PB8标准库蜂鸣器初始化 void LED_BEEP_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(LED_RCC, ENABLE); // PB0~PB7 LED推挽输出 GPIO_InitStruct.GPIO_Pin GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3| GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(LED_PORT, GPIO_InitStruct); // PB8蜂鸣器 GPIO_InitStruct.GPIO_Pin BEEP8_PIN; GPIO_InitStruct.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(BEEP8_PORT, GPIO_InitStruct); // 上电LED全灭、蜂鸣器静音 GPIO_SetBits(LED_PORT, GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3| GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7); GPIO_SetBits(BEEP8_PORT, BEEP8_PIN); } // 全部LED熄灭 void LED_AllOff(void) { GPIO_SetBits(LED_PORT, GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3| GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7); } // 流水灯 void LED_RunningWater(void) { LED_AllOff(); switch(ny) { case 0: GPIO_ResetBits(LED_PORT, GPIO_Pin_7); break; case 1: GPIO_ResetBits(LED_PORT, GPIO_Pin_6); break; case 2: GPIO_ResetBits(LED_PORT, GPIO_Pin_5); break; case 3: GPIO_ResetBits(LED_PORT, GPIO_Pin_4); break; case 4: GPIO_ResetBits(LED_PORT, GPIO_Pin_3); break; case 5: GPIO_ResetBits(LED_PORT, GPIO_Pin_2); break; case 6: GPIO_ResetBits(LED_PORT, GPIO_Pin_1); break; case 7: GPIO_ResetBits(LED_PORT, GPIO_Pin_0); break; } ny (ny1)%8; } // PB8标准库蜂鸣器播放音符 void PlayNote(uint note, uint beat) { uint total_time beat; uint half_period; beat_delay beat; if(note REST) { GPIO_SetBits(BEEP8_PORT, BEEP8_PIN); delay_ms(total_time); return; } timer_val NOTE_TABLE[note]; half_period 256 - (timer_val 0xFF); while(total_time 0) { GPIO_ResetBits(BEEP8_PORT, BEEP8_PIN); delay_us(half_period); GPIO_SetBits(BEEP8_PORT, BEEP8_PIN); delay_us(half_period); total_time--; LED_RunningWater(); } GPIO_SetBits(BEEP8_PORT, BEEP8_PIN); } // PB8标准库播放整首乐曲 void PlayMusic(uint music[][2]) { uchar i0; while(music[i][0] ! REST || music[i][1] ! 0) { PlayNote(music[i][0], music[i][1]); i; } } // 串口中断服务函数 void USART1_IRQHandler(void) { uchar dat; if(USART_GetITStatus(USART1, USART_IT_RXNE) ! RESET) { USART_ClearITPendingBit(USART1, USART_IT_RXNE); dat USART_ReceiveData(USART1); uart_rec_buf dat; rec_flag 1; } } // 串口发送字符串 void UART_SendString(char *str) { while(*str ! \0) { USART_SendData(USART1, *str); while(USART_GetFlagStatus(USART1, USART_FLAG_TC) RESET); USART_ClearFlag(USART1, USART_FLAG_TC); } } void Delay_us(uint16_t us) { SysTick-LOAD 72 * us; SysTick-CTRL | 0x05; // 开启时钟计数器 while ((SysTick-CTRL SysTick_CTRL_COUNTFLAG) 0); SysTick-CTRL ~SysTick_CTRL_ENABLE; } int main(void) { uchar cmd; SysTick_Init(72); LED_BEEP_Init(); USART1_Init(9600); UART_SendString(UART Ready!\r\nA~E: PB8蜂鸣器播放乐曲\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:小星星 (PB8)\r\n); PlayMusic(music_star); break; case B: UART_SendString(Play:欢乐颂 (PB8)\r\n); PlayMusic(music_joy); break; case C: UART_SendString(Play:两只老虎 (PB8)\r\n); PlayMusic(music_tiger); break; case D: UART_SendString(Play:生日快乐 (PB8)\r\n); PlayMusic(music_birth); break; case E: UART_SendString(Play:茉莉花 (PB8)\r\n); PlayMusic(music_jasmine); break; default: break; } } } }2. SysTick.c#include SysTick.h static u8 fac_us0; static u16 fac_ms0; void SysTick_Init(u8 SYSCLK) { SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); fac_usSYSCLK/8; fac_ms(u16)fac_us*1000; } void delay_us(u32 nus) { u32 temp; SysTick-LOADnus*fac_us; SysTick-VAL0x00; SysTick-CTRL|SysTick_CTRL_ENABLE_Msk; do { tempSysTick-CTRL; }while((temp0x01)!(temp(116))); SysTick-CTRL~SysTick_CTRL_ENABLE_Msk; SysTick-VAL 0X00; } void delay_ms(u16 nms) { u32 temp; SysTick-LOAD(u32)nms*fac_ms; SysTick-VAL 0x00; SysTick-CTRL|SysTick_CTRL_ENABLE_Msk; do { tempSysTick-CTRL; }while((temp0x01)!(temp(116))); SysTick-CTRL~SysTick_CTRL_ENABLE_Msk; SysTick-VAL 0X00; }3. usart.c#include usart.h /******************************************************************************* * 函 数 名 : USART1_Init * 函数功能 : USART1初始化函数 * 输 入 : bound:波特率 * 输 出 : 无 *******************************************************************************/ void USART1_Init(u32 bound) { //GPIO端口设置 GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); /* 配置GPIO的模式和IO口 */ GPIO_InitStructure.GPIO_PinGPIO_Pin_9;//TX //串口输出PA9 GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz; GPIO_InitStructure.GPIO_ModeGPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOA,GPIO_InitStructure); /* 初始化串口输入IO */ GPIO_InitStructure.GPIO_PinGPIO_Pin_10;//RX //串口输入PA10 GPIO_InitStructure.GPIO_ModeGPIO_Mode_IN_FLOATING; //模拟输入 GPIO_Init(GPIOA,GPIO_InitStructure); /* 初始化GPIO */ //USART1 初始化设置 USART_InitStructure.USART_BaudRate bound;//波特率设置 USART_InitStructure.USART_WordLength USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART1, USART_InitStructure); //初始化串口1 USART_Cmd(USART1, ENABLE); //使能串口1 USART_ClearFlag(USART1, USART_FLAG_TC); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断 //Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel USART1_IRQn;//串口1中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority3;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority 3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; //IRQ通道使能 NVIC_Init(NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、 } /******************************************************************************* * 函 数 名 : USART1_IRQHandler * 函数功能 : USART1中断函数 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ void USART1_IRQHandler1(void) //串口1中断服务程序 { u8 r; if(USART_GetITStatus(USART1, USART_IT_RXNE) ! RESET) //接收中断 { r USART_ReceiveData(USART1);//(USART1-DR); //读取接收到的数据 USART_SendData(USART1,r); while(USART_GetFlagStatus(USART1,USART_FLAG_TC) ! SET); } USART_ClearFlag(USART1,USART_FLAG_TC); }