开源小项目 - 基于无源蜂鸣器实现的音乐播放器

这篇具有很好参考价值的文章主要介绍了开源小项目 - 基于无源蜂鸣器实现的音乐播放器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、音乐简谱相关知识

1、音符

2、音调

3、识读简谱

1. 找到简谱中C调的音符对应的蜂鸣器频率(确定音调对应的频率)

2. 确定蜂鸣器演奏一拍所需的时间(即确定一个音调对应的节拍数)

3.创建结构体确定一个音符所需的两个属性(音调频率、节拍数)

4.将《两只老虎》简谱的每个音符使用结构体数组编写好代码

5.创建静态歌单列表

二、音乐播放器实现过程

1、 无源蜂鸣器初始化配置

2、 蜂鸣器频率、占空比、使能配置

3、 音乐播放器列表清单初始化

4、 音乐播放器初始化

5、 设计音乐播放器处理函数

6、 其他业务代码(暂停、播放、上一曲、下一曲、音量调节)

三、使用方法

1、初始化无源蜂鸣器

2、创建歌曲简谱

3、初始化音乐播放器

4、 创建定时器运行音乐播放器处理函数

5、 调用源码中的接口函数实现音乐播放器的功能

6、 测试代码

END


        该项目使用无源蜂鸣器实现静态音乐播放器,即需要用户手动添加歌曲节点,读者可自行扩展成动态节点,实现动态音乐播放器列表,主要实现功能如下:

  1. 支持歌曲的暂停与播放
  2. 支持上一首、下一首歌曲切换
  3. 支持自定义音乐播放模式:顺序播放、随机播放、单曲循环
  4. 支持自定义设定音乐音量大小(0-100)

        主要运用知识点:

  1. 定时器配置与使用
  2. PWM配置与使用
  3. C语言结构体、结构体嵌套、枚举使用
  4. C语言指针、一维数组、二维数组、指针数组使用
  5. C语言链表使用

        源码中支持的库函数清单如下:

void                    beep_init(void);                                /* 无源蜂鸣器初始化 */
void                    beep_handler(void);                             /* 蜂鸣器音乐播放处理函数 定时执行 */
void                    music_init(void);                               /* 播放器初始化 */
void                    music_pause_playback(void);                     /* 暂停播放 */
void                    music_resume_playback(void);                    /* 恢复播放 */
void                    music_set_volume(unsigned char volume);         /* 设置音量大小 */
unsigned char           music_get_volume(void);                         /* 获取音量大小 */
void                    music_set_mode(ENUM_MUSIC_MODE_TYPEDEF mode);   /* 设置播放模式 */
ENUM_MUSIC_MODE_TYPEDEF music_get_mode(void);                           /* 获取播放模式 */
void                    music_switch_previous(void);                    /* 播放上一首 */
void                    music_switch_next(void);                        /* 播放下一首 */

蜂鸣器(音乐播放器)驱动源码下载:无源蜂鸣器实现音乐播放器功能


一、音乐简谱相关知识

        简谱是一种用简单符号表示音乐音高和节奏的音乐记谱方法。关于简谱中的相关知识点统计如下:

        1. 音符:简谱中用不同形状的符号表示不同音高的音符。常见的音符有:CDEFGAB。它们分别代表了音阶中的不同音名。

        2. 节拍:节拍是音乐中的基本时间单位,用来划分音乐的节奏。在简谱中,节拍可以用不同的符号和线条表示,如四分音符、八分音符等。

        3. 拍号:简谱中的拍号用来表示每小节中的拍数,常见的拍号有2/43/44/4等。这些拍号告诉演奏者每小节有多少拍和每拍的时值。

        4. 调号:调号在简谱中用来表示音乐作品所采用的调性。调号可以影响乐谱中所有音符的音高,使其适应特定的音阶。

        5. 连线:在简谱中,如果需要表示音符的音长超过一个小节,可以使用连线将两个相同音符连接起来,延长音符的时值。

        6. 休止符:除了音符之外,简谱还包括用来表示休止的符号。休止符用来表示音乐中的停顿或静默。

