/****************************************************************************** * @file bsp_ct1642.c * @brief CBR3 CT1642 类数码管驱动实现(软件移位) * @author cyWu <1917507415@qq.com> * @date 2026.09.04 * @version V1.1.0,转机 180°:段码 A↔D/B↔E/C↔F(BOARD_DISP_ROTATE_180) * @history * - V1.1.0, 2026.09.04, cyWu, 数码管整板旋转 180° 后补偿段码 * - V1.0.1, 2026.08.29, cyWu, IO 分离:CT1642 独立 PG0=CLK/PG1=DATA * * @note 驱动协议严格参照 CT1642 手册(与 CSDN yitx 一致): * 一帧 = 18bit,CLK 上升沿采样 DATA(DATA 只在 CLK=低时改变): * [0:3] C1-C4 位选(低有效,入参 com 取反后发送,先发 C1) * [4:9] 6 位保留位(固定填 1) * [10:17] Q2-Q9 段码(LSB 先,seg 的 bit0 → Q2 引脚) * 发完 18bit 后:CLK=高时 DATA 0→1 = 锁存;CLK=低时 DATA 0→1 = 输出。 * 段码/位选物理映射由 PCB 决定,SCAN_TEST 扫段/扫位已定型(见 board_pin.h)。 * 本驱动独立使用 PG0/PG1,与 AHT20 的 PC4/PC5 互不干扰。 ******************************************************************************/ #include "bsp_ct1642.h" #include "board_pin.h" #include "asm/gpio.h" /* 软件延时:1 次空循环(24MHz 下 ~0.1us 量级,gpio 调用本身开销另计)。 * ⚠ 提速历程:8→2(鬼影明显→一点点)→1(继续压残余微亮)。 * 若出现显示乱码/错位(低于 CT1642 最小脉宽),回调到 2。 * 软件压到极限后残余的微亮 = 硬件层(Q2-Q9 段线加 1~10k 下拉可根除)。 */ static void ct1642_delay(void) { volatile uint32_t i; for (i = 0; i < 1; i++) { /* nop */ } } /** * @brief 初始化 PG0(CLK)/PG1(DATA) 为推挽输出,初始 0 * @note ⚠ 必须在第一次发帧前调用(app_disp_init 内)。 * CT1642 为纯输入从机,两根线全程推挽驱动即可,无需上拉/开漏。 * @return 无 */ void bsp_ct1642_init(void) { gpio_set_pull_up(BOARD_DISP_CLK_PIN, 0); gpio_set_pull_down(BOARD_DISP_CLK_PIN, 0); gpio_set_die(BOARD_DISP_CLK_PIN, 1); gpio_direction_output(BOARD_DISP_CLK_PIN, 0); gpio_set_pull_up(BOARD_DISP_DATA_PIN, 0); gpio_set_pull_down(BOARD_DISP_DATA_PIN, 0); gpio_set_die(BOARD_DISP_DATA_PIN, 1); gpio_direction_output(BOARD_DISP_DATA_PIN, 0); } static void ct1642_clk(uint8_t level) { gpio_direction_output(BOARD_DISP_CLK_PIN, level); ct1642_delay(); } static void ct1642_data(uint8_t level) { gpio_direction_output(BOARD_DISP_DATA_PIN, level); ct1642_delay(); } /** * @brief 7 段数码管数字转段码(共阴,A=bit0 ... G=bit6,DP=bit7) * @param digit 0~9;10='-'(G 段);11=全灭 * @return 段码 */ uint8_t bsp_ct1642_seg(uint8_t digit) { static const uint8_t seg_tbl[12] = { 0x3F, /* 0: abcdef */ 0x06, /* 1: bc */ 0x5B, /* 2: abdeg */ 0x4F, /* 3: abcdg */ 0x66, /* 4: bcfg */ 0x6D, /* 5: acdfg */ 0x7D, /* 6: acdefg */ 0x07, /* 7: abc */ 0x7F, /* 8: abcdefg */ 0x6F, /* 9: abcdfg */ 0x40, /* '-': g */ 0x00, /* 灭 */ }; if (digit > 11) { digit = 11; } return seg_tbl[digit]; } /** * @brief 发一帧 18bit(不含消影):C1-C4 → 6保留 → Q2-Q9,再锁存+输出 * @note 内部函数;com/seg 的消影由 bsp_ct1642_send_frame 负责。 */ static void ct1642_xfer(uint8_t seg, uint8_t com) { uint8_t i; uint8_t posn = (uint8_t)(~com); /* C1-C4 低有效:位0=选中位 */ /* 起始空闲:CLK=低,DATA=高(规格“上电 CLK 低、DATA 高”) */ ct1642_data(1); ct1642_clk(0); /* 1. 移位 18bit:C1-C4(4) → 6位保留(填1) → Q2-Q9(8,LSB=Q2=seg bit0) */ for (i = 0; i < 4; i++) { /* C1-C4,先发 C1 */ ct1642_data((uint8_t)(posn & 0x01u)); ct1642_clk(0); ct1642_clk(1); /* 下降沿改完,上升沿采样 */ posn >>= 1; } for (i = 0; i < 6; i++) { /* 6 位保留,固定填 1 */ ct1642_data(1); ct1642_clk(0); ct1642_clk(1); } for (i = 0; i < 8; i++) { /* Q2-Q9 段码,先发 Q2(=seg bit0) */ ct1642_data((uint8_t)(seg & 0x01u)); ct1642_clk(0); ct1642_clk(1); seg >>= 1; } /* 2. 锁存:CLK 保持高,DATA 给 0→1 上升沿 */ ct1642_clk(1); ct1642_data(0); ct1642_data(1); /* 3. 输出:CLK 拉低,DATA 给 0→1 上升沿(刷新该位显示) */ ct1642_clk(0); ct1642_data(0); ct1642_data(1); } /** * @brief 发一帧刷新一位数码管(CT1642 18bit 协议) * @param seg 段码(bit7=DP ... bit0=A,与 seg_tbl 一致;先发 bit0→Q2) * @param com COM 位选掩码(bit3=C4 ... bit0=C1,仅 1 位置位;硬件低有效内部取反) * @note ⚠ 不做任何消影预帧:CT1642 移位过程实时输出,“持续刷新”才是鬼影根因 * (切位瞬间杂段),预帧/全灭帧都无法根除,且每帧翻倍会饿死 ISR 喂狗导致 * 上电复位(实测)。鬼影的正确解法在应用层:数字没变就不刷新(值变化才 * 刷一次,其余时间锁存静止显示)。本函数保持单帧、极短,适配 ISR 上下文。 * @return 无 */ void bsp_ct1642_send_frame(uint8_t seg, uint8_t com) { #if BOARD_DISP_ROTATE_180 /* 管倒过来看:视觉 A/B/C 落在物理 D/E/F,视觉 D/E/F 落在物理 A/B/C;G/DP 不动 */ seg = (uint8_t)(((seg & 0x07u) << 3) | ((seg & 0x38u) >> 3) | (seg & 0xC0u)); #endif // ct1642_xfer(0, 0); ct1642_xfer(seg, com); /* 单帧:18bit 移位 + 锁存 + 输出,无消影预帧 */ }