STM8单片机用无源蜂鸣器播放两只老虎

1,388 阅读6分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

  有源蜂鸣器只能发出固定频率的声音,如果要播放歌曲必须使用无源蜂鸣器来实现,因为无源蜂鸣器时通过PWM的输出频率来发出声音了,当PWM的频率设置为特定值时,蜂鸣器就会发出do re mi fa so la xi 的声音了,这样就可以通过无源蜂鸣器来播放音乐了。

驱动原理图如下: image.png 要实现和弦音必须通过2个IO口来控制,其中一个口控制蜂鸣器供电,另一个口控制震荡频率。

  下面直接上代码:

#ifndef __BUZZER_H
#define __BUZZER_H
#include "stm8s103f3p.h"

// do re mi fa so la xi的低音频率(单位hz)
#define BUZZER_LOW_DO_1         (262)  //ARR值  2045
#define BUZZER_LOW_RE_2         (294)  //ARR值  1820
#define BUZZER_LOW_MI_3         (330)  //ARR值  1620
#define BUZZER_LOW_FA_4         (349)  //ARR值  1525
#define BUZZER_LOW_SO_5         (392)  //ARR值  1355
#define BUZZER_LOW_LA_6         (440)  //ARR值  1205
#define BUZZER_LOW_XI_7         (494)  //ARR值  1078 
// 中音
#define BUZZER_MIDDLE_DO_1      (523)  //ARR值 
#define BUZZER_MIDDLE_RE_2      (587)  //ARR值 
#define BUZZER_MIDDLE_MI_3      (659)  //ARR值 
#define BUZZER_MIDDLE_FA_4      (698)  //ARR值 
#define BUZZER_MIDDLE_SO_5      (784)  //ARR值 
#define BUZZER_MIDDLE_LA_6      (880)  //ARR值 
#define BUZZER_MIDDLE_XI_7      (988)  //ARR值 
// 高音
#define BUZZER_HIGH_DO_1        (1047)  //ARR值 
#define BUZZER_HIGH_RE_2        (1175)  //ARR值 
#define BUZZER_HIGH_MI_3        (1319)  //ARR值 
#define BUZZER_HIGH_FA_4        (1397)  //ARR值 
#define BUZZER_HIGH_SO_5        (1568)  //ARR值 
#define BUZZER_HIGH_LA_6        (1760)  //ARR值 
#define BUZZER_HIGH_XI_7        (1976)  //ARR值 


typedef enum
{
	FREQ_2K,
	FREQ_2K3,
	FREQ_2K6,
	FREQ_2K9,
	FREQ_NO,
	FREQ_LOW_DO_1,
	FREQ_LOW_RE_2,
	FREQ_LOW_MI_3,
	FREQ_LOW_FA_4,
	FREQ_LOW_SO_5,
	FREQ_LOW_LA_6,
	FREQ_LOW_XI_7
} FREQ_Type; //频率类型

typedef enum
{
	MONO = 0,	 //单音
	POLY_ON = 1,  //开机和弦
	POLY_OFF = 2  //关机和弦
} Tone_Type; //蜂鸣器声音类型

typedef struct
{
	FREQ_Type Freq;	//频率
	unsigned char OSCTime; //振荡持续时间,最小单位为10ms
	unsigned char PWRTime; //供电持续时间,最小单位为10ms
} TONE_Def; //音调结构体


void buzzer_gpio_init(void);
void BeepPwrOn(void);
void BeepPwrOff(void);
void BEEP_On(void);
void BEEP_Off(void);

void TIM1_Init(void);

void BuzzerStart(Tone_Type ToneType);
void BuzzerCtrl(void);
void BEEP_SetFreq(FREQ_Type Freq);

void MusicStart(void);

#endif /* __BUZZER_H */

首先将需要用到的固定频率在头文件中用宏定义定义出来,用的时候直接通过宏定义来调用指定频率。

#include "buzzer.h"
#include "stm8s103f3p.h"
#include "delay.h"

_Bool Beep_Power  @PD_ODR:3;        //PD3 用于蜂鸣器供电控制
_Bool Beep_Pre  @PD_ODR:4;          //PD4 用于频率控制

