Files
JBao_Temp_Humi_Fan_FW/apps/usr_app/app_disp.c
T
wuchuyuan 5cc7e9dbd6 接入温湿度风扇产品固件(仲裁/条件/定时/显示),并补齐规格书与外设驱动
新增 usr_app 产品逻辑:手动>定时>条件仲裁、条件控温湿、定时任务、数码管显示、
按键/LED、风扇档位、NVM 掉电恢复、配对与开关机,上电按规格恢复上次状态。
新增 usr_periph:AHT20、CT1642、板级引脚与硬件封装;见宝协议 cond_config
改为 7 字节编解码,并接入产品 DP。
补充《温湿度风扇_固件需求规格书》,工程加入对应源文件。
.gitignore 忽略 WorkBuddy/Cursor 本地会话
2026-08-31 15:17:27 +08:00

279 lines
9.3 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.08.29
* @version V1.1.0
* @history
* - V1.0.0, 2026.08.29, cyWu, 首次发布
* - V1.0.1, 2026.08.29, cyWu, 改动态扫描:20ms 节拍只算数据写缓冲,
* 另起 BOARD_DISP_SCAN_MS 快节拍定时器轮询点亮 1 位,解决闪烁
* - V1.1.0, 2026.08.31, cyWu, P1 加告警闪烁:温度/湿度管独立 500ms 亮灭,
* BOARD_DISP_ALARM_TEST 宏可强制模拟超限;fault 时 "--" 优先
*
* @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 灭,与吹风解耦;
* 闪烁触发源由 app_disp_set_temp/humi_alarm 喂入(P2 条件引擎),
* 传感器故障(连续 3 次失败)优先显示 "--" 不闪。
******************************************************************************/
#include "app_disp.h"
#include "app_th.h"
#include "app_power.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"
static uint8_t s_inited;
/* 4 位段码缓冲,下标顺序 = com 顺序:0 温度十 / 1 温度个 / 2 湿度十 / 3 湿度个 */
static uint8_t s_disp_buf[4];
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
};
static uint8_t s_scan_idx; /* 动态扫描轮询指针 0..3 */
/* 告警闪烁(P1,规格书 8.2):比较成立侧 500ms 亮/500ms 灭 */
static uint8_t s_temp_alarm; /* 1=温度比较成立(P2 条件引擎喂) */
static uint8_t s_humi_alarm; /* 1=湿度比较成立(P2 条件引擎喂) */
static uint8_t s_blink_phase; /* 0=亮半周期,1=灭半周期 */
static uint16_t s_blink_ms; /* 半周期内累计毫秒 */
/* 段码索引: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_inited) {
return;
}
s_inited = 1;
s_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_inited) {
return;
}
/* 闪烁相位推进:满 BOARD_DISP_BLINK_MS 切换亮/灭半周期 */
s_blink_ms = (uint16_t)(s_blink_ms + dt);
if (s_blink_ms >= BOARD_DISP_BLINK_MS) {
s_blink_ms = 0;
s_blink_phase ^= 1;
}
#if BOARD_DISP_ALARM_TEST == 1
s_temp_alarm = 1; /* P1 调试:强制温度管闪 */
#elif BOARD_DISP_ALARM_TEST == 2
s_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_temp_alarm && s_blink_phase) {
t1 = SEG_OFF;
t0 = SEG_OFF;
}
if (s_humi_alarm && s_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 无总线竞争,
* 扫描回调无需锁、无跳过。
* 软关机流程:4 位全灭(com=0x00 → 所有 Cx 高,位选全关)。
* @return 无
*/
void app_disp_scan(void)
{
#if !BOARD_DISP_ENABLE
return;
#endif
if (!s_inited) {
return;
}
/* 软关机流程:数码管锁存灭(规格书 5.1:风扇 0、数码管锁存灭、LED 灭) */
if (app_power_poweroff_pending()) {
bsp_ct1642_send_frame(0x00, 0x00); /* com=0x00 → 4 位全灭 */
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_scan_idx], 0xF);
s_scan_idx = (uint8_t)((s_scan_idx + 1) % 4);
#else
/* 正常显示(0) / 位选验证(2):4 位分显不同数字,必须分时轮询。
* 鬼影(切位杂段)靠 ct1642_delay 8→2 提速缩短杂段窗口来减轻。 */
bsp_ct1642_send_frame(s_disp_buf[s_scan_idx], s_com_tbl[s_scan_idx]);
s_scan_idx = (uint8_t)((s_scan_idx + 1) % 4);
#endif
}
/**
* @brief 设置温度比较成立标志:成立时温度管 500ms 闪烁(P2 条件引擎喂)
* @param en 1=成立(闪),0=不成立(常亮数值)
* @return 无
*/
void app_disp_set_temp_alarm(uint8_t en)
{
s_temp_alarm = en ? 1 : 0;
}
/**
* @brief 设置湿度比较成立标志:成立时湿度管 500ms 闪烁(P2 条件引擎喂)
* @param en 1=成立(闪),0=不成立(常亮数值)
* @return 无
*/
void app_disp_set_humi_alarm(uint8_t en)
{
s_humi_alarm = en ? 1 : 0;
}
/**
* @brief 温度或湿度管是否处于告警闪烁
* @return 1=任一侧告警
*/
uint8_t app_disp_is_alarm(void)
{
return (s_temp_alarm || s_humi_alarm) ? 1 : 0;
}