完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本文通过将乐谱里的每个音符的声音频率和声音时长保存在两个数组里面。
1.使用通用定时器TIM4实现无中断的微秒级延时函数,控制每个音符的发声时长。 2.使用系统滴答时钟Systick实现带有中断的输出控制,在中断函数里实现蜂鸣器端口输出电平反转,并且根据当前播放音符的频率重新设置中断产生时间。 一、播放的原理 播放的乐谱: 1.1 C音调乐谱对应的音频(Hz): 根据乐谱的基础知识可知,低音的下面加点,高音的上面加点,普通的不加点。 //用枚举定义,记录所有的音频。 enum Low_frequency{l_dao=262,l_re=286,l_mi=311,l_fa=349,l_sao=392,l_la=440,l_xi=494}; enum Normal_frequency{dao=523,re=587,mi=659,fa=698,sao=784,la=880,xi=987}; enum High_frequency{h_dao=1046,h_re=1174,h_mi=1318,h_fa=1396,h_sao=1567,h_la=1760,h_xi=1975}; 1.2 乐谱对应的节拍-音长: 本次乐谱的节拍为每分钟72拍,可以算出每个节拍的时长: 然后看乐谱的第一小节: 最后将整个乐谱的音频和音长记录在两个数组里。 。 二、播放音乐的具体实现 2.1 无中断的毫秒延时函数 //TIM_4初始化函数 void TIM_4(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); TIM_TimeBaseStructure.TIM_Period = 1; TIM_TimeBaseStructure.TIM_Prescaler=(72-1); TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Down; TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); } //延时n个us void delay_us(unsigned int nus) { TIM4->CNT=nus-1; TIM4->CR1|=TIM_CR1_CEN; while((TIM4->SR & TIM_FLAG_Update) != SET) ; TIM4->CR1&=(~TIM_CR1_CEN); TIM4->SR &=~(TIM_FLAG_Update); } //延时n个毫秒 void delay_ms(unsigned int nms) { int count; for(count=0;count } 2.2 Systick的中断初始化和中断函数 中断初始化: void SysTick_Init(int n) { /* SystemFrequency /1000 1ms 中断一次 * SystemFrequency / 100000 10us 中断一次 * SystemFrequency / 1000000 1us 中断一次 */ if (SysTick_Config(SystemCoreClock/1000000*n)) { /* Capture error */ while(1) ; } } 中断函数: //stm32f10x_it.c,官方库函数文件里的中断函数 //以下是需要添加的代码 #define Buzzer_TOGGLE {GPIOC->ODR ^=GPIO_Pin_0;} //PC0是连接蜂鸣器的引脚 extern int C; void SysTick_Init(int n); //在 SysTick_Handler函数里面添加的代码 void SysTick_Handler(void) { Buzzer_TOGGLE; SysTick_Init(C); } 2.3 main.c文件的代码 #include "stm32f10x.h" #define Buzzer_TOGGLE {GPIOC->ODR ^=GPIO_Pin_0;}//PC0连接的是蜂鸣器,可以根据自己的实际情况修改端口 int C; void key_init(void); void TIM_4(void); void Buzzer_init(void);//PC0连接的是蜂鸣器,可以根据自己的实际情况修改端口 void SysTick_Init(int n); void delay_us(unsigned int nus); void delay_ms(unsigned int nms); /** * @brief 主函数 * @param 无 * @retval 无 */ int main(void) { enum Low_frequency{l_dao=262,l_re=286,l_mi=311,l_fa=349,l_sao=392,l_la=440,l_xi=494}; enum Normal_frequency{dao=523,re=587,mi=659,fa=698,sao=784,la=880,xi=987}; enum High_frequency{h_dao=1046,h_re=1174,h_mi=1318,h_fa=1396,h_sao=1567,h_la=1760,h_xi=1975}; int j; int i_f; //以下是《渴望》片头曲的一段简谱“好人一生平安” unsigned int f[]={re,mi,re,dao,l_la,dao,l_la,//每行对应一小节音调 l_sao,l_mi,l_sao,l_la,dao, l_la,dao,sao,la,mi,sao, re, mi,re,mi,sao,mi, l_sao,l_mi,l_sao,l_la,dao, l_la,l_la,dao,l_la,l_sao,l_re,l_mi, l_sao, re,re,sao,la,sao, fa,mi,sao,mi, la,sao,mi,re,mi,l_la,dao, re, mi,re,mi,sao,mi, l_sao,l_mi,l_sao,l_la,dao, l_la,dao,re,l_la,dao,re,mi, re, l_la,dao,re,l_la,dao,re,mi, re, 0xff};//以0xff作为音调的结束标志 //以下是简谱中每个音调的节拍 //“4”对应4个延时单位,“2”对应两个延时单位,“1”对应1个延时单位 unsigned char JP[]={4,1,1,4,1,1,2,//每行对应一小节音调的节拍 2,2,2,2,8, 4,2,3,1,2,2, 10, 4,2,2,4,4, 2,2,2,2,4, 2,2,2,2,2,2,2, 10, 4,4,4,2,2, 4,2,4,4, 4,2,2,2,2,2,2, 10, 4,2,2,4,4, 2,2,2,2,6, 4,2,2,4,1,1,4, 10, 4,2,2,4,1,1,4, 10}; // key_init();//按键1的初始化函数,可以不用。 TIM_4(); // while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==0)//等待按键按下,执行播放音乐 // delay_ms(5); Buzzer_init(); while(1) { i_f=0; while(f[i_f]!=0xff) { C=(1000*1000/2)/f[i_f]; //ms->us:1000,s->ms:1000,方波半周期翻转:1/2 SysTick_Init(C); //翻转时长更新,重新配置翻转时长 for(j=0;j i_f++; } } } void key_init(void) { GPIO_InitTypeDef keyInit; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); keyInit.GPIO_Pin=GPIO_Pin_0; keyInit.GPIO_Mode=GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA,&keyInit); } void TIM_4(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); TIM_TimeBaseStructure.TIM_Period = 1; TIM_TimeBaseStructure.TIM_Prescaler=(72-1); TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Down; TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); } void Buzzer_init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); } void SysTick_Init(int n) { /* SystemFrequency /1000 1ms 中断一次 * SystemFrequency / 100000 10us 中断一次 * SystemFrequency / 1000000 1us 中断一次 */ if (SysTick_Config(SystemCoreClock/1000000*n)) { /* Capture error */ while(1) ; } } void delay_us(unsigned int nus) { TIM4->CNT=nus-1; TIM4->CR1|=TIM_CR1_CEN; while((TIM4->SR & TIM_FLAG_Update) != SET) ; TIM4->CR1&=(~TIM_CR1_CEN); TIM4->SR &=~(TIM_FLAG_Update); } void delay_ms(unsigned int nms) { int count; for(count=0;count } /*********************************************END OF FILE**********************/ 最后,就可以听到蜂鸣器播放的音乐了。。。。。。 三、参考书籍 《单片机C语言应用100例》,王东锋等,6.3.4 实例47:用定时器T0的模式0控制播放《好人一生平安》 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1950 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1732 浏览 1 评论
1201 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
795 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1759 浏览 2 评论
2007浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
848浏览 4评论
stm32f4下spi+dma读取数据不对是什么原因导致的?
293浏览 3评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
645浏览 3评论
651浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-2-9 08:43 , Processed in 0.503316 second(s), Total 43, Slave 37 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191