- #ifndef CHARLIEPLEX_H
- #define CHARLIEPLEX_H
- #include "hal_data.h"
- /* ================= 引脚配置(请根据实际板子修改) ================= */
- #define CHARLIEPLEX_PIN_A BSP_IO_PORT_00_PIN_00 // 引脚A
- #define CHARLIEPLEX_PIN_B BSP_IO_PORT_00_PIN_01 // 引脚B
- #define CHARLIEPLEX_PIN_C BSP_IO_PORT_00_PIN_02 // 引脚C
- #define CHARLIEPLEX_PIN_D BSP_IO_PORT_00_PIN_03 // 引脚D
- #define CHARLIEPLEX_PIN_E BSP_IO_PORT_00_PIN_04 // 引脚E
- /* ================= 函数声明 ================= */
- void Charlieplex_Init(void);
- void Charlieplex_Display(uint8_t digit1, uint8_t digit0, uint8_t icon_mask);
- #endif
初始化:在main函数开头调用Charlieplex_Init()配置 GPIO。调用显示:在主循环或定时器中断中调用Charlieplex_Display(),建议 1ms 刷新一次避免闪烁。
- #include "Charlieplex.h"
- /* ================= 内部变量与表 ================= */
- // 共阴数码管段码表(0-9,a-g+dp)
- static const uint8_t seg_table[] = {
- 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
- };
- // 引脚数组,方便循环操作
- static const bsp_io_port_pin_t pins[] = {
- CHARLIEPLEX_PIN_A, CHARLIEPLEX_PIN_B,
- CHARLIEPLEX_PIN_C, CHARLIEPLEX_PIN_D, CHARLIEPLEX_PIN_E
- };
- /* ================= 内部辅助函数 ================= */
- // 将所有引脚设为高阻态(输入模式)
- static void _Charlieplex_AllHighZ(void)
- {
- for (uint8_t i = 0; i < 5; i++) {
- R_PORT_PinCfgSet(pins[i], BSP_IO_CFG_INPUT);
- }
- }
- // 驱动单个LED:anode_idx=阳极引脚索引(0-4),cathode_idx=阴极引脚索引(0-4)
- static void _Charlieplex_DriveLED(uint8_t anode_idx, uint8_t cathode_idx)
- {
- _Charlieplex_AllHighZ();
- // 阳极设为推挽输出高
- R_PORT_PinCfgSet(pins[anode_idx], BSP_IO_CFG_OUTPUT_PUSH_PULL);
- R_PORT_PinWrite(pins[anode_idx], BSP_IO_LEVEL_HIGH);
- // 阴极设为推挽输出低
- R_PORT_PinCfgSet(pins[cathode_idx], BSP_IO_CFG_OUTPUT_PUSH_PULL);
- R_PORT_PinWrite(pins[cathode_idx], BSP_IO_LEVEL_LOW);
- }
- /* ================= 核心函数实现 ================= */
- /**
- * [url=/u/brief]@brief[/url] 查理复用初始化
- * 开启端口时钟,所有引脚初始化为高阻态
- */
- void Charlieplex_Init(void)
- {
- // 开启P0端口时钟(若用其他端口请修改)
- R_SYS_ModuleClockEnable(SYS_MODULE_PORT0);
- // 所有引脚初始化为高阻
- _Charlieplex_AllHighZ();
- }
- /**
- * @brief 查理复用显示函数
- * @param digit1: 十位数字(0-9),超过9则不显示
- * @param digit0: 个位数字(0-9),超过9则不显示
- * @param icon_mask: 图标掩码(bit0-bit3对应4个图标)
- */
- void Charlieplex_Display(uint8_t digit1, uint8_t digit0, uint8_t icon_mask)
- {
- uint8_t seg;
-
- /* ---------------- 1. 显示十位数码管 ---------------- */
- if (digit1 <= 9) {
- seg = seg_table[digit1];
- // 段映射:a=A阳B阴, b=A阳C阴, c=A阳D阴, d=A阳E阴, e=B阳A阴, f=B阳C阴, g=B阳D阴, dp=B阳E阴
- if (seg & 0x01) { _Charlieplex_DriveLED(0, 1); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // a
- if (seg & 0x02) { _Charlieplex_DriveLED(0, 2); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // b
- if (seg & 0x04) { _Charlieplex_DriveLED(0, 3); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // c
- if (seg & 0x08) { _Charlieplex_DriveLED(0, 4); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // d
- if (seg & 0x10) { _Charlieplex_DriveLED(1, 0); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // e
- if (seg & 0x20) { _Charlieplex_DriveLED(1, 2); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // f
- if (seg & 0x40) { _Charlieplex_DriveLED(1, 3); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // g
- if (seg & 0x80) { _Charlieplex_DriveLED(1, 4); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // dp
- }
-
- /* ---------------- 2. 显示个位数码管 ---------------- */
- if (digit0 <= 9) {
- seg = seg_table[digit0];
- // 段映射:a=C阳A阴, b=C阳B阴, c=C阳D阴, d=C阳E阴, e=D阳A阴, f=D阳B阴, g=D阳C阴, dp=D阳E阴
- if (seg & 0x01) { _Charlieplex_DriveLED(2, 0); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // a
- if (seg & 0x02) { _Charlieplex_DriveLED(2, 1); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // b
- if (seg & 0x04) { _Charlieplex_DriveLED(2, 3); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // c
- if (seg & 0x08) { _Charlieplex_DriveLED(2, 4); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // d
- if (seg & 0x10) { _Charlieplex_DriveLED(3, 0); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // e
- if (seg & 0x20) { _Charlieplex_DriveLED(3, 1); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // f
- if (seg & 0x40) { _Charlieplex_DriveLED(3, 2); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // g
- if (seg & 0x80) { _Charlieplex_DriveLED(3, 4); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // dp
- }
-
- /* ---------------- 3. 显示图标 ---------------- */
- // 图标映射:图标1=E阳A阴, 图标2=E阳B阴, 图标3=E阳C阴, 图标4=E阳D阴
- if (icon_mask & 0x01) { _Charlieplex_DriveLED(4, 0); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // 图标1
- if (icon_mask & 0x02) { _Charlieplex_DriveLED(4, 1); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // 图标2
- if (icon_mask & 0x04) { _Charlieplex_DriveLED(4, 2); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // 图标3
- if (icon_mask & 0x08) { _Charlieplex_DriveLED(4, 3); R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS); } // 图标4
-
- // 最后恢复高阻,避免串扰
- _Charlieplex_AllHighZ();
- }
三、调用代码验证
直接上测试代码,我在主循环里让数码管从 00 跑到 99,图标依次点亮,大家可直接复制到 e2s 里试:
- #include "hal_data.h"
- #include "Charlieplex.h"
- void main(void)
- {
- /* 初始化系统时钟(根据实际项目配置) */
- R_SYS_ClockSourceEnable(SYS_CLOCK_SOURCE_MAIN_OSC);
- R_SYS_ClockConfig(SYS_CLOCK_TYPE_ICLK, 8, SYS_CLOCK_SOURCE_MAIN_OSC);
-
- /* 查理复用初始化 */
- Charlieplex_Init();
-
- uint8_t num = 0;
- uint8_t icon = 0x01;
-
- while (1)
- {
- /* 显示数字十位、个位,图标轮流点亮 */
- Charlieplex_Display(num / 10, num % 10, icon);
-
- /* 延时切换数字和图标 */
- R_BSP_SoftwareDelay(200, BSP_DELAY_UNITS_MILLISECONDS);
- num = (num > 99) ? 0 : (num + 1);
- icon = (icon > 0x08) ? 0x01 : (icon << 1);
- }
- }
注:延时用了瑞萨 FSP 的R_BSP_SoftwareDelay,大家可根据自己的库替换,或用定时器中断刷新更稳定。
四、预期输出结果
烧录后你会看到:
- 两个数码管从00开始,每秒加 1,循环到99;
- 4 个图标(需硬件对应)从第一个开始,每隔 200ms 切换下一个,轮流点亮;
- 刷新频率足够时,数码管无闪烁,图标显示稳定。
因为手机拍摄出来的效果会有闪烁比较严重的现象,这里就没有放图。这是因为查理复用利用的是人眼的视觉暂留,但是对于手机这种设备来说是可以看到快速闪烁的。
五、移植方法
想移到自己的板子?主要改这几点:
- 引脚定义:打开Charlieplex.h,修改CHARLIEPLEX_PIN_A等宏为你实际使用的端口和引脚(如 P105)。
- 时钟使能:在Charlieplex_Init()中添加对应端口的时钟使能代码(如用 P1 口需调用R_SYS_ModuleClockEnable(SYS_MODULE_PORT1))。
- 共阴 / 共阳切换:若数码管是共阳,在显示函数中将高低电平逻辑反转即可。
六、注意事项
踩过的坑分享给大家,避免重蹈覆辙:
- 限流电阻:每个 LED 必须串 220Ω-1kΩ 的限流电阻,别直接接 GPIO!
- 刷新频率:推荐用定时器中断(1ms-5ms 一次),主循环延时易被打断导致闪烁。
- 高阻态设置:未使用的引脚务必设为输入模式(高阻),否则会有串扰(LED 微亮)。
- 引脚顺序:硬件接线必须与软件定义的 A→B→C→D→E 顺序一致,否则显示乱码。
- 驱动能力:若 LED 较多或较亮,建议用三极管扩流,瑞萨 GPIO 单引脚驱动能力一般为几 mA。