1、音符

        在简谱中,用以表示音的高低及相互关系的基本符号,为七个阿拉伯数字:即1、2、3、4、5、6、7,唱作doremifasollasi,称为唱名。

开源小项目 - 基于无源蜂鸣器实现的音乐播放器,嵌入式STM32开发,开源项目,单片机,嵌入式硬件,stm32,c语言,开源
图1  音符说明

注意:一个音符由这个音的音高和对应的时值组成。

        在简谱中,如果音符时值的长短用短横线“-”表示,就称为单纯音符。单纯音符除四分音符外,有以下两种形式:

1.  在基本音符右侧加记一条短横线

        表示增长原音符时值的一倍。这类加记在音符右侧、使音符时值增长的短横线,称为增时线。增时线越多,音符的时值越长。

2.  在基本音符下方加记一条短横线

        表示缩短原音符时值的一半。这类加记在音符下方、使音符时值缩短的短横线,称为减时线。减时线越多,音符的时值越短。

开源小项目 - 基于无源蜂鸣器实现的音乐播放器,嵌入式STM32开发,开源项目,单片机,嵌入式硬件,stm32,c语言,开源
图2  单纯音符说明

        在简谱中,加记在单纯音符的右侧的,使音符时值增长的小圆点"·",称为附点。加记附点的音符称为附点音符,附点本身并无一定的长短,其长短由前面的单纯音符来决定。附点音符会增长原音符时值的一半,常用于四分音符和小于四分音符的各种音符之后。

开源小项目 - 基于无源蜂鸣器实现的音乐播放器,嵌入式STM32开发,开源项目,单片机,嵌入式硬件,stm32,c语言,开源
图3  附点音符说明

2、音调

        在一些其他乐谱中,可能会存在一些更高或更低的音,如:

        1.在基本音符上方加记一个"·",表示该音升高一个八度,称为高音;

        2.加记两个":",则表示该音升高两个八度,称为倍高音。

        3.在基本音符下方加记一个"·",表示该音降低一个八度,称为低音;

​​​​​​​​​​​​​​        4.加记两个":",则表示该音降低两个八度,称为倍低音。

开源小项目 - 基于无源蜂鸣器实现的音乐播放器,嵌入式STM32开发,开源项目,单片机,嵌入式硬件,stm32,c语言,开源
图4  音调说明

3、识读简谱

        接下来我将以儿歌《两只老虎的简谱来为大家简单讲解。

开源小项目 - 基于无源蜂鸣器实现的音乐播放器,嵌入式STM32开发,开源项目,单片机,嵌入式硬件,stm32,c语言,开源
图5  《两只老虎》简谱

        由上图的简谱可知,该乐谱演奏使用的是C调,且每个音符使用节拍为一拍(4/4)。例如,第一个音符(1)代表使用C调音阶中的第一个音(do),且四分音符的时值为4。因此如果我们想用无源蜂鸣器演奏出上图中的这样的一首儿歌,我们需要:

1. 找到简谱中C调的音符对应的蜂鸣器频率(确定音调对应的频率)

开源小项目 - 基于无源蜂鸣器实现的音乐播放器,嵌入式STM32开发,开源项目,单片机,嵌入式硬件,stm32,c语言,开源
图6  C调对应的蜂鸣器频率

2. 确定蜂鸣器演奏一拍所需的时间(即确定一个音调对应的节拍数)

        源代码中我给定1/4拍为200ms,那么一拍需要的时间为800ms(用户也可根据歌曲的节奏自行设计)。

3.创建结构体确定一个音符所需的两个属性(音调频率、节拍数)

/* 音符 */
typedef struct {
    unsigned short frequency; /* 音调 -- 表现形式为蜂鸣器频率 */
    unsigned char duration;   /* 节拍 -- 表现形式为重复次数 */
} STRUCT_MUSIC_NOTE_TYPEDEF;

4.将《两只老虎》简谱的每个音符使用结构体数组编写好代码

        下面代码中的{523, 4}代表do的频率是523Hz,由于定义的基准节拍为1/4拍,歌曲中一个音符是4/4拍,因此这里填写4。

