这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战
有源蜂鸣器只能发出固定频率的声音,如果要播放歌曲必须使用无源蜂鸣器来实现,因为无源蜂鸣器时通过PWM的输出频率来发出声音了,当PWM的频率设置为特定值时,蜂鸣器就会发出do re mi fa so la xi 的声音了,这样就可以通过无源蜂鸣器来播放音乐了。
驱动原理图如下:
要实现和弦音必须通过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);
}
}
按照同样的方式,想要播放哪首音乐,自己就可以用代码实现了。