利用STM32播放音乐
使用STM32F411CC,接上一个无源蜂鸣器,播放音乐。
无源蜂鸣器需要外部输入PWM信号驱动蜂鸣器振动发声。
(1)蜂鸣器接在PB10上。
(2)使用定时器2控制输出PWM。
第一次构思的版本:
1 // 音频参数结构体 2 // 周期:每个音符都有自己的周期,参考https://wenku.baidu.com/view/4d69b296dd88d0d233d46a80.html 3 // 节拍:大概就是每个音符持续多长的时间 4 struct tone_play_data { 5 uint16_t period; 6 uint16_t duration; 7 }; 8 9 struct tone_play_ctrl { 10 uint32_t arrCount; 11 const struct tone_play_data *toneParam; 12 }; 13 14 // 定义一组测试使用的音符 15 // 本来想用另外一个硬件定时器来专门控制节拍时长,以便达到精确控制 16 // 后来想了想就没有这么做了 17 // 现在只采用一个硬件定时器来控制节拍的时长,当让不很精确,不过也差不太远吧 18 #define COUNT_10MS(N) ((N)*10) 19 const struct tone_play_data testTone[] = { 20 {1912, COUNT_10MS(200)}, 21 {1703, COUNT_10MS(200)}, 22 {1517, COUNT_10MS(200)}, 23 {1432, COUNT_10MS(200)}, 24 {1275, COUNT_10MS(200)}, 25 {1136, COUNT_10MS(200)}, 26 {1012, COUNT_10MS(200)}, 27 {955, COUNT_10MS(200)}, 28 {851, COUNT_10MS(200)}, 29 {758, COUNT_10MS(200)}, 30 {751, COUNT_10MS(200)}, 31 {637, COUNT_10MS(200)}, 32 {568, COUNT_10MS(200)}, 33 {508, COUNT_10MS(200)}, 34 {3816, COUNT_10MS(200)}, 35 36 // 注意不要忘记最后的这组元素,用来控制结束播放的 37 {0, 0}, 38 }; 39 40 // 播放控制变量,每次启动播放前必须初始化 41 static struct tone_play_ctrl mController = { 42 .arrCount = 0, 43 .toneParam = &testTone[0], 44 }; 45 46 // 定时器中断回调,要是硬件自己处理该多好啊,这样的效率有点低 47 // 定时器1&8有个RCR(repetition counter register),也许应该换定时器1&8的 48 extern void TIM2_IRQHandler(void) 49 { 50 if (TIM_GetFlagStatus(TIM2, TIM_IT_Update)) 51 { 52 // 进来一次说明经历了ARR时长 53 mController.arrCount ++; 54 55 // duration单位是ms,ARR经过我们的配置后是1us,所以比较时要对duration乘以1000 56 // 这里的音符的时长精度肯定有较大的误差,但是咱们暂不考虑处理这个 57 if ((TIM2->ARR + 1) * mController.arrCount >= mController.toneParam->duration * 1000) 58 { 59 mController.toneParam++; 60 if (0 < mController.toneParam->period) 61 { 62 // 音符切换,必须重置arrCount,重设定时器的ARR寄存器和PWM的CCR寄存器 63 mController.arrCount = 0; 64 TIM_SetAutoreload(TIM2, mController.toneParam->period - 1); 65 TIM_SetCompare3(TIM2, (mController.toneParam->period + 1) / 2); 66 } 67 else 68 { 69 // 播放完毕了就关闭定时器 70 TIM_Cmd(TIM2, DISABLE); 71 } 72 } 73 74 TIM_ClearFlag(TIM2, TIM_IT_Update); 75 } 76 } 77 78 /* 79 STM32F411CC使用的外部晶振16M,所得到的sysclk为100MHZ 80 */ 81 void environment_setup(void) 82 { 83 /* TIMER2, CHANNEL 3, PB10 */ 84 GPIO_InitTypeDef gpioInitData = { 85 GPIO_Pin_10, 86 GPIO_Mode_AF, 87 GPIO_Fast_Speed, 88 GPIO_OType_PP, 89 GPIO_PuPd_UP, 90 }; 91 TIM_TimeBaseInitTypeDef timeData = { 92 .TIM_CounterMode = TIM_CounterMode_Up, 93 .TIM_ClockDivision = TIM_CKD_DIV1, 94 }; 95 TIM_OCInitTypeDef ocdata = { 96 .TIM_OCMode = TIM_OCMode_PWM2, // PWM模式2:CNT>CCR时输出有效 97 .TIM_OutputState = TIM_OutputState_Enable, 98 .TIM_OutputNState = TIM_OutputNState_Enable, // 仅Timer1&8, PWM模式用不上这个 99 .TIM_OCPolarity = TIM_OCPolarity_High, 100 .TIM_OCNPolarity = TIM_OCNPolarity_Low, // PWM模式用不上这个 101 .TIM_OCIdleState = TIM_OCIdleState_Set, // PWM模式用不上这个 102 .TIM_OCNIdleState = TIM_OCNIdleState_Reset, // PWM模式用不上这个 103 }; 104 105 /* 定时器溢出周期, 106 系统主频100MHz不分频(TIM_ClockDivision=TIM_CKD_DIV1)直接给到定时器2作为计数时钟, 107 计数时钟信号100倍分频(TIM_Prescaler=100-1)就是1MHz,一个时钟周期就是1us。 108 所以对计数时钟信号计数n个(TIM_Prescaler=n-1)就达到了n微秒。 109 注意,我们后面主要控制 TIM_Prescaler 来达到音乐频率的控制。 110 这里TIM_Prescaler=99是固定配置,一个计数时钟周期为1us, 111 如果你使用的是72MHz(比如STM32F103),或者TIM_ClockDivision做了分频,要注意更改。 112 */ 113 timeData.TIM_Prescaler = 100 - 1; 114 timeData.TIM_Period = toneParam->period - 1; 115 // PWM占空比50%,就是 TIM_Period 的一半 116 ocdata.TIM_Pulse = (timeData.TIM_Period + 1) / 2; 117 118 // 我们要使用中断 119 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 120 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); 121 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 122 123 // 初始化GPIO以及功能复用 124 GPIO_Init(GPIOB, &gpioInitData); 125 GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_TIM2); 126 127 // 定时器中断分配 128 NVIC_InitTypeDef nvicInitData; 129 nvicInitData.NVIC_IRQChannel = TIM2_IRQn; 130 nvicInitData.NVIC_IRQChannelPreemptionPriority = 2; 131 nvicInitData.NVIC_IRQChannelSubPriority = 2; 132 nvicInitData.NVIC_IRQChannelCmd = ENABLE; 133 NVIC_Init(&nvicInitData); 134 135 TIM_DeInit(TIM2); 136 TIM_TimeBaseInit(TIM2, &timeData); 137 TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 启用中断 138 TIM_OC3Init(TIM2, &ocdata); //TIM2的通道2PWM 模式设置 139 TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable); //使能预装载寄存器 140 TIM_Cmd(TIM2, ENABLE); // 启动定时器 141 } 142 143 /* 144 接口函数,注意形参传值的正确性 145 */ 146 void tone_play(const struct tone_play_data *param) 147 { 148 TIM_Cmd(TIM2, DISABLE); 149 if (NULL == param) 150 return; 151 mController.arrCount = 0; 152 mController.toneParam = param; 153 TIM_SetAutoreload(TIM2, mController.toneParam->period - 1); 154 TIM_SetCompare3(TIM2, (mController.toneParam->period + 1) / 2); 155 TIM_Cmd(TIM2, ENABLE); 156 } 157 158 void tone_stop(void) 159 { 160 TIM_Cmd(TIM2, DISABLE); 161 }
第二次构思的版本:
上面的代码还可以再继续优化一下:
(1)每个音符的频率都是固定的,我们可以弄一个固定的音符表格。
(2)我们设定一个变量,用来设定一个节拍的周期,这样对于1/4,1/8之类的节拍就只要对应的乘除就好。
(3)比上面那种处理方式要节约空间。
1 // 最新编辑:2020/9/24 2 typedef enum { 3 TONE_END = 0, 4 // 好像有个不发音的?保持1KHz的频率 5 TONE_MUTE, 6 TONE_LOW_1, 7 TONE_LOW_2, 8 TONE_LOW_3, 9 TONE_LOW_4, 10 TONE_LOW_5, 11 TONE_LOW_6, 12 TONE_LOW_7, 13 TONE_MIDDLE_1, 14 TONE_MIDDLE_2, 15 TONE_MIDDLE_3, 16 TONE_MIDDLE_4, 17 TONE_MIDDLE_5, 18 TONE_MIDDLE_6, 19 TONE_MIDDLE_7, 20 TONE_HIGH_1, 21 TONE_HIGH_2, 22 TONE_HIGH_3, 23 TONE_HIGH_4, 24 TONE_HIGH_5, 25 TONE_HIGH_6, 26 TONE_HIGH_7, 27 // 将你的音符索引添加到这一行的上面 28 TONE_TOTAL 29 } ToneSymbIdx; 30 31 #define TONE_SYMBOL_END_PERIOD (0) 32 #define TONE_SYMBOL_MUTE_PERIOD (1000) 33 const uint16_t toneSymbols[TONE_TOTAL] = { 34 TONE_SYMBOL_END_PERIOD, 35 TONE_SYMBOL_MUTE_PERIOD, 36 // 低音12345657 37 3816, 3401, 3030, 2865, 2551, 2272, 2024, 38 // 中音12345657 39 1912, 1703, 1517, 1432, 1275, 1136, 1012, 40 // 高音12345657 41 955, 851, 758, 751, 637, 568, 508, 42 }; 43 // 音乐节拍主要分为: 44 // 1/4,2/4,3/4,4/4 45 const uint32_t toneBeatBaseUS = 128000; 46 #define TONE_BEAT_1_4_MULTIPLIER (1) 47 #define TONE_BEAT_2_4_MULTIPLIER (2) 48 #define TONE_BEAT_3_4_MULTIPLIER (4) 49 #define TONE_BEAT_4_4_MULTIPLIER (8) 50 51 struct tone_play_data { 52 ToneSymbIdx symbolIndex; 53 uint8_t beatMultiple; 54 }; 55 56 struct tone_play_ctrl { 57 uint32_t arrCount; 58 const struct tone_play_data *toneParam; 59 }; 60 61 // 相比前面的设计,这样一首歌就可以节省很多空间出来了 62 const struct tone_play_data testTone[] = { 63 {TONE_LOW_1, TONE_BEAT_1_4_MULTIPLIER}, 64 {TONE_LOW_2, TONE_BEAT_2_4_MULTIPLIER}, 65 {TONE_LOW_3, TONE_BEAT_3_4_MULTIPLIER}, 66 {TONE_LOW_4, TONE_BEAT_4_4_MULTIPLIER}, 67 {TONE_LOW_5, TONE_BEAT_1_4_MULTIPLIER}, 68 {TONE_LOW_6, TONE_BEAT_2_4_MULTIPLIER}, 69 {TONE_LOW_7, TONE_BEAT_3_4_MULTIPLIER}, 70 {TONE_MUTE, TONE_BEAT_1_4_MULTIPLIER}, 71 {TONE_MIDDLE_1, TONE_BEAT_1_4_MULTIPLIER}, 72 {TONE_MIDDLE_2, TONE_BEAT_2_4_MULTIPLIER}, 73 {TONE_MIDDLE_3, TONE_BEAT_3_4_MULTIPLIER}, 74 {TONE_MIDDLE_4, TONE_BEAT_4_4_MULTIPLIER}, 75 {TONE_MIDDLE_5, TONE_BEAT_1_4_MULTIPLIER}, 76 {TONE_MIDDLE_6, TONE_BEAT_2_4_MULTIPLIER}, 77 {TONE_MIDDLE_7, TONE_BEAT_3_4_MULTIPLIER}, 78 {TONE_MUTE, TONE_BEAT_1_4_MULTIPLIER}, 79 {TONE_HIGH_1, TONE_BEAT_1_4_MULTIPLIER}, 80 {TONE_HIGH_2, TONE_BEAT_2_4_MULTIPLIER}, 81 {TONE_HIGH_3, TONE_BEAT_3_4_MULTIPLIER}, 82 {TONE_HIGH_4, TONE_BEAT_4_4_MULTIPLIER}, 83 {TONE_HIGH_5, TONE_BEAT_1_4_MULTIPLIER}, 84 {TONE_HIGH_6, TONE_BEAT_3_4_MULTIPLIER}, 85 {TONE_HIGH_7, TONE_BEAT_4_4_MULTIPLIER}, 86 87 // 最后一个元素必须是{0, 0} 88 {TONE_END, 0}, 89 }; 90 91 // 播放控制变量,每次启动播放前必须初始化 92 static struct tone_play_ctrl mController; 93 94 // 定时器中断回调,要是硬件自己处理该多好啊,这样的效率有点低 95 // 定时器1&8有个RCR(repetition counter register),也许应该换定时器1&8的 96 extern void TIM2_IRQHandler(void) 97 { 98 if (TIM_GetFlagStatus(TIM2, TIM_IT_Update)) 99 { 100 // 进来一次说明经历了ARR时长 101 mController.arrCount ++; 102 103 // duration单位是ms,ARR经过我们的配置后是1us,所以比较时要对duration乘以1000 104 // 这里的音符的时长精度肯定有较大的误差,但是咱们暂不考虑处理这个 105 if ((TIM2->ARR + 1) * mController.arrCount >= mController.toneParam->beatMultiple * toneBeatBaseUS) 106 { 107 mController.toneParam++; 108 if (0 < mController.toneParam->beatMultiple && TONE_END < mController.toneParam->symbolIndex) 109 { 110 // 音符切换,必须重置arrCount,重设定时器的ARR寄存器和PWM的CCR寄存器 111 mController.arrCount = 0; 112 TIM_SetAutoreload(TIM2, toneSymbols[mController.toneParam->symbolIndex] - 1); 113 114 // 如果是静音音符,直接控制PWM周期与节拍周期一致就能达到静音了 115 if (TONE_MUTE == mController.toneParam->symbolIndex) 116 TIM_SetCompare3(TIM2, toneSymbols[mController.toneParam->symbolIndex]); 117 else 118 TIM_SetCompare3(TIM2, (toneSymbols[mController.toneParam->symbolIndex] + 1) / 2); 119 } 120 else 121 { 122 // 播放完毕了就关闭定时器 123 TIM_Cmd(TIM2, DISABLE); 124 TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable); 125 } 126 } 127 128 TIM_ClearFlag(TIM2, TIM_IT_Update); 129 } 130 } 131 132 /* 133 STM32F411CC使用的外部晶振16M,所得到的sysclk为100MHZ 134 */ 135 void environment_setup(void) 136 { 137 /* TIMER2, CHANNEL 3, PB10 */ 138 GPIO_InitTypeDef gpioInitData = { 139 GPIO_Pin_10, 140 GPIO_Mode_AF, 141 GPIO_Fast_Speed, 142 GPIO_OType_PP, 143 GPIO_PuPd_UP, 144 }; 145 TIM_TimeBaseInitTypeDef timeData = { 146 .TIM_CounterMode = TIM_CounterMode_Up, 147 .TIM_ClockDivision = TIM_CKD_DIV1, 148 }; 149 TIM_OCInitTypeDef ocdata = { 150 .TIM_OCMode = TIM_OCMode_PWM2, // PWM模式2:CNT>CCR时输出有效 151 .TIM_OutputState = TIM_OutputState_Enable, 152 .TIM_OutputNState = TIM_OutputNState_Enable, // 仅Timer1&8, PWM模式用不上这个 153 .TIM_OCPolarity = TIM_OCPolarity_High, 154 .TIM_OCNPolarity = TIM_OCNPolarity_Low, // PWM模式用不上这个 155 .TIM_OCIdleState = TIM_OCIdleState_Set, // PWM模式用不上这个 156 .TIM_OCNIdleState = TIM_OCNIdleState_Reset, // PWM模式用不上这个 157 }; 158 159 /* 定时器溢出周期, 160 系统主频100MHz不分频(TIM_ClockDivision=TIM_CKD_DIV1)直接给到定时器2作为计数时钟, 161 计数时钟信号100倍分频(TIM_Prescaler=100-1)就是1MHz,一个时钟周期就是1us。 162 所以对计数时钟信号计数n个(TIM_Prescaler=n-1)就达到了n微秒。 163 注意,我们后面主要控制 TIM_Prescaler 来达到音乐频率的控制。 164 这里TIM_Prescaler=99是固定配置,一个计数时钟周期为1us, 165 如果你使用的是72MHz(比如STM32F103),或者TIM_ClockDivision做了分频,要注意更改。 166 */ 167 timeData.TIM_Prescaler = 100 - 1; // 这个值为99,固定不动,如果是72M,则改成71 168 timeData.TIM_Period = 1000 - 1; // 这个999是1ms,后面会动态更改,这里随便写 169 // PWM占空比50%,就是 TIM_Period 的一半 170 ocdata.TIM_Pulse = 500; 171 172 // 我们要使用中断 173 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 174 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); 175 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 176 177 // 初始化GPIO以及功能复用 178 GPIO_Init(GPIOB, &gpioInitData); 179 GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_TIM2); 180 181 // 定时器中断分配 182 NVIC_InitTypeDef nvicInitData; 183 nvicInitData.NVIC_IRQChannel = TIM2_IRQn; 184 nvicInitData.NVIC_IRQChannelPreemptionPriority = 2; 185 nvicInitData.NVIC_IRQChannelSubPriority = 2; 186 nvicInitData.NVIC_IRQChannelCmd = ENABLE; 187 NVIC_Init(&nvicInitData); 188 189 TIM_DeInit(TIM2); 190 TIM_TimeBaseInit(TIM2, &timeData); 191 TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 启用中断 192 TIM_OC3Init(TIM2, &ocdata); //TIM2的通道2PWM 模式设置 193 TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable); //使能预装载寄存器 194 TIM_Cmd(TIM2, DISABLE); // 启动定时器 195 } 196 197 /* 198 接口函数,注意形参传值的正确性 199 */ 200 void tone_play(const struct tone_play_data *param) 201 { 202 if (NULL == param) 203 return; 204 // 如果你有RTOS,这里要注意使用互斥量,保证线程安全 205 TIM_Cmd(TIM2, DISABLE); 206 mController.arrCount = 0; 207 mController.toneParam = param; 208 TIM_SetAutoreload(TIM2, toneSymbols[mController.toneParam->symbolIndex] - 1); 209 TIM_SetCompare3(TIM2, (toneSymbols[mController.toneParam->symbolIndex] + 1) / 2); 210 TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable); 211 TIM_Cmd(TIM2, ENABLE); 212 } 213 214 void tone_stop(void) 215 { 216 // 如果你有RTOS,这里要注意使用互斥量,保证线程安全 217 TIM_Cmd(TIM2, DISABLE); 218 TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable); 219 }
最终版本(源码H和C文件都贴上来了,复制黏贴后就能使用):
1 // H文件内容 2 #if defined(__BEEP_MUSIC_SUPPORT__) 3 typedef enum tonesymbol { 4 TONE_SYMB_END = 0, 5 // 好像有个不发音的?保持1KHz的频率 6 TONE_SYMB_MUTE, 7 TONE_SYMB_LOW_1, 8 TONE_SYMB_LOW_1H, 9 TONE_SYMB_LOW_2, 10 TONE_SYMB_LOW_2H, 11 TONE_SYMB_LOW_3, 12 TONE_SYMB_LOW_3H, 13 TONE_SYMB_LOW_4, 14 TONE_SYMB_LOW_4H, 15 TONE_SYMB_LOW_5, 16 TONE_SYMB_LOW_5H, 17 TONE_SYMB_LOW_6, 18 TONE_SYMB_LOW_6H, 19 TONE_SYMB_LOW_7, 20 TONE_SYMB_LOW_7H, 21 TONE_SYMB_MIDDLE_1, 22 TONE_SYMB_MIDDLE_1H, 23 TONE_SYMB_MIDDLE_2, 24 TONE_SYMB_MIDDLE_2H, 25 TONE_SYMB_MIDDLE_3, 26 TONE_SYMB_MIDDLE_3H, 27 TONE_SYMB_MIDDLE_4, 28 TONE_SYMB_MIDDLE_4H, 29 TONE_SYMB_MIDDLE_5, 30 TONE_SYMB_MIDDLE_5H, 31 TONE_SYMB_MIDDLE_6, 32 TONE_SYMB_MIDDLE_6H, 33 TONE_SYMB_MIDDLE_7, 34 TONE_SYMB_MIDDLE_7H, 35 TONE_SYMB_HIGH_1, 36 TONE_SYMB_HIGH_1H, 37 TONE_SYMB_HIGH_2, 38 TONE_SYMB_HIGH_2H, 39 TONE_SYMB_HIGH_3, 40 TONE_SYMB_HIGH_3H, 41 TONE_SYMB_HIGH_4, 42 TONE_SYMB_HIGH_4H, 43 TONE_SYMB_HIGH_5, 44 TONE_SYMB_HIGH_5H, 45 TONE_SYMB_HIGH_6, 46 TONE_SYMB_HIGH_6H, 47 TONE_SYMB_HIGH_7, 48 TONE_SYMB_HIGH_7H, 49 // 将你的音符索引添加到这一行的上面 50 TONE_TOTAL 51 } ToneSymbIdx; 52 53 typedef enum tonebeat { 54 TONE_BEAT_END = 0, 55 TONE_BEAT_4_1, // 4/1拍 56 TONE_BEAT_3_1, // 3/1拍 57 TONE_BEAT_2_1, // 2/1拍 58 TONE_BEAT_3_2, // 3/2拍 59 TONE_BEAT_1_1, // 1/1拍 60 TONE_BEAT_3_4, // 3/4拍 61 TONE_BEAT_1_2, // 1/2拍 62 TONE_BEAT_1_3, // 1/3拍 63 TONE_BEAT_1_4, // 1/4拍 64 65 TONE_BEAT_TOTAL, 66 } ToneBeatIdx; 67 68 struct tone_play_data { 69 ToneSymbIdx symbolIndex; 70 ToneBeatIdx beatIndex; 71 }; 72 73 extern const struct tone_play_data beep_Du[]; 74 /* 75 // 定义自己的音乐曲谱 76 // const struct tone_play_data beep_Du[] = { 77 // {TONE_SYMB_HIGH_1, TONE_BEAT_1_2}, 78 // // 最后一个元素必须是{0, 0} 79 // {TONE_SYMB_END, TONE_BEAT_END}, 80 // }; 81 */ 82 83 /* 84 接口函数,注意形参传值的正确性 85 */ 86 extern void tone_play(const struct tone_play_data *param); 87 extern void tone_stop(void); 88 #endif 89 90 // C文件内容 91 #if defined(__BEEP_MUSIC_SUPPORT__) // TONE PLAY 92 // 每个音符的周期 93 #define TONE_SYMBOL_END_PERIOD (0) 94 #define TONE_SYMBOL_MUTE_PERIOD (10000) 95 static const uint16_t toneSymbolPeriod[TONE_TOTAL] = { 96 #ifdef ADD_SYMB_VALUE 97 #undef ADD_SYMB_VALUE 98 #endif 99 #define ADD_SYMB_VALUE(E,V) (V) 100 ADD_SYMB_VALUE(TONE_SYMB_END, TONE_SYMBOL_END_PERIOD), 101 ADD_SYMB_VALUE(TONE_SYMB_MUTE, TONE_SYMBOL_MUTE_PERIOD), 102 ADD_SYMB_VALUE(TONE_SYMB_LOW_1, 2865), 103 ADD_SYMB_VALUE(TONE_SYMB_LOW_1H, 2703), 104 ADD_SYMB_VALUE(TONE_SYMB_LOW_2, 2551), 105 ADD_SYMB_VALUE(TONE_SYMB_LOW_2H, 2410), 106 ADD_SYMB_VALUE(TONE_SYMB_LOW_3, 2273), 107 ADD_SYMB_VALUE(TONE_SYMB_LOW_3H, 2155), 108 ADD_SYMB_VALUE(TONE_SYMB_LOW_4, 2024), 109 ADD_SYMB_VALUE(TONE_SYMB_LOW_4H, 1912), 110 ADD_SYMB_VALUE(TONE_SYMB_LOW_5, 1805), 111 ADD_SYMB_VALUE(TONE_SYMB_LOW_5H, 1704), 112 ADD_SYMB_VALUE(TONE_SYMB_LOW_6, 1608), 113 ADD_SYMB_VALUE(TONE_SYMB_LOW_6H, 1517), 114 ADD_SYMB_VALUE(TONE_SYMB_LOW_7, 1000), 115 ADD_SYMB_VALUE(TONE_SYMB_LOW_7H, 1000), 116 ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_1, 1433), 117 ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_1H, 1351), 118 ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_2, 1276), 119 ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_2H, 1205), 120 ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_3, 1137), 121 ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_3H, 1078), 122 ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_4, 1012), 123 ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_4H, 956), 124 ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_5, 903), 125 ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_5H, 852), 126 ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_6, 804), 127 ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_6H, 759), 128 ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_7, 1000), 129 ADD_SYMB_VALUE(TONE_SYMB_MIDDLE_7H, 1000), 130 ADD_SYMB_VALUE(TONE_SYMB_HIGH_1, 716), 131 ADD_SYMB_VALUE(TONE_SYMB_HIGH_1H, 676), 132 ADD_SYMB_VALUE(TONE_SYMB_HIGH_2, 638), 133 ADD_SYMB_VALUE(TONE_SYMB_HIGH_2H, 603), 134 ADD_SYMB_VALUE(TONE_SYMB_HIGH_3, 569), 135 ADD_SYMB_VALUE(TONE_SYMB_HIGH_3H, 539), 136 ADD_SYMB_VALUE(TONE_SYMB_HIGH_4, 506), 137 ADD_SYMB_VALUE(TONE_SYMB_HIGH_4H, 478), 138 ADD_SYMB_VALUE(TONE_SYMB_HIGH_5, 452), 139 ADD_SYMB_VALUE(TONE_SYMB_HIGH_5H, 426), 140 ADD_SYMB_VALUE(TONE_SYMB_HIGH_6, 402), 141 ADD_SYMB_VALUE(TONE_SYMB_HIGH_6H, 379), 142 ADD_SYMB_VALUE(TONE_SYMB_HIGH_7, 1000), 143 ADD_SYMB_VALUE(TONE_SYMB_HIGH_7H, 1000), 144 #undef ADD_SYMB_VALUE 145 }; 146 147 // 每个节拍的周期 148 static const uint16_t toneBeatDuration[TONE_BEAT_TOTAL] = { 149 #ifdef ADD_BEAT_VAL_MS 150 #undef ADD_BEAT_VAL_MS 151 #endif 152 #define ADD_BEAT_VAL_MS(E,V) (V) 153 ADD_BEAT_VAL_MS(TONE_BEAT_END, 0), 154 ADD_BEAT_VAL_MS(TONE_BEAT_4_1, 2000), // 4/1拍 155 ADD_BEAT_VAL_MS(TONE_BEAT_3_1, 3000), // 3/1拍 156 ADD_BEAT_VAL_MS(TONE_BEAT_2_1, 1000), // 2/1拍 157 ADD_BEAT_VAL_MS(TONE_BEAT_3_2, 750), // 3/2拍 158 ADD_BEAT_VAL_MS(TONE_BEAT_1_1, 500), // 1/1拍 159 ADD_BEAT_VAL_MS(TONE_BEAT_3_4, 313), // 3/4拍 160 ADD_BEAT_VAL_MS(TONE_BEAT_1_2, 250), // 1/2拍 161 ADD_BEAT_VAL_MS(TONE_BEAT_1_3, 125), // 1/3拍 162 ADD_BEAT_VAL_MS(TONE_BEAT_1_4, 63), // 1/4拍 163 #undef ADD_BEAT_VAL_MS 164 }; 165 166 struct tone_play_ctrl { 167 uint32_t arrCount; 168 const struct tone_play_data *toneParam; 169 }; 170 171 const struct tone_play_data beep_Du[] = { 172 // 一闪一闪亮晶晶,1=C2/4 173 {TONE_SYMB_HIGH_1, TONE_BEAT_1_3}, 174 // 最后一个元素必须是{0, 0} 175 {TONE_SYMB_END, TONE_BEAT_END}, 176 }; 177 178 /* 179 const struct tone_play_data testTone[] = { 180 // 一闪一闪亮晶晶,1=C2/4 181 {TONE_SYMB_MIDDLE_1, TONE_BEAT_1_2}, 182 {TONE_SYMB_MIDDLE_1, TONE_BEAT_1_2}, 183 {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2}, 184 {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2}, 185 {TONE_SYMB_MIDDLE_6, TONE_BEAT_1_2}, 186 {TONE_SYMB_MIDDLE_6, TONE_BEAT_1_2}, 187 {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_1}, 188 189 {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2}, 190 {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2}, 191 {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2}, 192 {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2}, 193 {TONE_SYMB_MIDDLE_2, TONE_BEAT_1_2}, 194 {TONE_SYMB_MIDDLE_2, TONE_BEAT_1_2}, 195 {TONE_SYMB_MIDDLE_1, TONE_BEAT_1_1}, 196 197 {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2}, 198 {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2}, 199 {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2}, 200 {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2}, 201 {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2}, 202 {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2}, 203 {TONE_SYMB_MIDDLE_2, TONE_BEAT_1_1}, 204 205 {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2}, 206 {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2}, 207 {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2}, 208 {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2}, 209 {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2}, 210 {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2}, 211 {TONE_SYMB_MIDDLE_2, TONE_BEAT_1_1}, 212 213 {TONE_SYMB_MIDDLE_1, TONE_BEAT_1_2}, 214 {TONE_SYMB_MIDDLE_1, TONE_BEAT_1_2}, 215 {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2}, 216 {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_2}, 217 {TONE_SYMB_MIDDLE_6, TONE_BEAT_1_2}, 218 {TONE_SYMB_MIDDLE_6, TONE_BEAT_1_2}, 219 {TONE_SYMB_MIDDLE_5, TONE_BEAT_1_1}, 220 221 {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2}, 222 {TONE_SYMB_MIDDLE_4, TONE_BEAT_1_2}, 223 {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2}, 224 {TONE_SYMB_MIDDLE_3, TONE_BEAT_1_2}, 225 {TONE_SYMB_MIDDLE_2, TONE_BEAT_1_2}, 226 {TONE_SYMB_MIDDLE_2, TONE_BEAT_1_2}, 227 {TONE_SYMB_MIDDLE_1, TONE_BEAT_1_1}, 228 229 // 最后一个元素必须是{0, 0} 230 {TONE_SYMB_END, TONE_BEAT_END}, 231 }; 232 */ 233 234 // 播放控制变量,每次启动播放前必须初始化 235 static struct tone_play_ctrl mTonePlayController; 236 237 // 定时器中断回调,要是硬件自己处理该多好啊,这样的效率有点低 238 // 定时器1&8有个RCR(repetition counter register),也许应该换定时器1&8的 239 extern void TIM2_IRQHandler(void) 240 { 241 if (TIM_GetFlagStatus(TIM2, TIM_IT_Update)) 242 { 243 // 进来一次说明经历了ARR时长 244 mTonePlayController.arrCount ++; 245 246 // duration单位是ms,ARR经过我们的配置后是1us,所以比较时要对duration乘以1000 247 // 这里的音符的时长精度肯定有较大的误差,但是咱们暂不考虑处理这个 248 if ((TIM2->ARR + 1) * mTonePlayController.arrCount >= toneBeatDuration[mTonePlayController.toneParam->beatIndex] * 1000) 249 { 250 mTonePlayController.toneParam++; 251 if (TONE_BEAT_END < mTonePlayController.toneParam->beatIndex && TONE_SYMB_END < mTonePlayController.toneParam->symbolIndex) 252 { 253 // 音符切换,必须重置arrCount,重设定时器的ARR寄存器和PWM的CCR寄存器 254 mTonePlayController.arrCount = 0; 255 TIM_SetAutoreload(TIM2, toneSymbolPeriod[mTonePlayController.toneParam->symbolIndex] - 1); 256 if (TONE_SYMB_MUTE == mTonePlayController.toneParam->symbolIndex) 257 TIM_SetCompare3(TIM2, toneSymbolPeriod[mTonePlayController.toneParam->symbolIndex]); 258 else 259 TIM_SetCompare3(TIM2, (toneSymbolPeriod[mTonePlayController.toneParam->symbolIndex] + 1) / 2); 260 } 261 else 262 { 263 // 播放完毕了就关闭定时器 264 TIM_Cmd(TIM2, DISABLE); 265 TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable); 266 } 267 } 268 269 TIM_ClearFlag(TIM2, TIM_IT_Update); 270 } 271 } 272 273 /* 274 STM32F411CC使用的外部晶振16M,所得到的sysclk为100MHZ 275 */ 276 static void _tone_play_env_setup(void) 277 { 278 /* TIMER2, CHANNEL 3, PB10 */ 279 GPIO_InitTypeDef gpioInitData = { 280 GPIO_Pin_10, 281 GPIO_Mode_AF, 282 GPIO_Fast_Speed, 283 GPIO_OType_PP, 284 GPIO_PuPd_UP, 285 }; 286 TIM_TimeBaseInitTypeDef timeData = { 287 .TIM_CounterMode = TIM_CounterMode_Up, 288 .TIM_ClockDivision = TIM_CKD_DIV1, 289 }; 290 TIM_OCInitTypeDef ocdata = { 291 .TIM_OCMode = TIM_OCMode_PWM2, // PWM模式2:CNT>CCR时输出有效 292 .TIM_OutputState = TIM_OutputState_Enable, 293 .TIM_OutputNState = TIM_OutputNState_Enable, // 仅Timer1&8, PWM模式用不上这个 294 .TIM_OCPolarity = TIM_OCPolarity_High, 295 .TIM_OCNPolarity = TIM_OCNPolarity_Low, // PWM模式用不上这个 296 .TIM_OCIdleState = TIM_OCIdleState_Set, // PWM模式用不上这个 297 .TIM_OCNIdleState = TIM_OCNIdleState_Reset, // PWM模式用不上这个 298 }; 299 300 /* 定时器溢出周期, 301 系统主频100MHz不分频(TIM_ClockDivision=TIM_CKD_DIV1)直接给到定时器2作为计数时钟, 302 计数时钟信号100倍分频(TIM_Prescaler=100-1)就是1MHz,一个时钟周期就是1us。 303 所以对计数时钟信号计数n个(TIM_Prescaler=n-1)就达到了n微秒。 304 注意,我们后面主要控制 TIM_Prescaler 来达到音乐频率的控制。 305 这里TIM_Prescaler=99是固定配置,一个计数时钟周期为1us, 306 如果你使用的是72MHz(比如STM32F103),或者TIM_ClockDivision做了分频,要注意更改。 307 */ 308 timeData.TIM_Prescaler = 100 - 1; 309 timeData.TIM_Period = 1000 - 1; 310 // PWM占空比50%,就是 TIM_Period 的一半 311 ocdata.TIM_Pulse = 500; 312 313 // 我们要使用中断 314 // NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 315 // RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); 316 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 317 318 // 初始化GPIO以及功能复用 319 GPIO_Init(GPIOB, &gpioInitData); 320 GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_TIM2); 321 322 // 定时器中断分配 323 NVIC_InitTypeDef nvicInitData; 324 nvicInitData.NVIC_IRQChannel = TIM2_IRQn; 325 nvicInitData.NVIC_IRQChannelPreemptionPriority = 2; 326 nvicInitData.NVIC_IRQChannelSubPriority = 2; 327 nvicInitData.NVIC_IRQChannelCmd = ENABLE; 328 NVIC_Init(&nvicInitData); 329 330 TIM_DeInit(TIM2); 331 TIM_TimeBaseInit(TIM2, &timeData); 332 TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 启用中断 333 TIM_OC3Init(TIM2, &ocdata); //TIM2的通道2PWM 模式设置 334 TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable); //使能预装载寄存器 335 TIM_Cmd(TIM2, DISABLE); // 启动定时器 336 } 337 338 /* 339 接口函数,注意形参传值的正确性 340 */ 341 void tone_play(const struct tone_play_data *param) 342 { 343 if (NULL == param) 344 return; 345 346 xSemaphoreTake(semTonePlay, portMAX_DELAY); 347 TIM_Cmd(TIM2, DISABLE); 348 mTonePlayController.arrCount = 0; 349 mTonePlayController.toneParam = param; 350 TIM_SetAutoreload(TIM2, toneSymbolPeriod[mTonePlayController.toneParam->symbolIndex] - 1); 351 TIM_SetCompare3(TIM2, (toneSymbolPeriod[mTonePlayController.toneParam->symbolIndex] + 1) / 2); 352 TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable); 353 TIM_Cmd(TIM2, ENABLE); 354 xSemaphoreGive(semTonePlay); 355 } 356 357 void tone_stop(void) 358 { 359 xSemaphoreTake(semTonePlay, portMAX_DELAY); 360 TIM_Cmd(TIM2, DISABLE); 361 TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable); 362 xSemaphoreGive(semTonePlay); 363 } 364 #endif
亲测可用。
如果转载,请注明出处。https://www.cnblogs.com/ssdq/