/* 两只老虎 歌单 C调 4/4 */
STRUCT_MUSIC_NOTE_TYPEDEF twoTigers_notes[32] = {
    /* 两只老虎 */
    {523, 4}, {587, 4}, {659, 4}, {523, 4},
    /* 两只老虎 */
    {523, 4}, {587, 4}, {659, 4}, {523, 4},
    /* 跑得快 */
    {659, 4}, {698, 4}, {784, 8},
    /* 跑得快 */
    {659, 4}, {698, 4}, {784, 8},
    /* 一只没有眼睛 */
    {784, 3}, {880, 1}, {784, 3}, {698, 1}, {659, 4}, {523, 4},
    /* 一只没有耳朵 */
    {784, 3}, {880, 1}, {784, 3}, {698, 1}, {659, 4}, {523, 4},
    /* 真奇怪 */
    {523, 4}, {784, 4}, {523, 8},
    /* 真奇怪 */
    {523, 4}, {784, 4}, {523, 8},
};

5.创建静态歌单列表

        这里我以另外两首歌《生日快乐》和《私奔》(看完房客的后遗症,哈哈哈)为例,创建结构体数组如下:

/* 生日快乐 歌单 F调 3/4 -->目前使用的是C调 */
STRUCT_MUSIC_NOTE_TYPEDEF birthday_notes[25] = {
    /* 祝你生日快乐 */
    {392, 3}, {392, 3}, {440, 6}, {392, 6}, {523, 6}, {494, 12},
    /* 祝你生日快乐 */
    {392, 3}, {392, 3}, {440, 6}, {392, 6}, {587, 6}, {523, 12},
    /* 祝你生日快乐 */
    {392, 3}, {392, 3}, {784, 6}, {659, 6}, {523, 6}, {494, 6}, {466, 6},
    /* 祝你生日快乐 */
    {698, 4}, {698, 1}, {659, 3}, {523, 3}, {587, 6}, {523, 12},
};

/* 私奔 歌单 A调 4/4 -->目前使用的是C调 */
STRUCT_MUSIC_NOTE_TYPEDEF elope_notes[41] = {
    /* 把青春献给 */
    {392, 1}, {784, 2}, {784, 2}, {784, 2}, {784, 2}, {784, 4}, {659, 2}, {587, 2},
    /* 身后那座 */
    {587, 2}, {659, 2}, {659, 2}, {784, 2}, {784, 2}, {784, 2}, {880, 4},
    /* 辉煌的都市 */
    {523, 4}, {523, 4}, {440, 2}, {523, 4}, {440, 2},
    /* 为了这个 */
    {440, 2}, {523, 2}, {523, 2}, {523, 2}, {523, 2}, {440, 2}, {392, 2},
    /* 美梦我们 */
    {392, 2}, {523, 2}, {523, 2}, {523, 2}, {523, 2}, {784, 6},
    /* 付出着代价 */
    {659, 2}, {587, 2}, {523, 2}, {523, 2}, {523, 2}, {659, 2}, {523, 2}, {587, 2},
};

        然后将三首歌的结构体数组存放到结构体指针数组中,方便我们后续使用链表进行索引

STRUCT_MUSIC_NOTE_TYPEDEF *notes[SONG_MAX] = {
    birthday_notes, /* 生日快乐 */
    twoTigers_notes,/* 两只老虎 */
    elope_notes     /* 私奔 */
};

        这里面还引用了枚举值SONG_MAX,该枚举用于定义我们的歌曲列表中有哪些歌曲,枚举代码说明如下:

/* 歌曲曲目定义 */
typedef enum {
    SONG_BIRTHDAY,  /* 歌曲 生日快乐 */
    SONG_TWO_TIGER, /* 歌曲 两只老虎 */
    SONG_ELOPE,     /* 歌曲 私奔 */
    SONG_MAX,
} ENUM_MUSIC_SONG_TYPEDEF;

        为什么需要创建这样的一个枚举类型呢?是为了方便我们后续找到我们的歌曲列表中有多少首歌,当我们新增一首歌之后,只需要将歌曲的枚举声明添加到SONG_ELOPE之后,SONG_MAX之前,这样当我们每次调用SONG_MAX就能获取到歌曲里有多少首歌啦!

