feat: 添加MP3驱动模块并集成到系统初始化

新增MP3驱动模块,包含初始化、播放、暂停、停止、音量设置等功能
修改USART3波特率为9600以适配MP3模块
在系统启动时初始化MP3模块并播放启动音效
添加MP3播放索引定义文件
This commit is contained in:
2026-02-23 14:31:55 +08:00
parent 297b0f2384
commit ce8d6fd2eb
8 changed files with 384 additions and 28 deletions

View File

@@ -0,0 +1,265 @@
#include "mp3_driver.h"
#include "elog.h"
#include <string.h>
/* 外部UART句柄声明 */
extern UART_HandleTypeDef huart3;
/**
* @brief 计算MP3命令校验和
* @param cmd 命令数组(不包含校验位)
* @param len 数据长度
* @return 校验和(高位在前,低位在后)
* @note 校验算法从版本号开始到数据结束所有字节相加后取反再加1
*/
static void MP3_CalcChecksum(const uint8_t *cmd, uint8_t len, uint8_t *high, uint8_t *low)
{
uint16_t sum = 0;
// 从版本号(索引1)开始累加,到数据结束
for (uint8_t i = 1; i < len; i++) {
sum += cmd[i];
}
// 取反加1 (即0x10000 - sum)
sum = 0x10000 - sum;
// 高位在前,低位在后
*high = (sum >> 8) & 0xFF;
*low = sum & 0xFF;
}
/**
* @brief 发送MP3命令
* @param cmd 命令数组
* @param len 命令长度
* @return HAL状态
*/
static HAL_StatusTypeDef MP3_SendCommand(const uint8_t *cmd, uint16_t len)
{
return HAL_UART_Transmit(&huart3, (uint8_t *)cmd, len, 100);
}
/**
* @brief MP3模块初始化
* @return HAL状态
*/
HAL_StatusTypeDef MP3_Init(void)
{
elog_d("MP3", "开始初始化MP3模块...");
// 命令1开启声音 (0x06 - 0x00 - 0x00 - 0x1E)
// 0x1E = 30, 表示音量为30
uint8_t init_cmd1[] = {0x7E, 0xFF, 0x06, 0x06, 0x00, 0x00, 0x1E, 0xFE, 0xD7, 0xEF};
uint8_t high1, low1;
MP3_CalcChecksum(init_cmd1, 7, &high1, &low1);
init_cmd1[7] = high1;
init_cmd1[8] = low1;
HAL_StatusTypeDef ret1 = MP3_SendCommand(init_cmd1, sizeof(init_cmd1));
elog_d("MP3", "开启声音命令返回值: %d", ret1);
// 延时等待模块响应
HAL_Delay(100);
// 命令2选择TF卡作为音源 (0x09 - 0x00 - 0x00 - 0x02)
uint8_t init_cmd2[] = {0x7E, 0xFF, 0x06, 0x09, 0x00, 0x00, 0x02, 0xFE, 0xF0, 0xEF};
uint8_t high2, low2;
MP3_CalcChecksum(init_cmd2, 7, &high2, &low2);
init_cmd2[7] = high2;
init_cmd2[8] = low2;
HAL_StatusTypeDef ret2 = MP3_SendCommand(init_cmd2, sizeof(init_cmd2));
elog_d("MP3", "选择TF卡命令返回值: %d", ret2);
if (ret1 == HAL_OK && ret2 == HAL_OK) {
elog_i("MP3", "MP3模块初始化完成");
return HAL_OK;
} else {
elog_e("MP3", "MP3模块初始化失败");
return HAL_ERROR;
}
}
/**
* @brief 按索引播放指定曲目
* @param index 曲目索引 (1-9999)
* @return HAL状态
*/
HAL_StatusTypeDef MP3_Play(uint16_t index)
{
if (index == 0 || index > 9999) {
elog_e("MP3", "无效的曲目索引: %d", index);
return HAL_ERROR;
}
// 构建播放命令0x7E 0xFF 0x06 0x12 0x00 high(索引) low(索引) checksum1 checksum2 0xEF
uint8_t play_cmd[10];
play_cmd[0] = MP3_HEADER;
play_cmd[1] = MP3_VERSION;
play_cmd[2] = MP3_LENGTH;
play_cmd[3] = MP3_CMD_PLAY_INDEX;
play_cmd[4] = 0x00;
play_cmd[5] = (index >> 8) & 0xFF; // 索引高字节
play_cmd[6] = index & 0xFF; // 索引低字节
// 计算校验和
uint8_t high, low;
MP3_CalcChecksum(play_cmd, 7, &high, &low);
play_cmd[7] = high;
play_cmd[8] = low;
play_cmd[9] = MP3_FOOTER;
// 发送命令
HAL_StatusTypeDef ret = MP3_SendCommand(play_cmd, sizeof(play_cmd));
if (ret == HAL_OK) {
elog_d("MP3", "播放曲目 %d", index);
} else {
elog_e("MP3", "播放曲目 %d 失败", index);
}
return ret;
}
/**
* @brief 停止播放
* @return HAL状态
*/
HAL_StatusTypeDef MP3_Stop(void)
{
// 构建停止命令0x7E 0xFF 0x06 0x16 0x00 0x00 0x00 checksum1 checksum2 0xEF
uint8_t stop_cmd[10];
stop_cmd[0] = MP3_HEADER;
stop_cmd[1] = MP3_VERSION;
stop_cmd[2] = MP3_LENGTH;
stop_cmd[3] = MP3_CMD_STOP;
stop_cmd[4] = 0x00;
stop_cmd[5] = 0x00;
stop_cmd[6] = 0x00;
// 计算校验和
uint8_t high, low;
MP3_CalcChecksum(stop_cmd, 7, &high, &low);
stop_cmd[7] = high;
stop_cmd[8] = low;
stop_cmd[9] = MP3_FOOTER;
HAL_StatusTypeDef ret = MP3_SendCommand(stop_cmd, sizeof(stop_cmd));
if (ret == HAL_OK) {
elog_d("MP3", "停止播放");
} else {
elog_e("MP3", "停止播放失败");
}
return ret;
}
/**
* @brief 暂停播放
* @return HAL状态
*/
HAL_StatusTypeDef MP3_Pause(void)
{
// 构建暂停命令0x7E 0xFF 0x06 0x0E 0x00 0x00 0x00 checksum1 checksum2 0xEF
uint8_t pause_cmd[10];
pause_cmd[0] = MP3_HEADER;
pause_cmd[1] = MP3_VERSION;
pause_cmd[2] = MP3_LENGTH;
pause_cmd[3] = MP3_CMD_PAUSE;
pause_cmd[4] = 0x00;
pause_cmd[5] = 0x00;
pause_cmd[6] = 0x00;
// 计算校验和
uint8_t high, low;
MP3_CalcChecksum(pause_cmd, 7, &high, &low);
pause_cmd[7] = high;
pause_cmd[8] = low;
pause_cmd[9] = MP3_FOOTER;
HAL_StatusTypeDef ret = MP3_SendCommand(pause_cmd, sizeof(pause_cmd));
if (ret == HAL_OK) {
elog_d("MP3", "暂停播放");
} else {
elog_e("MP3", "暂停播放失败");
}
return ret;
}
/**
* @brief 设置音量
* @param volume 音量值 (0-30)
* @return HAL状态
*/
HAL_StatusTypeDef MP3_SetVolume(uint8_t volume)
{
if (volume > 30) {
volume = 30; // 最大音量30
}
// 构建音量设置命令0x7E 0xFF 0x06 0x06 0x00 0x00 volume checksum1 checksum2 0xEF
uint8_t vol_cmd[10];
vol_cmd[0] = MP3_HEADER;
vol_cmd[1] = MP3_VERSION;
vol_cmd[2] = MP3_LENGTH;
vol_cmd[3] = MP3_CMD_SET_VOLUME;
vol_cmd[4] = 0x00;
vol_cmd[5] = 0x00;
vol_cmd[6] = volume;
// 计算校验和
uint8_t high, low;
MP3_CalcChecksum(vol_cmd, 7, &high, &low);
vol_cmd[7] = high;
vol_cmd[8] = low;
vol_cmd[9] = MP3_FOOTER;
HAL_StatusTypeDef ret = MP3_SendCommand(vol_cmd, sizeof(vol_cmd));
if (ret == HAL_OK) {
elog_d("MP3", "设置音量: %d", volume);
} else {
elog_e("MP3", "设置音量失败");
}
return ret;
}
/**
* @brief 设置音源
* @param source 音源类型 (1=U盘, 2=SD卡/TF卡)
* @return HAL状态
*/
HAL_StatusTypeDef MP3_SetSource(uint8_t source)
{
if (source != MP3_SOURCE_U_DISK && source != MP3_SOURCE_SD_CARD) {
elog_e("MP3", "无效的音源类型: %d", source);
return HAL_ERROR;
}
// 构建音源设置命令0x7E 0xFF 0x06 0x09 0x00 0x00 source checksum1 checksum2 0xEF
uint8_t source_cmd[10];
source_cmd[0] = MP3_HEADER;
source_cmd[1] = MP3_VERSION;
source_cmd[2] = MP3_LENGTH;
source_cmd[3] = MP3_CMD_SET_SOURCE;
source_cmd[4] = 0x00;
source_cmd[5] = 0x00;
source_cmd[6] = source;
// 计算校验和
uint8_t high, low;
MP3_CalcChecksum(source_cmd, 7, &high, &low);
source_cmd[7] = high;
source_cmd[8] = low;
source_cmd[9] = MP3_FOOTER;
HAL_StatusTypeDef ret = MP3_SendCommand(source_cmd, sizeof(source_cmd));
if (ret == HAL_OK) {
elog_d("MP3", "设置音源: %d", source);
} else {
elog_e("MP3", "设置音源失败");
}
return ret;
}

