蜂鸣器PWM驱动方式改进,可中断方式播放曲目

2019-07-21 05:43发布

蜂鸣器有有源和无源之分,此处用的是无源蜂鸣器。信号驱动有IO模拟PWM和硬件PWM,鉴于IO模拟PWM太占用中断资源,严重影响MCU效率,故采用硬件产生PWM。本帖主要介绍蜂鸣器PWM的两种使用方式:
1:将播放程序放到主函数main中的while(1)来播放曲目。此种方式,仅适合任务量轻,时序要求不高的系统,一旦单片机处理的任务太多,而while(1)中的程序得不到及时执行,就会出现曲目变形,节奏不符。不过也可以用RTOS操作系统来弥补缺憾,在此不介绍!
2:将播放的程序放到定时器中断中。执行效率高,耗时极少,不影响其他任务
/*说明
  硬件平台--STM32F103VET6-(可适用STM32F103VCT6)
  采用标准库

  功能:蜂鸣器信号采用PWM方式,由NPN三极管驱动电磁蜂鸣器,播放特定曲目
  引脚:PB1----TIM3CH4
*/


方式1实例:

                                                    beep.h
#ifndef __BEEP_H
#define __BEEP_H         
#include "sys.h"

#define RCC_BEEP_CLK     RCC_APB2Periph_GPIOB                                                                  
#define BEEP_Pin     GPIO_Pin_1
#define BEEP_GPIO    GPIOB
//蜂鸣器端口定义
#define BEEP PBout(1)        // BEEP,蜂鸣器接口                  
// 定义低音音名(数值单位:Hz)
#define L1 262
#define L2 294
#define L3 330
#define L4 349
#define L5 392
#define L6 440
#define L7 494

// 定义中音音名
#define M1 523
#define M2 587
#define M3 659
#define M4 698
#define M5 784
#define M6 880
#define M7 988

// 定义高音音名
#define H1 1047
#define H2 1175
#define H3 1319
#define H4 1397
#define H5 1568
#define H6 1760
#define H7 1976

// 定义时值单位,决定演奏速度(数值单位:ms)
#define TT 2000
typedef struct
{
  short mName; // 音名:取值L1~L7、M1~M7、H1~H7分别表示低音、中音、高音的1234567,取值0表示休止符
  short mTime; // 时值:取值T、T/2、T/4、T/8、T/16、T/32分别表示全音符、二分音符、四分音符、八分音符.,取值0表示演奏结束
}tNote;

extern u8 beep_switch;
void BEEP_PWM_Init(u16 arr,u16 psc);
void buzzerSound(unsigned short usFreq)        ;
void musicPlay(void) ;
void buzzerQuiet(void);

#endif

                                                                                           beep.c
#include "beep.h"
#include "delay.h"
            