const TONE_Def Tone5[] =      //两只老虎(两只老虎两只老虎 跑得快 跑得快)
{
	{FREQ_LOW_DO_1, 25, 25},//1
	{FREQ_LOW_RE_2, 25, 25},//2
	{FREQ_LOW_MI_3, 25, 25},//3
	{FREQ_LOW_DO_1, 25, 25},//1
	{FREQ_LOW_DO_1, 25, 25},//1
	{FREQ_LOW_RE_2, 25, 25},//2
	{FREQ_LOW_MI_3, 25, 25},//3
	{FREQ_LOW_DO_1, 25, 25},//1
	{FREQ_LOW_RE_2, 25, 25},//2
	{FREQ_LOW_MI_3, 25, 25},//3
	{FREQ_LOW_FA_4, 50, 25},//4 稍有停顿
	{FREQ_LOW_RE_2, 25, 25},//2
	{FREQ_LOW_MI_3, 25, 25},//3
	{FREQ_LOW_FA_4, 100, 25},//4 和弦效果
	{FREQ_NO, 0, 0} //停止
};

// do-re-mi-fa-so-la-ti
TONE_Def T1[] = {{FREQ_2K, 25, 25},{FREQ_NO, 0, 0}}; //do 1
TONE_Def T2[] = {{FREQ_2K3, 25, 25},{FREQ_NO, 0, 0}};//re 2
TONE_Def T3[] = {{FREQ_2K6, 25, 25},{FREQ_NO, 0, 0}};//mi 3
TONE_Def T4[] = {{FREQ_2K9, 25, 25},{FREQ_NO, 0, 0}};//fa 4

// 频率 震荡持续时间 供电持续时间
TONE_Def Tone1[] = {{FREQ_2K6, 100, 20},{FREQ_NO, 0, 0}};//单音
TONE_Def Tone2[] = {{FREQ_2K3, 20, 20},{FREQ_2K6, 20, 20},{FREQ_2K9, 210, 10},{FREQ_NO, 0, 0}};//开机和弦音
TONE_Def Tone3[] = {{FREQ_2K9, 20, 20},{FREQ_2K6, 20, 20},{FREQ_2K3, 210, 10},{FREQ_NO, 0, 0}};//关机和弦音

TONE_Def  * pTone;
unsigned char BuzzerStatus =2;

//控制引脚初始化
void buzzer_gpio_init(void)
{
	PD_DDR|=(1<<3);					//PD3 输出  控制蜂鸣器供电
	PD_CR1|=(1<<3);					//PD3 推挽输出

	PD_DDR|=(1<<4);					//PD4 输出  控制蜂鸣器频率
	PD_CR1|=(1<<4);					//PD4 推挽输出
}

//电源控制
void BeepPwrOn(void)
{
	Beep_Power=1;
}
void BeepPwrOff(void)
{
	Beep_Power=0;
}
//频率控制
void BEEP_On(void)
{
	TIM1_CR1 |=(1<<0);        //使能定时器
	Beep_Pre=1;               //频率控制引脚打开
}
void BEEP_Off(void)
{
	TIM1_CR1 &=~(1<<0);      //禁止定时器
	Beep_Pre=0;               //关闭频率控制引脚
}

//设置铃声类型 0 1 2
void BuzzerStart(Tone_Type ToneType)
{
	switch (ToneType)
	{
	case MONO:          	//0 单音
		pTone = Tone1;
		break;
	case POLY_ON:       	//1 开机
		pTone = Tone2;
		break;
	case POLY_OFF:      	//2 关机
		pTone = Tone3;
		break;
	default:
		pTone = Tone1;    	//单音
		break;
	}
	BuzzerStatus = 1;
}

void MusicStart(void)
{
	/*
	  char i;
	  for(i=1;i<5;i++)
	  {
	     if(i==1)pTone=T1;
	     if(i==2)pTone=T2;
	     if(i==3)pTone=T3;
	     if(i==4)pTone=T4;
	    BuzzerStatus = 1;
	    while(BuzzerStatus!=3)
	    {
	        BuzzerCtrl();
	        delay_ms(15);
	    }
	  }
	*/
	pTone=Tone5;
	BuzzerStatus = 1;
	while(BuzzerStatus!=3)
	{
		BuzzerCtrl();
		delay_ms(15);
	}
}