二、音乐播放器实现过程

1、 无源蜂鸣器初始化配置

无源蜂鸣器配置主要分为两个部分:GPIO引脚配置与复用的定时器通道配置,代码如下:

/* 无源蜂鸣器使用引脚 */
#define     BEEP_RCC_CLK        RCC_APB2Periph_GPIOB
#define     BEEP_GPIO_PORT      GPIOB
#define     BEEP_GPIO_PIN       GPIO_Pin_1

/**
 * @功能描述 GPIO基础功能初始化
 * @入口参数 无
 * @输出参数 无
 */
static void beep_gpio_config(void)
{
    RCC_APB2PeriphClockCmd(BEEP_RCC_CLK | RCC_APB2Periph_AFIO, ENABLE);   /* 使能端口时钟并开启复用时钟 */
    
    GPIO_InitTypeDef  GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin     = BEEP_GPIO_PIN;
    GPIO_InitStructure.GPIO_Speed   = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode    = GPIO_Mode_AF_PP;
    GPIO_Init(BEEP_GPIO_PORT, &GPIO_InitStructure);
}

/**
 * @功能描述 定时器复用功能PWM初始化
 * @入口参数 无
 * @输出参数 无
 */
static void beep_pwm_config(void) 
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); /* 开启时钟 */
    
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;     /* 定时器初始化 */
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;        /* 分频因子 */
    TIM_TimeBaseInitStruct.TIM_CounterMode   = TIM_CounterMode_Up;  /* 向上计数模式 */
    TIM_TimeBaseInitStruct.TIM_Period        = 7200 - 1;            /* 自动重装载值 */
    TIM_TimeBaseInitStruct.TIM_Prescaler     = 5 - 1;               /* 预分频值 */
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);                /* 完成定时器初始化 */
    
    TIM_OCInitTypeDef TIM_OCInitStruct; /* 定时器通道初始化 */
    TIM_OCInitStruct.TIM_OCMode      = TIM_OCMode_PWM1;             /* 初始化输出比较 */
    TIM_OCInitStruct.TIM_OCPolarity  = TIM_OCPolarity_High;
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStruct.TIM_Pulse       = 0;
    TIM_OC4Init(TIM3, &TIM_OCInitStruct);                           /* 定时器通道x初始化 */
    
    TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);               /* OCx预装载寄存器使能 */
    TIM_ARRPreloadConfig(TIM3, ENABLE);
    
    TIM_Cmd(TIM3, ENABLE);
}

/**
 * @功能描述 无源蜂鸣器初始化
 * @入口参数 无
 * @输出参数 无
 */
void beep_init(void)
{
    beep_gpio_config();
    beep_pwm_config();
}

2、 蜂鸣器频率、占空比、使能配置

        频率配置主要用于设置蜂鸣器的输出音调,即可以演奏出歌曲中的任意音调;占空比配置主要用于设置蜂鸣器的输出音量,即通过占空比控制蜂鸣器喇叭输出的高低;使能配置主要用于设置蜂鸣器是否输出,即蜂鸣器是否需要播放当前的音乐(切换播放与暂停功能的由来)。该部分代码如下:

/**
 * @功能描述 设置蜂鸣器输出音量
 * @入口参数 sound - 音量 0-100
 * @输出参数 无
 */
static void beep_set_sound(unsigned char sound)
{
    TIM_SetCompare4(TIM3, sound*7200/100);
}

/**
 * @功能描述 设置蜂鸣器输出频率
 * @入口参数 freq 频率
 * @输出参数 无
 */
static void beep_set_freq(unsigned short freq)
{
    TIM_PrescalerConfig(TIM3, 72000000/7200/freq, TIM_PSCReloadMode_Immediate);
}

/**
 * @功能描述 设置蜂鸣器是否输出
 * @入口参数 enable true-使能 false-失能
 * @输出参数 无
 */
static void beep_enable(bool enable)
{
    if(enable == true) {
        TIM_Cmd(TIM3, ENABLE);
    }
    else {
        TIM_Cmd(TIM3, DISABLE);
    }
}

