- 新增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等
810 lines
24 KiB
C
810 lines
24 KiB
C
/* USER CODE BEGIN Header */
|
||
/**
|
||
******************************************************************************
|
||
* File Name : freertos.c
|
||
* Description : Code for freertos applications
|
||
******************************************************************************
|
||
* @attention
|
||
*
|
||
* Copyright (c) 2026 STMicroelectronics.
|
||
* All rights reserved.
|
||
*
|
||
* This software is licensed under terms that can be found in the LICENSE file
|
||
* in the root directory of this software component.
|
||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||
*
|
||
******************************************************************************
|
||
*/
|
||
/* USER CODE END Header */
|
||
|
||
/* Includes ------------------------------------------------------------------*/
|
||
#include "FreeRTOS.h"
|
||
#include "cmsis_os.h"
|
||
#include "main.h"
|
||
#include "task.h"
|
||
|
||
/* Private includes ----------------------------------------------------------*/
|
||
/* USER CODE BEGIN Includes */
|
||
#include "elog.h"
|
||
#include "gbk_text.h"
|
||
#include "motor_driver.h"
|
||
#include "screen.h"
|
||
#include "tim.h"
|
||
#include "usart.h"
|
||
#include <stdint.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <sys/_intsup.h>
|
||
#include "mp3_driver.h"
|
||
/* USER CODE END Includes */
|
||
|
||
/* Private typedef -----------------------------------------------------------*/
|
||
/* USER CODE BEGIN PTD */
|
||
|
||
/* USER CODE END PTD */
|
||
|
||
/* Private define ------------------------------------------------------------*/
|
||
/* USER CODE BEGIN PD */
|
||
|
||
/* USER CODE END PD */
|
||
|
||
/* Private macro -------------------------------------------------------------*/
|
||
/* USER CODE BEGIN PM */
|
||
#ifdef __GNUC__
|
||
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
|
||
#else
|
||
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
|
||
#endif
|
||
|
||
PUTCHAR_PROTOTYPE {
|
||
HAL_UART_Transmit(&huart5, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
|
||
return ch;
|
||
}
|
||
/* USER CODE END PM */
|
||
|
||
/* Private variables ---------------------------------------------------------*/
|
||
/* USER CODE BEGIN Variables */
|
||
|
||
/* 按键状态位: bit0..bit3 分别对应 M__KEY, M__KEYC7, HOT_KEY, TIME_KEY,
|
||
* 为1表示按下 */
|
||
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 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 */
|
||
osThreadId_t defaultTaskHandle;
|
||
const osThreadAttr_t defaultTask_attributes = {
|
||
.name = "defaultTask",
|
||
.stack_size = 128 * 4,
|
||
.priority = (osPriority_t)osPriorityNormal,
|
||
};
|
||
/* Definitions for KeyTask */
|
||
osThreadId_t KeyTaskHandle;
|
||
const osThreadAttr_t KeyTask_attributes = {
|
||
.name = "KeyTask",
|
||
.stack_size = 512 * 4,
|
||
.priority = (osPriority_t)osPriorityRealtime,
|
||
};
|
||
/* Definitions for Sensor_Task */
|
||
osThreadId_t Sensor_TaskHandle;
|
||
const osThreadAttr_t Sensor_Task_attributes = {
|
||
.name = "Sensor_Task",
|
||
.stack_size = 512 * 4,
|
||
.priority = (osPriority_t)osPriorityHigh7,
|
||
};
|
||
/* Definitions for Motor_Task */
|
||
osThreadId_t Motor_TaskHandle;
|
||
const osThreadAttr_t Motor_Task_attributes = {
|
||
.name = "Motor_Task",
|
||
.stack_size = 512 * 4,
|
||
.priority = (osPriority_t)osPriorityNormal1,
|
||
};
|
||
/* Definitions for Screen_Tsak */
|
||
osThreadId_t Screen_TsakHandle;
|
||
const osThreadAttr_t Screen_Tsak_attributes = {
|
||
.name = "Screen_Tsak",
|
||
.stack_size = 1024 * 4,
|
||
.priority = (osPriority_t)osPriorityNormal,
|
||
};
|
||
/* Definitions for MP3_Play_Task */
|
||
osThreadId_t MP3_Play_TaskHandle;
|
||
const osThreadAttr_t MP3_Play_Task_attributes = {
|
||
.name = "MP3_Play_Task",
|
||
.stack_size = 512 * 4,
|
||
.priority = (osPriority_t)osPriorityNormal,
|
||
};
|
||
/* Definitions for init_ok */
|
||
osEventFlagsId_t init_okHandle;
|
||
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);
|
||
void Key(void *argument);
|
||
void Sensor(void *argument);
|
||
void Motor(void *argument);
|
||
void Screen(void *argument);
|
||
void MP3(void *argument);
|
||
|
||
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
|
||
|
||
/**
|
||
* @brief FreeRTOS initialization
|
||
* @param None
|
||
* @retval None
|
||
*/
|
||
void MX_FREERTOS_Init(void) {
|
||
/* USER CODE BEGIN Init */
|
||
easylogger_init(); /* 初始化 EasyLogger */
|
||
__enable_irq(); // 开启全局中断
|
||
elog_i("task_init", "任务初始!");
|
||
/* USER CODE END Init */
|
||
|
||
/* USER CODE BEGIN RTOS_MUTEX */
|
||
/* add mutexes, ... */
|
||
/* USER CODE END RTOS_MUTEX */
|
||
|
||
/* USER CODE BEGIN RTOS_SEMAPHORES */
|
||
/* add semaphores, ... */
|
||
/* USER CODE END RTOS_SEMAPHORES */
|
||
|
||
/* USER CODE BEGIN RTOS_TIMERS */
|
||
/* start timers, add new ones, ... */
|
||
/* USER CODE END RTOS_TIMERS */
|
||
|
||
/* USER CODE BEGIN RTOS_QUEUES */
|
||
/* add queues, ... */
|
||
/* USER CODE END RTOS_QUEUES */
|
||
|
||
/* Create the thread(s) */
|
||
/* creation of defaultTask */
|
||
defaultTaskHandle =
|
||
osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
|
||
|
||
/* creation of KeyTask */
|
||
KeyTaskHandle = osThreadNew(Key, NULL, &KeyTask_attributes);
|
||
|
||
/* creation of Sensor_Task */
|
||
Sensor_TaskHandle = osThreadNew(Sensor, NULL, &Sensor_Task_attributes);
|
||
|
||
/* creation of Motor_Task */
|
||
Motor_TaskHandle = osThreadNew(Motor, NULL, &Motor_Task_attributes);
|
||
|
||
/* creation of Screen_Tsak */
|
||
Screen_TsakHandle = osThreadNew(Screen, NULL, &Screen_Tsak_attributes);
|
||
|
||
/* creation of MP3_Play_Task */
|
||
MP3_Play_TaskHandle = osThreadNew(MP3, NULL, &MP3_Play_Task_attributes);
|
||
|
||
/* USER CODE BEGIN RTOS_THREADS */
|
||
/* add threads, ... */
|
||
/* USER CODE END RTOS_THREADS */
|
||
|
||
/* Create the event(s) */
|
||
/* creation of init_ok */
|
||
init_okHandle = osEventFlagsNew(&init_ok_attributes);
|
||
|
||
/* USER CODE BEGIN RTOS_EVENTS */
|
||
|
||
/* USER CODE END RTOS_EVENTS */
|
||
}
|
||
|
||
/* USER CODE BEGIN Header_StartDefaultTask */
|
||
/**
|
||
* @brief Function implementing the defaultTask thread.
|
||
* @param argument: Not used
|
||
* @retval None
|
||
*/
|
||
/* USER CODE END Header_StartDefaultTask */
|
||
void StartDefaultTask(void *argument) {
|
||
/* USER CODE BEGIN StartDefaultTask */
|
||
|
||
// 启动TIM1的PWM输出
|
||
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); // 启动PWM输出(加热)
|
||
|
||
Screen_Init();
|
||
|
||
// Screen_DrawText16_GBK(1, 30, text_device_loading, text_device_loading_LEN,
|
||
// TEXT_COLOR);
|
||
Screen_ShowInZone(1, text_loading, text_loading_LEN);
|
||
|
||
// 等待bit0+bit1+bit2都被设置(所有模块初始化完成)
|
||
uint32_t flags =
|
||
osEventFlagsWait(init_okHandle, // 目标事件标志组
|
||
(1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) |
|
||
(1 << 4), // 要等待的bit位(多个用|拼接)
|
||
osFlagsWaitAll, // 等待所有bit都被设置
|
||
osWaitForever // 永久等待(直到满足条件)
|
||
);
|
||
|
||
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_stop, text_stop_LEN);
|
||
Screen_ShowInZone(2, text_massage_off, text_massage_off_LEN);
|
||
Screen_ShowInZone(3, text_heat_off, text_heat_off_LEN);
|
||
}
|
||
|
||
elog_d("Init", "完成所有的初始化");
|
||
|
||
/* Infinite loop */
|
||
for (;;) {
|
||
|
||
// 可选:等待发送完成
|
||
// while(__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TC) == RESET);
|
||
|
||
osDelay(10000); // 延时1秒
|
||
}
|
||
/* USER CODE END StartDefaultTask */
|
||
}
|
||
|
||
/* USER CODE BEGIN Header_Key */
|
||
/**
|
||
* @brief Function implementing the KeyTask thread.
|
||
* @param argument: Not used
|
||
* @retval None
|
||
*/
|
||
/* USER CODE END Header_Key */
|
||
void Key(void *argument) {
|
||
/* USER CODE BEGIN Key */
|
||
elog_d("Init", "Key task started");
|
||
/* 防抖轮询实现四个低电平有效按键检测 */
|
||
GPIO_TypeDef *key_ports[4] = {M__KEY_GPIO_Port, M__KEYC7_GPIO_Port,
|
||
HOT_KEY_GPIO_Port, TIME_KEY_GPIO_Port};
|
||
const uint16_t key_pins[4] = {M__KEY_Pin, M__KEYC7_Pin, HOT_KEY_Pin,
|
||
TIME_KEY_Pin};
|
||
|
||
uint8_t press_count[4] = {0};
|
||
uint8_t release_count[4] = {0};
|
||
uint8_t pressed[4] = {0};
|
||
const uint8_t THRESH = 3; /* 3 * 10ms = 30ms 防抖 */
|
||
|
||
osEventFlagsSet(init_okHandle, 1 << 0); // 设置初始化完成标志
|
||
|
||
for (;;) {
|
||
for (int i = 0; i < 4; i++) {
|
||
GPIO_PinState level = HAL_GPIO_ReadPin(key_ports[i], key_pins[i]);
|
||
if (level == GPIO_PIN_RESET) { /* 低电平有效 */
|
||
press_count[i]++;
|
||
release_count[i] = 0;
|
||
if (!pressed[i] && press_count[i] >= THRESH) {
|
||
pressed[i] = 1;
|
||
key_state |= (1 << i); /* bit0..3 分别对应四个按键按下状态 */
|
||
}
|
||
} else {
|
||
release_count[i]++;
|
||
press_count[i] = 0;
|
||
if (pressed[i] && release_count[i] >= THRESH) {
|
||
pressed[i] = 0;
|
||
key_state &= ~(1 << i);
|
||
}
|
||
}
|
||
}
|
||
osDelay(10); /* 10ms 轮询间隔 */
|
||
}
|
||
/* USER CODE END Key */
|
||
}
|
||
|
||
/* USER CODE BEGIN Header_Sensor */
|
||
/**
|
||
* @brief Function implementing the Sensor_Task thread.
|
||
* @param argument: Not used
|
||
* @retval None
|
||
*/
|
||
/* USER CODE END Header_Sensor */
|
||
void Sensor(void *argument) {
|
||
/* USER CODE BEGIN Sensor */
|
||
elog_d("Init", "Sensor task started");
|
||
/* 按键处理任务 - 按键状态切换控制 */
|
||
osEventFlagsSet(init_okHandle, 1 << 1);
|
||
|
||
/* Infinite loop */
|
||
for (;;) {
|
||
/* 检测新按下的按键 */
|
||
uint8_t key_pressed = key_state & ~key_state_prev;
|
||
key_state_prev = key_state;
|
||
|
||
/* ===== M__KEY (bit0) 控制 ===== */
|
||
if (key_pressed & (1 << 0)) {
|
||
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 {
|
||
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)) {
|
||
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)) {
|
||
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 {
|
||
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 按下:循环设定时间 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 {
|
||
// 运行中按下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;
|
||
}
|
||
}
|
||
|
||
osDelay(20); /* 20ms 检查一次按键状态 */
|
||
}
|
||
/* USER CODE END Sensor */
|
||
}
|
||
|
||
/* USER CODE BEGIN Header_Motor */
|
||
/**
|
||
* @brief Function implementing the Motor_Task thread.
|
||
* @param argument: Not used
|
||
* @retval None
|
||
*/
|
||
/* USER CODE END Header_Motor */
|
||
void Motor(void *argument) {
|
||
/* USER CODE BEGIN Motor */
|
||
elog_d("Init", "Motor task started");
|
||
|
||
/* 电机驱动初始化 */
|
||
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++;
|
||
|
||
// 每1000ms调用一次Timer_Tick(每2个循环周期)
|
||
if (tick_counter % 2 == 0) {
|
||
Timer_Tick(); // 倒计时处理
|
||
}
|
||
|
||
// 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
|
||
* @retval None
|
||
*/
|
||
/* 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);
|
||
|
||
// HAL_UART_Transmit(&huart1, (uint8_t *)buf, strlen(buf), HAL_MAX_DELAY);
|
||
/* Infinite loop */
|
||
for (;;) {
|
||
|
||
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 */
|
||
}
|
||
|
||
/* USER CODE BEGIN Header_MP3 */
|
||
|
||
/**
|
||
* @brief Function implementing the MP3_Play_Task thread.
|
||
* @param argument: Not used
|
||
* @retval None
|
||
*/
|
||
/* USER CODE END Header_MP3 */
|
||
|
||
void MP3(void *argument) {
|
||
/* USER CODE BEGIN MP3 */
|
||
elog_d("Init", "MP3 task started");
|
||
elog_d("MP3", "USART3波特率: 9600");
|
||
|
||
// 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", "等待模块上电稳定...");
|
||
// 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;
|
||
}
|
||
|
||
|
||
|
||
elog_i("MP3", "模块初始化完成");
|
||
MP3_Play(1);
|
||
osEventFlagsSet(init_okHandle, 1 << 4);
|
||
|
||
/* Infinite loop */
|
||
|
||
for (;;) {
|
||
|
||
osDelay(10000);
|
||
}
|
||
/* USER CODE END MP3 */
|
||
}
|
||
|
||
/* Private application code --------------------------------------------------*/
|
||
/* USER CODE BEGIN Application */
|
||
|
||
/* USER CODE END Application */
|