利用TIM模块演奏音乐
大致思路
- 利用一个定时器输出
PWM波控制蜂鸣器音调,另一个定时器模块控制播放的节奏
准备部分
设计电路
准备材料
- 导线若干,稳压二极管,无源蜂鸣器
设计电路图
- 将蜂鸣器按照极性接分别接在信号源和接地端
- 同时在蜂鸣器侧反向并联一个稳压二极管,防止反向电流损坏
GPIO管脚
如图所示
初始化硬件
配置时钟
- 使能RCC,改为高速外部晶振
![img]()
- 配置时钟树
![img]()
配置TIM模块
- 利用
TIM3控制节奏,使能TIM3的中断
![img]()
- 为方便后续控制节奏,将
TIM3的预分频改为\(16800 - 1\),向上计数
![img]()
- 利用
TIM4输出PWM波控制音调 - 将预分频器调为\(84 - 1\),允许“自动重载预装载值”,向上计数
![img]()
搭建电路
- 已知
TIM4的1通道为PB6,
![img]()
- 在原理图中找到
PB6的位置
![img]()
- 再在开发板上找到该管脚的实际位置
![img]()
- 根据先前的电路图在面包板上进行连接
![img]()
代码部分
思路
流程图
- 主函数
![img]()
- 中断
![img]()
存储乐谱
这里选用《春日影》前8小节
- 使用环状链表的方式存储乐谱,
- 值域为音符的时值和音调
- 指针域为下一节点的索引值
如图所示
计算数据
时值
- 已知
TIM3的预分频值为\(16800 - 1\),计数器向上计数一次需要\(\frac{1}{\frac{84 \times 10^6}{16800}} = 0.0002\)秒 - 所以设全音符的重装载值为\(10000\)
音调
- 已知
TIM4的预分频值为\(84 - 1\),所以计数器频率为\(\frac{84 \times 10^6}{84} = 1 \times 10^6\)Hz - 根据十二平均律,将音符的音名对应的重装载值在
main.h中进行宏定义
/* USER CODE BEGIN Private defines */
#define C3 7633
#define D3 6802
#define E3 6060
#define F3 5713
#define G3 5101
#define A3 4544
#define B3 4048
#define C4 3816
#define D4 3400
#define E4 3029
#define F4 2864
#define G4 2550
#define A4 2272
#define B4 2023
#define C5 1911
#define D5 1703
#define E5 1516
#define F5 1432
#define G5 1275
#define A5 1135
#define B5 1011
#define C6 850
#define E6 757
#define F6 715
#define G6 637
#define A6 567
#define B6 505
/* USER CODE END Private defines */
设计链表
- 新建头文件
music_node.h,对结构体进行定义
#ifndef MUSIC_NODE_H
#define MUSIC_NODE_H
typedef struct{int len;//存储时值的重装载值int tone;//存储音调int next_node;//存储指针域,即下一节点索引值
}music;
#endif
初始化音乐播放
- 在
stm32f4xx_it.c中定义如下变量
/* USER CODE BEGIN PV */
music my_music[34];//存储乐谱的环状链表
int my_index;//链表索引值
extern TIM_HandleTypeDef htim4;//外部声明TIM4的句柄
/* USER CODE END PV */
- 在
stm32f4xx_it.h内声明初始化音乐播放的函数
/* USER CODE BEGIN EFP */
void music_init(void);
/* USER CODE END EFP */
- 在
stm32f4xx_it.c中定义该函数
/* USER CODE BEGIN 1 */
void music_init(void){my_index = 0;//初始化索引值/********对节点进行初始化*******/my_music[0].len = 2500 - 1;my_music[0].tone = E5;my_music[0].next_node = 1;my_music[1].len = 1250 - 1;my_music[1].tone = D5;my_music[1].next_node = 2;my_music[2].len = 2500 - 1;my_music[2].tone = C5;my_music[2].next_node = 3;my_music[3].len = 1250 - 1;my_music[3].tone = D5;my_music[3].next_node = 4;my_music[4].len = 1875 - 1;my_music[4].tone = E5;my_music[4].next_node = 5;my_music[5].len = 625 - 1;my_music[5].tone = F5;my_music[5].next_node = 6;my_music[6].len = 1250 - 1;my_music[6].tone = E5;my_music[6].next_node = 7;my_music[7].len = 3750 - 1;my_music[7].tone = D5;my_music[7].next_node = 8;my_music[8].len = 2500 - 1;my_music[8].tone = E5;my_music[8].next_node = 9;my_music[9].len = 1250 - 1;my_music[9].tone = D5;my_music[9].next_node = 10;my_music[10].len = 2500 - 1;my_music[10].tone = E5;my_music[10].next_node = 11;my_music[11].len = 1250 - 1;my_music[11].tone = D5;my_music[11].next_node = 12;my_music[12].len = 1875 - 1;my_music[12].tone = E5;my_music[12].next_node = 13;my_music[13].len = 625 - 1;my_music[13].tone = F5;my_music[13].next_node = 14;my_music[14].len = 1250 - 1;my_music[14].tone = E5;my_music[14].next_node = 15;my_music[15].len = 3750 - 1;my_music[15].tone = D5;my_music[15].next_node = 16;my_music[16].len = 2500 - 1;my_music[16].tone = E5;my_music[16].next_node = 17;my_music[17].len = 1250 - 1;my_music[17].tone = D5;my_music[17].next_node = 18;my_music[18].len = 2500 - 1;my_music[18].tone = C5;my_music[18].next_node = 19;my_music[19].len = 1250 - 1;my_music[19].tone = D5;my_music[19].next_node = 20;my_music[20].len = 1875 - 1;my_music[20].tone = E5;my_music[20].next_node = 21;my_music[21].len = 625 - 1;my_music[21].tone = F5;my_music[21].next_node = 22;my_music[22].len = 1250 - 1;my_music[22].tone = E5;my_music[22].next_node = 23;my_music[23].len = 3750 - 1;my_music[23].tone = D5;my_music[23].next_node = 24;my_music[24].len = 2500 - 1;my_music[24].tone = E5;my_music[24].next_node = 25;my_music[25].len = 1250 - 1;my_music[25].tone = D5;my_music[25].next_node = 26;my_music[26].len = 2500 - 1;my_music[26].tone = C5;my_music[26].next_node = 27;my_music[27].len = 1250 - 1;my_music[27].tone = D5;my_music[27].next_node = 28;my_music[28].len = 1875 - 1;my_music[28].tone = E5;my_music[28].next_node = 29;my_music[29].len = 625 - 1;my_music[29].tone = F5;my_music[29].next_node = 30;my_music[30].len = 1250 - 1;my_music[30].tone = E5;my_music[30].next_node = 31;my_music[31].len = 2500 - 1;my_music[31].tone = D5;my_music[31].next_node = 32;my_music[32].len = 312;my_music[32].tone = C4;my_music[32].next_node = 33;my_music[33].len = 312;my_music[33].tone = D4;my_music[33].next_node = 0;/**************************/HAL_TIM_Base_Start_IT(&htim3);//开启TIM3的中断功能HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);//开启TIM4的PWM输出__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, my_music[0].len / 2);//将比较值设为时值对应重装载值的一半
}
/* USER CODE END 1 */
主程序部分
- 在主函数内调用
music_init()函数,完成音乐播放的初始化
/* USER CODE BEGIN 2 */HAL_Delay(999);//开机一秒后再播放音乐music_init();/* USER CODE END 2 */
实验效果
- 开机一秒后循环播放《春日影》前8个小节的音乐













