Files
JBao_Sprayer_FW/apps/usr_app/app_spray.c
T
2026-09-09 15:03:50 +08:00

209 lines
6.2 KiB
C
Raw 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.
/******************************************************************************
* @file app_spray.c
* @brief 喷淋作业引擎:一轮 = 单次时长 × 次数 + 间隔(规格书第 10 章)
* @author cyWu <1917507415@qq.com>
* @date 2026.09.09
* @version V1.0.0
* @history
* - V1.0.0, 2026.09.09, cyWu, P0 首次发布(默认 10s×3 次+3s,只跑一轮)
*
* @note 本模块是**唯一切泵者**(规格书 7.2):键/DP/联动/计划都只请求
* "开一轮 / 停"。20ms 节拍倒数,禁止阻塞 delay。间隔等待也算作业态
* (DP1=1、绿灯继续闪),但泵已关、不记运行秒(P2 由 app_runrec 负责)。
******************************************************************************/
#include "system/includes.h"
#include "app_spray.h"
#include "app_pump.h"
#include "jb_product.h"
#include "board_pin.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[APP_SPRAY]"
#define LOG_INFO_ENABLE
#include "debug.h"
/** 作业阶段 */
typedef enum {
SPRAY_PHASE_PUMP_ON = 0, /**< 泵开段:倒数单次时长 */
SPRAY_PHASE_WAIT /**< 间隔段:泵关,倒数间隔(仅 interval>0 出现) */
} SprayPhase_t;
/** 模块状态:全程由 20ms 业务节拍(usr_timer_tick)驱动,无跨上下文竞争 */
typedef struct {
uint8_t initialized;
uint8_t state; /**< 0=空闲,1=作业中 */
uint8_t phase; /**< 当前段(SPRAY_PHASE_* */
uint16_t total_count; /**< 本轮总次数(来自 DP7 */
uint16_t round; /**< 已完成的次数 */
uint32_t dur_ms; /**< 单次时长 ms */
uint32_t wait_ms; /**< 间隔 ms0=中间不停泵) */
uint32_t remain_ms; /**< 当前段剩余 ms */
} SprayCtx_t;
static SprayCtx_t s_spray;
/**
* @brief 从 gDevData.spray_config 解析本轮参数(非法回退默认)
* @note DP7 布局(规格书 10.2):Byte0-1 时长 uint16 大端、Byte2 次数、
* Byte3-4 间隔 uint16 大端。非法 = 时长 0 或次数 0。
* @return 无
*/
static void spray_parse_cfg(void)
{
const uint8_t *p = gDevData.spray_config;
uint16_t dur = (uint16_t)(((uint16_t)p[0] << 8) | p[1]);
uint8_t cnt = p[2];
uint16_t itv = (uint16_t)(((uint16_t)p[3] << 8) | p[4]);
if ((dur == 0) || (cnt == 0)) {
/* 从未收到合法 DP7P0 无 VM,理论上 userInit 已写默认,此处兜底) */
dur = BOARD_SPRAY_DEFAULT_DUR_S;
cnt = BOARD_SPRAY_DEFAULT_COUNT;
itv = BOARD_SPRAY_DEFAULT_INTERVAL_S;
log_info("dp7 invalid, use default %us x%u + %us\n",
(unsigned)dur, (unsigned)cnt, (unsigned)itv);
}
s_spray.dur_ms = (uint32_t)dur * 1000u;
s_spray.wait_ms = (uint32_t)itv * 1000u;
s_spray.total_count = cnt;
}
/**
* @brief 初始化作业引擎(泵保持关,参数待 start 时解析)
* @return APP_SPRAY_OK=成功
*/
AppSpray_Ret_t app_spray_init(void)
{
if (s_spray.initialized) {
return APP_SPRAY_OK;
}
#if BOARD_SPRAY_ENABLE
app_pump_set(0); /* 上电绝不喷 */
s_spray.state = 0;
s_spray.phase = SPRAY_PHASE_PUMP_ON;
s_spray.round = 0;
s_spray.remain_ms = 0;
s_spray.initialized = 1;
#else
log_info("spray disabled\n");
#endif
return APP_SPRAY_OK;
}
/**
* @brief 开一轮(幂等:作业中不重开)
* @return 无
*/
void app_spray_start(void)
{
if (!s_spray.initialized) {
return;
}
if (s_spray.state != 0) {
return; /* 已在作业中:保持当前轮(规格书 10.5 幂等) */
}
spray_parse_cfg();
s_spray.round = 0;
s_spray.phase = SPRAY_PHASE_PUMP_ON;
s_spray.remain_ms = s_spray.dur_ms;
s_spray.state = 1;
app_pump_set(1); /* 两脚同时高 */
log_info("spray start dur=%u ms x%u wait=%u ms\n",
(unsigned)s_spray.dur_ms, (unsigned)s_spray.total_count,
(unsigned)s_spray.wait_ms);
}
/**
* @brief 停:立刻两脚拉低、结束作业
* @return 无
*/
void app_spray_stop(void)
{
if (!s_spray.initialized) {
return;
}
if (s_spray.state == 0) {
return; /* 已空闲,保持关(幂等) */
}
app_pump_set(0); /* 两脚同时低 */
s_spray.state = 0;
s_spray.round = 0;
s_spray.remain_ms = 0;
log_info("spray stop\n");
}
/**
* @brief 作业节拍:段倒数与切泵(20ms 调用)
* @param dt 距上次调用的毫秒数
* @return 无
*/
void app_spray_poll(uint16_t dt)
{
if (!s_spray.initialized || (s_spray.state == 0)) {
return;
}
if (dt == 0) {
return;
}
if (s_spray.phase == SPRAY_PHASE_PUMP_ON) {
/* 泵开段倒数 */
if (s_spray.remain_ms <= dt) {
s_spray.round++;
if (s_spray.round >= s_spray.total_count) {
/* 次数用尽:作业自然结束,泵关 */
app_pump_set(0);
s_spray.state = 0;
s_spray.remain_ms = 0;
log_info("spray round done, pump off\n");
return;
}
if (s_spray.wait_ms > 0) {
/* 进入间隔段:泵关等待 */
s_spray.phase = SPRAY_PHASE_WAIT;
s_spray.remain_ms = s_spray.wait_ms;
app_pump_set(0);
} else {
/* 间隔=0:不停泵,马上喷下一次 */
s_spray.remain_ms = s_spray.dur_ms;
}
} else {
s_spray.remain_ms -= dt;
}
} else {
/* 间隔段倒数 */
if (s_spray.remain_ms <= dt) {
s_spray.phase = SPRAY_PHASE_PUMP_ON;
s_spray.remain_ms = s_spray.dur_ms;
app_pump_set(1); /* 两脚同时高 */
} else {
s_spray.remain_ms -= dt;
}
}
}
/**
* @brief 查询是否处于作业态(含间隔等待,整轮期间为 1)
* @return 1=作业中,0=空闲
*/
uint8_t app_spray_is_running(void)
{
#if BOARD_SPRAY_ENABLE
return (uint8_t)(s_spray.state != 0);
#else
return 0;
#endif
}
/**
* @brief 查询模块是否已初始化
* @return APP_SPRAY_OK=已初始化,APP_SPRAY_ERR_NOT_INIT=未初始化
*/
AppSpray_Ret_t app_spray_get_status(void)
{
return s_spray.initialized ? APP_SPRAY_OK : APP_SPRAY_ERR_NOT_INIT;
}