Files
JBao_Temp_Humi_Fan_FW/apps/usr_app/app_th.c
T

245 lines
6.8 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_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;
}