//响声控制
void BuzzerCtrl(void)
{
	static TONE_Def Tone;             	//音调 频率 震荡时间 供电时间

	switch (BuzzerStatus)
	{
	case 1:                         	//调用音调数组
		Tone = *pTone;
		if (Tone.Freq != FREQ_NO)     	//频率不为0
		{
			//先判断供电持续时间
			if (Tone.PWRTime != 0)      //供电时间不为0
			{
				Tone.PWRTime --;
				BeepPwrOn();
			}
			else
			{
				BuzzerStatus = 3;
				break;
			}
			//再判断振荡持续时间
			if (Tone.OSCTime != 0)      //振荡时间不为0
			{
				Tone.OSCTime--;
				BEEP_SetFreq(Tone.Freq);//设置频率
				BEEP_On();
			}
			else
			{
				BeepPwrOff();
				BuzzerStatus = 3;
				break;
			}
			//判断完成,开始递减计时
			BuzzerStatus = 2;
		}
		else 							//频率为0
		{
			BuzzerStatus = 3;
		}
		break;
	case 2:             				//延迟 直到时间为0 切换下一个音调
		if (Tone.PWRTime != 0)
		{
			Tone.PWRTime --;
		}
		else
		{
			BeepPwrOff();
		}
		if (Tone.OSCTime != 0)
		{
			Tone.OSCTime --;
		}
		else
		{
			BEEP_Off();
			pTone ++;   				//取下一个音调
			BuzzerStatus = 1;
		}
		break;
	default:
		break;
	}
}
//定时器1初始化
void TIM1_Init(void)
{
	TIM1_CR1|=(1<<7);                   //自动预装载允许
	TIM1_IER = 0x01;                    //允许更新中断
	TIM1_PSCRH = 0x00;                  //进行3分频 =16M/(15+1)=1M  CNTR计数一次为 1/1M=1us
	TIM1_PSCRL = 0x0e;
	TIM1_ARRH = 205/255;            //设置定时值
	TIM1_ARRL = 205%255;

	TIM1_CR1 |=0x01;                    //使能定时器
}

//定时器1 中断函数
//250us取反一次 500us一个周期 频率为2K
@far @interrupt void TIM1_OVER_Int(void)
{
	TIM1_SR1 = 0x00;
	Beep_Pre=~Beep_Pre;
}
//改变蜂鸣器频率
void BEEP_SetFreq(FREQ_Type Freq)
{
	unsigned char prescal=0;
	switch(Freq)
	{
	case FREQ_2K:
		prescal=255;      				//2K    265
		break;
	case FREQ_2K3:
		prescal=230;      				//2.3K  230
		break;
	case FREQ_2K6:
		prescal=205;      				//2.6K  205
		break;
	case FREQ_2K9:
		prescal=182;      				//2.9K  182
		break;
	case FREQ_LOW_DO_1:
		prescal=2045;
		break;
	case FREQ_LOW_RE_2:
		prescal=1820;
		break;
	case FREQ_LOW_MI_3:
		prescal=1620;
		break;
	case FREQ_LOW_FA_4:
		prescal=1525;
		break;
	case FREQ_LOW_SO_5:
		prescal=1355;
		break;
	case FREQ_LOW_LA_6:
		prescal=1205;
		break;
	case FREQ_LOW_XI_7:
		prescal=1078;
		break;

	default:
		prescal=265;
	}
	TIM1_CR1|=(1<<7);                   //自动预装载允许
	TIM1_IER = 0x01;                    //允许更新中断
	TIM1_PSCRH = 0x00;                  //进行3分频 =16M/(15+1)=1M  CNTR计数一次为 1/1M=1us
	TIM1_PSCRL = 0x0e;
	TIM1_ARRH = prescal/255;            //设置定时值
	TIM1_ARRL = prescal%255;

	TIM1_CR1 |=0x01;                    //使能定时器
}

单片机PD3口控制蜂鸣器供电,PD4口输出PWM波形。通过改变定时器的定时时间来改变输出PWM的频率,为了代码写起来简单一点,这里两只老虎的歌曲只播放了一小节。

在主函数中直接调用就可以播放音乐了。

#include "stm8s103f3p.h"
#include "delay.h"
#include "buzzer.h"

extern unsigned char BuzzerStatus;
void CLK_Init(void)
{
	CLK_SWR=0xe1; 			//HSI为主时钟源  16MHz CPU时钟频率
	CLK_CKDIVR=0x00;		//CPU时钟0分频,系统时钟0分频
}

main()
{
	unsigned char i;
	CLK_Init();
	_asm("sim");            //禁止中断
	delay_init(16);
	buzzer_gpio_init();    
	_asm("rim");            //开启中断
	while (1)
	{   
             MusicStart();
             delay_ms(500);    
	}
}

按照同样的方式,想要播放哪首音乐,自己就可以用代码实现了。