/**
 * @功能描述 设置蜂鸣器播放音乐的音量 音调 使能
 * @入口参数 sound - 音量 0-100
 * @入口参数 freq  - 音调(蜂鸣器频率)
 * @入口参数 enable - 开/关蜂鸣器
 * @输出参数 无
 */
void beep_settings(unsigned char sound, unsigned short freq, bool enable)
{
    beep_set_sound(sound);
    beep_set_freq(freq);
    beep_enable(enable);
}

3、 音乐播放器列表清单初始化

        接着我们就需要定义我们的静态歌曲列表中有哪些歌曲了,代码如下:

STRUCT_BEEP_MUSIC_TYPEDEF myMusic[SONG_MAX] = {
    {true, 10, 0, 0, 0, 0, 25, "Happy Birthday", 0, 0, 0},
    {true, 50, 0, 0, 0, 0, 32, "Two Tigers",     0, 0, 0},
    {true, 80, 0, 0, 0, 0, 41, "Elope",          0, 0, 0},
};

        这里使用了一个新的结构体STRUCT_BEEP_MUSIC_TYPEDEF,其具体定义如下:

/* 音谱 */
typedef struct BEEP_MUSIC{
    bool isPause;                       /* 暂停/恢复音乐播放标志位 true-暂停 false-恢复 */
    unsigned char volume;               /* 歌曲播放的音量大小 0-100 */
    unsigned char repeats;              /* 当前节拍 需要重复的次数 */
    unsigned char repeat_cnt;           /* 当前节拍 重复次数计数值 */
    unsigned short curr_tone;           /* 当前的音调 */
    unsigned short tone_cnt;            /* 当前音调所处的计数值 */
    
    unsigned short total_tone;          /* 总音调的个数 */
    const char* song_title;             /* 歌曲名称 */
    STRUCT_MUSIC_NOTE_TYPEDEF *notes;   /* 歌曲音符表 */
    
    struct BEEP_MUSIC *pre_music;       /* 指向上一首歌曲 */
    struct BEEP_MUSIC *next_music;      /* 指向下一首歌曲 */
} STRUCT_BEEP_MUSIC_TYPEDEF;

        这个结构体用于描述音乐播放相关的信息,包括音量、播放状态、音调等,同时也包含了链表结构,因此也就可以串联多首歌曲了。

4、 音乐播放器初始化

static STRUCT_BEEP_MUSIC_TYPEDEF *cur_music;    /* 当前播放歌曲指针 */
static ENUM_MUSIC_MODE_TYPEDEF music_mode;      /* 歌单播放模式 */

/**
 * @功能描述 音乐播放器初始化函数
 * @入口参数 无
 * @输出参数 无
 */
void music_init(void)
{
    unsigned char i;
    
    /* 加载所有歌曲的歌单数据 */
    for(i=0; i<SONG_MAX; i++) {
        /* 为notes成员分配内存 */
        myMusic[i].notes = (STRUCT_MUSIC_NOTE_TYPEDEF *)malloc(myMusic[i].total_tone * sizeof(STRUCT_MUSIC_NOTE_TYPEDEF));
        /* 使用memcpy函数复制歌曲音符数据 */
        memcpy(myMusic[i].notes, notes[i], myMusic[i].total_tone * sizeof(STRUCT_MUSIC_NOTE_TYPEDEF));
    }

    /* 使用循环链表连接所有的歌曲 */
    for(i=0; i<SONG_MAX; i++) {
        (i == 0)          ? (myMusic[0].pre_music = &myMusic[SONG_MAX-1])  : (myMusic[i].pre_music = &myMusic[i-1]);
        (i == SONG_MAX-1) ? (myMusic[SONG_MAX-1].next_music = &myMusic[0]) : (myMusic[i].next_music = &myMusic[i+1]);
    }
    
    cur_music = &myMusic[0];    /* 初始化歌曲指针指向第一首歌 */
    music_mode = MODE_RANDOM;    /* 初始化播放模式为顺序播放模式 */
}

5、 设计音乐播放器处理函数

/**
 * @功能描述 蜂鸣器音乐播放器入口处理函数
 * @入口参数 无
 * @输出参数 无
 */
