伪和弦实现的硬件图
实现的伪和弦的原理
BUZ-H引脚(需要接在单片机的PWM口上)负责输出音频频率,BUZ-L引脚(任意IO均可)负责对C40的充电作控制。
例如,要发出“叮”的一声,在BUZ-H输出2400Hz的方波,BUZ-L输出72ms的高电平,此段时间内,蜂鸣器将由幅值5V的频率驱动(如果要声音响一点可以用12V供电),发出类似击打瞬间的声音,同时对C40充电;72ms后,BUZ-L输出低电平,这个时候,无源蜂鸣器(最好用压电式,电磁式的声音不好听,推荐TDK的17mm的无源蜂鸣器)将由C40来提供驱动电流,并且随著电容放电,声音逐渐减弱,产生类似风铃尾音的效果。1秒后,关闭BUZ-H的输出。
这种发声方式,其实还是单音频发声,只是在单音频的基础上,用电容的充放电模拟了击打和尾音,产生比较丰满悦耳的听感,所以称之为“伪和弦”。
下面例程 带音量控制,带家电常用的音调组合,还附上一个超级玛丽的1UP声音,欢迎大家测试。
使用STM32F103实现伪和弦的代码
bsp_beepmusic.c
#include "bsp_beepmusic.h"
static uint8_t GPIO_Sign = 0;
static uint16_t PrescalerValue;
// 定义常用频率,数字多少就是多少Hz
#define _28 2850
#define _24 2400
#define _22 2250
#define _21 2100
#define _18 1850
#define _16 1650
// 定义duo,rui,mi,fa,so等等
#define _l1 130
#define _l2 146
#define _l3 164
#define _l4 174
#define _l5 196
#define _l6 220
#define _l7 246
#define _1 261
#define _2 293
#define _3 329
#define _4 349
#define _5 392
#define _6 440
#define _7 494
#define _1d1 523
#define _2d1 587
#define _3d1 659
#define _4d1 698
#define _5d1 784
#define _6d1 880
#define _7d1 987
#define _1d2 1046
#define _2d2 1175
#define _3d2 1318
#define _4d2 1397
#define _5d2 1568
#define _6d2 1760
#define _7d2 1976
#define _1d3 (_1d2*2)
#define _2d3 (_2d2*2)
#define _3d3 (_3d2*2)
#define _4d3 (_4d2*2)
#define _5d3 (_5d2*2)
#define _6d3 (_6d2*2)
#define _7d3 (_7d2*2)
//定义不同的乐曲数组,0 结束
const uint16_t Music1_FrqTab[] = //频率表,对应BUZ-H的输出频率,0 结束
{
_18, _18, _21, _21, _24, _24, _28, _28, 0,
};
const uint8_t Music1_TimeTab[] = //BUZ-L输出时间,偶数高电平,奇数为低电平尾音,x4ms,0 结束
{
20, 30, 20, 30, 20, 30, 20, 255, 0,
};
const uint16_t Music2_FrqTab[] =
{
_21, _21, _22, _22, _24, _24, 0
};
const uint8_t Music2_TimeTab[] = //x4ms
{
10, 18, 10, 18, 10, 255, 0,
};
const uint16_t Music3_FrqTab[] =
{
_24, _24, 0,
};
const uint8_t Music3_TimeTab[] = //x4ms
{
18, 255, 0,
};
const uint16_t Music4_FrqTab[] =
{
_28, _28, _24, _24, _21, _21, 0
};
const uint8_t Music4_TimeTab[] = //x4ms
{
10, 18, 10, 18, 10, 255, 0,
};
const uint16_t Music5_FrqTab[] =
{
_16, _16, _18, _18, _21, _21, 0
};
const uint8_t Music5_TimeTab[] = //x4ms
{
6, 16, 6, 16, 6, 255, 0,
};
const uint16_t Music6_FrqTab[] =
{
_21, _21, _18, _18, _16, _16, 0
};
const uint8_t Music6_TimeTab[] = //x4ms
{
6, 16, 6, 16, 6, 255, 0,
};
const uint16_t Music7_FrqTab[] =
{
1324, 1324, 1574, 1574, 2645, 2645, 2114, 2114, 2347, 2347, 3154, 3154, 0,
};
const uint8_t Music7_TimeTab[] = //x4ms
{
15, 25, 15, 25, 15, 25, 15, 25, 15, 25, 15, 255, 0,
};
uint8_t Music_Vol = 250; //音量 137
uint8_t flg_MusicPlaying = 0; //非0表示正在发声
volatile BeepMusicNameTypeDef_t Music_Triger = MUSIC_NONE; //触发发声标志 Music_Triger=MUSIC_DING;
uint16_t const* music_frq_tab;
uint8_t const* music_interval_tab;
static uint16_t BuzzH_TimPeriod = 0;
void bsp_BeepMusic_SetVolumn(uint8_t vol) //设置音量,并且初始化TIM输出
{
uint32_t lTmp;
TIM_OCInitTypeDef T3_OCInitStruct;
lTmp = BuzzH_TimPeriod;
lTmp *= vol;
lTmp /= 255;
T3_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
T3_OCInitStruct.TIM_Pulse = lTmp;
T3_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
T3_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC1Init(TIM2, &T3_OCInitStruct);
}
void bsp_BeepMusic_SetFrq(uint16_t frq) // 设置频率并初始化TIM
{
uint32_t clk = 0;
TIM_TimeBaseInitTypeDef T3_TimeBaseStruct;
clk = SystemCoreClock; //72000000hz
clk /= frq;
clk --;
BuzzH_TimPeriod = clk;
/* Time base configuration */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
T3_TimeBaseStruct.TIM_Prescaler = 0x9;// (72MHZ / (9+1)) = 7.2MHZ
T3_TimeBaseStruct.TIM_Period = clk;
T3_TimeBaseStruct.TIM_ClockDivision = 0x0;
T3_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &T3_TimeBaseStruct);
bsp_BeepMusic_SetVolumn(Music_Vol);
}
void bsp_BeepMusic_ChangeFrq(uint16_t frq) //改变频率
{
uint16_t clk = 0;
uint16_t compareValue = 0;
clk = SystemCoreClock / frq; //7200000
clk --;
/* Time base configuration */
//TIM2->ARR = clk;
TIM_SetAutoreload(TIM2, clk);
// clk *= Music_Vol;
// clk /= 255;
// TIM2->CCR1 = clk;
compareValue = (clk * Music_Vol) / 255;
TIM_SetCompare1(TIM2, compareValue);
}
void bsp_BeepMusic_Frq_Enable(void)
{
TIM_Cmd(TIM2, ENABLE);//TIM2->CCER|=0x0001;
}
void bsp_BeepMusic_Frq_Diable(void)
{
TIM_Cmd(TIM2, DISABLE);//TIM2->CCER&=(~0x0001);
}
void bsp_BeepMusicIoInit(void)
{
GPIO_InitTypeDef tmpGPIO;
RCC_APB2PeriphClockCmd(BUZZ_H_GPIO_RCC | BUZZ_L_GPIO_RCC, ENABLE);
tmpGPIO.GPIO_Speed = GPIO_Speed_50MHz;
tmpGPIO.GPIO_Mode = GPIO_Mode_Out_PP;
tmpGPIO.GPIO_Pin = BUZZ_L_GPIO_PIN;
GPIO_Init(BUZZ_L_GPIO_PORT, &tmpGPIO);
tmpGPIO.GPIO_Mode = GPIO_Mode_AF_PP;
tmpGPIO.GPIO_Pin = BUZZ_H_GPIO_PIN;
GPIO_Init(BUZZ_H_GPIO_PORT, &tmpGPIO);
BUZZ_L_1();
}
void bsp_BeepMusic_Init(void)
{
bsp_BeepMusicIoInit();
bsp_BeepMusic_SetFrq(4000);
TIM_ARRPreloadConfig(TIM2, ENABLE);
//启用CCR1寄存器的影子寄存器(直到产生更新事件才更改设置)
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
bsp_BeepMusic_Frq_Diable();
bsp_BeepMusic_Frq_Enable();
}
void bsp_BeepMusic_Select(void)
{
switch(Music_Triger)
{
case MUSIC_PWR_UP:
music_frq_tab = Music1_FrqTab;
music_interval_tab = Music1_TimeTab;
break;
case MUSIC_TURN_ON:
music_frq_tab = Music2_FrqTab;
music_interval_tab = Music2_TimeTab;
break;
case MUSIC_DING:
music_frq_tab = Music3_FrqTab;
music_interval_tab = Music3_TimeTab;
break;
case MUSIC_TURN_OFF:
music_frq_tab = Music4_FrqTab;
music_interval_tab = Music4_TimeTab;
break;
case MUSIC_UP:
music_frq_tab = Music5_FrqTab;
music_interval_tab = Music5_TimeTab;
break;
case MUSIC_DOWN:
music_frq_tab = Music6_FrqTab;
music_interval_tab = Music6_TimeTab;
break;
case MUSIC_1UP:
music_frq_tab = Music7_FrqTab;
music_interval_tab = Music7_TimeTab;
break;
default:
flg_MusicPlaying = 0;
return;
}
flg_MusicPlaying = 1;
}
/*******************************************************************************
* 名 称: bsp_BeepMusic_Server
* 功 能: 播放音乐的服务函数
* 入口参数: 无
* 出口参数: 无
* 作 者: Roger-WY.
* 创建日期: 2016-08-05
* 修 改:
* 修改日期:
* 备 注: 需要每隔4ms调用一次
*******************************************************************************/
void bsp_BeepMusic_Server(void)
{
static uint8_t sPlayStep = 0;
static uint8_t sPlayCt = 0;
uint8_t cTmp = 0;
if(Music_Triger != MUSIC_NONE)
{
bsp_BeepMusic_Select();
Music_Triger = MUSIC_NONE;
sPlayStep = 0;
sPlayCt = 0;
bsp_BeepMusic_ChangeFrq(music_frq_tab[sPlayStep]);
bsp_BeepMusic_Frq_Enable();
BUZZ_L_1();
}
if(flg_MusicPlaying)
{
cTmp = music_interval_tab[sPlayStep];
sPlayCt ++;
if(sPlayCt >= cTmp)
{
sPlayCt = 0;
sPlayStep ++;
bsp_BeepMusic_ChangeFrq(music_frq_tab[sPlayStep]);
if(music_interval_tab[sPlayStep] == 0)
{
flg_MusicPlaying = 0;
}
else
{
BUZZ_L_XOR();
}
}
}
else
{
BUZZ_L_0();
bsp_BeepMusic_Frq_Diable();
}
}
/*******************************************************************************
* 名 称: bsp_BeepMusic_Play
* 功 能: 播放音乐的函数
* 入口参数: 无
* 出口参数: 无
* 作 者: Roger-WY.
* 创建日期: 2016-08-05
* 修 改:
* 修改日期:
* 备 注:
*******************************************************************************/
void bsp_BeepMusic_Play(BeepMusicNameTypeDef_t playWhichMusic)
{
Music_Triger = playWhichMusic;
}
/*******************************************************************************
* 名 称: bsp_BeepMusic_Server
* 功 能: 播放音乐的服务函数
* 入口参数: 无
* 出口参数: 无
* 作 者: Roger-WY.
* 创建日期: 2016-08-05
* 修 改:
* 修改日期:
* 备 注:
*******************************************************************************/
uint8_t bsp_BeepMusic_PlayStatus(void)
{
return (flg_MusicPlaying);
}
bsp_beepmusic.h
#ifndef __BSP_BEEPMUSIC_H__
#define __BSP_BEEPMUSIC_H__
#include "stm32f10x.h"
//BUZ-H定义IO
#define BUZZ_H_GPIO_RCC RCC_APB2Periph_GPIOA
#define BUZZ_H_GPIO_PORT GPIOA
#define BUZZ_H_GPIO_PIN GPIO_Pin_0
//BUZ-L定义IO
#define BUZZ_L_GPIO_RCC RCC_APB2Periph_GPIOA
#define BUZZ_L_GPIO_PORT GPIOA
#define BUZZ_L_GPIO_PIN GPIO_Pin_1
#define BUZZ_L_1() GPIO_SetBits(BUZZ_L_GPIO_PORT, BUZZ_L_GPIO_PIN)
#define BUZZ_L_0() GPIO_ResetBits(BUZZ_L_GPIO_PORT, BUZZ_L_GPIO_PIN)
#define BUZZ_L_XOR() {if((BUZZ_L_GPIO_PORT->ODR & BUZZ_L_GPIO_PIN) != 0) BUZZ_L_0(); else BUZZ_L_1();}
//音乐声定义
typedef enum __musicname
{
MUSIC_NONE = 0,
MUSIC_PWR_UP ,
MUSIC_TURN_ON,
MUSIC_DING ,
MUSIC_TURN_OFF,
MUSIC_UP ,
MUSIC_DOWN ,
MUSIC_1UP ,
MUSIC_TWOTIGER ,
}BeepMusicNameTypeDef_t;
void bsp_BeepMusic_Init(void);
void bsp_BeepMusic_Server(void);
void bsp_BeepMusic_Play(BeepMusicNameTypeDef_t playWhichMusic);
uint8_t bsp_BeepMusic_PlayStatus(void);
#endif
main.c
int main(void)
{
static uint8_t musicWhich = MUSIC_NONE;
/***********************************************
* 描述: 初始化硬件设备
*/
bsp_Init(); /* 为了是main函数看起来更简洁些,将硬件初始化的代码封装到这个函数 */
app_SeggerRttInit();
app_ShowPowerOnInfo();
bsp_BeepMusic_Init();
/***********************************************
* 描述: 创建软定时器
*/
bsp_StartAutoTimer( 0,5000 ); /* 启动自动重载软定时器0 用于5S定时任务 */
/***********************************************
* 描述: 进入主程序循环体
*/
while(1)
{
/***********************************************
* 描述:用户可以在此函数内执行CPU休眠和喂狗等
* 这个函数在bsp.c 中定义。
*/
bsp_Idle(); /* 调用MODS_Poll() */
/* 用户执行的任务 */
/* 1S定时到 */
if(bsp_CheckTimer(0)) {
bsp_LedToggle(1); /* 翻转LED1 */
//每隔5s播放一个自带音效
musicWhich ++;
bsp_BeepMusic_Play(musicWhich);
if(musicWhich >= 7)
{
musicWhich = 0;
}
//bsp_BeepMusic_Play(MUSIC_1UP); //单个发音测试
}
}
return 0;
}
使用的方法
- 调用以下初始化函数,初始化需要用的定时器及IO
bsp_BeepMusic_Init(void);
- 将以下伪和弦产生的服务函数放在 4ms 的中断服务函数中周期性调用
bsp_BeepMusic_Server(void);
- 在任意需要播放音乐的地方调用以下函数即可播放
bsp_BeepMusic_Play(BeepMusicNameTypeDef_t playWhichMusic);
完成源码可在CSDN下载,如果您没有积分,可以给我留言,我单独发给您。