用STM32F103的PWM和定时器,让无源蜂鸣器唱出《两只老虎》

张开发
2026/4/13 15:09:42 15 分钟阅读

分享文章

用STM32F103的PWM和定时器,让无源蜂鸣器唱出《两只老虎》
用STM32F103的PWM和定时器实现《两只老虎》音乐盒记得第一次听到电子设备播放音乐时那种神奇的感觉至今难忘。作为一个嵌入式开发者用单片机播放音乐不仅是技术实践更是一种情怀。本文将带你用STM32F103的PWM和定时器让无源蜂鸣器唱出经典的《两只老虎》从乐理到代码实现完整解析。1. 硬件基础与原理1.1 无源蜂鸣器的工作原理无源蜂鸣器与有源蜂鸣器的核心区别在于内部是否集成振荡电路特性无源蜂鸣器有源蜂鸣器驱动方式需要外部PWM信号直流电压即可音调控制可调频率能演奏音乐固定频率价格相对便宜稍贵应用场景音乐播放、报警音简单提示音无源蜂鸣器本质上是一个电磁线圈振动膜结构当输入方波信号时线圈会产生交变磁场带动振动膜周期性振动发声。振动频率与输入方波频率一致这就是我们能用它演奏音乐的基础。1.2 STM32的PWM生成机制STM32的定时器模块非常灵活以TIM4为例PWM生成涉及几个关键参数时钟源通常使用内部72MHz时钟预分频器(Prescaler)降低定时器时钟频率自动重装载值(ARR)决定PWM周期比较寄存器(CCR)决定PWM占空比PWM频率计算公式PWM频率 定时器时钟 / ((Prescaler 1) * (ARR 1))在代码中我们使用HAL库配置TIM4的CH2通道输出PWMhtim4.Instance TIM4; htim4.Init.Prescaler 9-1; // 72MHz / 9 8MHz htim4.Init.CounterMode TIM_COUNTERMODE_UP; htim4.Init.Period 65535-1; // ARR值 htim4.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(htim4);2. 从乐谱到代码的转换2.1 音乐频率的数学基础《两只老虎》主要使用C大调音阶各音符对应频率如下音符频率(Hz)简谱表示C4261.631D4293.662E4329.633F4349.234G4392.005A4440.006在代码中我们定义频率数组uint16_t C_FREQ[] {0,262,294,330,349,392,440,494,523}; // 0为休止符1-8对应简谱1-7和高音12.2 歌曲编码实现《两只老虎》的简谱转换为两个数组音符序列记录每个音符的音高节拍序列记录每个音符的持续时间/* 频率序列 */ uint8_t music_two_tiger_f[] { 1,2,3,1, 1,2,3,1, 3,4,5, 3,4,5, 5,6,5,4,3,1, 5,6,5,4,3,1, 1,5,1, 1,5,1 }; /* 节拍序列 */ uint8_t music_two_tiger_t[] { P_1,P_1,P_1,P_1, P_1,P_1,P_1,P_1, P_1,P_1,P_2, P_1,P_1,P_2, P_4_3,P_4_1,P_4_3,P_4_1,P_1,P_1, P_4_3,P_4_1,P_4_3,P_4_1,P_1,P_1, P_1,P_1,P_2, P_1,P_1,P_2 };其中节拍定义#define P_1 1 // 1拍 #define P_2 2 // 2拍 #define P_4_1 1 // 1/4拍 #define P_4_3 3 // 3/4拍3. 系统设计与实现3.1 硬件连接使用STM32F103C8T6最小系统板连接方式PB7 → 蜂鸣器正极蜂鸣器负极 → GND建议串联一个100Ω限流电阻注意无源蜂鸣器有正负极之分接反会导致音量减小3.2 软件架构系统采用双定时器协同工作TIM4产生PWM波控制音高TIM21ms中断控制节拍核心代码逻辑void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim-Instance TIM2) { // 节拍定时器 static uint32_t tick 0; if(tick current_note_duration) { tick 0; play_next_note(); // 切换到下一个音符 } } } void play_next_note() { current_note music_two_tiger_f[note_index]; current_note_duration music_two_tiger_t[note_index] * base_tempo; if(current_note 0) { // 休止符 __HAL_TIM_SET_COMPARE(htim4, TIM_CHANNEL_2, 0); } else { uint16_t arr (8000000 / C_FREQ[current_note]) - 1; __HAL_TIM_SET_AUTORELOAD(htim4, arr); __HAL_TIM_SET_COMPARE(htim4, TIM_CHANNEL_2, arr/2); // 50%占空比 } note_index (note_index 1) % music_length; }3.3 定时器配置关键参数TIM2配置节拍控制htim2.Instance TIM2; htim2.Init.Prescaler 7200-1; // 72MHz/7200 10kHz htim2.Init.Period 10-1; // 10kHz/10 1kHz (1ms) HAL_TIM_Base_Start_IT(htim2);TIM4配置PWM生成htim4.Instance TIM4; htim4.Init.Prescaler 9-1; // 72MHz/9 8MHz htim4.Init.Period 65535-1; // 初始ARR HAL_TIM_PWM_Start(htim4, TIM_CHANNEL_2);4. 调音技巧与优化4.1 音准校准方法使用手机调音器APP监测蜂鸣器发出的音高微调C_FREQ数组中的频率值常见问题高音偏高减小ARR计算时的基准频率低音偏闷检查蜂鸣器共振特性实测优化后的频率表uint16_t C_FREQ[] {0,262,294,330,350,392,440,495,525}; // 调整了4(F4)和7(B4)的音高4.2 节奏感优化影响节奏感的三个因素基准节拍时长music_two_tiger_t_echo值建议值120-180ms对应每分钟100-80拍中断优先级确保TIM2中断不被阻塞HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);音符过渡处理在切换音符时重置计数器__HAL_TIM_SET_COUNTER(htim4, 0);4.3 扩展功能实现多歌曲管理系统struct MUSIC_T { uint8_t *f; // 频率数组指针 uint8_t *t; // 节拍数组指针 uint16_t len; // 数组长度 uint16_t t_each; // 基准节拍(ms) uint8_t *name; // 歌曲名称 }; struct MUSIC_T music_t; music_t.f music_two_tiger_f; music_t.t music_two_tiger_t; music_t.len sizeof(music_two_tiger_f)/sizeof(uint8_t); music_t.t_each 150; // 1/4拍150ms music_t.name Two Tigers;音量控制void set_volume(uint8_t vol) { // vol: 0-100 uint16_t pulse (ARR * vol) / 100; __HAL_TIM_SET_COMPARE(htim4, TIM_CHANNEL_2, pulse); }在项目调试过程中最令人惊喜的时刻莫过于第一次听到蜂鸣器完整奏响《两只老虎》的瞬间。那种将理论知识转化为实际成果的成就感正是嵌入式开发的魅力所在。建议初学者可以尝试修改乐谱数组创造属于自己的单片机音乐作品。

更多文章