feat: 优化界面设计与传感器功能
主要更改包括: 1. 传感器功能改进: - 实现水位传感器状态变化检测,正确处理有水/无水状态 - 优化温湿度数据显示,使用更清晰的格式 2. LCD界面优化: - 适配80*160屏幕尺寸,调整所有页面布局 - 温湿度页面:使用"C"作为温度单位,优化显示位置 - 食物重量页面:调整显示位置,简化状态文本 - 水位页面:替换圆形图标为矩形背景,提升视觉效果 - 系统状态页面:简化状态文本,优化布局 - 统一标题栏设计,添加分隔线 3. 功能增强: - 实现ST7735_DrawLine函数,用于绘制分隔线 - 实现ST7735_FillCircle函数,用于图形绘制 - 添加步进电机驱动代码,支持电机控制 4. 其他优化: - 修复未使用变量的编译警告 - 调整字体大小和显示位置,提高可读性 - 优化颜色方案,提升视觉体验 这些更改使界面更加美观、清晰,同时增强了系统的功能和稳定性。
This commit is contained in:
@@ -46,6 +46,7 @@ add_executable(${CMAKE_PROJECT_NAME}
|
||||
Core/Bsp/MultiButton-master/Multi_Button.c
|
||||
Core/Bsp/BSP_Device/bsp_aht30/bsp_aht30.c
|
||||
Core/Bsp/BSP_Device/bsp_rtc/bsp_rtc.c
|
||||
Core/Bsp/bsp_motor/stepper_motor.c
|
||||
)
|
||||
# Add STM32CubeMX generated sources
|
||||
add_subdirectory(cmake/stm32cubemx)
|
||||
@@ -74,6 +75,7 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
|
||||
Core/Bsp/MultiButton-master
|
||||
Core/Bsp/BSP_Device/bsp_aht30
|
||||
Core/Bsp/BSP_Device/bsp_rtc
|
||||
Core/Bsp/bsp_motor
|
||||
)
|
||||
|
||||
# Add project symbols (macros)
|
||||
|
||||
@@ -59,6 +59,9 @@ HAL_StatusTypeDef MP3_Init(void)
|
||||
HAL_StatusTypeDef ret1 = MP3_SendCommand(init_cmd1, sizeof(init_cmd1));
|
||||
elog_d("MP3", "开启声音命令返回值: %d", ret1);
|
||||
|
||||
// 设置为低音量-测试用
|
||||
MP3_SetVolume(2);
|
||||
|
||||
// 延时等待模块响应
|
||||
HAL_Delay(100);
|
||||
|
||||
|
||||
@@ -554,6 +554,108 @@ void ST7735_InvertColors(bool invert) {
|
||||
ST7735_Unselect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在ST7735液晶显示屏上绘制一条直线
|
||||
*
|
||||
* @param x0 直线起始点的X坐标
|
||||
* @param y0 直线起始点的Y坐标
|
||||
* @param x1 直线结束点的X坐标
|
||||
* @param y1 直线结束点的Y坐标
|
||||
* @param color 直线的颜色
|
||||
*/
|
||||
void ST7735_DrawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color) {
|
||||
// 检查坐标是否超出显示屏边界
|
||||
if ((x0 >= ST7735_WIDTH) || (y0 >= ST7735_HEIGHT) || (x1 >= ST7735_WIDTH) || (y1 >= ST7735_HEIGHT))
|
||||
return;
|
||||
|
||||
// 选择显示屏以进行操作
|
||||
ST7735_Select();
|
||||
|
||||
// 使用Bresenham算法绘制直线
|
||||
int16_t dx = abs((int16_t)x1 - (int16_t)x0);
|
||||
int16_t dy = abs((int16_t)y1 - (int16_t)y0);
|
||||
int16_t sx = (x0 < x1) ? 1 : -1;
|
||||
int16_t sy = (y0 < y1) ? 1 : -1;
|
||||
int16_t err = dx - dy;
|
||||
|
||||
int16_t current_x = x0;
|
||||
int16_t current_y = y0;
|
||||
|
||||
while (1) {
|
||||
// 绘制当前点
|
||||
ST7735_DrawPixel(current_x, current_y, color);
|
||||
|
||||
// 检查是否到达终点
|
||||
if (current_x == x1 && current_y == y1)
|
||||
break;
|
||||
|
||||
// 计算下一步
|
||||
int16_t e2 = 2 * err;
|
||||
if (e2 > -dy) {
|
||||
err -= dy;
|
||||
current_x += sx;
|
||||
}
|
||||
if (e2 < dx) {
|
||||
err += dx;
|
||||
current_y += sy;
|
||||
}
|
||||
}
|
||||
|
||||
// 取消选择显示屏,结束操作
|
||||
ST7735_Unselect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在ST7735液晶显示屏上绘制一个填充圆
|
||||
*
|
||||
* @param x 圆心的X坐标
|
||||
* @param y 圆心的Y坐标
|
||||
* @param radius 圆的半径
|
||||
* @param color 填充颜色
|
||||
*/
|
||||
void ST7735_FillCircle(uint16_t x, uint16_t y, uint16_t radius, uint16_t color) {
|
||||
// 检查坐标是否超出显示屏边界
|
||||
if ((x >= ST7735_WIDTH) || (y >= ST7735_HEIGHT))
|
||||
return;
|
||||
|
||||
// 选择显示屏以进行操作
|
||||
ST7735_Select();
|
||||
|
||||
// 使用中点圆算法绘制填充圆
|
||||
int16_t x_pos = radius;
|
||||
int16_t y_pos = 0;
|
||||
int16_t err = 1 - radius;
|
||||
|
||||
while (x_pos >= y_pos) {
|
||||
// 绘制水平扫描线
|
||||
if ((y + y_pos) < ST7735_HEIGHT) {
|
||||
ST7735_FillRectangle(x - x_pos, y + y_pos, 2 * x_pos + 1, 1, color);
|
||||
}
|
||||
if ((y - y_pos) >= 0 && y_pos != 0) {
|
||||
ST7735_FillRectangle(x - x_pos, y - y_pos, 2 * x_pos + 1, 1, color);
|
||||
}
|
||||
if ((y + x_pos) < ST7735_HEIGHT && x_pos != y_pos) {
|
||||
ST7735_FillRectangle(x - y_pos, y + x_pos, 2 * y_pos + 1, 1, color);
|
||||
}
|
||||
if ((y - x_pos) >= 0 && x_pos != y_pos) {
|
||||
ST7735_FillRectangle(x - y_pos, y - x_pos, 2 * y_pos + 1, 1, color);
|
||||
}
|
||||
|
||||
// 计算下一步
|
||||
if (err < 0) {
|
||||
y_pos++;
|
||||
err += 2 * y_pos + 1;
|
||||
} else {
|
||||
x_pos--;
|
||||
err += 2 * (y_pos - x_pos) + 1;
|
||||
y_pos++;
|
||||
}
|
||||
}
|
||||
|
||||
// 取消选择显示屏,结束操作
|
||||
ST7735_Unselect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置ST7735显示器的伽马曲线
|
||||
*
|
||||
|
||||
@@ -315,6 +315,16 @@ extern "C"
|
||||
// 解释:此函数用于在显示屏上的指定位置绘制图像,图像数据以数组形式提供
|
||||
void ST7735_DrawImage(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint16_t *data);
|
||||
|
||||
// 在指定位置绘制一条直线
|
||||
// 参数:x0 - 直线起始X坐标;y0 - 直线起始Y坐标;x1 - 直线结束X坐标;y1 - 直线结束Y坐标;color - 直线颜色
|
||||
// 解释:此函数用于在显示屏上绘制一条直线,使用Bresenham算法实现
|
||||
void ST7735_DrawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color);
|
||||
|
||||
// 绘制一个填充圆
|
||||
// 参数:x - 圆心X坐标;y - 圆心Y坐标;radius - 圆的半径;color - 填充颜色
|
||||
// 解释:此函数用于在显示屏上绘制一个填充的圆形
|
||||
void ST7735_FillCircle(uint16_t x, uint16_t y, uint16_t radius, uint16_t color);
|
||||
|
||||
// 反转显示屏颜色
|
||||
// 参数:invert - 是否反转颜色(true/false)
|
||||
// 解释:此函数用于反转显示屏上的颜色,可用于显示反转效果
|
||||
|
||||
237
Core/Bsp/bsp_motor/stepper_motor.c
Normal file
237
Core/Bsp/bsp_motor/stepper_motor.c
Normal file
@@ -0,0 +1,237 @@
|
||||
#include "stepper_motor.h"
|
||||
#include "cmsis_os.h"
|
||||
|
||||
/* USER CODE BEGIN 1 */
|
||||
|
||||
/**
|
||||
* @brief 28BYJ-48步进电机驱动源文件
|
||||
*
|
||||
* 28BYJ-48是5线4相永磁步进电机,采用ULN2003驱动器
|
||||
* 控制序列如下:
|
||||
*
|
||||
* 全步模式(4拍):
|
||||
* IN1 IN2 IN3 IN4
|
||||
* 1 0 0 0 -> Step 1
|
||||
* 0 1 0 0 -> Step 2
|
||||
* 0 0 1 0 -> Step 3
|
||||
* 0 0 0 1 -> Step 4
|
||||
*
|
||||
* 半步模式(8拍):
|
||||
* IN1 IN2 IN3 IN4
|
||||
* 1 0 0 0 -> Step 1
|
||||
* 1 1 0 0 -> Step 2
|
||||
* 0 1 0 0 -> Step 3
|
||||
* 0 1 1 0 -> Step 4
|
||||
* 0 0 1 0 -> Step 5
|
||||
* 0 0 1 1 -> Step 6
|
||||
* 0 0 0 1 -> Step 7
|
||||
* 1 0 0 1 -> Step 8
|
||||
*/
|
||||
|
||||
// 步进电机控制序列数组 - 全步模式
|
||||
static const uint8_t full_step_sequence[4][4] = {
|
||||
{1, 0, 0, 0}, // Step 1
|
||||
{0, 1, 0, 0}, // Step 2
|
||||
{0, 0, 1, 0}, // Step 3
|
||||
{0, 0, 0, 1} // Step 4
|
||||
};
|
||||
|
||||
// 步进电机控制序列数组 - 半步模式
|
||||
static const uint8_t half_step_sequence[8][4] = {
|
||||
{1, 0, 0, 0}, // Step 1
|
||||
{1, 1, 0, 0}, // Step 2
|
||||
{0, 1, 0, 0}, // Step 3
|
||||
{0, 1, 1, 0}, // Step 4
|
||||
{0, 0, 1, 0}, // Step 5
|
||||
{0, 0, 1, 1}, // Step 6
|
||||
{0, 0, 0, 1}, // Step 7
|
||||
{1, 0, 0, 1} // Step 8
|
||||
};
|
||||
|
||||
// 当前步序位置
|
||||
static uint8_t current_step = 0;
|
||||
static StepperMode_t current_mode = STEPPER_MODE_FULL_STEP;
|
||||
|
||||
/**
|
||||
* @brief 设置步进电机各引脚状态
|
||||
* @param in1~in4 各相的高低电平状态 (0或1)
|
||||
*/
|
||||
static void Set_Stepper_Pins(uint8_t in1, uint8_t in2, uint8_t in3, uint8_t in4)
|
||||
{
|
||||
// 设置IN1引脚
|
||||
if(in1) {
|
||||
HAL_GPIO_WritePin(STEPPER_IN1_PORT, STEPPER_IN1_PIN, GPIO_PIN_SET);
|
||||
} else {
|
||||
HAL_GPIO_WritePin(STEPPER_IN1_PORT, STEPPER_IN1_PIN, GPIO_PIN_RESET);
|
||||
}
|
||||
|
||||
// 设置IN2引脚
|
||||
if(in2) {
|
||||
HAL_GPIO_WritePin(STEPPER_IN2_PORT, STEPPER_IN2_PIN, GPIO_PIN_SET);
|
||||
} else {
|
||||
HAL_GPIO_WritePin(STEPPER_IN2_PORT, STEPPER_IN2_PIN, GPIO_PIN_RESET);
|
||||
}
|
||||
|
||||
// 设置IN3引脚
|
||||
if(in3) {
|
||||
HAL_GPIO_WritePin(STEPPER_IN3_PORT, STEPPER_IN3_PIN, GPIO_PIN_SET);
|
||||
} else {
|
||||
HAL_GPIO_WritePin(STEPPER_IN3_PORT, STEPPER_IN3_PIN, GPIO_PIN_RESET);
|
||||
}
|
||||
|
||||
// 设置IN4引脚
|
||||
if(in4) {
|
||||
HAL_GPIO_WritePin(STEPPER_IN4_PORT, STEPPER_IN4_PIN, GPIO_PIN_SET);
|
||||
} else {
|
||||
HAL_GPIO_WritePin(STEPPER_IN4_PORT, STEPPER_IN4_PIN, GPIO_PIN_RESET);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化步进电机
|
||||
*/
|
||||
void Stepper_Motor_Init(void)
|
||||
{
|
||||
|
||||
Stepper_Motor_Stop(); // 停止
|
||||
|
||||
// 重置步序位置
|
||||
current_step = 0;
|
||||
current_mode = STEPPER_MODE_FULL_STEP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 单步驱动步进电机 (全步模式)
|
||||
* @param direction 转动方向 (STEPPER_DIR_CW 或 STEPPER_DIR_CCW)
|
||||
*/
|
||||
void Stepper_Motor_OneStep_Full(StepperDir_t direction)
|
||||
{
|
||||
uint8_t step_idx;
|
||||
|
||||
if(direction == STEPPER_DIR_CW) {
|
||||
// 顺时针:步序递增
|
||||
step_idx = current_step % 4;
|
||||
current_step++;
|
||||
} else {
|
||||
// 逆时针:步序递减
|
||||
if(current_step == 0) {
|
||||
current_step = 4; // 准备递减到3
|
||||
}
|
||||
current_step--;
|
||||
step_idx = current_step % 4;
|
||||
}
|
||||
|
||||
// 根据步序设置引脚状态
|
||||
Set_Stepper_Pins(
|
||||
full_step_sequence[step_idx][0],
|
||||
full_step_sequence[step_idx][1],
|
||||
full_step_sequence[step_idx][2],
|
||||
full_step_sequence[step_idx][3]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 单步驱动步进电机 (半步模式)
|
||||
* @param direction 转动方向 (STEPPER_DIR_CW 或 STEPPER_DIR_CCW)
|
||||
*/
|
||||
void Stepper_Motor_OneStep_Half(StepperDir_t direction)
|
||||
{
|
||||
uint8_t step_idx;
|
||||
|
||||
if(direction == STEPPER_DIR_CW) {
|
||||
// 顺时针:步序递增
|
||||
step_idx = current_step % 8;
|
||||
current_step++;
|
||||
} else {
|
||||
// 逆时针:步序递减
|
||||
if(current_step == 0) {
|
||||
current_step = 8; // 准备递减到7
|
||||
}
|
||||
current_step--;
|
||||
step_idx = current_step % 8;
|
||||
}
|
||||
|
||||
// 根据步序设置引脚状态
|
||||
Set_Stepper_Pins(
|
||||
half_step_sequence[step_idx][0],
|
||||
half_step_sequence[step_idx][1],
|
||||
half_step_sequence[step_idx][2],
|
||||
half_step_sequence[step_idx][3]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 驱动步进电机转动指定步数
|
||||
* @param steps 需要转动的步数
|
||||
* @param direction 转动方向
|
||||
* @param delay_ms 每步之间的延时 (毫秒)
|
||||
* @param mode 驱动模式
|
||||
*/
|
||||
void Stepper_Motor_RotateSteps(uint16_t steps, StepperDir_t direction, uint16_t delay_ms, StepperMode_t mode)
|
||||
{
|
||||
for(uint16_t i = 0; i < steps; i++) {
|
||||
if(mode == STEPPER_MODE_FULL_STEP) {
|
||||
Stepper_Motor_OneStep_Full(direction);
|
||||
} else {
|
||||
Stepper_Motor_OneStep_Half(direction);
|
||||
}
|
||||
|
||||
// 延时,控制转速
|
||||
osDelay(delay_ms);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 驱动步进电机转动指定角度
|
||||
* @param angle 需要转动的角度 (0~360)
|
||||
* @param direction 转动方向
|
||||
* @param delay_ms 每步之间的延时 (毫秒)
|
||||
* @param mode 驱动模式
|
||||
*/
|
||||
void Stepper_Motor_RotateAngle(float angle, StepperDir_t direction, uint16_t delay_ms, StepperMode_t mode)
|
||||
{
|
||||
uint16_t steps;
|
||||
|
||||
// 计算需要的步数
|
||||
// 28BYJ-48步距角为5.625°,即每步5.625度
|
||||
// 在全步模式下,每转一圈需要4096步才能达到实际输出轴一圈
|
||||
// 所以每度需要 4096 / 360 ≈ 11.38 步
|
||||
if(mode == STEPPER_MODE_FULL_STEP) {
|
||||
steps = (uint16_t)(angle * (STEPPER_FULL_STEP_PER_REVOLUTION / 360.0));
|
||||
} else {
|
||||
steps = (uint16_t)(angle * (STEPPER_HALF_STEP_PER_REVOLUTION / 360.0));
|
||||
}
|
||||
|
||||
Stepper_Motor_RotateSteps(steps, direction, delay_ms, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 驱动步进电机转动指定圈数
|
||||
* @param rounds 需要转动的圈数
|
||||
* @param direction 转动方向
|
||||
* @param delay_ms 每步之间的延时 (毫秒)
|
||||
* @param mode 驱动模式
|
||||
*/
|
||||
void Stepper_Motor_RotateRounds(uint8_t rounds, StepperDir_t direction, uint16_t delay_ms, StepperMode_t mode)
|
||||
{
|
||||
uint32_t total_steps;
|
||||
|
||||
if(mode == STEPPER_MODE_FULL_STEP) {
|
||||
total_steps = rounds * STEPPER_FULL_STEP_PER_REVOLUTION;
|
||||
} else {
|
||||
total_steps = rounds * STEPPER_HALF_STEP_PER_REVOLUTION;
|
||||
}
|
||||
|
||||
Stepper_Motor_RotateSteps((uint16_t)total_steps, direction, delay_ms, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 停止步进电机
|
||||
*/
|
||||
void Stepper_Motor_Stop(void)
|
||||
{
|
||||
// 将所有引脚设置为低电平,电机停止
|
||||
Set_Stepper_Pins(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* USER CODE END 1 */
|
||||
108
Core/Bsp/bsp_motor/stepper_motor.h
Normal file
108
Core/Bsp/bsp_motor/stepper_motor.h
Normal file
@@ -0,0 +1,108 @@
|
||||
#ifndef __STEPPER_MOTOR_H
|
||||
#define __STEPPER_MOTOR_H
|
||||
|
||||
#include "main.h"
|
||||
|
||||
/* USER CODE BEGIN 0 */
|
||||
|
||||
/**
|
||||
* @brief 28BYJ-48步进电机驱动头文件
|
||||
*
|
||||
* 该文件提供对28BYJ-48步进电机的基本控制功能
|
||||
* 包括正转、反转、单步转动等操作
|
||||
*
|
||||
* 28BYJ-48电机参数:
|
||||
* - 步距角:5.625° (64步/圈,如果使用内部减速比为64:1)
|
||||
* - 减速比:64:1
|
||||
* - 实际输出轴转一圈需要4096步 (64 * 64)
|
||||
*/
|
||||
|
||||
// 步进电机驱动引脚定义 - 可根据实际硬件连接修改
|
||||
#define STEPPER_IN1_PORT STEPPER_IN1_GPIO_Port
|
||||
#define STEPPER_IN1_PIN STEPPER_IN1_Pin
|
||||
|
||||
#define STEPPER_IN2_PORT STEPPER_IN2_GPIO_Port
|
||||
#define STEPPER_IN2_PIN STEPPER_IN2_Pin
|
||||
|
||||
#define STEPPER_IN3_PORT STEPPER_IN3_GPIO_Port
|
||||
#define STEPPER_IN3_PIN STEPPER_IN3_Pin
|
||||
|
||||
#define STEPPER_IN4_PORT STEPPER_IN4_GPIO_Port
|
||||
#define STEPPER_IN4_PIN STEPPER_IN4_Pin
|
||||
|
||||
// 步进电机相关常量定义
|
||||
#define STEPPER_FULL_STEP_PER_REVOLUTION 4096 // 一圈需要的步数 (64*64)
|
||||
#define STEPPER_HALF_STEP_PER_REVOLUTION 2048 // 半步模式下一圈需要的步数
|
||||
#define STEPPER_WAVE_DRIVE_PER_REVOLUTION 4096 // 波形驱动模式下一圈需要的步数
|
||||
|
||||
// 电机状态定义
|
||||
typedef enum {
|
||||
STEPPER_STATUS_OK = 0,
|
||||
STEPPER_STATUS_ERROR,
|
||||
STEPPER_STATUS_BUSY
|
||||
} StepperStatus_t;
|
||||
|
||||
// 电机转动方向定义
|
||||
typedef enum {
|
||||
STEPPER_DIR_CW = 0, // 顺时针
|
||||
STEPPER_DIR_CCW // 逆时针
|
||||
} StepperDir_t;
|
||||
|
||||
// 电机驱动模式定义
|
||||
typedef enum {
|
||||
STEPPER_MODE_FULL_STEP = 0, // 全步模式
|
||||
STEPPER_MODE_HALF_STEP // 半步模式
|
||||
} StepperMode_t;
|
||||
|
||||
/**
|
||||
* @brief 初始化步进电机
|
||||
*/
|
||||
void Stepper_Motor_Init(void);
|
||||
|
||||
/**
|
||||
* @brief 单步驱动步进电机 (全步模式)
|
||||
* @param direction 转动方向 (STEPPER_DIR_CW 或 STEPPER_DIR_CCW)
|
||||
*/
|
||||
void Stepper_Motor_OneStep_Full(StepperDir_t direction);
|
||||
|
||||
/**
|
||||
* @brief 单步驱动步进电机 (半步模式)
|
||||
* @param direction 转动方向 (STEPPER_DIR_CW 或 STEPPER_DIR_CCW)
|
||||
*/
|
||||
void Stepper_Motor_OneStep_Half(StepperDir_t direction);
|
||||
|
||||
/**
|
||||
* @brief 驱动步进电机转动指定步数
|
||||
* @param steps 需要转动的步数
|
||||
* @param direction 转动方向
|
||||
* @param delay_ms 每步之间的延时 (毫秒)
|
||||
* @param mode 驱动模式
|
||||
*/
|
||||
void Stepper_Motor_RotateSteps(uint16_t steps, StepperDir_t direction, uint16_t delay_ms, StepperMode_t mode);
|
||||
|
||||
/**
|
||||
* @brief 驱动步进电机转动指定角度
|
||||
* @param angle 需要转动的角度 (0~360)
|
||||
* @param direction 转动方向
|
||||
* @param delay_ms 每步之间的延时 (毫秒)
|
||||
* @param mode 驱动模式
|
||||
*/
|
||||
void Stepper_Motor_RotateAngle(float angle, StepperDir_t direction, uint16_t delay_ms, StepperMode_t mode);
|
||||
|
||||
/**
|
||||
* @brief 驱动步进电机转动指定圈数
|
||||
* @param rounds 需要转动的圈数
|
||||
* @param direction 转动方向
|
||||
* @param delay_ms 每步之间的延时 (毫秒)
|
||||
* @param mode 驱动模式
|
||||
*/
|
||||
void Stepper_Motor_RotateRounds(uint8_t rounds, StepperDir_t direction, uint16_t delay_ms, StepperMode_t mode);
|
||||
|
||||
/**
|
||||
* @brief 停止步进电机
|
||||
*/
|
||||
void Stepper_Motor_Stop(void);
|
||||
|
||||
/* USER CODE END 0 */
|
||||
|
||||
#endif /* __STEPPER_MOTOR_H */
|
||||
@@ -87,6 +87,14 @@ void Error_Handler(void);
|
||||
#define M3_IO_GPIO_Port GPIOB
|
||||
#define MOTOR_Pin GPIO_PIN_6
|
||||
#define MOTOR_GPIO_Port GPIOC
|
||||
#define STEPPER_IN1_Pin GPIO_PIN_7
|
||||
#define STEPPER_IN1_GPIO_Port GPIOC
|
||||
#define STEPPER_IN2_Pin GPIO_PIN_8
|
||||
#define STEPPER_IN2_GPIO_Port GPIOC
|
||||
#define STEPPER_IN3_Pin GPIO_PIN_9
|
||||
#define STEPPER_IN3_GPIO_Port GPIOC
|
||||
#define STEPPER_IN4_Pin GPIO_PIN_8
|
||||
#define STEPPER_IN4_GPIO_Port GPIOA
|
||||
|
||||
/* USER CODE BEGIN Private defines */
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "bsp_aht30.h"
|
||||
#include "bsp_rtc.h"
|
||||
#include "i2c.h"
|
||||
#include "stepper_motor.h" // 添加步进电机驱动头文件
|
||||
|
||||
/* USER CODE END Includes */
|
||||
|
||||
@@ -52,6 +53,10 @@
|
||||
#define KEY2_MANUAL_FEED // 按键2:手动喂食(仅手动模式有效)
|
||||
#define KEY3_DISPLAY_NEXT // 按键3:切换显示界面
|
||||
#define KEY4_TIME_SET // 按键4:长按设置时间
|
||||
/***********************人体存在和水位检测作为两个单独的按键(GPIO输入)******************* */
|
||||
#define KEY_WATER_LEVEL // 水位检测
|
||||
#define KEY_BODY_EXIST // 人体存在
|
||||
|
||||
/* USER CODE END PD */
|
||||
|
||||
/* Private macro -------------------------------------------------------------*/
|
||||
@@ -80,10 +85,26 @@ typedef struct {
|
||||
uint8_t system_mode; // 系统模式 (0=手动, 1=自动)
|
||||
} Sensor_Data_t;
|
||||
|
||||
// 喂食控制命令枚举
|
||||
typedef enum {
|
||||
FEED_CMD_NONE = 0, // 无命令
|
||||
FEED_CMD_MANUAL, // 手动喂食
|
||||
FEED_CMD_AUTO, // 自动喂食
|
||||
FEED_CMD_REMOTE, // 远程喂食
|
||||
FEED_CMD_TEST // 测试喂食
|
||||
} Feed_Cmd_t;
|
||||
|
||||
// 全局变量
|
||||
static LCD_Page_t current_page = LCD_PAGE_TIME; // 当前显示页面
|
||||
static Sensor_Data_t sensor_data = {0}; // 传感器数据
|
||||
static volatile uint8_t lcd_force_refresh = 0; // 强制刷新标志(RTC更新时设置)
|
||||
static volatile uint8_t system_mode = 1; // 系统模式:1=自动模式,0=手动模式(全局变量)
|
||||
static volatile uint8_t feeding_in_progress = 0; // 喂食进行中标志
|
||||
|
||||
// 喂食控制相关全局变量
|
||||
static volatile Feed_Cmd_t feed_command = FEED_CMD_NONE; // 喂食命令标志
|
||||
static volatile uint16_t feed_angle = 90; // 喂食角度(默认90度)
|
||||
static volatile uint8_t feed_amount = 1; // 喂食份数
|
||||
|
||||
/* USER CODE END Variables */
|
||||
/* Definitions for defaultTask */
|
||||
@@ -121,6 +142,13 @@ const osThreadAttr_t sensor_attributes = {
|
||||
.stack_size = 1024 * 4,
|
||||
.priority = (osPriority_t) osPriorityNormal,
|
||||
};
|
||||
/* Definitions for step_motor */
|
||||
osThreadId_t step_motorHandle;
|
||||
const osThreadAttr_t step_motor_attributes = {
|
||||
.name = "step_motor",
|
||||
.stack_size = 512 * 4,
|
||||
.priority = (osPriority_t) osPriorityNormal,
|
||||
};
|
||||
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
/* USER CODE BEGIN FunctionPrototypes */
|
||||
@@ -140,6 +168,7 @@ extern void wifi_task_mqtt(void *argument);
|
||||
void LCD_Task(void *argument);
|
||||
void button_task(void *argument);
|
||||
void sensorTask(void *argument);
|
||||
void step_motor_task(void *argument);
|
||||
|
||||
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
|
||||
|
||||
@@ -151,6 +180,9 @@ void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
|
||||
void MX_FREERTOS_Init(void) {
|
||||
/* USER CODE BEGIN Init */
|
||||
ST7735_Init(); // 初始化ST7735显示屏
|
||||
|
||||
|
||||
|
||||
/* USER CODE END Init */
|
||||
|
||||
/* USER CODE BEGIN RTOS_MUTEX */
|
||||
@@ -185,6 +217,9 @@ void MX_FREERTOS_Init(void) {
|
||||
/* creation of sensor */
|
||||
sensorHandle = osThreadNew(sensorTask, NULL, &sensor_attributes);
|
||||
|
||||
/* creation of step_motor */
|
||||
step_motorHandle = osThreadNew(step_motor_task, NULL, &step_motor_attributes);
|
||||
|
||||
/* USER CODE BEGIN RTOS_THREADS */
|
||||
/* add threads, ... */
|
||||
/* USER CODE END RTOS_THREADS */
|
||||
@@ -248,7 +283,6 @@ void LCD_Task(void *argument)
|
||||
|
||||
char display_str[32];
|
||||
RTC_Time_t rtc_time;
|
||||
uint16_t text_color = ST7735_WHITE;
|
||||
uint16_t bg_color = ST7735_BLACK;
|
||||
|
||||
// 显示区域参数 - 适配160x80横屏
|
||||
@@ -272,89 +306,137 @@ void LCD_Task(void *argument)
|
||||
case LCD_PAGE_TIME:
|
||||
// 时间显示页面
|
||||
ST7735_FillScreen(bg_color);
|
||||
ST7735_WriteString(2, 2, "Time", &Font_7x10, text_color, bg_color);
|
||||
|
||||
|
||||
// 绘制标题栏
|
||||
ST7735_FillRectangle(0, 0, ST7735_WIDTH, 15, ST7735_BLUE);
|
||||
ST7735_WriteString(5, 2, "Time", &Font_7x10, ST7735_WHITE, ST7735_BLUE);
|
||||
|
||||
// 使用RTC时间(独立于SNTP,即使网络断开也能显示)
|
||||
if (BSP_RTC_GetTime(&rtc_time) == 0) {
|
||||
// 显示时间(大字体,只显示时:分,不显示秒)
|
||||
snprintf(display_str, sizeof(display_str), "%02d:%02d", rtc_time.hour, rtc_time.minute);
|
||||
ST7735_WriteString(25, 20, display_str, &Font_16x26, text_color, bg_color);
|
||||
ST7735_WriteString(40, 30, display_str, &Font_16x26, ST7735_WHITE, bg_color);
|
||||
|
||||
// 显示年月日(小字体,向下移动并居中)
|
||||
snprintf(display_str, sizeof(display_str), "%04d-%02d-%02d",
|
||||
rtc_time.year, rtc_time.month, rtc_time.day);
|
||||
ST7735_WriteString(20, 60, display_str, &Font_7x10, ST7735_CYAN, bg_color);
|
||||
ST7735_WriteString(30, 65, display_str, &Font_7x10, ST7735_CYAN, bg_color);
|
||||
} else {
|
||||
ST7735_WriteString(25, 20, "--:--", &Font_16x26, text_color, bg_color);
|
||||
ST7735_WriteString(20, 60, "----/--/--", &Font_7x10, ST7735_CYAN, bg_color);
|
||||
ST7735_WriteString(40, 30, "--:--", &Font_16x26, ST7735_WHITE, bg_color);
|
||||
ST7735_WriteString(30, 65, "----/--/--", &Font_7x10, ST7735_CYAN, bg_color);
|
||||
}
|
||||
break;
|
||||
|
||||
case LCD_PAGE_TEMP_HUMI:
|
||||
// 温湿度页面
|
||||
ST7735_FillScreen(bg_color);
|
||||
ST7735_WriteString(2, 2, "Temp & Humi", &Font_7x10, text_color, bg_color);
|
||||
|
||||
// 绘制标题栏
|
||||
ST7735_FillRectangle(0, 0, ST7735_WIDTH, 15, ST7735_GREEN);
|
||||
ST7735_WriteString(5, 2, "Temp & Humi", &Font_7x10, ST7735_WHITE, ST7735_GREEN);
|
||||
|
||||
// 绘制分隔线
|
||||
ST7735_DrawLine(0, 15, ST7735_WIDTH, 15, ST7735_WHITE);
|
||||
|
||||
// 显示温度(橙色 - 国际标准温度色)
|
||||
snprintf(display_str, sizeof(display_str), "T: %.2f C", sensor_data.temperature);
|
||||
ST7735_WriteString(5, 20, display_str, &Font_11x18, ST7735_ORANGE, bg_color);
|
||||
snprintf(display_str, sizeof(display_str), "Temp:");
|
||||
ST7735_WriteString(5, 25, display_str, &Font_7x10, ST7735_WHITE, bg_color);
|
||||
snprintf(display_str, sizeof(display_str), "%.3fC", sensor_data.temperature);
|
||||
ST7735_WriteString(40, 25, display_str, &Font_16x26, ST7735_ORANGE, bg_color);
|
||||
|
||||
// 显示湿度(青色 - 国际标准湿度色)
|
||||
snprintf(display_str, sizeof(display_str), "H: %.2f %%", sensor_data.humidity);
|
||||
ST7735_WriteString(5, 45, display_str, &Font_11x18, ST7735_CYAN, bg_color);
|
||||
snprintf(display_str, sizeof(display_str), "Humi:");
|
||||
ST7735_WriteString(5, 55, display_str, &Font_7x10, ST7735_WHITE, bg_color);
|
||||
snprintf(display_str, sizeof(display_str), "%.3f%%", sensor_data.humidity);
|
||||
ST7735_WriteString(40, 55, display_str, &Font_16x26, ST7735_CYAN, bg_color);
|
||||
break;
|
||||
|
||||
case LCD_PAGE_FOOD_WEIGHT:
|
||||
// 食物重量页面
|
||||
ST7735_FillScreen(bg_color);
|
||||
ST7735_WriteString(2, 2, "Food Weight", &Font_7x10, text_color, bg_color);
|
||||
|
||||
// 绘制标题栏
|
||||
ST7735_FillRectangle(0, 0, ST7735_WIDTH, 15, ST7735_YELLOW);
|
||||
ST7735_WriteString(5, 2, "Food Weight", &Font_7x10, ST7735_BLACK, ST7735_YELLOW);
|
||||
|
||||
// 绘制分隔线
|
||||
ST7735_DrawLine(0, 15, ST7735_WIDTH, 15, ST7735_WHITE);
|
||||
|
||||
// 显示食物重量
|
||||
snprintf(display_str, sizeof(display_str), "%.1f g", sensor_data.food_weight);
|
||||
ST7735_WriteString(25, 20, display_str, &Font_16x26, text_color, bg_color);
|
||||
snprintf(display_str, sizeof(display_str), "%.2f g", sensor_data.food_weight);
|
||||
ST7735_WriteString(15, 25, display_str, &Font_16x26, ST7735_WHITE, bg_color);
|
||||
|
||||
// 显示状态
|
||||
if (sensor_data.food_weight < 50.0f) {
|
||||
ST7735_WriteString(20, 55, "Low", &Font_11x18, ST7735_RED, bg_color);
|
||||
ST7735_WriteString(20, 65, "LOW", &Font_11x18, ST7735_RED, bg_color);
|
||||
} else if (sensor_data.food_weight < 100.0f) {
|
||||
ST7735_WriteString(15, 55, "Medium", &Font_11x18, ST7735_YELLOW, bg_color);
|
||||
ST7735_WriteString(15, 65, "MEDIUM", &Font_11x18, ST7735_YELLOW, bg_color);
|
||||
} else {
|
||||
ST7735_WriteString(20, 55, "Good", &Font_11x18, ST7735_GREEN, bg_color);
|
||||
ST7735_WriteString(20, 65, "GOOD", &Font_11x18, ST7735_GREEN, bg_color);
|
||||
}
|
||||
break;
|
||||
|
||||
case LCD_PAGE_WATER_LEVEL:
|
||||
// 水位页面
|
||||
ST7735_FillScreen(bg_color);
|
||||
ST7735_WriteString(2, 2, "Water Level", &Font_7x10, text_color, bg_color);
|
||||
|
||||
// 绘制标题栏
|
||||
ST7735_FillRectangle(0, 0, ST7735_WIDTH, 15, ST7735_CYAN);
|
||||
ST7735_WriteString(5, 2, "Water Level", &Font_7x10, ST7735_BLACK, ST7735_CYAN);
|
||||
|
||||
// 绘制分隔线
|
||||
ST7735_DrawLine(0, 15, ST7735_WIDTH, 15, ST7735_WHITE);
|
||||
|
||||
// 根据水位传感器状态显示
|
||||
if (sensor_data.water_level == 0) {
|
||||
ST7735_WriteString(20, 20, "NO WATER", &Font_16x26, ST7735_RED, bg_color);
|
||||
ST7735_WriteString(35, 55, "Add water", &Font_11x18, ST7735_YELLOW, bg_color);
|
||||
// 绘制警告背景
|
||||
ST7735_FillRectangle(0, 20, ST7735_WIDTH, 30, ST7735_RED);
|
||||
ST7735_WriteString(20, 20, "NO WATER", &Font_16x26, ST7735_WHITE, ST7735_RED);
|
||||
ST7735_WriteString(10, 60, "Add water", &Font_16x26, ST7735_YELLOW, bg_color);
|
||||
} else {
|
||||
ST7735_WriteString(50, 25, "OK", &Font_16x26, ST7735_GREEN, bg_color);
|
||||
ST7735_WriteString(35, 55, "Water OK", &Font_11x18, text_color, bg_color);
|
||||
// 绘制正常背景
|
||||
ST7735_FillRectangle(0, 20, ST7735_WIDTH, 30, ST7735_GREEN);
|
||||
ST7735_WriteString(5, 20, "WATER OK", &Font_16x26, ST7735_WHITE, ST7735_GREEN);
|
||||
ST7735_WriteString(10, 60, "Not add", &Font_16x26, ST7735_WHITE, bg_color);
|
||||
}
|
||||
break;
|
||||
|
||||
case LCD_PAGE_SYSTEM_STATUS:
|
||||
// 系统状态页面
|
||||
ST7735_FillScreen(bg_color);
|
||||
ST7735_WriteString(2, 2, "System Status", &Font_7x10, text_color, bg_color);
|
||||
|
||||
// 绘制标题栏
|
||||
ST7735_FillRectangle(0, 0, ST7735_WIDTH, 15, ST7735_MAGENTA);
|
||||
ST7735_WriteString(5, 2, "System", &Font_7x10, ST7735_WHITE, ST7735_MAGENTA);
|
||||
|
||||
// 绘制分隔线
|
||||
ST7735_DrawLine(0, 15, ST7735_WIDTH, 15, ST7735_WHITE);
|
||||
|
||||
// 显示MQTT连接状态
|
||||
snprintf(display_str, sizeof(display_str), "MQTT:");
|
||||
ST7735_WriteString(5, 25, display_str, &Font_7x10, ST7735_WHITE, bg_color);
|
||||
if (WIFI_Is_MQTT_Connected()) {
|
||||
ST7735_WriteString(5, 20, "MQTT: OK", &Font_11x18, ST7735_GREEN, bg_color);
|
||||
ST7735_WriteString(40, 25, "OK", &Font_7x10, ST7735_GREEN, bg_color);
|
||||
} else {
|
||||
ST7735_WriteString(5, 20, "MQTT: OFF", &Font_11x18, ST7735_RED, bg_color);
|
||||
ST7735_WriteString(40, 25, "OFF", &Font_7x10, ST7735_RED, bg_color);
|
||||
}
|
||||
|
||||
// 显示系统模式
|
||||
snprintf(display_str, sizeof(display_str), "Mode:");
|
||||
ST7735_WriteString(5, 45, display_str, &Font_7x10, ST7735_WHITE, bg_color);
|
||||
if (sensor_data.system_mode) {
|
||||
ST7735_WriteString(5, 45, "Mode: AUTO", &Font_11x18, text_color, bg_color);
|
||||
ST7735_WriteString(40, 45, "AUTO", &Font_7x10, ST7735_GREEN, bg_color);
|
||||
} else {
|
||||
ST7735_WriteString(5, 45, "Mode: MANUAL", &Font_11x18, text_color, bg_color);
|
||||
ST7735_WriteString(40, 45, "MANUAL", &Font_7x10, ST7735_YELLOW, bg_color);
|
||||
}
|
||||
|
||||
// 显示喂食状态
|
||||
snprintf(display_str, sizeof(display_str), "Feed:");
|
||||
ST7735_WriteString(5, 65, display_str, &Font_7x10, ST7735_WHITE, bg_color);
|
||||
if (feeding_in_progress) {
|
||||
ST7735_WriteString(40, 65, "Busy", &Font_7x10, ST7735_ORANGE, bg_color);
|
||||
} else {
|
||||
ST7735_WriteString(40, 65, "Idle", &Font_7x10, ST7735_WHITE, bg_color);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -464,6 +546,131 @@ void sensorTask(void *argument)
|
||||
/* USER CODE END sensorTask */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 请求喂食操作
|
||||
* @param cmd: 喂食命令类型
|
||||
* @param angle: 转动角度
|
||||
* @param amount: 喂食份数
|
||||
* @retval 1=请求成功, 0=请求失败(正在喂食中)
|
||||
*/
|
||||
uint8_t Request_Feed(Feed_Cmd_t cmd, uint16_t angle, uint8_t amount) {
|
||||
if (feeding_in_progress) {
|
||||
elog_w("FEED", "喂食进行中,无法接受新命令");
|
||||
return 0;
|
||||
}
|
||||
|
||||
feed_command = cmd;
|
||||
feed_angle = angle;
|
||||
feed_amount = amount;
|
||||
|
||||
elog_i("FEED", "喂食请求已提交: cmd=%d, angle=%d, amount=%d", cmd, angle, amount);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前喂食命令
|
||||
* @retval 当前喂食命令
|
||||
*/
|
||||
Feed_Cmd_t Get_Feed_Command(void) {
|
||||
return feed_command;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除喂食命令
|
||||
*/
|
||||
void Clear_Feed_Command(void) {
|
||||
feed_command = FEED_CMD_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 执行喂食操作
|
||||
* @param cmd: 喂食命令类型
|
||||
* @param angle: 转动角度
|
||||
* @param amount: 喂食份数
|
||||
*/
|
||||
static void Execute_Feed(Feed_Cmd_t cmd, uint16_t angle, uint8_t amount) {
|
||||
if (feeding_in_progress) {
|
||||
return; // 防止重复执行
|
||||
}
|
||||
|
||||
feeding_in_progress = 1;
|
||||
|
||||
// 根据命令类型播放不同音频
|
||||
switch (cmd) {
|
||||
case FEED_CMD_MANUAL:
|
||||
MP3_Play(FEED_MANUAL_TRIGGER);
|
||||
elog_i("FEED", "执行手动喂食: 角度%d度, %d份", angle, amount);
|
||||
break;
|
||||
case FEED_CMD_AUTO:
|
||||
MP3_Play(FEED_AUTO_START);
|
||||
elog_i("FEED", "执行自动喂食: 角度%d度, %d份", angle, amount);
|
||||
break;
|
||||
case FEED_CMD_REMOTE:
|
||||
MP3_Play(REMOTE_CMD_RECEIVED);
|
||||
elog_i("FEED", "执行远程喂食: 角度%d度, %d份", angle, amount);
|
||||
break;
|
||||
case FEED_CMD_TEST:
|
||||
elog_i("FEED", "执行测试喂食: 角度%d度", angle);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// 执行实际的喂食动作
|
||||
for (uint8_t i = 0; i < amount; i++) {
|
||||
Stepper_Motor_RotateAngle(angle, STEPPER_DIR_CW, 2, STEPPER_MODE_FULL_STEP);
|
||||
if (i < amount - 1) {
|
||||
osDelay(1000); // 多份之间间隔1秒
|
||||
}
|
||||
}
|
||||
|
||||
// 等待食物落下
|
||||
osDelay(3000);
|
||||
|
||||
// 停止电机
|
||||
Stepper_Motor_Stop();
|
||||
|
||||
// 播放完成音效
|
||||
MP3_Play(FEED_COMPLETE);
|
||||
elog_i("FEED", "喂食完成");
|
||||
|
||||
feeding_in_progress = 0;
|
||||
}
|
||||
|
||||
/* USER CODE BEGIN Header_step_motor_task */
|
||||
/**
|
||||
* @brief Function implementing the step_motor thread.
|
||||
* @param argument: Not used
|
||||
* @retval None
|
||||
*/
|
||||
/* USER CODE END Header_step_motor_task */
|
||||
void step_motor_task(void *argument)
|
||||
{
|
||||
/* USER CODE BEGIN step_motor_task */
|
||||
// 初始化步进电机
|
||||
Stepper_Motor_Init();
|
||||
|
||||
/* Infinite loop */
|
||||
for(;;)
|
||||
{
|
||||
// 检查是否有喂食命令需要执行
|
||||
if (feed_command != FEED_CMD_NONE && !feeding_in_progress) {
|
||||
Feed_Cmd_t current_cmd = feed_command;
|
||||
uint16_t current_angle = feed_angle;
|
||||
uint8_t current_amount = feed_amount;
|
||||
|
||||
// 清除命令标志
|
||||
Clear_Feed_Command();
|
||||
|
||||
// 执行喂食
|
||||
Execute_Feed(current_cmd, current_angle, current_amount);
|
||||
}
|
||||
|
||||
osDelay(100); // 短暂延时,保持任务响应性
|
||||
}
|
||||
/* USER CODE END step_motor_task */
|
||||
}
|
||||
|
||||
/* Private application code --------------------------------------------------*/
|
||||
/* USER CODE BEGIN Application */
|
||||
|
||||
@@ -475,6 +682,8 @@ static Button KEY1; // 按键1
|
||||
static Button KEY2; // 按键2
|
||||
static Button KEY3; // 按键3
|
||||
static Button KEY4; // 按键4
|
||||
static Button M3_IO; // M3 IO
|
||||
static Button HC_SR505; //HC-SR505
|
||||
|
||||
uint8_t read_button_gpio(uint8_t button_id) {
|
||||
switch (button_id) {
|
||||
@@ -490,14 +699,58 @@ uint8_t read_button_gpio(uint8_t button_id) {
|
||||
case 4:
|
||||
return HAL_GPIO_ReadPin(KEY4_GPIO_Port, KEY4_Pin);
|
||||
break;
|
||||
case 5:
|
||||
return HAL_GPIO_ReadPin(M3_IO_GPIO_Port, M3_IO_Pin);
|
||||
break;
|
||||
case 6:
|
||||
return HAL_GPIO_ReadPin(HC_SR505_IO_GPIO_Port, HC_SR505_IO_Pin);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void key1_single_click_handler(Button *btn) { elog_i("KEY", "按键1单击"); }
|
||||
/**
|
||||
* @brief 按键1处理函数:模式切换(自动/手动)
|
||||
* @param btn: 按键句柄
|
||||
*/
|
||||
void key1_single_click_handler(Button *btn) {
|
||||
// 切换系统模式
|
||||
system_mode = !system_mode; // 0和1之间切换
|
||||
|
||||
// 更新传感器数据中的模式状态
|
||||
sensor_data.system_mode = system_mode;
|
||||
|
||||
// 记录日志
|
||||
elog_i("KEY", "按键1单击 - 模式切换: %s", system_mode ? "自动模式" : "手动模式");
|
||||
|
||||
// 播放模式切换音效
|
||||
MP3_Play(system_mode ? MODE_AUTO : MODE_MANUAL);
|
||||
|
||||
// 强制刷新LCD显示以更新模式状态
|
||||
lcd_force_refresh = 1;
|
||||
}
|
||||
|
||||
void key2_single_click_handler(Button *btn) { elog_i("KEY", "按键2单击"); }
|
||||
/**
|
||||
* @brief 按键2处理函数:手动喂食(仅手动模式有效)
|
||||
* @param btn: 按键句柄
|
||||
*/
|
||||
void key2_single_click_handler(Button *btn) {
|
||||
// 检查是否为手动模式
|
||||
if (!system_mode) {
|
||||
// 手动模式下才允许手动喂食
|
||||
if (Request_Feed(FEED_CMD_MANUAL, 90, 1)) {
|
||||
elog_i("KEY", "按键2单击 - 手动喂食请求已提交");
|
||||
} else {
|
||||
elog_w("KEY", "喂食进行中,请稍后再试");
|
||||
MP3_Play(SYS_ERROR_ALARM);
|
||||
}
|
||||
} else {
|
||||
// 自动模式下按键无效
|
||||
elog_w("KEY", "当前为自动模式,按键2无效");
|
||||
MP3_Play(SYS_ERROR_ALARM);
|
||||
}
|
||||
}
|
||||
|
||||
void key3_single_click_handler(Button *btn) {
|
||||
elog_i("KEY", "按键3单击");
|
||||
@@ -506,13 +759,28 @@ void key3_single_click_handler(Button *btn) {
|
||||
|
||||
void key4_single_click_handler(Button *btn) { elog_i("KEY", "按键4单击"); }
|
||||
|
||||
void M3_long_press_start_handler(Button *btn) {
|
||||
elog_i("KEY", "M3水位传感器检测到有水(低电平)");
|
||||
sensor_data.water_level = 1;
|
||||
}
|
||||
|
||||
void M3_press_up_handler(Button *btn) {
|
||||
elog_i("KEY", "M3水位传感器检测到无水(高电平)");
|
||||
sensor_data.water_level = 0;
|
||||
}
|
||||
void HC_SR055_long_click_handler(Button *btn) { elog_i("KEY", "HC-SR505触发"); }
|
||||
|
||||
|
||||
|
||||
|
||||
void user_button_init(void) {
|
||||
// 初始化按键 (active_level: 0=低电平有效, 1=高电平有效)
|
||||
button_init(&KEY1, read_button_gpio, 0, 1);
|
||||
button_init(&KEY2, read_button_gpio, 0, 2);
|
||||
button_init(&KEY3, read_button_gpio, 0, 3);
|
||||
button_init(&KEY4, read_button_gpio, 0, 4);
|
||||
|
||||
button_init(&M3_IO, read_button_gpio, 0, 5);
|
||||
button_init(&HC_SR505, read_button_gpio, 1, 6);
|
||||
elog_i("BUTTON", "按键初始化完成");
|
||||
|
||||
// 设置按键回调函数
|
||||
@@ -520,6 +788,9 @@ void user_button_init(void) {
|
||||
button_attach(&KEY2, BTN_SINGLE_CLICK, key2_single_click_handler);
|
||||
button_attach(&KEY3, BTN_SINGLE_CLICK, key3_single_click_handler);
|
||||
button_attach(&KEY4, BTN_SINGLE_CLICK, key4_single_click_handler);
|
||||
button_attach(&M3_IO, BTN_LONG_PRESS_START, M3_long_press_start_handler);
|
||||
button_attach(&M3_IO, BTN_PRESS_UP, M3_press_up_handler);
|
||||
button_attach(&HC_SR505, BTN_LONG_PRESS_START, HC_SR055_long_click_handler); // 长按触发
|
||||
|
||||
elog_i("BUTTON", "按键回调函数设置完成");
|
||||
|
||||
@@ -528,9 +799,41 @@ void user_button_init(void) {
|
||||
button_start(&KEY2);
|
||||
button_start(&KEY3);
|
||||
button_start(&KEY4);
|
||||
button_start(&M3_IO);
|
||||
button_start(&HC_SR505);
|
||||
|
||||
elog_i("BUTTON", "按键任务已启动");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前系统模式
|
||||
* @retval 系统模式 (0=手动, 1=自动)
|
||||
*/
|
||||
uint8_t Get_System_Mode(void) {
|
||||
return system_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置系统模式
|
||||
* @param mode: 目标模式 (0=手动, 1=自动)
|
||||
*/
|
||||
void Set_System_Mode(uint8_t mode) {
|
||||
if (mode <= 1) {
|
||||
system_mode = mode;
|
||||
sensor_data.system_mode = mode;
|
||||
lcd_force_refresh = 1; // 强制刷新显示
|
||||
elog_i("SYSTEM", "系统模式设置为: %s", mode ? "自动" : "手动");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查是否正在喂食
|
||||
* @retval 1=正在喂食, 0=空闲
|
||||
*/
|
||||
uint8_t Is_Feeding_In_Progress(void) {
|
||||
return feeding_in_progress;
|
||||
}
|
||||
|
||||
/* USER CODE END KEY Prototypes */
|
||||
|
||||
/* USER CODE BEGIN LCD_Page_Functions */
|
||||
@@ -619,5 +922,4 @@ void RTC_TimeUpdateCallback(void) {
|
||||
|
||||
/* USER CODE END LCD_Page_Functions */
|
||||
|
||||
/* USER CODE END Application */
|
||||
|
||||
/* USER CODE END Application */
|
||||
@@ -51,11 +51,11 @@ void MX_GPIO_Init(void)
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
|
||||
/*Configure GPIO pin Output Level */
|
||||
HAL_GPIO_WritePin(GPIOA, HX711_SCK_Pin|LCD_DC_Pin|LCD_RESET_Pin|GPIO_PIN_8, GPIO_PIN_RESET);
|
||||
HAL_GPIO_WritePin(GPIOA, HX711_SCK_Pin|LCD_DC_Pin|LCD_RESET_Pin|STEPPER_IN4_Pin, GPIO_PIN_RESET);
|
||||
|
||||
/*Configure GPIO pin Output Level */
|
||||
HAL_GPIO_WritePin(GPIOC, LCD_CS_Pin|LCD_BLK_Pin|MOTOR_Pin|GPIO_PIN_7
|
||||
|GPIO_PIN_8|GPIO_PIN_9, GPIO_PIN_RESET);
|
||||
HAL_GPIO_WritePin(GPIOC, LCD_CS_Pin|LCD_BLK_Pin|MOTOR_Pin|STEPPER_IN1_Pin
|
||||
|STEPPER_IN2_Pin|STEPPER_IN3_Pin, GPIO_PIN_RESET);
|
||||
|
||||
/*Configure GPIO pin Output Level */
|
||||
HAL_GPIO_WritePin(GPIOB, LED2_Pin|LED1_Pin, GPIO_PIN_SET);
|
||||
@@ -72,17 +72,17 @@ void MX_GPIO_Init(void)
|
||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
||||
HAL_GPIO_Init(HX711_DOUT_GPIO_Port, &GPIO_InitStruct);
|
||||
|
||||
/*Configure GPIO pins : HX711_SCK_Pin LCD_DC_Pin LCD_RESET_Pin PA8 */
|
||||
GPIO_InitStruct.Pin = HX711_SCK_Pin|LCD_DC_Pin|LCD_RESET_Pin|GPIO_PIN_8;
|
||||
/*Configure GPIO pins : HX711_SCK_Pin LCD_DC_Pin LCD_RESET_Pin STEPPER_IN4_Pin */
|
||||
GPIO_InitStruct.Pin = HX711_SCK_Pin|LCD_DC_Pin|LCD_RESET_Pin|STEPPER_IN4_Pin;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
|
||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
|
||||
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
|
||||
|
||||
/*Configure GPIO pins : LCD_CS_Pin LCD_BLK_Pin MOTOR_Pin PC7
|
||||
PC8 PC9 */
|
||||
GPIO_InitStruct.Pin = LCD_CS_Pin|LCD_BLK_Pin|MOTOR_Pin|GPIO_PIN_7
|
||||
|GPIO_PIN_8|GPIO_PIN_9;
|
||||
/*Configure GPIO pins : LCD_CS_Pin LCD_BLK_Pin MOTOR_Pin STEPPER_IN1_Pin
|
||||
STEPPER_IN2_Pin STEPPER_IN3_Pin */
|
||||
GPIO_InitStruct.Pin = LCD_CS_Pin|LCD_BLK_Pin|MOTOR_Pin|STEPPER_IN1_Pin
|
||||
|STEPPER_IN2_Pin|STEPPER_IN3_Pin;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
|
||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
|
||||
|
||||
10
hahha.ioc
10
hahha.ioc
@@ -35,7 +35,7 @@ Dma.USART1_TX.0.Priority=DMA_PRIORITY_VERY_HIGH
|
||||
Dma.USART1_TX.0.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
|
||||
FREERTOS.FootprintOK=true
|
||||
FREERTOS.IPParameters=Tasks01,FootprintOK,configTOTAL_HEAP_SIZE
|
||||
FREERTOS.Tasks01=defaultTask,41,256,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;wifi_mqtt,40,3000,wifi_task_mqtt,As external,NULL,Dynamic,NULL,NULL;LCD_SHOW_Task,40,1024,LCD_Task,Default,NULL,Dynamic,NULL,NULL;button,50,512,button_task,Default,NULL,Dynamic,NULL,NULL;sensor,24,1024,sensorTask,Default,NULL,Dynamic,NULL,NULL
|
||||
FREERTOS.Tasks01=defaultTask,41,256,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL;wifi_mqtt,40,3000,wifi_task_mqtt,As external,NULL,Dynamic,NULL,NULL;LCD_SHOW_Task,40,1024,LCD_Task,Default,NULL,Dynamic,NULL,NULL;button,50,512,button_task,Default,NULL,Dynamic,NULL,NULL;sensor,24,1024,sensorTask,Default,NULL,Dynamic,NULL,NULL;step_motor,24,512,step_motor_task,Default,NULL,Dynamic,NULL,NULL
|
||||
FREERTOS.configTOTAL_HEAP_SIZE=30000
|
||||
File.Version=6
|
||||
GPIO.groupedBy=Group By Peripherals
|
||||
@@ -147,6 +147,8 @@ PA6.Locked=true
|
||||
PA6.Signal=GPIO_Output
|
||||
PA7.Mode=TX_Only_Simplex_Unidirect_Master
|
||||
PA7.Signal=SPI1_MOSI
|
||||
PA8.GPIOParameters=GPIO_Label
|
||||
PA8.GPIO_Label=STEPPER_IN4
|
||||
PA8.Locked=true
|
||||
PA8.Signal=GPIO_Output
|
||||
PA9.Mode=Asynchronous
|
||||
@@ -215,10 +217,16 @@ PC6.GPIOParameters=GPIO_Label
|
||||
PC6.GPIO_Label=MOTOR
|
||||
PC6.Locked=true
|
||||
PC6.Signal=GPIO_Output
|
||||
PC7.GPIOParameters=GPIO_Label
|
||||
PC7.GPIO_Label=STEPPER_IN1
|
||||
PC7.Locked=true
|
||||
PC7.Signal=GPIO_Output
|
||||
PC8.GPIOParameters=GPIO_Label
|
||||
PC8.GPIO_Label=STEPPER_IN2
|
||||
PC8.Locked=true
|
||||
PC8.Signal=GPIO_Output
|
||||
PC9.GPIOParameters=GPIO_Label
|
||||
PC9.GPIO_Label=STEPPER_IN3
|
||||
PC9.Locked=true
|
||||
PC9.Signal=GPIO_Output
|
||||
PCC.Checker=false
|
||||
|
||||
Reference in New Issue
Block a user