View File

@@ -0,0 +1,45 @@
#ifndef __MP3_DRIVER_H
#define __MP3_DRIVER_H
#ifdef __cplusplus
extern "C" {
#endif
#include "stm32f1xx_hal.h"
#include <stdint.h>
/* MP3命令定义 */
#define MP3_HEADER 0x7E // 帧头
#define MP3_VERSION 0xFF // 版本号
#define MP3_LENGTH 0x06 // 数据长度
#define MP3_FOOTER 0xEF // 帧尾
/* MP3功能码 */
#define MP3_CMD_SET_VOLUME 0x06 // 设置音量
#define MP3_CMD_SET_EQ 0x07 // 设置EQ
#define MP3_CMD_SET_MODE 0x08 // 设置播放模式
#define MP3_CMD_SET_SOURCE 0x09 // 设置音源
#define MP3_CMD_PLAY 0x0D // 播放
#define MP3_CMD_PAUSE 0x0E // 暂停
#define MP3_CMD_PREV 0x0A // 上一曲
#define MP3_CMD_NEXT 0x0B // 下一曲
#define MP3_CMD_PLAY_INDEX 0x12 // 按索引播放
#define MP3_CMD_STOP 0x16 // 停止
/* MP3音源选择 */
#define MP3_SOURCE_U_DISK 0x01 // U盘
#define MP3_SOURCE_SD_CARD 0x02 // TF卡/SD卡
/* 函数声明 */
HAL_StatusTypeDef MP3_Init(void);
HAL_StatusTypeDef MP3_Play(uint16_t index);
HAL_StatusTypeDef MP3_Stop(void);
HAL_StatusTypeDef MP3_Pause(void);
HAL_StatusTypeDef MP3_SetVolume(uint8_t volume);
HAL_StatusTypeDef MP3_SetSource(uint8_t source);
#ifdef __cplusplus
}
#endif
#endif /* __MP3_DRIVER_H */

