Files
SmartMassager_STM32/Core/Src/mp3_driver.c
wangbeihong b883e0a7f9 feat: 实现智能按摩器完整功能 (定时/语音/电机/显示)
- 新增MP3语音模块驱动,集成18个语音提示,支持播放/停止/音量调节
- 实现0/10/20/30分钟循环定时倒计时,实时显示剩余时间,定时结束自动停机
- 优化电机控制:一档力度提升至75%,降档冲击时间延长至800ms,降档时触发100%冲击防卡顿
- 屏幕显示重构为分区动态更新:时间状态(Zone1)/档位(Zone2)/加热(Zone3),性能优化
- 新增LED指示:运行时RUN_LED以500ms周期闪烁,无效操作ERR_LED亮1秒
- 操作逻辑改进:所有操作需先设定时间,否则播放"请先设定时间"并亮ERR_LED
- 加热控制由GPIO改为PWM(TIM1_CH1),占空比300
- 调整USART3波特率为9600适配MP3模块
- FreeRTOS任务优化:Motor周期500ms,Screen周期10ms,新增定时器秒Tick处理
- 完善开发文档:MP3集成指南、校验和验证脚本、文件重命名工具等

新增文件:mp3_driver.h/c, 多项开发文档
修改文件:freertos.c, gbk_text.h/c, motor_driver.c, screen.h/c, usart.c, gpio.c等
2026-02-17 23:52:17 +08:00

266 lines
6.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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;
}