1、喷淋器 P1/P2功能实现
2、红灯按连接态慢闪
This commit is contained in:
@@ -0,0 +1,314 @@
|
||||
/******************************************************************************
|
||||
* @file app_runrec.c
|
||||
* @brief CBR2 31 天运行记录(规格书 14.1 节,DP18 RO,124B)
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.09.09
|
||||
* @version V1.0.0,首次发布
|
||||
*
|
||||
* @note 什么叫在运行:app_pump_get()==1(PC2 与 PA6 **都已导通**),
|
||||
* 间隔等待/作业态开但泵关不计(14.1)。日期轴:未对时只往 slot[0]
|
||||
* 加、不旋转;对时后按本地日历左移;掉电再上电先由 app_nvm_load 灌 VM。
|
||||
*
|
||||
* ⚠ 上下文分工(勿再踩复位事故):
|
||||
* - app_runrec_tick(20ms 中断):只把泵导通的毫秒折成 pending 秒,
|
||||
* 不碰 s_rr.rec[]、不写 flash。
|
||||
* - app_runrec_rotate_if_needed(500ms 任务):收下 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] 对应本地日期 yyyymmdd;0=未对时 */
|
||||
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 yyyymmdd;0=未对时
|
||||
*/
|
||||
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 yyyymmdd;0=未对时
|
||||
* @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
|
||||
}
|
||||
Reference in New Issue
Block a user