void beep_music_handler(void)
{
    /* 判断歌曲不存在 或 按下暂停键 就退出播放 */
    if(cur_music == NULL || cur_music->isPause == true) {
        beep_enable(false);
        return;
    }
    
    if(cur_music->curr_tone >= cur_music->total_tone) {
        music_switch_next();
    }
    
    if(cur_music->repeat_cnt >= cur_music->repeats) {
        cur_music->repeat_cnt = 0;
        cur_music->curr_tone ++;
        cur_music->repeats = cur_music->notes[cur_music->curr_tone].duration;
    }
    
    beep_settings(cur_music->volume, cur_music->notes[cur_music->curr_tone].frequency, 1);
    
    cur_music->repeat_cnt ++;
}

        这段代码是一个处理蜂鸣器音乐播放的函数beep_music_handler,主要功能如下:

  1. 首先判断当前的歌曲是否存在(即cur_music是否为NULL)或者当前歌曲是否处于暂停状态(isPause为true),如果是,则停止蜂鸣器的播放并退出函数。
  2. 如果当前音符的索引curr_tone大于等于总音符数total_tone,则切换到下一首歌曲(调用music_switch_next函数)。
  3. 如果当前歌曲的重复次数repeat_cnt大于等于设定的重复次数repeats,则重置重复计数器,并将当前音符索引指向下一个音符,同时更新重复次数为下一个音符的持续时间。
  4. 根据当前音符的音量和频率设置蜂鸣器的音调和音量,播放该音符。
  5. 增加当前歌曲的重复计数器值。

        这个函数实现了音乐播放器的核心逻辑,包括切换音符、切换歌曲、设置音调和音量等操作,以实现音乐的连续播放。

6、 其他业务代码(暂停、播放、上一曲、下一曲、音量调节)

        针对这部分的业务代码就比较简单了,只要能够理解STRUCT_BEEP_MUSIC_TYPEDEF结构体就知道这部分内容是在干嘛了,代码如下:

/**
 * @功能描述 音乐播放器 暂停播放音乐
 * @入口参数 无
 * @输出参数 无
 */
void music_pause_playback(void)
{
    cur_music->isPause = true;
}

/**
 * @功能描述 音乐播放器 恢复播放音乐
 * @入口参数 无
 * @输出参数 无
 */
void music_resume_playback(void)
{
    cur_music->isPause = false;
}

/**
 * @功能描述 音乐播放器 设置音量大小
 * @入口参数 volume - 音量 0-100
 * @输出参数 无
 */
void music_set_volume(unsigned char volume)
{
    cur_music->volume = volume;
}

/**
 * @功能描述 音乐播放器 获取音量大小
 * @入口参数 无
 * @输出参数 volume - 音量 0-100
 */
unsigned char music_get_volume(void)
{
    return (cur_music->volume);
}

/**
 * @功能描述 音乐播放器 设置歌曲播放模式
 * @入口参数 mode - 模式 顺序 单曲 随机
 * @输出参数 无
 */
void music_set_mode(ENUM_MUSIC_MODE_TYPEDEF mode)
{
    music_mode = mode;
}

/**
 * @功能描述 音乐播放器 获取歌曲播放模式
 * @入口参数 无
 * @输出参数 mode - 模式 顺序 单曲 随机
 */
ENUM_MUSIC_MODE_TYPEDEF music_get_mode(void)
{
    return (music_mode);
}

/**
 * @功能描述 音乐播放器 切换上一首
 * @入口参数 无
 * @输出参数 无
 */
void music_switch_previous(void)
{
    if(cur_music->pre_music != NULL) {
        /* 先清除正在播放的歌曲进度 */
        cur_music->isPause = true;
        cur_music->repeats = 0;
        cur_music->repeat_cnt = 0;
        cur_music->curr_tone = 0;
        cur_music->tone_cnt = 0;
        
        cur_music = cur_music->pre_music;
        cur_music->isPause = false;
    }
}

/**
 * @功能描述 生成指定范围内的随机整数
 * @入口参数 min-max 指定范围
 * @输出参数 无
 */
static int myRandom(int min, int max)
{
    return min + rand() % (max - min + 1);
}

