- 新增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等
266 lines
6.9 KiB
C
266 lines
6.9 KiB
C
#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;
|
||
}
|