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等
This commit is contained in:
@@ -6,46 +6,46 @@
|
||||
// GBK文本数组声明(定义在gbk_text.c中)
|
||||
extern const uint8_t text_device_name_GBK[];
|
||||
extern const uint8_t text_loading_GBK[];
|
||||
extern const uint8_t text_massage_on_GBK[];
|
||||
extern const uint8_t text_massage_off_GBK[];
|
||||
extern const uint8_t text_heat_on_GBK[];
|
||||
extern const uint8_t text_heat_off_GBK[];
|
||||
extern const uint8_t text_gear_GBK[];
|
||||
extern const uint8_t text_time_GBK[];
|
||||
extern const uint8_t text_cont_GBK[];
|
||||
extern const uint8_t text_gear_1_GBK[];
|
||||
extern const uint8_t text_gear_2_GBK[];
|
||||
extern const uint8_t text_gear_3_GBK[];
|
||||
extern const uint8_t text_time_1_GBK[];
|
||||
extern const uint8_t text_time_2_GBK[];
|
||||
extern const uint8_t text_time_3_GBK[];
|
||||
extern const uint8_t text_remaining_GBK[];
|
||||
extern const uint8_t text_running_GBK[];
|
||||
extern const uint8_t text_paused_GBK[];
|
||||
extern const uint8_t text_ended_GBK[];
|
||||
extern const uint8_t text_stop_GBK[];
|
||||
|
||||
// GBK文本长度宏(预计算,不含\0)
|
||||
#define text_device_name_LEN (10)
|
||||
#define text_loading_LEN (6)
|
||||
#define text_massage_on_LEN (8)
|
||||
#define text_massage_off_LEN (8)
|
||||
#define text_heat_on_LEN (8)
|
||||
#define text_heat_off_LEN (8)
|
||||
#define text_gear_LEN (4)
|
||||
#define text_time_LEN (6)
|
||||
#define text_cont_LEN (4)
|
||||
#define text_remaining_LEN (8)
|
||||
#define text_running_LEN (6)
|
||||
#define text_paused_LEN (6)
|
||||
#define text_ended_LEN (6)
|
||||
#define text_massage_off_LEN (13)
|
||||
#define text_heat_on_LEN (13)
|
||||
#define text_heat_off_LEN (13)
|
||||
#define text_gear_1_LEN (15)
|
||||
#define text_gear_2_LEN (15)
|
||||
#define text_gear_3_LEN (15)
|
||||
#define text_time_1_LEN (15)
|
||||
#define text_time_2_LEN (15)
|
||||
#define text_time_3_LEN (15)
|
||||
#define text_remaining_LEN (13)
|
||||
#define text_stop_LEN (14)
|
||||
|
||||
// 兼容旧代码的别名
|
||||
#define text_device_name text_device_name_GBK
|
||||
#define text_loading text_loading_GBK
|
||||
#define text_massage_on text_massage_on_GBK
|
||||
#define text_massage_off text_massage_off_GBK
|
||||
#define text_heat_on text_heat_on_GBK
|
||||
#define text_heat_off text_heat_off_GBK
|
||||
#define text_gear text_gear_GBK
|
||||
#define text_time text_time_GBK
|
||||
#define text_cont text_cont_GBK
|
||||
#define text_gear_1 text_gear_1_GBK
|
||||
#define text_gear_2 text_gear_2_GBK
|
||||
#define text_gear_3 text_gear_3_GBK
|
||||
#define text_time_1 text_time_1_GBK
|
||||
#define text_time_2 text_time_2_GBK
|
||||
#define text_time_3 text_time_3_GBK
|
||||
#define text_remaining text_remaining_GBK
|
||||
#define text_running text_running_GBK
|
||||
#define text_paused text_paused_GBK
|
||||
#define text_ended text_ended_GBK
|
||||
#define text_stop text_stop_GBK
|
||||
|
||||
#endif // __GBK_TEXT_H__
|
||||
|
||||
45
Core/Inc/mp3_driver.h
Normal file
45
Core/Inc/mp3_driver.h
Normal 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 */
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "main.h"
|
||||
#include "task.h"
|
||||
|
||||
|
||||
/* Private includes ----------------------------------------------------------*/
|
||||
/* USER CODE BEGIN Includes */
|
||||
#include "elog.h"
|
||||
@@ -36,7 +35,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/_intsup.h>
|
||||
|
||||
#include "mp3_driver.h"
|
||||
/* USER CODE END Includes */
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
@@ -64,7 +63,7 @@ PUTCHAR_PROTOTYPE {
|
||||
/* USER CODE END PM */
|
||||
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
/* USER CODE BEGIN Variables */
|
||||
/* USER CODE BEGIN Variables */
|
||||
|
||||
/* 按键状态位: bit0..bit3 分别对应 M__KEY, M__KEYC7, HOT_KEY, TIME_KEY,
|
||||
* 为1表示按下 */
|
||||
@@ -72,8 +71,14 @@ volatile uint8_t key_state = 0;
|
||||
volatile uint8_t key_state_prev = 0; /* 上一次的按键状态,用于检测按键变化 */
|
||||
|
||||
/* 设备状态标志:0表示关闭,1表示打开 */
|
||||
static uint8_t hot_state = 0; /* HOT 设备状态 */
|
||||
static uint8_t time_state = 0; /* TIME_KEY 时间状态 */
|
||||
static uint8_t hot_state = 0; /* HOT 设备状态 */
|
||||
|
||||
/* 定时器相关变量 */
|
||||
static uint8_t timer_minutes = 0; // 当前设定的分钟数 (0/10/20/30)
|
||||
static uint32_t remaining_seconds = 0; // 剩余秒数
|
||||
static uint8_t is_running = 0; // 运行状态标志 (0=停止, 1=运行)
|
||||
static uint8_t last_display_minutes = 0xFF; // 上一次显示的分钟数
|
||||
static uint8_t run_led_state = 0; // RUN_LED闪烁状态 (0=灭, 1=亮)
|
||||
|
||||
/* USER CODE END Variables */
|
||||
/* Definitions for defaultTask */
|
||||
@@ -125,6 +130,127 @@ const osEventFlagsAttr_t init_ok_attributes = {.name = "init_ok"};
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
/* USER CODE BEGIN FunctionPrototypes */
|
||||
|
||||
/**
|
||||
* @brief 检查是否可以进行操作(电机/加热)
|
||||
* @return 1=允许操作, 0=禁止操作
|
||||
*/
|
||||
uint8_t Timer_CanOperate(void) { return (timer_minutes > 0) ? 1 : 0; }
|
||||
|
||||
/**
|
||||
* @brief 获取剩余秒数(供外部调用显示倒计时)
|
||||
* @return 剩余秒数
|
||||
*/
|
||||
uint32_t Timer_GetRemainingSeconds(void) { return remaining_seconds; }
|
||||
|
||||
/**
|
||||
* @brief 获取设定的分钟数
|
||||
* @return 设定的分钟数
|
||||
*/
|
||||
uint8_t Timer_GetMinutes(void) { return timer_minutes; }
|
||||
|
||||
/**
|
||||
* @brief 获取运行状态
|
||||
* @return 1=运行中, 0=停止
|
||||
*/
|
||||
uint8_t Timer_IsRunning(void) { return is_running; }
|
||||
|
||||
/**
|
||||
* @brief 动态生成剩余时间 GBK 字符串
|
||||
* @param minutes 剩余分钟数 (0-99)
|
||||
* @param buf 输出缓冲区(至少需要 14 字节)
|
||||
* @return 生成的字符串长度
|
||||
*/
|
||||
static uint8_t FormatRemainingTime(uint8_t minutes, uint8_t *buf) {
|
||||
uint8_t len = 0;
|
||||
|
||||
// 固定前缀 "时间:剩余" (GBK: 0xCA,0xB1,0xBC,0xE4,0x3A,0xCA,0xA3,0xD3,0xE0)
|
||||
const uint8_t prefix[] = {0xCA, 0xB1, 0xBC, 0xE4, 0x3A,
|
||||
0xCA, 0xA3, 0xD3, 0xE0};
|
||||
for (uint8_t i = 0; i < sizeof(prefix); i++) {
|
||||
buf[len++] = prefix[i];
|
||||
}
|
||||
|
||||
// 动态数字部分:将分钟数转换为 ASCII
|
||||
if (minutes >= 10) {
|
||||
buf[len++] = '0' + (minutes / 10); // 十位
|
||||
}
|
||||
buf[len++] = '0' + (minutes % 10); // 个位
|
||||
|
||||
// 固定后缀 "分" (GBK: 0xB7, 0xD6)
|
||||
buf[len++] = 0xB7;
|
||||
buf[len++] = 0xD6;
|
||||
|
||||
return len;
|
||||
}
|
||||
// 全局缓冲区
|
||||
static uint8_t gbk_remaining_buf[16];
|
||||
|
||||
/**
|
||||
* @brief 更新 Zone 1 的时间显示
|
||||
*/
|
||||
static void Display_UpdateTime(void) {
|
||||
if (is_running) {
|
||||
// 运行中:显示剩余时间(动态计算)
|
||||
uint8_t remaining_minutes = remaining_seconds / 60;
|
||||
uint8_t len = FormatRemainingTime(remaining_minutes, gbk_remaining_buf);
|
||||
Screen_ShowInZone(1, gbk_remaining_buf, len);
|
||||
} else if (timer_minutes > 0) {
|
||||
// 已设定时间但未运行:显示设定时间
|
||||
switch (timer_minutes) {
|
||||
case 10:
|
||||
Screen_ShowInZone(1, text_time_1, text_time_1_LEN);
|
||||
break;
|
||||
case 20:
|
||||
Screen_ShowInZone(1, text_time_2, text_time_2_LEN);
|
||||
break;
|
||||
case 30:
|
||||
Screen_ShowInZone(1, text_time_3, text_time_3_LEN);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// 无定时:显示停止状态
|
||||
Screen_ShowInZone(1, text_stop, text_stop_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 定时器倒计时处理(每秒调用一次)
|
||||
*/
|
||||
void Timer_Tick(void) {
|
||||
if (is_running && remaining_seconds > 0) {
|
||||
remaining_seconds--;
|
||||
|
||||
// 时间到,停止所有设备
|
||||
if (remaining_seconds == 0) {
|
||||
is_running = 0;
|
||||
timer_minutes = 0;
|
||||
|
||||
// 停止电机
|
||||
Motor_SetGear(0);
|
||||
|
||||
// 停止加热
|
||||
hot_state = 0;
|
||||
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
|
||||
|
||||
elog_i("Timer", "定时结束,设备已停止");
|
||||
|
||||
// 播放语音: 定时结束,按摩结束
|
||||
MP3_Play(18);
|
||||
|
||||
// 更新 Zone 1 显示为停止状态
|
||||
Screen_ShowInZone(1, text_stop, text_stop_LEN);
|
||||
|
||||
// 更新 Zone 2 显示为停止状态(按摩已停止)
|
||||
Screen_ShowInZone(2, text_massage_off, text_massage_off_LEN);
|
||||
|
||||
// 重置显示记录,避免下次启动时的显示问题
|
||||
last_display_minutes = 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* USER CODE END FunctionPrototypes */
|
||||
|
||||
void StartDefaultTask(void *argument);
|
||||
@@ -228,10 +354,11 @@ void StartDefaultTask(void *argument) {
|
||||
if (flags == ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4))) {
|
||||
|
||||
// 所有初始化完成,开始执行按摩逻辑
|
||||
// Screen_DrawText16V_GBK(1, 30, text_stop_massage, text_stop_massage_LEN, 15);
|
||||
Screen_ShowInZone(1, text_ended, text_ended_LEN);
|
||||
// Screen_DrawText16V_GBK(1, 30, text_stop_massage, text_stop_massage_LEN,
|
||||
// 15);
|
||||
Screen_ShowInZone(1, text_stop, text_stop_LEN);
|
||||
Screen_ShowInZone(2, text_massage_off, text_massage_off_LEN);
|
||||
Screen_ShowInZone(3, text_heat_off,text_heat_off_LEN);
|
||||
Screen_ShowInZone(3, text_heat_off, text_heat_off_LEN);
|
||||
}
|
||||
|
||||
elog_d("Init", "完成所有的初始化");
|
||||
@@ -315,43 +442,158 @@ void Sensor(void *argument) {
|
||||
|
||||
/* ===== M__KEY (bit0) 控制 ===== */
|
||||
if (key_pressed & (1 << 0)) {
|
||||
/* M__KEY 按下:加档(提高转速) */
|
||||
elog_i("Key", "M__KEY按下 - 加档");
|
||||
if (Motor_GetGear() == 0) {
|
||||
Motor_StartupBoost();
|
||||
if (Timer_CanOperate()) { // 添加判断
|
||||
/* M__KEY 按下:加档(提高转速) */
|
||||
elog_i("Key", "M__KEY按下 - 加档");
|
||||
if (Motor_GetGear() == 0) {
|
||||
Motor_StartupBoost();
|
||||
is_running = 1; // 开始运行
|
||||
remaining_seconds = timer_minutes * 60;
|
||||
// 播放语音: 按摩开始
|
||||
MP3_Play(7);
|
||||
} else {
|
||||
// 检查当前档位,如果已经是3档则提示已到最大
|
||||
uint8_t old_gear = Motor_GetGear();
|
||||
if (old_gear == 3) {
|
||||
MP3_Play(11); // 已到最大档位
|
||||
} else {
|
||||
Motor_GearUp();
|
||||
uint8_t new_gear = Motor_GetGear();
|
||||
// 档位变化时播放语音
|
||||
if (new_gear == 1) {
|
||||
MP3_Play(8); // 一档
|
||||
} else if (new_gear == 2) {
|
||||
MP3_Play(9); // 二档
|
||||
} else if (new_gear == 3) {
|
||||
MP3_Play(10); // 三档
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Motor_GearUp();
|
||||
elog_w("Key", "M__KEY无效 - 请先设定时间");
|
||||
// 播放语音: 请先设定时间
|
||||
MP3_Play(17);
|
||||
elog_d("LED", "Sensor任务: ERR_LED亮");
|
||||
HAL_GPIO_WritePin(ERR_LED_GPIO_Port, ERR_LED_Pin, GPIO_PIN_RESET); // ERR亮
|
||||
osDelay(1000); // LED亮1秒
|
||||
elog_d("LED", "Sensor任务: ERR_LED灭");
|
||||
HAL_GPIO_WritePin(ERR_LED_GPIO_Port, ERR_LED_Pin, GPIO_PIN_SET); // ERR灭
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== M__KEYC7 (bit1) 控制 ===== */
|
||||
if (key_pressed & (1 << 1)) {
|
||||
/* M__KEYC7 按下:降档(降低转速) */
|
||||
elog_i("Key", "M__KEYC7按下 - 降档");
|
||||
Motor_GearDown();
|
||||
if (Timer_CanOperate()) { // 添加判断
|
||||
/* M__KEYC7 按下:降档(降低转速) */
|
||||
elog_i("Key", "M__KEYC7按下 - 降档");
|
||||
Motor_GearDown();
|
||||
uint8_t new_gear = Motor_GetGear();
|
||||
|
||||
// 降档时播放语音
|
||||
if (new_gear == 0) {
|
||||
MP3_Play(14); // 按摩停止
|
||||
is_running = 0;
|
||||
} else if (new_gear == 1) {
|
||||
MP3_Play(12); // 一档
|
||||
} else if (new_gear == 2) {
|
||||
MP3_Play(13); // 二档
|
||||
}
|
||||
} else {
|
||||
elog_w("Key", "M__KEYC7无效 - 请先设定时间");
|
||||
// 播放语音: 请先设定时间
|
||||
MP3_Play(17);
|
||||
elog_d("LED", "Sensor任务: ERR_LED亮");
|
||||
HAL_GPIO_WritePin(ERR_LED_GPIO_Port, ERR_LED_Pin, GPIO_PIN_RESET); // ERR亮
|
||||
osDelay(1000); // LED亮1秒
|
||||
elog_d("LED", "Sensor任务: ERR_LED灭");
|
||||
HAL_GPIO_WritePin(ERR_LED_GPIO_Port, ERR_LED_Pin, GPIO_PIN_SET); // ERR灭
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== HOT_KEY (bit2) 控制 ===== */
|
||||
if (key_pressed & (1 << 2)) {
|
||||
/* HOT_KEY 按下:切换热功能 */
|
||||
hot_state = !hot_state;
|
||||
if (hot_state) {
|
||||
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 300);
|
||||
elog_d("Hot", "设置PWM为300");
|
||||
if (Timer_CanOperate()) { // 添加判断
|
||||
/* HOT_KEY 按下:切换热功能 */
|
||||
hot_state = !hot_state;
|
||||
if (hot_state) {
|
||||
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 300);
|
||||
elog_d("Hot", "设置PWM为300");
|
||||
// 播放语音: 加热已开启
|
||||
MP3_Play(15);
|
||||
} else {
|
||||
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
|
||||
elog_d("Hot", "设置PWM为0");
|
||||
// 播放语音: 加热已关闭
|
||||
MP3_Play(16);
|
||||
}
|
||||
} else {
|
||||
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0); /* 关闭加热 */
|
||||
elog_d("Hot", "设置PWM为0");
|
||||
elog_w("Key", "HOT_KEY无效 - 请先设定时间");
|
||||
// 播放语音: 请先设定时间
|
||||
MP3_Play(17);
|
||||
elog_d("LED", "Sensor任务: ERR_LED亮");
|
||||
HAL_GPIO_WritePin(ERR_LED_GPIO_Port, ERR_LED_Pin, GPIO_PIN_RESET); // ERR亮
|
||||
osDelay(1000); // LED亮1秒
|
||||
elog_d("LED", "Sensor任务: ERR_LED灭");
|
||||
HAL_GPIO_WritePin(ERR_LED_GPIO_Port, ERR_LED_Pin, GPIO_PIN_SET); // ERR灭
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== TIME_KEY (bit3) 控制 ===== */
|
||||
if (key_pressed & (1 << 3)) {
|
||||
/* TIME_KEY 按下:切换定时 */
|
||||
time_state = !time_state;
|
||||
if (time_state) {
|
||||
// TODO: 添加定时启动逻辑
|
||||
/* TIME_KEY 按下:循环设定时间 0→10→20→30→0 */
|
||||
if (!is_running) {
|
||||
if (timer_minutes >= 30) {
|
||||
timer_minutes = 0;
|
||||
} else {
|
||||
timer_minutes += 10;
|
||||
}
|
||||
elog_i("Timer", "设定时间: %d 分钟", timer_minutes);
|
||||
|
||||
// 播放语音提示
|
||||
if (timer_minutes == 10) {
|
||||
MP3_Play(2); // 定时10分钟
|
||||
} else if (timer_minutes == 20) {
|
||||
MP3_Play(3); // 定时20分钟
|
||||
} else if (timer_minutes == 30) {
|
||||
MP3_Play(4); // 定时30分钟
|
||||
} else if (timer_minutes == 0) {
|
||||
MP3_Play(5); // 定时已取消
|
||||
}
|
||||
|
||||
// 如果设定了时间,显示在屏幕上(调用你的倒计时接口)
|
||||
if (timer_minutes > 0) {
|
||||
remaining_seconds = timer_minutes * 60;
|
||||
Display_UpdateTime();
|
||||
} else {
|
||||
Screen_ShowInZone(1, text_stop, text_stop_LEN);
|
||||
}
|
||||
} else {
|
||||
// TODO: 添加定时关闭逻辑
|
||||
// 运行中按下TIME_KEY,取消定时并停止
|
||||
is_running = 0;
|
||||
timer_minutes = 0;
|
||||
remaining_seconds = 0;
|
||||
|
||||
// 停止电机和加热
|
||||
Motor_SetGear(0);
|
||||
hot_state = 0;
|
||||
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
|
||||
|
||||
elog_i("Timer", "定时取消,设备已停止");
|
||||
|
||||
// 播放语音: 按摩已停止
|
||||
MP3_Play(6);
|
||||
|
||||
// 更新 Zone 1 显示为停止状态
|
||||
Screen_ShowInZone(1, text_stop, text_stop_LEN);
|
||||
|
||||
// 延时一下,确保屏幕更新完成
|
||||
osDelay(50);
|
||||
|
||||
// 更新 Zone 2 显示为停止状态(按摩已停止)
|
||||
Screen_ShowInZone(2, text_massage_off, text_massage_off_LEN);
|
||||
|
||||
// 重置显示记录
|
||||
last_display_minutes = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,27 +616,65 @@ void Motor(void *argument) {
|
||||
/* 电机驱动初始化 */
|
||||
Motor_Init();
|
||||
|
||||
/* 初始化LED状态 (LED是低电平点亮,GPIO_PIN_SET灭,GPIO_PIN_RESET亮) */
|
||||
HAL_GPIO_WritePin(RUN_LED_GPIO_Port, RUN_LED_Pin, GPIO_PIN_SET); // RUN灭
|
||||
HAL_GPIO_WritePin(ERR_LED_GPIO_Port, ERR_LED_Pin, GPIO_PIN_SET); // ERR灭
|
||||
run_led_state = 0;
|
||||
elog_d("LED", "Motor任务初始化: RUN_LED灭, ERR_LED灭");
|
||||
|
||||
osEventFlagsSet(init_okHandle, 1 << 2);
|
||||
|
||||
/* Infinite loop */
|
||||
static uint32_t tick_counter = 0;
|
||||
|
||||
for (;;) {
|
||||
/* 电机任务主要功能:
|
||||
* - 监控电机状态(超温保护、过流保护等)
|
||||
* - 定期打印状态信息
|
||||
* - 响应紧急停止信号
|
||||
*/
|
||||
tick_counter++;
|
||||
|
||||
/* 每1秒打印一次电机状态 */
|
||||
uint8_t current_gear = Motor_GetGear();
|
||||
elog_i("Motor", "当前档位: %u (0=停止,1=低,2=中,3=高)", current_gear);
|
||||
// 每1000ms调用一次Timer_Tick(每2个循环周期)
|
||||
if (tick_counter % 2 == 0) {
|
||||
Timer_Tick(); // 倒计时处理
|
||||
}
|
||||
|
||||
HAL_GPIO_TogglePin(RUN_LED_GPIO_Port, RUN_LED_Pin);
|
||||
osDelay(1000);
|
||||
// RUN_LED闪烁控制:运行时500ms翻转一次 (LED低电平点亮)
|
||||
if (is_running) {
|
||||
run_led_state = !run_led_state;
|
||||
HAL_GPIO_WritePin(RUN_LED_GPIO_Port, RUN_LED_Pin, run_led_state ? GPIO_PIN_RESET : GPIO_PIN_SET);
|
||||
} else {
|
||||
// 停止时保持灭
|
||||
if (run_led_state != 0) {
|
||||
run_led_state = 0;
|
||||
HAL_GPIO_WritePin(RUN_LED_GPIO_Port, RUN_LED_Pin, GPIO_PIN_SET);
|
||||
}
|
||||
}
|
||||
|
||||
// ERR_LED确保熄灭(LED低电平点亮,所以灭用GPIO_PIN_SET)
|
||||
static uint8_t last_err_led_state = 0;
|
||||
HAL_GPIO_WritePin(ERR_LED_GPIO_Port, ERR_LED_Pin, GPIO_PIN_SET);
|
||||
if (last_err_led_state != 0) {
|
||||
elog_d("LED", "Motor任务: ERR_LED灭");
|
||||
last_err_led_state = 0;
|
||||
}
|
||||
|
||||
// 每1000ms更新显示(每2个循环周期检查一次)
|
||||
if (tick_counter % 2 == 0) {
|
||||
if (is_running) {
|
||||
uint8_t current_minutes = remaining_seconds / 60;
|
||||
// 只有当分钟数变化时才刷新屏幕
|
||||
if (current_minutes != last_display_minutes) {
|
||||
last_display_minutes = current_minutes;
|
||||
Display_UpdateTime();
|
||||
elog_d("Screen", "更新倒计时显示: %d分", current_minutes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osDelay(500); // 500ms循环周期
|
||||
}
|
||||
/* USER CODE END Motor */
|
||||
}
|
||||
|
||||
/* USER CODE BEGIN Header_Screen */
|
||||
|
||||
/**
|
||||
* @brief Function implementing the Screen_Tsak thread.
|
||||
* @param argument: Not used
|
||||
@@ -403,6 +683,9 @@ void Motor(void *argument) {
|
||||
/* USER CODE END Header_Screen */
|
||||
void Screen(void *argument) {
|
||||
/* USER CODE BEGIN Screen */
|
||||
static uint8_t last_hot_state = 0; // 记录上一次的热状态
|
||||
static uint8_t last_gear = 0xFF; // 记录上一次的挡位(初始为无效值)
|
||||
|
||||
elog_d("Init", "Screen task started");
|
||||
|
||||
osEventFlagsSet(init_okHandle, 1 << 3);
|
||||
@@ -410,8 +693,50 @@ void Screen(void *argument) {
|
||||
// HAL_UART_Transmit(&huart1, (uint8_t *)buf, strlen(buf), HAL_MAX_DELAY);
|
||||
/* Infinite loop */
|
||||
for (;;) {
|
||||
if (hot_state) {
|
||||
|
||||
uint8_t current_gear = Motor_GetGear();
|
||||
|
||||
// 挡位变化时更新屏幕(第2区域显示挡位)
|
||||
if (current_gear != last_gear) {
|
||||
last_gear = current_gear;
|
||||
// 0 档位显示停止
|
||||
if (current_gear == 0) {
|
||||
|
||||
Screen_ShowInZone(2, text_massage_off, text_massage_off_LEN);
|
||||
}
|
||||
|
||||
// 1级
|
||||
if (current_gear == 1) {
|
||||
Screen_ShowInZone(2, text_gear_1, text_gear_1_LEN);
|
||||
}
|
||||
// 2级
|
||||
|
||||
else if (current_gear == 2) {
|
||||
|
||||
Screen_ShowInZone(2, text_gear_2, text_gear_2_LEN);
|
||||
|
||||
}
|
||||
// 3级
|
||||
|
||||
else if (current_gear == 3) {
|
||||
|
||||
Screen_ShowInZone(2, text_gear_3, text_gear_3_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
// 只在状态变化时更新屏幕
|
||||
if (hot_state != last_hot_state) {
|
||||
last_hot_state = hot_state;
|
||||
|
||||
if (hot_state) {
|
||||
// 第3区域显示热功能开启
|
||||
Screen_ShowInZone(3, text_heat_on, text_heat_on_LEN);
|
||||
} else {
|
||||
// 第3区域显示热功能关闭
|
||||
Screen_ShowInZone(3, text_heat_off, text_heat_off_LEN);
|
||||
}
|
||||
}
|
||||
osDelay(10); // 添加延时,避免CPU空转
|
||||
}
|
||||
/* USER CODE END Screen */
|
||||
}
|
||||
@@ -424,32 +749,54 @@ void Screen(void *argument) {
|
||||
* @retval None
|
||||
*/
|
||||
/* USER CODE END Header_MP3 */
|
||||
|
||||
void MP3(void *argument) {
|
||||
/* USER CODE BEGIN MP3 */
|
||||
elog_d("Init", "MP3 task started");
|
||||
uint8_t init_cmd1[] = {0x7E, 0xFF, 0x06, 0x06, 0x00,
|
||||
0x00, 0x1E, 0xFE, 0xD7, 0xEF}; // 开启声音
|
||||
uint8_t init_cmd2[] = {0x7E, 0xFF, 0x06, 0x09, 0x00,
|
||||
0x00, 0x02, 0xFE, 0xF0, 0xEF}; // TF卡
|
||||
uint8_t play_cmd[] = {0x7E, 0xFF, 0x06, 0x12, 0x00,
|
||||
0x00, 0x01, 0xFE, 0xE8, 0xEF}; // 播放文件1
|
||||
elog_d("MP3", "USART3波特率: 9600");
|
||||
|
||||
// 等待模块上电稳定
|
||||
osDelay(2000);
|
||||
// uint8_t init_cmd1[] = {0x7E, 0xFF, 0x06, 0x06, 0x00,
|
||||
// 0x00, 0x1E, 0xFE, 0xD7, 0xEF}; // 开启声音
|
||||
// uint8_t init_cmd2[] = {0x7E, 0xFF, 0x06, 0x09, 0x00,
|
||||
// 0x00, 0x02, 0xFE, 0xF0, 0xEF}; // TF卡
|
||||
// uint8_t play_cmd[] = {0x7E, 0xFF, 0x06, 0x12, 0x00,
|
||||
// 0x00, 0x01, 0xFE, 0xE8, 0xEF}; // 播放文件1
|
||||
|
||||
// 发送初始化命令1,等待模块 ACK
|
||||
HAL_UART_Transmit(&huart3, init_cmd1, sizeof(init_cmd1), 100);
|
||||
// // 等待模块上电稳定
|
||||
// elog_d("MP3", "等待模块上电稳定...");
|
||||
// osDelay(2000);
|
||||
|
||||
// // 发送初始化命令1,等待模块 ACK
|
||||
// elog_d("MP3", "发送初始化命令1: 开启声音");
|
||||
// HAL_StatusTypeDef ret1 = HAL_UART_Transmit(&huart3, init_cmd1, sizeof(init_cmd1), 100);
|
||||
// elog_d("MP3", "命令1返回值: %d", ret1);
|
||||
// osDelay(100);
|
||||
|
||||
// // 发送初始化命令2
|
||||
// elog_d("MP3", "发送初始化命令2: 选择TF卡");
|
||||
// HAL_StatusTypeDef ret2 = HAL_UART_Transmit(&huart3, init_cmd2, sizeof(init_cmd2), 100);
|
||||
// elog_d("MP3", "命令2返回值: %d", ret2);
|
||||
// osDelay(500);
|
||||
|
||||
// 等待模块上电稳定
|
||||
osDelay(2000);
|
||||
|
||||
// 初始化MP3模块
|
||||
HAL_StatusTypeDef ret = MP3_Init();
|
||||
if (ret != HAL_OK) {
|
||||
elog_e("MP3", "MP3模块初始化失败");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 发送初始化命令2
|
||||
HAL_UART_Transmit(&huart3, init_cmd2, sizeof(init_cmd2), 100);
|
||||
|
||||
elog_i("MP3", "模块初始化完成");
|
||||
MP3_Play(1);
|
||||
osEventFlagsSet(init_okHandle, 1 << 4);
|
||||
|
||||
/* Infinite loop */
|
||||
|
||||
for (;;) {
|
||||
// 发送播放命令
|
||||
HAL_UART_Transmit(&huart3, play_cmd, sizeof(play_cmd), 100);
|
||||
|
||||
osDelay(10000);
|
||||
}
|
||||
|
||||
@@ -8,47 +8,47 @@ const uint8_t text_device_name_GBK[] = {0xD6, 0xC7, 0xC4, 0xDC, 0xB0, 0xB4, 0xC4
|
||||
// 对应GBK编码:0xBC, 0xD3, 0xD4, 0xD8, 0xD6, 0xD0
|
||||
const uint8_t text_loading_GBK[] = {0xBC, 0xD3, 0xD4, 0xD8, 0xD6, 0xD0};
|
||||
|
||||
// 对应文本(UTF8):按摩启动
|
||||
// 对应GBK编码:0xB0, 0xB4, 0xC4, 0xA6, 0xC6, 0xF4, 0xB6, 0xAF
|
||||
const uint8_t text_massage_on_GBK[] = {0xB0, 0xB4, 0xC4, 0xA6, 0xC6, 0xF4, 0xB6, 0xAF};
|
||||
// 对应文本(UTF8):按摩:设备停止
|
||||
// 对应GBK编码:0xB0, 0xB4, 0xC4, 0xA6, 0x3A, 0xC9, 0xE8, 0xB1, 0xB8, 0xCD, 0xA3, 0xD6, 0xB9
|
||||
const uint8_t text_massage_off_GBK[] = {0xB0, 0xB4, 0xC4, 0xA6, 0x3A, 0xC9, 0xE8, 0xB1, 0xB8, 0xCD, 0xA3, 0xD6, 0xB9};
|
||||
|
||||
// 对应文本(UTF8):按摩停止
|
||||
// 对应GBK编码:0xB0, 0xB4, 0xC4, 0xA6, 0xCD, 0xA3, 0xD6, 0xB9
|
||||
const uint8_t text_massage_off_GBK[] = {0xB0, 0xB4, 0xC4, 0xA6, 0xCD, 0xA3, 0xD6, 0xB9};
|
||||
// 对应文本(UTF8):加热:开始加热
|
||||
// 对应GBK编码:0xBC, 0xD3, 0xC8, 0xC8, 0x3A, 0xBF, 0xAA, 0xCA, 0xBC, 0xBC, 0xD3, 0xC8, 0xC8
|
||||
const uint8_t text_heat_on_GBK[] = {0xBC, 0xD3, 0xC8, 0xC8, 0x3A, 0xBF, 0xAA, 0xCA, 0xBC, 0xBC, 0xD3, 0xC8, 0xC8};
|
||||
|
||||
// 对应文本(UTF8):加热启动
|
||||
// 对应GBK编码:0xBC, 0xD3, 0xC8, 0xC8, 0xC6, 0xF4, 0xB6, 0xAF
|
||||
const uint8_t text_heat_on_GBK[] = {0xBC, 0xD3, 0xC8, 0xC8, 0xC6, 0xF4, 0xB6, 0xAF};
|
||||
// 对应文本(UTF8):加热:停止加热
|
||||
// 对应GBK编码:0xBC, 0xD3, 0xC8, 0xC8, 0x3A, 0xCD, 0xA3, 0xD6, 0xB9, 0xBC, 0xD3, 0xC8, 0xC8
|
||||
const uint8_t text_heat_off_GBK[] = {0xBC, 0xD3, 0xC8, 0xC8, 0x3A, 0xCD, 0xA3, 0xD6, 0xB9, 0xBC, 0xD3, 0xC8, 0xC8};
|
||||
|
||||
// 对应文本(UTF8):加热停止
|
||||
// 对应GBK编码:0xBC, 0xD3, 0xC8, 0xC8, 0xCD, 0xA3, 0xD6, 0xB9
|
||||
const uint8_t text_heat_off_GBK[] = {0xBC, 0xD3, 0xC8, 0xC8, 0xCD, 0xA3, 0xD6, 0xB9};
|
||||
// 对应文本(UTF8):按摩:当前低档位
|
||||
// 对应GBK编码:0xB0, 0xB4, 0xC4, 0xA6, 0x3A, 0xB5, 0xB1, 0xC7, 0xB0, 0xB5, 0xCD, 0xB5, 0xB5, 0xCE, 0xBB
|
||||
const uint8_t text_gear_1_GBK[] = {0xB0, 0xB4, 0xC4, 0xA6, 0x3A, 0xB5, 0xB1, 0xC7, 0xB0, 0xB5, 0xCD, 0xB5, 0xB5, 0xCE, 0xBB};
|
||||
|
||||
// 对应文本(UTF8):%d级
|
||||
// 对应GBK编码:0x25, 0x64, 0xBC, 0xB6
|
||||
const uint8_t text_gear_GBK[] = {0x25, 0x64, 0xBC, 0xB6};
|
||||
// 对应文本(UTF8):按摩:当前中档位
|
||||
// 对应GBK编码:0xB0, 0xB4, 0xC4, 0xA6, 0x3A, 0xB5, 0xB1, 0xC7, 0xB0, 0xD6, 0xD0, 0xB5, 0xB5, 0xCE, 0xBB
|
||||
const uint8_t text_gear_2_GBK[] = {0xB0, 0xB4, 0xC4, 0xA6, 0x3A, 0xB5, 0xB1, 0xC7, 0xB0, 0xD6, 0xD0, 0xB5, 0xB5, 0xCE, 0xBB};
|
||||
|
||||
// 对应文本(UTF8):%d分钟
|
||||
// 对应GBK编码:0x25, 0x64, 0xB7, 0xD6, 0xD6, 0xD3
|
||||
const uint8_t text_time_GBK[] = {0x25, 0x64, 0xB7, 0xD6, 0xD6, 0xD3};
|
||||
// 对应文本(UTF8):按摩:当前高档位
|
||||
// 对应GBK编码:0xB0, 0xB4, 0xC4, 0xA6, 0x3A, 0xB5, 0xB1, 0xC7, 0xB0, 0xB8, 0xDF, 0xB5, 0xB5, 0xCE, 0xBB
|
||||
const uint8_t text_gear_3_GBK[] = {0xB0, 0xB4, 0xC4, 0xA6, 0x3A, 0xB5, 0xB1, 0xC7, 0xB0, 0xB8, 0xDF, 0xB5, 0xB5, 0xCE, 0xBB};
|
||||
|
||||
// 对应文本(UTF8):持续
|
||||
// 对应GBK编码:0xB3, 0xD6, 0xD0, 0xF8
|
||||
const uint8_t text_cont_GBK[] = {0xB3, 0xD6, 0xD0, 0xF8};
|
||||
// 对应文本(UTF8):时间:设定10分钟
|
||||
// 对应GBK编码:0xCA, 0xB1, 0xBC, 0xE4, 0x3A, 0xC9, 0xE8, 0xB6, 0xA8, 0x31, 0x30, 0xB7, 0xD6, 0xD6, 0xD3
|
||||
const uint8_t text_time_1_GBK[] = {0xCA, 0xB1, 0xBC, 0xE4, 0x3A, 0xC9, 0xE8, 0xB6, 0xA8, 0x31, 0x30, 0xB7, 0xD6, 0xD6, 0xD3};
|
||||
|
||||
// 对应文本(UTF8):剩余%d分
|
||||
// 对应GBK编码:0xCA, 0xA3, 0xD3, 0xE0, 0x25, 0x64, 0xB7, 0xD6
|
||||
const uint8_t text_remaining_GBK[] = {0xCA, 0xA3, 0xD3, 0xE0, 0x25, 0x64, 0xB7, 0xD6};
|
||||
// 对应文本(UTF8):时间:设定20分钟
|
||||
// 对应GBK编码:0xCA, 0xB1, 0xBC, 0xE4, 0x3A, 0xC9, 0xE8, 0xB6, 0xA8, 0x32, 0x30, 0xB7, 0xD6, 0xD6, 0xD3
|
||||
const uint8_t text_time_2_GBK[] = {0xCA, 0xB1, 0xBC, 0xE4, 0x3A, 0xC9, 0xE8, 0xB6, 0xA8, 0x32, 0x30, 0xB7, 0xD6, 0xD6, 0xD3};
|
||||
|
||||
// 对应文本(UTF8):运行中
|
||||
// 对应GBK编码:0xD4, 0xCB, 0xD0, 0xD0, 0xD6, 0xD0
|
||||
const uint8_t text_running_GBK[] = {0xD4, 0xCB, 0xD0, 0xD0, 0xD6, 0xD0};
|
||||
// 对应文本(UTF8):时间:设定30分钟
|
||||
// 对应GBK编码:0xCA, 0xB1, 0xBC, 0xE4, 0x3A, 0xC9, 0xE8, 0xB6, 0xA8, 0x33, 0x30, 0xB7, 0xD6, 0xD6, 0xD3
|
||||
const uint8_t text_time_3_GBK[] = {0xCA, 0xB1, 0xBC, 0xE4, 0x3A, 0xC9, 0xE8, 0xB6, 0xA8, 0x33, 0x30, 0xB7, 0xD6, 0xD6, 0xD3};
|
||||
|
||||
// 对应文本(UTF8):已暂停
|
||||
// 对应GBK编码:0xD2, 0xD1, 0xD4, 0xDD, 0xCD, 0xA3
|
||||
const uint8_t text_paused_GBK[] = {0xD2, 0xD1, 0xD4, 0xDD, 0xCD, 0xA3};
|
||||
// 对应文本(UTF8):时间:剩余%d分
|
||||
// 对应GBK编码:0xCA, 0xB1, 0xBC, 0xE4, 0x3A, 0xCA, 0xA3, 0xD3, 0xE0, 0x25, 0x64, 0xB7, 0xD6
|
||||
const uint8_t text_remaining_GBK[] = {0xCA, 0xB1, 0xBC, 0xE4, 0x3A, 0xCA, 0xA3, 0xD3, 0xE0, 0x25, 0x64, 0xB7, 0xD6};
|
||||
|
||||
// 对应文本(UTF8):已结束
|
||||
// 对应GBK编码:0xD2, 0xD1, 0xBD, 0xE1, 0xCA, 0xF8
|
||||
const uint8_t text_ended_GBK[] = {0xD2, 0xD1, 0xBD, 0xE1, 0xCA, 0xF8};
|
||||
// 对应文本(UTF8):机器运行已停止
|
||||
// 对应GBK编码:0xBB, 0xFA, 0xC6, 0xF7, 0xD4, 0xCB, 0xD0, 0xD0, 0xD2, 0xD1, 0xCD, 0xA3, 0xD6, 0xB9
|
||||
const uint8_t text_stop_GBK[] = {0xBB, 0xFA, 0xC6, 0xF7, 0xD4, 0xCB, 0xD0, 0xD0, 0xD2, 0xD1, 0xCD, 0xA3, 0xD6, 0xB9};
|
||||
|
||||
|
||||
@@ -20,13 +20,15 @@
|
||||
|
||||
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
void Motor_StartAll(void);
|
||||
void Motor_StopAll(void);
|
||||
|
||||
/* PWM parameters */
|
||||
#define PWM_ARR 99
|
||||
#define PWM_STEPS (PWM_ARR + 1)
|
||||
|
||||
/* 三档占空比(百分比),索引为 0..2 对应 档位 1..3(档位0为停止) */
|
||||
static const uint8_t gears_percent[3] = {55, 65, 75};
|
||||
static const uint8_t gears_percent[3] = {75, 85, 95};
|
||||
/* current_gear: 0 = 停止, 1 = 低, 2 = 中, 3 = 高 */
|
||||
static uint8_t current_gear = 0; /* 0..3 */
|
||||
|
||||
@@ -34,25 +36,32 @@ static inline uint32_t percent_to_compare(uint8_t percent) {
|
||||
return ((uint32_t)percent * PWM_STEPS) / 100U;
|
||||
}
|
||||
|
||||
/* 将两个电机都设置为指定占空比(只使用 TIM4_CH2, TIM4_CH4 输出,CH1/CH3 停止以保持单方向) */
|
||||
/* 将两个电机都设置为指定占空比(使用 AT8236-MS H桥驱动) */
|
||||
static void set_both_motors_percent(uint8_t percent) {
|
||||
uint32_t cmp = percent_to_compare(percent);
|
||||
|
||||
/* 停止并关闭 CH1、使用 CH2 输出 */
|
||||
//HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_1);
|
||||
/* 电机1: TIM4_CH1->AIN1, TIM4_CH2->AIN2
|
||||
* 正转:AIN1=PWM, AIN2=0
|
||||
* 反转:AIN1=0, AIN2=PWM
|
||||
* 这里用正转模式:CH1输出PWM, CH2为0 */
|
||||
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, (uint16_t)cmp);
|
||||
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_2, 100);
|
||||
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_2, 0);
|
||||
|
||||
/* 停止并关闭 CH3、使用 CH4 输出 */
|
||||
//HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_3);
|
||||
/* 电机2: TIM4_CH3->BIN1, TIM4_CH4->BIN2
|
||||
* 正转:BIN1=PWM, BIN2=0
|
||||
* 这里用正转模式:CH3输出PWM, CH4为0 */
|
||||
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_3, (uint16_t)cmp);
|
||||
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_4, 100);
|
||||
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_4, 0);
|
||||
}
|
||||
/* 启动时短暂提升到 75% 并保持 500ms,然后降到一档(档位1) */
|
||||
/* 启动时短暂提升到 100% 并保持 500ms,然后降到一档(档位1) */
|
||||
void Motor_StartupBoost(void) {
|
||||
set_both_motors_percent(80); /* 启动提升到 80% */
|
||||
// 确保PWM通道已启动
|
||||
Motor_StartAll();
|
||||
set_both_motors_percent(100); /* 启动提升到 100% */
|
||||
elog_d("Motor", "启动冲击: 100%%");
|
||||
osDelay(1500);
|
||||
current_gear = 1; /* 启动后回到档位1(最低速) */
|
||||
elog_d("Motor", "设置档位: %d, PWM: %d%%", current_gear, gears_percent[current_gear - 1]);
|
||||
set_both_motors_percent(gears_percent[current_gear - 1]);
|
||||
}
|
||||
|
||||
@@ -61,9 +70,13 @@ void Motor_SetGear(uint8_t gear) {
|
||||
if (gear > 3) gear = 3;
|
||||
current_gear = gear;
|
||||
if (current_gear == 0) {
|
||||
// set_both_motors_percent(0);
|
||||
// 完全停止所有PWM输出
|
||||
Motor_Stop();
|
||||
elog_d("Motor", "Motor_SetGear: 档位0, 停止");
|
||||
} else {
|
||||
// 确保PWM通道已启动
|
||||
Motor_StartAll();
|
||||
elog_d("Motor", "Motor_SetGear: 档位%d, PWM: %d%%", current_gear, gears_percent[current_gear - 1]);
|
||||
set_both_motors_percent(gears_percent[current_gear - 1]);
|
||||
}
|
||||
}
|
||||
@@ -72,10 +85,15 @@ void Motor_SetGear(uint8_t gear) {
|
||||
void Motor_NextGear(void) {
|
||||
current_gear++;
|
||||
if (current_gear > 3) current_gear = 0;
|
||||
|
||||
if (current_gear == 0) {
|
||||
// set_both_motors_percent(0);
|
||||
// 完全停止所有PWM输出
|
||||
Motor_Stop();
|
||||
elog_d("Motor", "Motor_NextGear: 档位0, 停止");
|
||||
} else {
|
||||
// 确保PWM通道已启动
|
||||
Motor_StartAll();
|
||||
elog_d("Motor", "Motor_NextGear: 档位%d, PWM: %d%%", current_gear, gears_percent[current_gear - 1]);
|
||||
set_both_motors_percent(gears_percent[current_gear - 1]);
|
||||
}
|
||||
}
|
||||
@@ -87,14 +105,33 @@ void Motor_GearUp(void) {
|
||||
|
||||
/* 向下减档(循环) */
|
||||
void Motor_GearDown(void) {
|
||||
uint8_t old_gear = current_gear;
|
||||
|
||||
if (current_gear == 0) {
|
||||
current_gear = 3;
|
||||
} else {
|
||||
current_gear--;
|
||||
}
|
||||
|
||||
elog_d("Motor", "Motor_GearDown: %d -> %d", old_gear, current_gear);
|
||||
|
||||
if (current_gear == 0) {
|
||||
set_both_motors_percent(0);
|
||||
// 完全停止所有PWM输出
|
||||
Motor_Stop();
|
||||
elog_d("Motor", "档位0, 停止");
|
||||
} else {
|
||||
// 确保PWM通道已启动
|
||||
Motor_StartAll();
|
||||
|
||||
// 如果从2档或3档降到1档,需要短暂冲击保持电机转动
|
||||
if (current_gear == 1 && old_gear >= 2) {
|
||||
// 短暂提升到100%来维持转动,然后降到1档
|
||||
elog_d("Motor", "降档冲击: 100%%");
|
||||
set_both_motors_percent(100);
|
||||
osDelay(800); // 800ms冲击,给电机更充裕的适应时间
|
||||
}
|
||||
|
||||
elog_d("Motor", "最终档位%d, PWM: %d%%", current_gear, gears_percent[current_gear - 1]);
|
||||
set_both_motors_percent(gears_percent[current_gear - 1]);
|
||||
}
|
||||
}
|
||||
@@ -122,10 +159,44 @@ void Motor_Init(void) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 完全停止电机并关闭所有PWM通道
|
||||
*/
|
||||
void Motor_StopAll(void) {
|
||||
// 设置所有通道的PWM为0
|
||||
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, 0);
|
||||
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_2, 0);
|
||||
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_3, 0);
|
||||
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_4, 0);
|
||||
|
||||
// 停止所有PWM通道
|
||||
HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_1);
|
||||
HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_2);
|
||||
HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_3);
|
||||
HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_4);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 重新启动所有PWM通道(用于启动电机)
|
||||
*/
|
||||
void Motor_StartAll(void) {
|
||||
// 重新启动PWM通道
|
||||
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
|
||||
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2);
|
||||
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_3);
|
||||
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_4);
|
||||
}
|
||||
|
||||
void Motor_Stop(void) {
|
||||
// 所有引脚输出0 - 完全停止
|
||||
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, 0);
|
||||
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_2, 0);
|
||||
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_3, 0);
|
||||
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_4, 0);
|
||||
|
||||
// 强制停止PWM通道,确保电机完全停止
|
||||
HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_1);
|
||||
HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_2);
|
||||
HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_3);
|
||||
HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_4);
|
||||
}
|
||||
|
||||
265
Core/Src/mp3_driver.c
Normal file
265
Core/Src/mp3_driver.c
Normal 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;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ void MX_USART3_UART_Init(void)
|
||||
|
||||
/* USER CODE END USART3_Init 1 */
|
||||
huart3.Instance = USART3;
|
||||
huart3.Init.BaudRate = 115200;
|
||||
huart3.Init.BaudRate = 9600; // MP3模块通常使用9600波特率
|
||||
huart3.Init.WordLength = UART_WORDLENGTH_8B;
|
||||
huart3.Init.StopBits = UART_STOPBITS_1;
|
||||
huart3.Init.Parity = UART_PARITY_NONE;
|
||||
|
||||
Reference in New Issue
Block a user