Files
SmartMassager_STM32/Core/Src/freertos.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

810 lines
24 KiB
C
Raw Permalink 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.
/* 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 */