u8 beep_switch=0;
// 定义乐曲:《最炫民族风》
const tNote MyScore[]=
{
  {L6,TT/4},{L3,TT/8},{L5,TT/8},{L6,TT/4},{L6,TT/8},{M1,TT/8},{M1,TT/4},{M2,TT/8},{M1,TT/8},{L6,TT/2},{M1,TT/4},{M1,TT/8},{L5,TT/8},{M1,TT/8},{M2,TT/8},{M3,TT/8},{M5,TT/8},
  {M5,TT/8},{M3,TT/8},{M2,TT/4},{M3,TT/2},{M6,TT/8},{M6,TT/8},{M6,TT/8},{M5,TT/8},{M3,TT/8},{M3,TT/4},{M1,TT/8},{L6,TT/8},{L6,TT/8},{L6,TT/8},{M3,TT/8},//苍茫的天涯是我的爱。。。最呀最摇
  {M2,TT/2},{M3,TT/8},{M3,TT/8},{M5,TT/8},{M3,TT/8},{M2,TT/8},{M3,TT/8},{M2,TT/8},{M1,TT/8},{L6,TT/4},{L5,TT/4},{L6,TT/2}, //摆,什么样的歌声才是最开怀
  {L6,TT/4},{L3,TT/8},{L5,TT/8},{L6,TT/4},{L6,TT/8},{M1,TT/8},{M1,TT/4},{M2,TT/8},{M1,TT/8},{L6,TT/2},{M1,TT/4},{M1,TT/8},{L5,TT/8},{M1,TT/8},{M2,TT/8},{M3,TT/8},{M5,TT/8},
  {M5,TT/8},{M3,TT/8},{M2,TT/4},{M3,TT/2},{M6,TT/8},{M6,TT/8},{M6,TT/8},{M5,TT/8},{M3,TT/8},{M3,TT/4},{M1,TT/8},{L6,TT/8},{L6,TT/8},{L6,TT/8},{M3,TT/8},//苍茫的天涯是我的爱。。。最呀最摇
  {M2,TT/2},{M3,TT/8},{M3,TT/8},{M5,TT/8},{M3,TT/8},{M2,TT/8},{M3,TT/8},{M2,TT/8},{M1,TT/8},{L6,TT/4},{L5,TT/4},{L6,TT/2}, //摆,什么样的歌声才是最开怀
  {M3,TT/8},{M3,TT/8},{M5,TT/8},{M3,TT/8},{M3,TT/8},{M5,TT/8},{M5,TT/8},{M6,TT/8},{H1,TT/8},{M6,TT/8},{M5,TT/4},{M6,TT/2},{L6,TT/4},{L6,TT/8},
  {L5,TT/8},{L6,TT/4},{M1,TT/4},{M2,TT/8},{M3,TT/16},{M2,TT/16},{M1,TT/8},{M2,TT/8},{M3,TT/2},{L6,TT/8},{M6,TT/8},{M6,TT/8},{M5,TT/8},{M2,TT/8},{M3,TT/16},{M2,TT/16},
  {M1,TT/8},{M2,TT/8},{M3,TT/2},//留下来
  {M1,TT/8},{L6,TT/8},{L6,TT/8},{M1,TT/8},{M2,TT/4},{L5,TT/8},{L5,TT/8},{M3,TT/8},{M5,TT/8},{M3,TT/8},{M2,TT/8},{M1,TT/2},{L6,TT/8},{M1,TT/8},{M2,TT/8},{M3,TT/8},{M2,TT/8},{M1,TT/8},
  {L5,TT/8},{L3,TT/8},{L6,TT/2},{L6,TT/4},{L6,TT/8},{L5,TT/8},{L6,TT/4},{M1,TT/4},{M2,TT/8},{M3,TT/16},{M2,TT/16},{M1,TT/8},{M2,TT/8},{M3,TT/2},{L6,TT/8},{M6,TT/8},
  {M6,TT/8},{M5,TT/8},{M2,TT/8},{M3,TT/16},{M2,TT/16},
  {M1,TT/8},{M2,TT/8},{M3,TT/2},//留下来
  {M1,TT/8},{L6,TT/8},{L6,TT/8},{M1,TT/8},{M2,TT/4},{L5,TT/8},{L5,TT/8},{M3,TT/8},{M5,TT/8},{M3,TT/8},{M2,TT/8},{M1,TT/4+TT/8},{M1,TT/8},{L6,TT/8},
  {M1,TT/8},{M2,TT/8},{M3,TT/8},{M5,TT/8},{M3,TT/8},{M3,TT/8},{M5,TT/8},{M6,TT/2},{M6,TT/2},{L6,TT/4},{L6,TT/8},{L5,TT/8},{L6,TT/4},
  {L6,TT/8},{M1,TT/8},{M2,TT/8},{M3,TT/16},{M2,TT/16},{M1,TT/8},{M2,TT/8},{M3,TT/2},
  {M6,TT/8},{M5,TT/8},{M3,TT/8},{M2,TT/8},{M5,TT/8},{M3,TT/8},{M2,TT/8},{M1,TT/8},{M1,TT/2},//登上天外云霄的舞台
  {L6,TT/4},{L6,TT/8},{L5,TT/8},{L6,TT/4},{M1,TT/4},{M2,TT/8},{M3,TT/16},{M2,TT/16},{M1,TT/8},{M2,TT/8},{M3,TT/2},{L6,TT/8},{M6,TT/8},{M6,TT/8},{M5,TT/8},{M2,TT/8},{M3,TT/16},{M2,TT/16},
  {M1,TT/8},{M2,TT/8},{M3,TT/2},//留下来
  {M1,TT/8},{L6,TT/8},{L6,TT/8},{M1,TT/8},{M2,TT/4},{L5,TT/8},{L5,TT/8},{M3,TT/8},{M5,TT/8},{M3,TT/8},{M2,TT/8},{M1,TT/2},{L6,TT/8},{M1,TT/8},{M2,TT/8},{M3,TT/8},{M2,TT/8},{M1,TT/8},
  {L5,TT/8},{L3,TT/8},{L6,TT/2},{L6,TT/4},{L6,TT/8},{L5,TT/8},{L6,TT/4},{M1,TT/4},{M2,TT/8},{M3,TT/16},{M2,TT/16},{M1,TT/8},{M2,TT/8},{M3,TT/2},{L6,TT/8},{M6,TT/8},
  {M6,TT/8},{M5,TT/8},{M2,TT/8},{M3,TT/16},{M2,TT/16},
  {M1,TT/8},{M2,TT/8},{M3,TT/2},//留下来
  {M1,TT/8},{L6,TT/8},{L6,TT/8},{M1,TT/8},{M2,TT/4},{L5,TT/8},{L5,TT/8},{M3,TT/8},{M5,TT/8},{M3,TT/8},{M2,TT/8},{M1,TT/4+TT/8},{M1,TT/8},{L6,TT/8},
  {M1,TT/8},{M2,TT/8},{M3,TT/8},{M5,TT/8},{M3,TT/8},{M3,TT/8},{M5,TT/8},{M6,TT/2},{M6,TT/2},
  {0,0},
};

//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数

