1、喷淋器 P1/P2功能实现

2、红灯按连接态慢闪
This commit is contained in:
2026-09-09 19:57:00 +08:00
parent 074960eb02
commit 0dd65e1552
31 changed files with 2805 additions and 258 deletions
+314
View File
@@ -0,0 +1,314 @@
/******************************************************************************
* @file app_runrec.c
* @brief CBR2 31 天运行记录(规格书 14.1 节,DP18 RO124B
* @author cyWu <1917507415@qq.com>
* @date 2026.09.09
* @version V1.0.0,首次发布
*
* @note 什么叫在运行:app_pump_get()==1PC2 与 PA6 **都已导通**),
* 间隔等待/作业态开但泵关不计(14.1)。日期轴:未对时只往 slot[0]
* 加、不旋转;对时后按本地日历左移;掉电再上电先由 app_nvm_load 灌 VM。
*
* ⚠ 上下文分工(勿再踩复位事故):
* - app_runrec_tick20ms 中断):只把泵导通的毫秒折成 pending 秒,
* 不碰 s_rr.rec[]、不写 flash。
* - app_runrec_rotate_if_needed500ms 任务):收下 pending 加到
* slot[0]、每拍灌 DP18、跨天旋转、满 600s / 跨天 / 首次对时才
* 写 VM。拔电没有软关机钩子,接受最多丢不满 10 分钟的秒数。
******************************************************************************/
#include <string.h>
#include "system/includes.h" /* local_irq_disable / local_irq_enable */
#include "app_runrec.h"
#include "app_pump.h"
#include "app_nvm.h"
#include "usr_rtc.h"
#include "jb_product.h"
#include "board_pin.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[APP_RREC]"
#define LOG_INFO_ENABLE
#include "debug.h"
#if BOARD_RUNREC_ENABLE
/** 运行记录上下文:31 格日历 + ISR/任务两侧的折秒状态 */
typedef struct {
uint32_t rec[BOARD_RUN_RECORD_DAYS]; /**< slot[0]=今天 … slot[30]=前 30 天(任务侧) */
uint32_t ymd; /**< slot[0] 对应本地日期 yyyymmdd0=未对时 */
uint16_t ms; /**< 不足 1 秒的毫秒零头(仅 ISR) */
volatile uint16_t pending_sec; /**< ISR 折秒,任务收下后加到 slot[0] */
uint16_t unsaved; /**< 距上次落盘累计的运行秒数(仅任务) */
} AppRunrecCtx_t;
static AppRunrecCtx_t s_rr;
#endif
/**
* @brief 公历日期 → 天数序号(Howard Hinnant 的 days_from_civil
* @param y 年(如 2026
* @param m 月 1~12
* @param d 日 1~31
* @return 距 1970-01-01 的天数(可为负)
*/
static int32_t runrec_days_from_civil(int y, int m, int d)
{
int era;
int yoe;
int doy;
int doe;
y -= (m <= 2) ? 1 : 0;
era = ((y >= 0) ? y : (y - 399)) / 400;
yoe = y - era * 400; /* [0,399] */
doy = (153 * (m + ((m > 2) ? -3 : 9)) + 2) / 5 + d - 1; /* [0,365] */
doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; /* [0,146096]*/
return (int32_t)(era * 146097L + (long)doe - 719468L);
}
/**
* @brief yyyymmdd 压缩日期 → 天数序号
* @param ymd yyyymmdd(如 20260904
* @return 天数序号
*/
static int32_t runrec_ymd_to_days(uint32_t ymd)
{
return runrec_days_from_civil((int)(ymd / 10000u),
(int)((ymd / 100u) % 100u),
(int)(ymd % 100u));
}
/**
* @brief 初始化运行记录
* @return 无
*/
void app_runrec_init(void)
{
#if !BOARD_RUNREC_ENABLE
log_info("runrec disabled\n");
return;
#else
memset(&s_rr, 0, sizeof(s_rr));
app_runrec_pack(gDevData.run_record); /* DP18 报全 0 */
log_info("runrec init, %u days\n", (unsigned)BOARD_RUN_RECORD_DAYS);
#endif
}
#if BOARD_RUNREC_ENABLE
/**
* @brief 把 ISR 折好的秒数加到 slot[0],满 10 分钟则请求写 VM
* @return 1=需要立刻写运行记录 item
*/
static uint8_t runrec_apply_pending(void)
{
uint16_t sec;
uint8_t need_vm = 0;
local_irq_disable();
sec = s_rr.pending_sec;
s_rr.pending_sec = 0;
local_irq_enable();
if (sec == 0) {
return 0;
}
s_rr.rec[0] += sec;
s_rr.unsaved = (uint16_t)(s_rr.unsaved + sec);
if (s_rr.unsaved >= BOARD_RUN_RECORD_SAVE_SEC) {
s_rr.unsaved = (uint16_t)(s_rr.unsaved % BOARD_RUN_RECORD_SAVE_SEC);
need_vm = 1;
}
if (need_vm) {
log_info("runrec %us reached, save vm\n", (unsigned)BOARD_RUN_RECORD_SAVE_SEC);
}
return need_vm;
}
#endif /* BOARD_RUNREC_ENABLE */
/**
* @brief 20ms 中断上下文节拍:泵实际导通才折秒到 pending,不碰 s_rr.rec
* @param dt 距上次调用的毫秒数
* @return 无
*/
void app_runrec_tick(uint16_t dt)
{
#if !BOARD_RUNREC_ENABLE
(void)dt;
return;
#else
if (!app_pump_get()) {
/* 没在喷:不累加,并丢弃不足 1 秒的零头(14.1:泵未导通不计入) */
s_rr.ms = 0;
return;
}
s_rr.ms = (uint16_t)(s_rr.ms + dt);
while (s_rr.ms >= 1000u) {
s_rr.ms = (uint16_t)(s_rr.ms - 1000u);
s_rr.pending_sec++; /* 任务侧再加到 slot[0],避免与跨天旋转打架 */
}
#endif
}
/**
* @brief 500ms 任务上下文:收下 pending、灌 DP18、跨天旋转、必要时写 VM
* @return 无
*/
void app_runrec_rotate_if_needed(void)
{
#if !BOARD_RUNREC_ENABLE
return;
#else
struct sys_time t;
uint32_t ymd;
int32_t diff;
int i;
uint8_t need_vm = runrec_apply_pending();
if (usr_rtc_get_time(&t)) {
ymd = (uint32_t)t.year * 10000u + (uint32_t)t.month * 100u + (uint32_t)t.day;
if (s_rr.ymd == 0) {
/* 首次对时:slot[0] 已是当天累计值,只登记日期,不旋转不清零 */
s_rr.ymd = ymd;
need_vm = 1;
log_info("runrec rtc synced, ymd=%lu\n", (unsigned long)ymd);
} else if (ymd != s_rr.ymd) {
diff = runrec_ymd_to_days(ymd) - runrec_ymd_to_days(s_rr.ymd);
if (diff <= 0) {
s_rr.ymd = ymd; /* 时钟回拨/异常:只纠正日期,不乱移数据 */
} else {
if (diff > BOARD_RUN_RECORD_DAYS) {
diff = BOARD_RUN_RECORD_DAYS; /* 跨太多天:整表作废 */
}
/* 左移 N 天:old[i] → new[i+N],前面 N 格清零 */
for (i = (int)BOARD_RUN_RECORD_DAYS - 1; i >= diff; i--) {
s_rr.rec[i] = s_rr.rec[i - diff];
}
for (i = 0; i < diff; i++) {
s_rr.rec[i] = 0;
}
s_rr.ymd = ymd;
need_vm = 1;
log_info("runrec rotate %ld day(s), ymd=%lu\n",
(long)diff, (unsigned long)ymd);
}
}
}
/* 每拍灌 DP18:读请求 / 10min 全量能拿到当前秒数;差分上报不带本 DP */
app_runrec_pack(gDevData.run_record);
if (need_vm) {
app_nvm_save_runrec(); /* 满 10 分钟或跨天 / 首次对时才写 flash */
}
#endif
}
/**
* @brief 取某天的运行秒数
* @param day 0=今天 … 30=前 30 天
* @return 秒数
*/
uint32_t app_runrec_get(uint8_t day)
{
#if !BOARD_RUNREC_ENABLE
(void)day;
return 0;
#else
if (day >= BOARD_RUN_RECORD_DAYS) {
return 0;
}
return s_rr.rec[day];
#endif
}
/**
* @brief 打包成 DP18 的 124B 载荷(31 × uint32 大端)
* @param out 输出缓冲
* @note 模块关闭时仍输出全 0(13.1:关闭时报全 0),
* 避免 app_nvm 把未初始化栈内存写进 VM 的 RUNREC item
* @return 无
*/
void app_runrec_pack(uint8_t *out)
{
uint8_t i;
#if !BOARD_RUNREC_ENABLE
memset(out, 0, APP_RUNREC_PACK_LEN);
return;
#else
for (i = 0; i < BOARD_RUN_RECORD_DAYS; i++) {
uint32_t v = s_rr.rec[i];
out[(uint8_t)(i * 4u) + 0u] = (uint8_t)(v >> 24);
out[(uint8_t)(i * 4u) + 1u] = (uint8_t)(v >> 16);
out[(uint8_t)(i * 4u) + 2u] = (uint8_t)(v >> 8);
out[(uint8_t)(i * 4u) + 3u] = (uint8_t)(v & 0xFFu);
}
#endif
}
/**
* @brief 从 VM 的 124B 载荷灌回 RAM
* @param in 输入缓冲
* @return 无
*/
void app_runrec_unpack(const uint8_t *in)
{
uint8_t i;
#if !BOARD_RUNREC_ENABLE
(void)in;
return;
#else
for (i = 0; i < BOARD_RUN_RECORD_DAYS; i++) {
s_rr.rec[i] = ((uint32_t)in[(uint8_t)(i * 4u) + 0u] << 24)
| ((uint32_t)in[(uint8_t)(i * 4u) + 1u] << 16)
| ((uint32_t)in[(uint8_t)(i * 4u) + 2u] << 8)
| (uint32_t)in[(uint8_t)(i * 4u) + 3u];
}
app_runrec_pack(gDevData.run_record); /* 开机立刻灌 DP18,不等 500ms */
#endif
}
/**
* @brief slot[0] 对应的本地日期
* @return yyyymmdd0=未对时
*/
uint32_t app_runrec_get_slot0_date(void)
{
#if !BOARD_RUNREC_ENABLE
return 0;
#else
return s_rr.ymd;
#endif
}
/**
* @brief 设置 slot[0] 对应日期
* @param ymd yyyymmdd0=未对时
* @return 无
*/
void app_runrec_set_slot0_date(uint32_t ymd)
{
#if BOARD_RUNREC_ENABLE
s_rr.ymd = ymd;
#else
(void)ymd;
#endif
}
/**
* @brief 清零 31 格与日期(出厂重置)
* @return 无
*/
void app_runrec_reset(void)
{
#if !BOARD_RUNREC_ENABLE
return;
#else
memset(&s_rr, 0, sizeof(s_rr));
app_runrec_pack(gDevData.run_record);
#endif
}