第一句子网 - 唯美句子、句子迷、好句子大全
第一句子网 > STM32F4应用笔记(二)利用蜂鸣器播放天空之城

STM32F4应用笔记(二)利用蜂鸣器播放天空之城

时间:2020-07-14 02:06:33

相关推荐

STM32F4应用笔记(二)利用蜂鸣器播放天空之城

音阶频率对照表

百度就可以查到,我对照的是下面网址中的:

/u012266559/article/details/51512616

单片机产生音乐的原理

音乐的产生主要是通过单片机的I/O口输出高低不同的脉冲信号来控制蜂鸣器发音,要想产生音频脉冲信号,需要算出某音频的周期(1/频率),然后将此周期除以2,即为半周期的时间。利用单片机定时器计时这个半周期的时间,每当计时到后就输出脉冲的I/O口反相,这样就在此I/O口上得到此脉冲的频率。

我是直接利用stm32的PWM端口输出PWM波(设置成占空比为50%)就可以实现,关键是每个音阶对应频率的方波如何求。

从下图看,只要理解原理,那么不管单片机的时钟频率多大,我们都可以自己求出相应的音阶所需要的arr值。

程序用到的单片机引脚

我用的是stm32开发板,利用PF9端口输出PWM波,PF8端口连着的是蜂鸣器,所以只需要用杜邦线把PF9和PF8端口连在一起就可以了。

程序中定义的的宏定义

10000的来历上图中有计算过程,然后音阶频率对照表中可以查到

低1DO 的频率是262,所以我们得到

低1DO 对应的arr值是 (R/262)-1 ,其余音符对应arr的计算方式同理。

#define R 10000 //84MHz/(psc+1)=10000

#define L1 (R/262)-1 //低1DO

关于音符0的处理以及改进

在我写完的程序中,我是这样处理的,一旦遇到音符0,让PWM停止输出。有想过关闭TIM14和PORTF时钟,但是没有用——因为用同样的方法,在一首歌结束之后就是一段相同频率的杂音。所以这种方法是不可以让PWM输出停止的。最后换了个方法,把PF9设置成普通IO口而且是输入模式就可以了,就不会有噪声了。

但是这种方法很麻烦,后来想到其实在遇到音符0的时候,只需要将PWM输出频率变大,让蜂鸣器发出一个人耳听不到的超声波就可以了——程序已经写完了,懒得改了。

主要程序代码

