245 lines
6.8 KiB
C
245 lines
6.8 KiB
C
/******************************************************************************
|
||
* @file app_th.c
|
||
* @brief CBR3 温湿度采集实现
|
||
* @author cyWu <1917507415@qq.com>
|
||
* @date 2026.09.03
|
||
* @version V1.3.0,对外温湿度与显示/上报绑死(T>=0.5℃ / H>=2%)
|
||
******************************************************************************/
|
||
|
||
#include "app_th.h"
|
||
#include "bsp_aht20.h"
|
||
#include "board_pin.h"
|
||
#include "system/includes.h"
|
||
|
||
#define LOG_TAG_CONST APP
|
||
#define LOG_TAG "[APP_TH]"
|
||
#define LOG_ERROR_ENABLE
|
||
#define LOG_INFO_ENABLE
|
||
#include "debug.h"
|
||
|
||
extern u32 timer_get_ms(void);
|
||
extern void clr_wdt(void);
|
||
|
||
/** 采样状态机 */
|
||
typedef enum {
|
||
TH_ST_IDLE = 0, /**< 空闲,等待下一个采样周期 */
|
||
TH_ST_WAIT_DATA /**< 命令已发出,等 ≥80ms 后读数 */
|
||
} ThState_t;
|
||
|
||
typedef struct {
|
||
uint8_t inited;
|
||
ThState_t state;
|
||
uint16_t poll_ms; /**< 距上次采样的累计毫秒 */
|
||
uint32_t wait_t0; /**< 测量命令发出时刻 */
|
||
int16_t temp_pub; /**< 对外温度 ×10(显示/上报/联动,|ΔT|>=0.5℃ 才改) */
|
||
uint8_t humi_pub; /**< 对外湿度 %(显示/上报/联动,|ΔH|>=2 才改) */
|
||
uint8_t valid; /**< 1=有有效采样 */
|
||
uint8_t fail_cnt; /**< 连续失败次数 */
|
||
uint8_t last_ok; /**< 最近一次采样是否成功(app_linkage 8.4 用) */
|
||
} AppThCtx_t;
|
||
|
||
static AppThCtx_t s_th;
|
||
|
||
/**
|
||
* 「本周期采样成功」事件(生产者=20ms 中断,消费者=500ms 任务上下文)
|
||
* ⚠ 中断里只写这三个 volatile,绝不做开关广播 / 打日志 / 写 flash
|
||
*/
|
||
static volatile uint8_t s_th_pending;
|
||
static volatile int16_t s_th_pending_t;
|
||
static volatile uint8_t s_th_pending_h;
|
||
|
||
/**
|
||
* @brief 记一次采样失败(发令 nack / 读数失败共用)
|
||
* @return 无
|
||
*/
|
||
static void th_on_fail(void)
|
||
{
|
||
if (s_th.fail_cnt < 0xFF) {
|
||
s_th.fail_cnt++;
|
||
}
|
||
s_th.last_ok = 0;
|
||
log_error("th sample fail %d\n", s_th.fail_cnt);
|
||
}
|
||
|
||
/**
|
||
* @brief 初始化温湿度模块
|
||
* @return 无
|
||
*/
|
||
void app_th_init(void)
|
||
{
|
||
#if !BOARD_TH_ENABLE
|
||
log_info("th disabled\n");
|
||
return;
|
||
#endif
|
||
if (s_th.inited) {
|
||
return;
|
||
}
|
||
bsp_aht20_init();
|
||
s_th.state = TH_ST_IDLE;
|
||
s_th.poll_ms = 0;
|
||
s_th.valid = 0;
|
||
s_th.fail_cnt = 0;
|
||
s_th.last_ok = 0;
|
||
s_th_pending = 0;
|
||
s_th_pending_t = 0;
|
||
s_th_pending_h = 0;
|
||
s_th.inited = 1;
|
||
log_info("th init period=%ums\n", (unsigned)BOARD_TH_PERIOD_MS);
|
||
}
|
||
|
||
/**
|
||
* @brief 业务节拍:内部 2s 才真正采一次;IO 分离后采集与显示互不干扰
|
||
* @param dt 距上次调用的毫秒数
|
||
* @return 无
|
||
*/
|
||
void app_th_poll(uint16_t dt)
|
||
{
|
||
#if !BOARD_TH_ENABLE
|
||
(void)dt;
|
||
return;
|
||
#endif
|
||
if (!s_th.inited) {
|
||
return;
|
||
}
|
||
|
||
if (s_th.state == TH_ST_IDLE) {
|
||
s_th.poll_ms = (uint16_t)(s_th.poll_ms + dt);
|
||
if (s_th.poll_ms >= BOARD_TH_PERIOD_MS) {
|
||
s_th.poll_ms = 0;
|
||
if (bsp_aht20_measure_start() == 0) {
|
||
s_th.state = TH_ST_WAIT_DATA;
|
||
s_th.wait_t0 = timer_get_ms();
|
||
} else {
|
||
/* 发令阶段 addr nack(未插/脱落)也算一次采样失败,不能直接跳过 */
|
||
th_on_fail();
|
||
}
|
||
}
|
||
} else if (s_th.state == TH_ST_WAIT_DATA) {
|
||
clr_wdt();
|
||
if ((timer_get_ms() - s_th.wait_t0) >= BOARD_AHT20_WAIT_MS) {
|
||
int16_t t = 0;
|
||
uint8_t h = 0;
|
||
int8_t ret = bsp_aht20_measure_finish(&t, &h);
|
||
|
||
/* IO 分离后无总线竞争,finish 不会返回 -2;0=成功,-1=通讯失败 */
|
||
s_th.state = TH_ST_IDLE;
|
||
if (ret == 0) {
|
||
int16_t dt_x10;
|
||
uint8_t dh;
|
||
|
||
/* 首次成功直接跟上;之后相对对外值达到阈值才改显示/上报/联动 */
|
||
if (!s_th.valid) {
|
||
s_th.temp_pub = t;
|
||
s_th.humi_pub = h;
|
||
} else {
|
||
dt_x10 = (int16_t)(t - s_th.temp_pub);
|
||
if (dt_x10 < 0) {
|
||
dt_x10 = (int16_t)(-dt_x10);
|
||
}
|
||
if (dt_x10 >= BOARD_TH_TEMP_PUB_DELTA_X10) {
|
||
s_th.temp_pub = t;
|
||
}
|
||
dh = (h > s_th.humi_pub) ? (uint8_t)(h - s_th.humi_pub)
|
||
: (uint8_t)(s_th.humi_pub - h);
|
||
if (dh >= BOARD_TH_HUMI_PUB_DELTA) {
|
||
s_th.humi_pub = h;
|
||
}
|
||
}
|
||
s_th.fail_cnt = 0;
|
||
s_th.valid = 1;
|
||
s_th.last_ok = 1;
|
||
/* 先填值再置 pending:消费者关中断取,生产者是中断,顺序无歧义 */
|
||
s_th_pending_t = s_th.temp_pub;
|
||
s_th_pending_h = s_th.humi_pub;
|
||
s_th_pending = 1;
|
||
log_info("th raw=%d.%dC/%d%% pub=%d.%dC/%d%%\n",
|
||
(int)(t / 10), (int)((t < 0) ? (-t % 10) : (t % 10)), (int)h,
|
||
(int)(s_th.temp_pub / 10),
|
||
(int)((s_th.temp_pub < 0) ? (-s_th.temp_pub % 10) : (s_th.temp_pub % 10)),
|
||
(int)s_th.humi_pub);
|
||
} else {
|
||
th_on_fail();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 是否已有有效采样
|
||
* @return 1=有效,0=从未成功
|
||
*/
|
||
uint8_t app_th_get_valid(void)
|
||
{
|
||
return s_th.valid;
|
||
}
|
||
|
||
/**
|
||
* @brief 对外温度(数码管 + DP3 + 联动)
|
||
* @return 温度 ×10
|
||
*/
|
||
int16_t app_th_get_temp_x10(void)
|
||
{
|
||
return s_th.temp_pub;
|
||
}
|
||
|
||
/**
|
||
* @brief 对外湿度(数码管 + DP4 + 联动)
|
||
* @return 湿度 0~100 整数 %
|
||
*/
|
||
uint8_t app_th_get_humi(void)
|
||
{
|
||
return s_th.humi_pub;
|
||
}
|
||
|
||
/**
|
||
* @brief 连续失败次数
|
||
* @return 连续失败次数
|
||
*/
|
||
uint8_t app_th_get_fail_cnt(void)
|
||
{
|
||
return s_th.fail_cnt;
|
||
}
|
||
|
||
/**
|
||
* @brief 传感器故障(连续失败 ≥ BOARD_TH_FAIL_MAX)
|
||
* @return 1=故障(显示 --、fault_code=1),0=正常
|
||
*/
|
||
uint8_t app_th_is_fault(void)
|
||
{
|
||
return (s_th.fail_cnt >= BOARD_TH_FAIL_MAX) ? 1 : 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 最近一次采样是否成功(规格书 8.4:联动只用本次成功值判)
|
||
* @return 1=成功,0=失败/未采样过
|
||
*/
|
||
uint8_t app_th_last_sample_ok(void)
|
||
{
|
||
return s_th.last_ok;
|
||
}
|
||
|
||
/**
|
||
* @brief 取走一次「本周期采样成功」事件(消费端,任务上下文)
|
||
* @param t_x10 出参:温度 ×10
|
||
* @param h 出参:湿度 %
|
||
* @return 1=取到新采样,0=无新采样
|
||
*/
|
||
uint8_t app_th_take_sample(int16_t *t_x10, uint8_t *h)
|
||
{
|
||
uint8_t got;
|
||
|
||
local_irq_disable();
|
||
got = s_th_pending;
|
||
if (got) {
|
||
s_th_pending = 0;
|
||
if (t_x10) {
|
||
*t_x10 = s_th_pending_t;
|
||
}
|
||
if (h) {
|
||
*h = s_th_pending_h;
|
||
}
|
||
}
|
||
local_irq_enable();
|
||
return got;
|
||
}
|