View File

@@ -0,0 +1,33 @@
/******************** 系统状态与初始化 ********************/
#define SYS_POWER_ON 1 ///< 系统上电完成-智能宠物喂食系统启动完成,进入待机模式
#define WIFI_CONNECT_OK 2 ///< WiFi连接成功-WiFi连接成功云平台数据同步已开启
#define WIFI_CONNECT_FAIL 3 ///< WiFi连接失败-WiFi连接失败请检查网络配置
/******************** 喂食模块 ********************/
#define FEED_AUTO_START 4 ///< 自动喂食启动-自动喂食模式启动,步进电机开始出粮
#define FEED_IN_PROGRESS 5 ///< 喂食进行中-正在出粮,称重模块实时监测中
#define FEED_COMPLETE 6 ///< 喂食完成-喂食完成,当前食物重量已达设定值
#define FEED_MANUAL_TRIGGER 7 ///< 手动喂食触发-手动喂食指令已接收,开始出粮
#define FOOD_LOW_ALARM 8 ///< 食物余量不足-警告:食物余量低于下限,请及时添加
/******************** 喂水模块 ********************/
#define WATER_REFILL_START 9 ///< 自动补水启动-水位低于阈值,水泵启动,开始自动补水
#define WATER_REFILL_DONE 10 ///< 补水完成-补水完成,水位已达设定上限
#define WATER_LOW_ALARM 11 ///< 水位过低-警告:水位过低,请检查水源或水泵
#define WATER_PIR_REFILL 12 ///< PIR联动补水-检测到宠物靠近,水位偏低,启动自动补水
/******************** 模式切换 ********************/
#define MODE_AUTO 13 ///< 切换自动模式-已切换至自动运行模式
#define MODE_MANUAL 14 ///< 切换手动模式-已切换至手动控制模式
/******************** 参数设置 ********************/
#define PARAM_SAVE_OK 15 ///< 参数设置成功-参数设置已保存,系统配置已更新
#define PARAM_TIME_SET 16 ///< 定时时间修改-自动喂食时间已更新
#define PARAM_WEIGHT_SET 17 ///< 重量阈值修改-喂食重量阈值已更新
/******************** 远程控制与异常 ********************/
#define REMOTE_CMD_RECEIVED 18 ///< 远程指令接收-接收到微信小程序远程控制指令
#define DATA_UPLOAD_FAIL 19 ///< 数据上传失败-数据上传失败,请检查网络连接
#define SYS_ERROR_ALARM 20 ///< 系统异常-系统检测到异常,请检查硬件模块

