Files
JBao_Temp_Humi_Fan_FW/apps/usr_app/app_disp.c
T

271 lines
9.1 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_disp.c
* @brief CBR3 数码管显示实现
* @author cyWu <1917507415@qq.com>
* @date 2026.09.02
* @version V2.0.0(规格书 V3.0.0):闪烁按第 8.2 节改——有**有效联动配置**
* 就按是否落在 [min,max] 闭区间闪(温度/湿度管独立 500ms 亮灭),
* 出范围就闪、与风扇是否在转、联动是否被手动作废无关;无有效
* 联动不因温湿度闪;fault 时 "--" 优先。闪烁源由 app_linkage
* 500ms 任务上下文)喂 app_disp_set_temp/humi_alarm。
*
* @note ⚠ CT1642 内部只有单个 12 位锁存器(4 位选 + 8 段码),无按位显示 RAM、
* 不自动扫描,一次只点亮当前锁存的那 1 位。要让 4 位都亮且不闪,必须由 MCU
* 持续轮询:本模块用 app_disp_run()20ms)算好 4 位段码放进 s_disp.buf
* 用 app_disp_scan()BOARD_DISP_SCAN_MS)每次点亮 1 位、4 位轮询。
* 告警闪烁(规格书 8.2):比较成立侧 500ms 亮/500ms 灭,与吹风解耦;
* 传感器故障(连续 3 次失败)优先显示 "--" 不闪。
******************************************************************************/
#include "app_disp.h"
#include "app_th.h"
#include "bsp_ct1642.h"
#include "board_pin.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[APP_DISP]"
#define LOG_INFO_ENABLE
#include "debug.h"
/** 显示运行时:段码缓冲 + 扫描指针 + 8.2 告警闪烁 */
typedef struct {
uint8_t inited;
uint8_t buf[4]; /* 段码,下标 = com 顺序:0 温度十 / 1 温度个 / 2 湿度十 / 3 湿度个 */
uint8_t scan_idx; /* 动态扫描轮询指针 0..3 */
uint8_t temp_alarm; /* 1=温度超范围(app_linkage 喂) */
uint8_t humi_alarm; /* 1=湿度超范围(app_linkage 喂) */
uint8_t blink_phase; /* 0=亮半周期,1=灭半周期 */
uint16_t blink_ms; /* 半周期内累计毫秒 */
} AppDispCtx_t;
static AppDispCtx_t s_disp;
static const uint8_t s_com_tbl[4] = {
BOARD_DISP_COM_TEMP_TEN, BOARD_DISP_COM_TEMP_ONE,
BOARD_DISP_COM_HUMI_TEN, BOARD_DISP_COM_HUMI_ONE
};
/* 段码索引:bsp_ct1642_seg(0~9)=数字,10='-'11=灭 */
#define SEG_MINUS 10
#define SEG_OFF 11
/**
* @brief 温度值 → 两位段码(0~99<0 显示 -9~-1<-9 钳位 -9>99 钳位 99
* @param temp_x10 温度 ×10
* @param out_t1 十位段码索引
* @param out_t0 个位段码索引
* @return 无
*/
static void disp_split_temp(int16_t temp_x10, uint8_t *out_t1, uint8_t *out_t0)
{
int16_t t = temp_x10 / 10; /* 取整数 ℃(P0 不显示小数) */
uint8_t tens;
uint8_t ones;
if (t < 0) {
if (t < -9) {
t = -9; /* 钳位 */
}
*out_t1 = SEG_MINUS;
*out_t0 = (uint8_t)(-t % 10);
return;
}
if (t > 99) {
t = 99;
}
tens = (uint8_t)(t / 10);
ones = (uint8_t)(t % 10);
*out_t1 = tens;
*out_t0 = ones;
}
/**
* @brief 湿度值 → 两位段码(0~99>99 钳位 99
* @param humi 湿度 %
* @param out_h1 十位段码索引
* @param out_h0 个位段码索引
* @return 无
*/
static void disp_split_humi(uint8_t humi, uint8_t *out_h1, uint8_t *out_h0)
{
uint8_t h = humi;
if (h > 99) {
h = 99;
}
*out_h1 = (uint8_t)(h / 10);
*out_h0 = (uint8_t)(h % 10);
}
/**
* @brief 初始化显示模块;注册快节拍动态扫描定时器
* @return 无
*/
void app_disp_init(void)
{
#if !BOARD_DISP_ENABLE
log_info("disp disabled\n");
return;
#endif
if (s_disp.inited) {
return;
}
s_disp.inited = 1;
s_disp.scan_idx = 0;
bsp_ct1642_init(); /* PG0(CLK)/PG1(DATA) 推挽输出 */
/* 默认写死 25 / 60,供 P0 台上扫段验证(规格书 P0 验收) */
s_disp.buf[0] = bsp_ct1642_seg(2);
s_disp.buf[1] = bsp_ct1642_seg(5);
s_disp.buf[2] = bsp_ct1642_seg(6);
s_disp.buf[3] = bsp_ct1642_seg(0);
log_info("disp init, scan %ums\n", (unsigned)BOARD_DISP_SCAN_MS);
/* 注意:快节拍动态扫描定时器在 usr_jb_init 里注册(usr_timer_add 声明在那) */
}
/**
* @brief 业务节拍(20ms):计算 4 位要显示什么,写入段码缓冲(不直接打管)
* @note 软关机由 app_disp_scan 处理。本函数只更新 s_disp.buf。
* @return 无
*/
void app_disp_run(uint16_t dt)
{
#if !BOARD_DISP_ENABLE
(void)dt;
return;
#endif
if (!s_disp.inited) {
return;
}
/* 闪烁相位推进:满 BOARD_DISP_BLINK_MS 切换亮/灭半周期 */
s_disp.blink_ms = (uint16_t)(s_disp.blink_ms + dt);
if (s_disp.blink_ms >= BOARD_DISP_BLINK_MS) {
s_disp.blink_ms = 0;
s_disp.blink_phase ^= 1;
}
#if BOARD_DISP_ALARM_TEST == 1
s_disp.temp_alarm = 1; /* P1 调试:强制温度管闪 */
#elif BOARD_DISP_ALARM_TEST == 2
s_disp.humi_alarm = 1; /* P1 调试:强制湿度管闪 */
#endif
#if BOARD_DISP_SCAN_TEST == 1
/* 段扫描:4 位同时点亮同一段,每 600ms 切 A→B→...→G→灭→全亮,定性段码位序 */
{
static uint32_t tick;
static uint8_t step;
static const uint8_t seg_seq[] = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x7F
};
if (++tick >= 30) { /* 30*20ms=600ms */
tick = 0;
step = (uint8_t)((step + 1) % (sizeof(seg_seq) / sizeof(seg_seq[0])));
}
uint8_t s = seg_seq[step];
s_disp.buf[0] = s; s_disp.buf[1] = s; s_disp.buf[2] = s; s_disp.buf[3] = s;
return;
}
#elif BOARD_DISP_SCAN_TEST == 2
/* 位扫描:4 位分别显示 1/2/3/4,定性 C1~C4 与物理管位对应 */
s_disp.buf[0] = bsp_ct1642_seg(1);
s_disp.buf[1] = bsp_ct1642_seg(2);
s_disp.buf[2] = bsp_ct1642_seg(3);
s_disp.buf[3] = bsp_ct1642_seg(4);
return;
#endif
if (app_th_is_fault()) {
/* 连续失败 3 次:双管 "--"(规格书 8.1;故障优先于闪烁,8.2) */
uint8_t m = bsp_ct1642_seg(SEG_MINUS);
s_disp.buf[0] = m; s_disp.buf[1] = m; s_disp.buf[2] = m; s_disp.buf[3] = m;
} else if (app_th_get_valid()) {
/* 有有效采样:显示真实值;比较成立且当前在灭半周期 → 该管熄灭 */
uint8_t t1, t0, h1, h0;
disp_split_temp(app_th_get_temp_x10(), &t1, &t0);
disp_split_humi(app_th_get_humi(), &h1, &h0);
if (s_disp.temp_alarm && s_disp.blink_phase) {
t1 = SEG_OFF;
t0 = SEG_OFF;
}
if (s_disp.humi_alarm && s_disp.blink_phase) {
h1 = SEG_OFF;
h0 = SEG_OFF;
}
s_disp.buf[0] = bsp_ct1642_seg(t1);
s_disp.buf[1] = bsp_ct1642_seg(t0);
s_disp.buf[2] = bsp_ct1642_seg(h1);
s_disp.buf[3] = bsp_ct1642_seg(h0);
} else {
/* 尚未有有效采样:写死 25 / 60,供 P0 台上扫段验证(规格书 P0 验收) */
s_disp.buf[0] = bsp_ct1642_seg(2);
s_disp.buf[1] = bsp_ct1642_seg(5);
s_disp.buf[2] = bsp_ct1642_seg(6);
s_disp.buf[3] = bsp_ct1642_seg(0);
}
}
/**
* @brief 显示动态扫描(BOARD_DISP_SCAN_MS 快节拍定时器回调):点亮 1 位、4 位轮询
* @note 必须短、不可阻塞。每次只发 1 帧,靠高频轮询让 4 位视觉同亮不闪。
* IO 分离后 CT1642 独占 PG0/PG1,与 AHT20 的 PC4/PC5 无总线竞争,
* 扫描回调无需锁、无跳过。
* @return 无
*/
void app_disp_scan(void)
{
#if !BOARD_DISP_ENABLE
return;
#endif
if (!s_disp.inited) {
return;
}
#if BOARD_DISP_SCAN_TEST == 1
/* 段扫描:4 位同显一段。⭐ 值变化才刷(无鬼影),com=0xF 四位全选同显。 */
static uint8_t seg = 0;
if (seg == s_disp.buf[0]) {
return;
}
seg = s_disp.buf[0];
bsp_ct1642_send_frame(s_disp.buf[s_disp.scan_idx], 0xF);
s_disp.scan_idx = (uint8_t)((s_disp.scan_idx + 1) % 4);
#else
/* 正常显示(0) / 位选验证(2):4 位分显不同数字,必须分时轮询。
* 鬼影(切位杂段)靠 ct1642_delay 8→2 提速缩短杂段窗口来减轻。 */
bsp_ct1642_send_frame(s_disp.buf[s_disp.scan_idx], s_com_tbl[s_disp.scan_idx]);
s_disp.scan_idx = (uint8_t)((s_disp.scan_idx + 1) % 4);
#endif
}
/**
* @brief 设置温度超范围标志:超范围时温度管 500ms 闪烁(app_linkage 喂,
* 规格书 8.2T>Tmax 或 T<Tmin;含等于不算超)
* @param en 1=超范围(闪),0=带内(常亮数值)
* @return 无
*/
void app_disp_set_temp_alarm(uint8_t en)
{
s_disp.temp_alarm = en ? 1 : 0;
}
/**
* @brief 设置湿度超范围标志:超范围时湿度管 500ms 闪烁(app_linkage 喂,
* 规格书 8.2H>Hmax 或 H<Hmin
* @param en 1=超范围(闪),0=带内(常亮数值)
* @return 无
*/
void app_disp_set_humi_alarm(uint8_t en)
{
s_disp.humi_alarm = en ? 1 : 0;
}
/**
* @brief 温度或湿度管是否处于告警闪烁
* @return 1=任一侧告警
*/
uint8_t app_disp_is_alarm(void)
{
return (s_disp.temp_alarm || s_disp.humi_alarm) ? 1 : 0;
}