STM32 控制蜂鸣器播放音乐的原理和实例

本文介绍了如何使用STM32单片机通过C语言控制蜂鸣器播放音乐,包括理解音乐播放原理,如乐谱对应音频和节拍,以及具体实现步骤,如使用TIM4实现毫秒延时和Systick中断控制蜂鸣器输出。

STM32 控制蜂鸣器播放音乐的原理和实例

本文通过将乐谱里的每个音符的声音频率和声音时长保存在两个数组里面。
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<nms;count++)
	delay_us(1000);
}

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<JP[i_f];j++)
	     delay_ms(208);
	    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<nms;count++)
	delay_us(1000);
}
/*********************************************END OF FILE**********************/

最后,就可以听到蜂鸣器播放的音乐了。。。。。。

三、参考书籍

《单片机C语言应用100例》,王东锋等,6.3.4 实例47:用定时器T0的模式0控制播放《好人一生平安》

评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值