View File

@@ -458,7 +458,7 @@ void wifi_task_mqtt(void *argument) {
elog_i(TAG, "生成ClientID: %s", client_id);
elog_i(TAG, "开始MQTT连接...");
if (!WIFI_Connect_MQTT("WIFI_TEST", "88888888", "www.beihong.wang", 1883,
if (!WIFI_Connect_MQTT("WIFI_TEST", "88888888", "mqtt.beihong.wang", 1883,
client_id, "STM32_MQTT", "123456")) {
elog_e(TAG, "MQTT连接失败任务退出");
vTaskDelete(NULL); // 删除自身任务

View File

@@ -19,17 +19,21 @@
/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#include "main.h"
#include "task.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "device_ctrl.h"
#include "dx_wf_24.h"
#include "elog.h"
#include "mp3_driver.h"
#include "spi_st7735s.h"
#include "stdio.h"
#include "mp3_play_index.h"
/* USER CODE END Includes */
@@ -56,23 +60,23 @@
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 256 * 4,
.priority = (osPriority_t) osPriorityHigh1,
.name = "defaultTask",
.stack_size = 256 * 4,
.priority = (osPriority_t)osPriorityHigh1,
};
/* Definitions for wifi_mqtt */
osThreadId_t wifi_mqttHandle;
const osThreadAttr_t wifi_mqtt_attributes = {
.name = "wifi_mqtt",
.stack_size = 3000 * 4,
.priority = (osPriority_t) osPriorityHigh,
.name = "wifi_mqtt",
.stack_size = 3000 * 4,
.priority = (osPriority_t)osPriorityHigh,
};
/* Definitions for LCD_SHOW_Task */
osThreadId_t LCD_SHOW_TaskHandle;
const osThreadAttr_t LCD_SHOW_Task_attributes = {
.name = "LCD_SHOW_Task",
.stack_size = 1024 * 4,
.priority = (osPriority_t) osPriorityHigh,
.name = "LCD_SHOW_Task",
.stack_size = 1024 * 4,
.priority = (osPriority_t)osPriorityHigh,
};
/* Private function prototypes -----------------------------------------------*/
@@ -87,10 +91,10 @@ void LCD_Task(void *argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
/**
* @brief FreeRTOS initialization
* @param None
* @retval None
*/
* @brief FreeRTOS initialization
* @param None
* @retval None
*/
void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN Init */
ST7735_Init(); // 初始化ST7735显示屏
@@ -114,7 +118,8 @@ void MX_FREERTOS_Init(void) {
/* Create the thread(s) */
/* creation of defaultTask */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
defaultTaskHandle =
osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* creation of wifi_mqtt */
wifi_mqttHandle = osThreadNew(wifi_task_mqtt, NULL, &wifi_mqtt_attributes);
@@ -129,7 +134,6 @@ void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
}
/* USER CODE BEGIN Header_StartDefaultTask */
@@ -139,8 +143,7 @@ void MX_FREERTOS_Init(void) {
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
void StartDefaultTask(void *argument) {
/* USER CODE BEGIN StartDefaultTask */
// 1. 打开运行灯
Device_Control(DEVICE_LED_RUN, 1);
@@ -148,6 +151,16 @@ void StartDefaultTask(void *argument)
// 2. 初始化日志和屏幕(在 RTOS 任务中)
easylogger_init();
// 初始化MP3模块
HAL_StatusTypeDef ret = MP3_Init();
if (ret != HAL_OK) {
elog_e("MP3", "MP3模块初始化失败");
return;
}
elog_i("MP3", "模块初始化完成");
MP3_Play(SYS_POWER_ON);
/* Infinite loop */
for (;;) {
@@ -166,8 +179,7 @@ void StartDefaultTask(void *argument)
* @retval None
*/
/* USER CODE END Header_LCD_Task */
void LCD_Task(void *argument)
{
void LCD_Task(void *argument) {
/* USER CODE BEGIN LCD_Task */
char time_str[16];
@@ -208,4 +220,3 @@ void LCD_Task(void *argument)
/* USER CODE BEGIN Application */
/* USER CODE END Application */

View File

@@ -100,7 +100,7 @@ void MX_USART3_UART_Init(void)
/* USER CODE END USART3_Init 1 */
huart3.Instance = USART3;
huart3.Init.BaudRate = 115200;
huart3.Init.BaudRate = 9600;
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;