#include "sys.h"#include "delay.h" #include "led.h"#include "timer.h"#include "key.h"#define ZERO 3000//#define R 10000 //F_CLOCK/(psc+1)=10000#define L1 (R/262)-1 //低1DO#define half_L1 (R/277)-1 //#1DO##define L2 (R/294)-1#define half_L2 (R/311)-1#define L3 (R/330)-1#define L4 (R/349)-1#define half_L4 (R/370)-1#define L5 (R/392)-1#define half_L5 (R/410)-1#define L6 (R/440)-1#define half_L6 (R/466)-1#define L7 (R/494)-1#define M1 (R/523)-1 //中1DO#define half_M1 (R/554)-1 //#1 DO##define M2 (R/587)-1#define half_M2 (R/622)-1#define M3 (R/659)-1#define M4 (R/698)-1#define half_M4 (R/740)-1#define M5 (R/784)-1#define half_M5 (R/831)-1#define M6 (R/880)-1#define half_M6 (R/932)-1#define M7 (R/988)-1#define H1 (R/1046)-1 //高1DO#define half_H1 (R/1109)-1 //#1DO##define H2 (R/1175)-1#define half_H2 (R/1245)-1#define H3 (R/1318)-1#define H4 (R/1397)-1#define half_H4 (R/1480)-1#define H5 (R/1568)-1#define half_H5 (R/1661)-1#define H6 (R/1760)-1#define half_H6 (R/1865)-1#define H7 (R/1967)-1int flag=0;//标志int x;int tune[] = { M6,M7,H1,M7,H1,H3,M7,M7,M7,M3,M3, M6,M5,M6,H1,M5,M5,M5,M3,M4,M3,M4,H1,M3,M3,ZERO,H1,H1,H1,M7,half_M4,M4,M7,M7,M7,ZERO,M6,M7,H1,M7,H1,H3,M7,M7,M7,M3,M3,M6,M5,M6,H1,M5,M5,M5,M2,M3,M4,H1,M7,M7,H1,H1,H2,H2,H3,H1,H1,H1,H1,M7,M6,M6,M7,half_M5,M6,M6,M6,H1,H2,H3,H2,H3,H5,H2,H2,H2,M5,M5,H1,M7,H1,H3,H3,H3,H3,H3,M6,M7,H1,M7,H2,H2,H1,M5,M5,M5,H4,H3,H2,H1,H3,H3,H3,H3,H6,H6,H5,H5,H3,H2,H1,H1,ZERO,H1,H2,H1,H2,H2,H5,H3,H3,H3,H3,H6,H6,H5,H5,H3,H2,H1,H1,ZERO,H1,H2,H1,H2,H2,M7,M6,M6,M6,M6,M7};float duration[]= { 0.5,0.5,1.5,0.5,1,1,1,1,1,0.5,0.5,1.5,0.5,1,1,1,1,1,1,1.5,0.5,1,1, 1,1,0.5,0.5,0.5,0.5, 1+0.5,0.5,1,1,1,1,1,0.5,0.5,1+0.5,0.5,1,1, 1,1,1,0.5,0.5,1+0.5,0.5,1,1,1,1,1,0.5,0.5, 1,0.5,0.25,0.25,0.25,0.5, 0.5,0.5,0.5,0.25,0.5,1,0.5,0.5,0.5,0.5,1,1, 1,1,1,0.5,0.5, 1+0.5,0.5,1,1,1,1,1,0.5,0.5, 1.5,0.5,1,1, 1,1,1,1,0.5,0.5,1,1,0.5,0.5, 1.5,0.25,0.5,1, 1,1,1,1,1,1,1,1, 1,1,1,1, 0.5,0.5,1,1,0.5,0.5,1,0.5,0.5,1,1, 1,1,1,1, 1,1,1,1,0.5,0.5,1,1,0.5,0.5, 1,0.5,0.25,0.5,1, 1,1,1,0.5,0.5};//这部分是整首曲子的节拍部分,也定义个序列duration,浮点(数组的个数和前面音符的个数是一样的,一一对应么)int length = sizeof(tune)/sizeof(tune[0]);//这里用了一个sizeof函数, 可以查出tone序列里有多少个音符//int length;//这里定义一个变量,后面用来表示共有多少个音符void main(void){Stm32_Clock_Init(336,8,2,7);//设置时钟,168Mhz delay_init(168); //延时初始化 for(x=0;x<length;x++)//循环音符的次数{ if(flag==1) //上一个音符是0,在遇到下一个音符前重新使用IO口的复用功能{//RCC->AHB1ENR|=1<<5; //使能PORTF时钟 //RCC->APB1ENR|=1<<8; //使能TIM14时钟GPIO_Set(GPIOF,PIN9,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);//复用功能,上拉输出GPIO_AF_Set(GPIOF,9,9); //PF9,AF9 flag=0;//将标志置0}if(tune[x]==ZERO){//RCC->APB1ENR|=0<<8; //关闭TIM14时钟 //RCC->AHB1ENR|=0<<5; //关闭PORTF时钟使PF9引脚无法输出PWM波GPIO_Set(GPIOF,PIN9,GPIO_MODE_IN,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);//将PF9设置成普通IO口,输入flag=1;//将关闭标志置1}GPIO_Set(GPIOF,PIN9,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);//复用功能,上拉输出GPIO_AF_Set(GPIOF,9,9); //PF9,AF9//设置成复用模式(此处就是PWM端口),输出TIM14_PWM_Init(tune[x],8400-1); //(arr,psc)if(flag==1)delay_ms(300*duration[x]);//否则延时会很长elsedelay_ms(400*duration[x]);//每个音符持续的时间,即节拍duration//设置成一个全拍400ms}//RCC->APB1ENR|=0<<8; //关闭TIM14时钟 //RCC->AHB1ENR|=0<<5; //关闭PORTF时钟GPIO_Set(GPIOF,PIN9,GPIO_MODE_IN,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);//把PF9设置成普通IO口输入就可以了,就不会有噪声了。//delay_ms(10000);//还是会有声音,不知道为什么//GPIO_AF_Set(GPIOF,9,9); //PF9,AF9 while(1);//防止程序跑飞 }//TIM14 PWM部分初始化函数//PWM输出初始化//arr:自动重装值//psc:时钟预分频数void TIM14_PWM_Init(u32 arr,u32 psc){ //此部分需手动修改IO口设置RCC->APB1ENR|=1<<8;//TIM14时钟使能 RCC->AHB1ENR|=1<<5;//使能PORTF时钟 //GPIO_Set(GPIOF,PIN9,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);//复用功能,上拉输出//GPIO_AF_Set(GPIOF,9,9); //PF9,AF9 TIM14->ARR=arr; //设定计数器自动重装值 TIM14->PSC=psc; //预分频器分频 TIM14->CCMR1|=6<<4;//CH1 PWM1模式 TIM14->CCMR1|=1<<3;//CH1 预装载使能 TIM14->CCER|=1<<0;//OC1 输出使能 TIM14->CCER|=1<<1;//OC1 低电平有效 TIM14->CR1|=1<<7; //ARPE使能 TIM14->CR1|=1<<0; //使能定时器14TIM14->CCR1=arr*0.5; //占空比= TIM14->CCR1 / arr(单位:%)//设置占空比为50%}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。