- disptest 合并两段: 快速位检查(0-9/多位数/999.9/36.5F-C) + 全量程扫测(0.1-999.0 C+F) - 删除 apptest 命令与 CmdAppTest - 说明书 §8 同步(删除 apptest, 更新 disptest), 补 §4.1 三态驱动防鬼影说明 - 仅 APP 改动, Boot 不动 (仍 V1.00.07) - docs/开发日志.md: 新增 §70
8.9 KiB
YF30137 数码管显示设计说明书
版本:V1.1(2026-09-06,融合当日数码管联调的位序修正与 GPIOA 时钟修复) 关联文档:
docs/YF30137_驱动真值表.md(完整段→脚真值表)、docs/开发日志.md§58~§68 固件:mcm-ddc-ble/src/app_display.c/h(驱动)、mcm-ddc-ble/src/cli_core.c(CLI 命令)
1. 概述
数码屏为 YF30137 七脚 Charlieplexing 温度显示模块:7 个 IO 脚两两复用电极,点亮 5 个数字位 + 2 个指示段。驱动由独立的 FreeRTOS 任务 display_task 逐段扫描完成。
温度显示格式:[百][十][个][F/C][小数] + °F + 小数点,量程 0.0 ~ 999.9(1 位小数)。
2. 硬件连接(脚号 → MCU 引脚)
数码管脚 17(网络 LED_1LED_7)与 MCU 引脚一一对应:
| 数码管脚 | 网络 | MCU 引脚 | 数码管脚 | 网络 | MCU 引脚 | |
|---|---|---|---|---|---|---|
| pin1 | LED_1 | PB0 | pin5 | LED_5 | PB11 | |
| pin2 | LED_2 | PB1 | pin6 | LED_6 | PB13 | |
| pin3 | LED_3 | PA0 | pin7 | LED_7 | PA6 | |
| pin4 | LED_4 | PA1 |
- 7 个脚分布在 GPIOA(PA0/PA1/PA6)与 GPIOB(PB0/PB1/PB11/PB13)两个端口。
- ⚠️ 两个端口时钟都必须使能(
RCC_APB2_PERIPH_GPIOA/RCC_APB2_PERIPH_GPIOB),否则 PA0/PA1/PA6 无输出(见 §9 踩坑 1)。
3. 显示位序定义(重要:当日修正)
| 位 | 含义 | 说明 |
|---|---|---|
| DIG1 | 百位 | 数字 0~9,前导零消隐 |
| DIG2 | 十位 | 数字 0~9,前导零消隐 |
| DIG3 | 个位 | 数字 0~9,恒显示 |
| DIG4 | 单位字母 | 华氏显 F、摄氏显 C(非数字位、非固定 F) |
| DIG5 | 小数位(十分位) | 数字 0~9,恒显示 |
| DIG6-A | 华氏标志 °F | 华氏点亮、摄氏熄灭 |
| DIG6-B | 小数点 | 恒点亮(小数位恒显示) |
旧代码曾把 DIG1~5 当作 5 个全功能数字位、DIG4 当普通数字位——已修正。 注意 DIG6-A/DIG6-B 是两个独立的单指示段,不是逐位小数点(硬件特性)。
4. Charlieplexing 驱动原理
段 S(A=0…G=6)在位 D(DIG1=1…DIG5=5)点亮,由一对脚 高→低 驱动,其余 5 脚高阻:
高脚索引 h = (S + D) % 7 → 脚号 = h + 1
低脚索引 l = (D - 1) + (S + D) / 7 → 脚号 = l + 1
DIG6 两个固定脚对:
| 段 | 功能 | 点亮方式 |
|---|---|---|
| A6 | 华氏 °F | 脚7 高、脚6 低(PA6 高 → PB13 低) |
| B6 | 小数点 | 脚1 高、脚7 低(PB0 高 → PA6 低) |
扫描要点:逐段点亮(每段 250µs)→ 点完立即把两脚切回高阻(输入)→ 点下一段;同一数字的多段不能同时点(脚共用,否则鬼影)。一个完整扫描帧约 20ms。
4.1 三态驱动与防鬼影(核心)
点亮某一段时,7 个脚里只有 2 个是输出(一高一低),其余 5 个脚必须全部「高阻」(输入浮空,GPIO_MODE_INPUT + GPIO_NO_PULL)——既不是置高、也不是置低。
为什么其它脚不能置高/置低(鬼影机理):以「脚6 高、脚1 低」点亮段 6→1 为例:
| 其它脚状态 | 后果 |
|---|---|
| 脚3 也置低 | 6 高 + 3 低 → 段 6→3 也被点亮(多余段) |
| 脚2 也置高 | 2 高 + 1 低 → 段 2→1 也被点亮(多余段) |
| 脚7 置高、脚5 置低 | 段 7→5 亮,甚至形成 6→5、7→1 等一串串扰 |
即:任一其它脚为高就会变成「电流源」,为低就会变成「电流汇」,都会和当前高低脚对构成寄生回路点亮别的段。唯一安全状态是浮空高阻(既不供流也不吸流)。
代码实现(app_display.c):
pin_input(idx) → GPIO_MODE_INPUT + GPIO_NO_PULL /* 高阻(浮空输入)*/
pin_high(idx) → GPIO_MODE_OUTPUT_PP + GPIO_SetBits /* 推挽输出高 */
pin_low(idx) → GPIO_MODE_OUTPUT_PP + GPIO_ResetBits/* 推挽输出低 */
static void seg_pair_on(uint8_t h, uint8_t l)
{
pin_high(h); /* 1. 只把高脚切输出高 */
pin_low(l); /* 2. 只把低脚切输出低 —— 其余 5 脚保持高阻不动 */
delay_n_10us(25); /* 3. 点亮 250µs */
pin_input(h); /* 4. 点完立刻把两脚放回高阻 */
pin_input(l);
}
支撑前提:display_task 每帧先 all_pins_input() 把 7 脚全部清成高阻,再逐段 seg_pair_on——因此任意时刻最多 2 个脚是输出,其余恒为高阻,杜绝串段。
为什么必须逐段点(时间复用):7 脚复用,同一数字的多段脚对互相牵连、不能同时点亮。数字 8(7 段)就是 7×250µs 依次点亮,20ms 一帧内扫完,靠人眼视觉暂留看起来是"整字同时亮"。
5. 字符段码表(bit0=A … bit6=G)
| 字符 | 段码 | 字符 | 段码 | 字符 | 段码 |
|---|---|---|---|---|---|
| 0 | 0x3F | 1 | 0x06 | 2 | 0x5B |
| 3 | 0x4F | 4 | 0x66 | 5 | 0x6D |
| 6 | 0x7D | 7 | 0x07 | 8 | 0x7F |
| 9 | 0x6F | A | 0x77 | b | 0x7C |
| C | 0x39 | d | 0x5E | E | 0x79 |
| F | 0x71 | — | 负号 G 段 | 0x40 |
F(单位字母)= 0x71(AEFG)、C(单位字母)= 0x39(ADEF),用于 DIG4。- 完整每个数字位的段→脚真值表见
docs/YF30137_驱动真值表.md(段→脚公式不变,只是位语义按 §3 修正)。
6. 温度解码(display_decode)
输入温度 value(float),输出 5 个位字符码:
- 钳位:
value < 0 → 0;value > 999.9 → 999.9; scaled = (int)(value × 10 + 0.5)(×10 转 0.1℃ 分辨率);- 落位:
DIG5(小数)=scaled%10→DIG3(个)=(scaled/10)%10→DIG2(十)=(scaled/100)%10→DIG1(百)=(scaled/1000)%10; - 前导零消隐:百位为 0 消隐,百位已消隐且十位为 0 再消隐十位;个位与小数位恒显示;
- DIG4 = 华氏
0x0F(F) / 摄氏0x0C(C); - 小数点(DIG6-B)恒亮;华氏标志(DIG6-A)随华氏/摄氏开关。
驱动不做 F/C 数值换算:华氏/摄氏只切换 DIG4 字母与 DIG6-A 指示,数值原样显示(单位换算由上层完成)。
7. 软件架构与接口(app_display.c/h)
| 接口 | 说明 |
|---|---|
void display_set(float value, uint8_t type, uint8_t base) |
设温度值 0.0~999.9;type 华氏/摄氏;base 已废弃(兼容保留) |
void display_set_raw(const uint8_t digits[5], uint8_t type) |
直接写 5 个位字符码(绕过温度解码,用于 Err/Lo/Hi 等状态字) |
void display_auto_set(uint8_t on) / uint8_t display_auto_get(void) |
自动扫描开关(关后 7 脚置高阻,交出手动控制) |
int display_pin_map(uint8_t pin_no, GPIO_Module**, uint16_t*) |
数码管脚号 1~7 → GPIO 端口+掩码 |
void display_task(void*) |
扫描任务:优先级 1、栈 256 字、20ms/帧、每段 250µs,循环喂狗 |
内部状态:g_disp_value/g_disp_type/g_disp_base(数值)、g_disp_raw/g_disp_raw_digits(裸码模式)、g_disp_auto(自动开关)。
8. CLI 命令(cli_core.c)
| 命令 | 说明 |
|---|---|
disp <value> [f] |
设温度显示;f = 华氏(DIG4 显 F + DIG6-A 亮),默认摄氏 |
| `dispauto [on | off]` |
dispset <pin1-7|all> [0|1|z] |
手动驱动数码管脚:0 低 / 1 高 / z 高阻;all 一键全脚 |
disptest |
显示自检:① 0.0 |
pinset <pxN> [0|1] |
通用 GPIO 读写(非数码管专用) |
9. 关键踩坑与修正记录(当日)
- GPIOA 时钟未使能 → PA0/PA1/PA6 无输出:数码管驱动删掉
LedInit后 GPIOA 时钟失去使能点(GPIOB 因 USART 仍在使能)。修复:display_task开头显式使能 GPIOA+GPIOB,dispset内再防御性使能一次。 - 位序理解错误:旧代码把 DIG1~5 当 5 个全功能数字位、DIG4 当数字位。修正:DIG4 是单位字母、DIG5 是小数位。
- DIG4 从"固定 F"改为"F/C":华氏显 F、摄氏显 C。
- 量程:0.0~999.9(百十个小·数共 4 位数字 + 1 位单位),非 9999.9。
- 裸码模式:
display_set_raw用于显示 09/AF 之外的状态字符(如 Err),display_set会清掉裸码回到数值解码。
10. 温度显示示例
| 温度 | DIG1(百) | DIG2(十) | DIG3(个) | DIG4 | DIG5(小数) | 小数点 | °F |
|---|---|---|---|---|---|---|---|
| 0.0 ℃ | (空) | (空) | 0 | C | 0 | 亮 | 灭 |
| 8.5 ℃ | (空) | (空) | 8 | C | 5 | 亮 | 灭 |
| 36.5 ℃ | (空) | 3 | 6 | C | 5 | 亮 | 灭 |
| 36.5 ℉ | (空) | 3 | 6 | F | 5 | 亮 | 亮 |
| 100.0 ℃ | 1 | 0 | 0 | C | 0 | 亮 | 灭 |
| 999.9 ℉ | 9 | 9 | 9 | F | 9 | 亮 | 亮 |