/**
 * @功能描述 音乐播放器 切换下一首
 * @入口参数 无
 * @输出参数 无
 */
void music_switch_next(void)
{
    if(cur_music->next_music != NULL) {
        /* 清除正在播放的歌曲进度 */
        cur_music->repeats = 0;
        cur_music->repeat_cnt = 0;
        cur_music->curr_tone = 0;
        cur_music->tone_cnt = 0;
        
        switch(music_mode) {
            case MODE_ORDER: {
                cur_music->isPause = true;
                cur_music = cur_music->next_music;
                cur_music->isPause = false;
            }break;
            
            case MODE_SONGLE: {
                
            }break;
            
            case MODE_RANDOM: {
                cur_music->isPause = true;
                cur_music = &myMusic[myRandom(0, SONG_MAX-1)];
                cur_music->isPause = false;
            }break;
            
            default: break;
        }
    }
}

三、使用方法

1、初始化无源蜂鸣器

        找到一个无源蜂鸣器,并选择使用STM32的引脚(注意需要带定时器复用功能的,如本例中的PB1),然后在main函数中调用函数beep_init()进行蜂鸣器的初始化。

2、创建歌曲简谱

        找希望播放的歌曲,创建对应的结构体数组,创建步骤见 1.3节 识读乐谱。

        这里注意需要有四个地方需要修改:①beep.h中的歌曲枚举需要新增自己的歌曲枚举定义;②创建歌曲对应的结构体数组;③*notes指针数组中需要添加对应的结构体数组;④myMusic结构体数组中需要添加对应的歌曲初始化内容。

3、初始化音乐播放器

        然后需要在main函数中调用music_init()函数去初始化我们的静态音乐播放列表。

        注意:music_init()函数中初始化歌曲默认指向的第1首歌,并且播放模式为顺序播放模式。

4、 创建定时器运行音乐播放器处理函数

        这里我们需要创建一个任务定时器用于定时运行函数beep_music_handler(),定时时间为我们之前给定的200ms。当然如果不想创建定时器的话呢,也可以使用延时函数进行处理。

        注意:使用延时函数处理的话可能会存在任务的运行会受到延时函数的阻塞,影响系统的运行流畅度,因此推荐使用定时器。

5、 调用源码中的接口函数实现音乐播放器的功能

        我们可以创建按键扫描与键值获取函数,用于使用按键控制音乐的播放与暂停、上一曲与下一曲、播放模式的切换等功能。文章来源地址https://www.toymoban.com/news/detail-838186.html

6、 测试代码

int main(void)
{
    delay_init();
    usart1_init(115200);
    basic_tim_init();
    
    /* 外设初始化 */
    led_init();
    key_init();
    lcd_init();
    power_init();
    beep_init();
    
    music_init();
    
    lcd_clearGram();
    lcd_refreshGram();
    
    timer_createTask(T_TASK1, key_scan_10ms, 10);       /* 按键扫描任务 10ms */
    timer_createTask(T_TASK2, beep_music_handler, 200); /* 音乐播放器处理任务 200ms */
    
    while(1) {
        switch(key_getValue()) {
            case KEY_OK_S: {
                music_pause_playback();
            }break;
            
            case KEY_DOWN_S: {
                music_resume_playback();
            }break;
            
            case KEY_LEFT_S: {
                music_switch_previous();
            }break;
            
            case KEY_RIGHT_S: {
                music_switch_next();
            }break;
        }
    }
}

END

