使用无源蜂鸣器实现伪和弦音乐

本文介绍了如何使用STM32单片机通过伪和弦技术实现音乐播放,详细讲解了硬件连接、原理以及提供的C代码实现。通过控制BUZ-H和BUZ-L引脚,结合电容充放电模拟击打和尾音,产生丰富的音频效果。文中给出了多个预设音调和音乐组合,并提供了相应的频率表和时间表。此外,还展示了如何在主程序中调用相关函数播放音乐。
摘要由CSDN通过智能技术生成

伪和弦实现的硬件图

实现伪和弦的电路

实现的伪和弦的原理

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;
}

使用的方法

  1. 调用以下初始化函数,初始化需要用的定时器及IO

bsp_BeepMusic_Init(void);

  1. 将以下伪和弦产生的服务函数放在 4ms 的中断服务函数中周期性调用

bsp_BeepMusic_Server(void);

  1. 在任意需要播放音乐的地方调用以下函数即可播放

bsp_BeepMusic_Play(BeepMusicNameTypeDef_t playWhichMusic);

完成源码可在CSDN下载,如果您没有积分,可以给我留言,我单独发给您。

评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

荻夜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值