void BEEP_PWM_Init(u16 arr,u16 psc)
{  
        GPIO_InitTypeDef GPIO_InitStructure;
        TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        TIM_OCInitTypeDef  TIM_OCInitStructure;
       
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_BEEP_CLK  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟使能
                                                                        
   //设置该引脚为复用输出功能,输出TIM3 CH4的PWM脉冲波形
        GPIO_InitStructure.GPIO_Pin = BEEP_Pin; //TIM3_CH4
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(BEEP_GPIO, &GPIO_InitStructure);
       
        TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值         80K
        TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  不分频
        TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
        TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
                 
        TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
        TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
        TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//TIM_OCPolarity_High;//TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
        TIM_OC4Init(TIM3, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
        TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIMx在CCR2上的预装载寄存器
       
        TIM_CCxCmd(TIM3,TIM_Channel_4 ,ENABLE);//输出开启
        TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIMx在ARR上的预装载寄存器
        TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设
}

// 蜂鸣器停止发声
void buzzerQuiet(void)       
{       
    TIM_SetCompare4(TIM3,0); //喇叭
    TIM_CCxCmd(TIM3,TIM_Channel_4 ,DISABLE);//输出关闭                
}
/////////////////////////////////////////////////////////         
//蜂鸣器发出指定频率的声音
//usFreq是发声频率,取值 (系统时钟/65536)+1 ~ 20000,单位:Hz
void buzzerSound(unsigned short usFreq)         
{
  unsigned long  ulVal;
  if((usFreq<=8000000/65536UL)||(usFreq>20000))
  {
        buzzerQuiet();// 蜂鸣器静音
  }
  else
  {
           ulVal=8000000/usFreq;
           TIM3->ARR =ulVal ;        //设置自动重装载寄存器周期的值(音调)
          TIM_SetCompare4(TIM3,ulVal /2);//音量
          TIM_CCxCmd(TIM3,TIM_Channel_4 ,ENABLE);//输出开启
  }  
}

// 演奏乐曲
void musicPlay(void)
{        
static u8 i=0;       
          if(MyScore.mTime != 0)
                {
                        buzzerSound(MyScore.mName);
                        delay_ms(MyScore.mTime);
                        i++;
                        buzzerQuiet(); // 蜂鸣器静音
                        delay_ms(10);// 10 ms
                }

}
                                                                                                             main.c
#include "sys.h"
#include "delay.h"
#include "beep.h"

int main(void)
{  
        delay_init();                     //延时函数初始化          
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级       
        BEEP_PWM_Init(14399,10);         //分频。PWM频率=72000/14400/11(Khz)
       
        while(1)
          {                       
               musicPlay();
      }
}

方式2实例:
相比方式1,beep.c中多个一个可中断播放的程序
u16 music_interrupt_play(void)
{
static u8 i=0;
u16 music_value=0;       
        if(MyScore.mTime >0)//if(MyScore.mTime != 0)
        {       
                buzzerSound(MyScore.mName);
                music_value=MyScore.mTime;
                ++i;
                return  music_value;
        }       
   else return  0;         
}

                                                                                        time.c
#include "timer.h"
#include "beep.h"

//通用定时器中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM6_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        NVIC_InitTypeDef NVIC_InitStructure;

        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); //时钟使能

        TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值         计数到5000为500ms
        TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  
        TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
        TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

        TIM_ITConfig(  //使能或者失能指定的TIM中断
                TIM6, //TIM6
                TIM_IT_Update ,
                ENABLE  //使能
                );
        NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;  //TIM2中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
        NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

        TIM_Cmd(TIM6, ENABLE);  //使能TIMx外设
                                                         
}

void TIM6_IRQHandler(void)   //TIM3中断
{
       
        static u16 time_add=0;
        static u16 time_design=2;
        static u8 beep_change_state=1;
        u16 sfdb=0;
        if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
         {
                    TIM_ClearITPendingBit(TIM6, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源
                     if(beep_switch==1)
                          {
                                  time_add++;
                                  if(beep_change_state)
                                   {
                                                beep_change_state=0;
                                               sfdb = music_interrupt_play();
                                                if(sfdb>0)
                                                   {          
                                                          time_add=0;
                                                          time_design=sfdb/10;          
                                                  }
                                                  else       
                                                  {                                         
                                                          time_add=0;
                                                          beep_switch=0;
                                                          time_design=2;
                                                          beep_change_state=1;
                                                          buzzerQuiet(); // 蜂鸣器静音
                                                  }       
                                     }
                                  if(time_design<=time_add)
                                  {
                                          buzzerQuiet(); // 蜂鸣器静音
                                          beep_change_state=1;
                                  }
                                 
                        }
                         else time_add=0;
       
         }
}
main.c




#include "sys.h"
#include "delay.h"
#include "beep.h"
#include "timer.h"

int main(void)
{  
        delay_init();                     //延时函数初始化          
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级       

        BEEP_PWM_Init(14399,10);         //分频。PWM频率=72000/14400/11(Khz)
        TIM6_Int_Init(999,719);//10Khz的计数频率,计数到10ms
        beep_switch=1;
        while(1)
         {       
          
         }
}


友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。