到了这里,关于开源小项目 - 基于无源蜂鸣器实现的音乐播放器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 三、HAL_无源蜂鸣器的驱动

    (1)KeilMDK:V5.38.0.0 (2)STM32CubeMX:V6.8.1 (3)MCU:STM32F407ZGT6         无源蜂鸣器内部没有振荡源,需要采用一定频率的方波才能驱动发声。详情参开以下文章。 八、51单片机之蜂鸣器_51蜂鸣器_朱嘉鼎的博客-CSDN博客 蜂鸣器简介无源蜂鸣器示例程序 https://blog.csdn.net/weixin_42727214/a

    2024年02月08日
    浏览(42)
  • 【STM32F103】谁在用蜂鸣器弹奏一曲东风破(无源蜂鸣器&PWM)

    蜂鸣器一般可以分为两种,一种是有源蜂鸣器,另一种是无源蜂鸣器。 有源蜂鸣器中的源是指震荡源,也就是给了电压再给个触发信号就会以固定的频率发出声响。 而无源蜂鸣器内部没有震荡源,我们可以将触发信号的频率传给无源蜂鸣器,这样就可以发出不同音阶的声响

    2024年01月19日
    浏览(47)
  • 无源蜂鸣器按特定频率发出相对应的音调

    蜂鸣器是什么呢?相信大家在做项目中肯定多多少少都会用到蜂鸣器,蜂鸣器呢是一种一体化结构的电子讯响器,采用直流电压供电,广泛应用于计算机、打印机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。比如台式电脑的主机开

    2024年02月02日
    浏览(39)
  • STM32-HAL-定时器(无源蜂鸣器的驱动)

    有源蜂鸣器: 有源蜂鸣器内部有一个发声电路,也就是“源”(震荡源,与无源蜂鸣器的区别),只要通电就可以响。 无源蜂鸣器: 无源蜂鸣器相当于扬声器,无源蜂鸣器直接接直流电,只在刚接触和离开的时候发声(利用电磁感应现象,通电、断电时推动振膜发声),一般

    2024年02月14日
    浏览(42)
  • 基于STM32制作的音乐播放器,用PWM控制蜂鸣器

    目录 效果展示  前言         一、设计背景         1.1、知识储备          二、系统设计方案         2.1、实现功能          2.2、硬件部分         2.3、软件部分          三、软件设计          3.1、设计流程图          3.2、音乐频率的设置          3.3、编

    2024年02月03日
    浏览(76)
  • STM32通过PWM输出使蜂鸣器实现播放音乐功能

    源码下载链接[点击跳转] https://download.csdn.net/download/Coin_Collecter/88641632        PWM ,全称 Pulse Width Modulation ,即脉宽调制技术,是一种通过改变信号的占空比来控制电路的技术。在 PWM 信号中,周期是固定的,而占空比则可以根据需要进行调整。通过改变占空比,可以控制电路

    2024年02月02日
    浏览(54)
  • FPGA实现蜂鸣器播放音乐实验,以儿歌《两只老虎》为例

    ** ** 开发板上电后,通过蜂鸣器播放《两只老虎》。 原理:想要让蜂鸣器播放音乐,就需要知道每个音符的频率,再通过换算将频率转换为周期,再结合开发板具体的系统时钟周期,便可计算出每个音符对应多少个系统时钟周期个数,通过对这些个数计数,便可控制蜂鸣器播

    2024年02月15日
    浏览(131)
  • 基于RT-Thread+STM32F407的蜂鸣器音乐播放器

    本项目为RT-Thread学习项目,参考于RT-Thread官网Demo示例 硬件基于STM32F407ZGT6正点原子探索者开发板+无源蜂鸣器模块 RTOS软件基于RT-Thread 4.0.5版本 编译器为官方的提供的RT-Thread Studio 会使用到STM32CubeMX 配置产生PWM波 会使用MobaXterm串口终端软件查看串口终端数据 官网Demo示例网址:

    2024年02月02日
    浏览(47)
  • 利用蜂鸣器播放音乐

            我们知道,蜂鸣器可分为有源蜂鸣器和无源蜂鸣器,有源蜂鸣器内置了频率发生电路,因此其通电就能够发出声音,但是其频率是固定的,而无源蜂鸣器内部没有频率发生电路,需要外界给予频率,因此适用于单片机控制使其发出不同的音调频率。在这一点有源蜂

    2024年02月12日
    浏览(51)
  • FPGA蜂鸣器播放音乐

      最近做EDA课设,看到自己的买的板子上有蜂鸣器,所以就打算做一个FPGA控制蜂鸣器播放音乐。   这里我使用的板子是睿智助学的FPGA开发板,板子上的芯片是 EP4CE6E22C8 ,如果是你使用的是其他开发板或者是自己做的板子,就根据原理图,在写完代码时绑定相应的引脚下

    2024年02月06日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包