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

2024-03-16 03:10

本文主要是介绍开源小项目 - 基于无源蜂鸣器实现的音乐播放器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、音乐简谱相关知识

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,称为唱名。

图1  音符说明

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

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

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

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

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

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

图2  单纯音符说明

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

图3  附点音符说明

2、音调

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

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

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

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

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

图4  音调说明

3、识读简谱

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

图5  《两只老虎》简谱

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

1. 找到简谱中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、 调用源码中的接口函数实现音乐播放器的功能

        我们可以创建按键扫描与键值获取函数,用于使用按键控制音乐的播放与暂停、上一曲与下一曲、播放模式的切换等功能。

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

这篇关于开源小项目 - 基于无源蜂鸣器实现的音乐播放器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/814093

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

阿里开源语音识别SenseVoiceWindows环境部署

SenseVoice介绍 SenseVoice 专注于高精度多语言语音识别、情感辨识和音频事件检测多语言识别: 采用超过 40 万小时数据训练,支持超过 50 种语言,识别效果上优于 Whisper 模型。富文本识别:具备优秀的情感识别,能够在测试数据上达到和超过目前最佳情感识别模型的效果。支持声音事件检测能力,支持音乐、掌声、笑声、哭声、咳嗽、喷嚏等多种常见人机交互事件进行检测。高效推

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定