数码之家

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信登录

微信扫一扫,快速登录

搜索
查看: 391|回复: 11

[Arduino] 查理复用(Charlieplexing)数码管

[复制链接]
发表于 2025-10-27 23:25:06 | 显示全部楼层 |阅读模式
本帖最后由 kpj001 于 2025-10-29 12:26 编辑

视频先登:https://www.bilibili.com/video/BV1s8sDzwEWg/
实物介绍(非广告):https://item.taobao.com/item.htm?id=800826018081

数码管通常分为共阳共阴,共同特点引脚比较多,一般我都不用的。
最近流行“查理复用(Charlieplexing)数码管”,就是单排引脚这种。(原理自行百度之)
到手发现居然没有现成驱动,于是试着从 01 10 这样的组合开始测试,几个回合就找出来段码。
这样需要显示0-199 整数的场合,直插面包板style,就多了一种及其便宜的选择(1毛钱一颗)。

代码为 arduino uno nano D6-D2 直插【看视频】,作为点亮测试还是可以的,实际使用可以考虑用中断代替延时。
当然这款数码管搭配arduino略显浪费,STC DIP 封装的才是良配,代码略微修改即可。

  1. const int seg188_pins[] = {6, 5, 4, 3, 2};
  2. int segDuration = 1;      // 每个段点亮时间,实测1ms合适
  3. bool digits[10][7] = {    // 7段数码管显示数字所需点亮之段码,格式: {a, b, c, d, e, f, g}
  4.   {1, 1, 1, 1, 1, 1, 0},  // 0
  5.   ...
  6.   {1, 1, 1, 1, 0, 1, 1}   // 9
  7. };

  8. int currentNumber = 0;
  9. unsigned long lastChangeTime = 0;
  10. int displayCounter = 0;

  11. void setup() {
  12.   Serial.begin(115200);
  13. }

  14. void loop() {  
  15.   showNumber(currentNumber);
  16.   
  17.   // 每隔一段时间改变显示的数字(测试用)
  18.   if (millis() - lastChangeTime > 100) {
  19.     lastChangeTime = millis();
  20.     currentNumber = (currentNumber + 1) % 200; // 循环显示0-199
  21.     Serial.println("Displaying: " + String(currentNumber));
  22.   }
  23. }

  24. void showNumber(int a) {            // 数字范围 0-199
  25.   int tens = (a % 100) / 10;
  26.   int ones = a % 10;
  27.   showDigit(3, ones);                 // 个位
  28.   if(a>9)showDigit(2, tens);        // 十位  
  29.   if(a>99)showDigit(1, 1);          // 百位
  30. }

  31. void showDigit(int digitPos, int number) {
  32.   bool* segments = digits[number];  
  33.   // 个位
  34.   if (digitPos == 3) {
  35.     if (segments[0]) lightSegment(0, 1); // a3
  36.     ...
  37.     if (segments[6]) lightSegment(4, 0); // g3
  38.   }
  39.   // 十位
  40.   else if (digitPos == 2) {
  41.     if (segments[0]) lightSegment(1, 2); // a2
  42.     ...
  43.     if (segments[6]) lightSegment(4, 3); // g2
  44.   }
  45.   // 百位,比较特殊,只有显示1 和不显示两种状态
  46.   else if (digitPos == 1) {
  47.     lightSegment(2, 3); // b1 (对应a1)
  48.     lightSegment(1, 3); // c1 (对应b1)
  49.   }
  50. }

  51. void lightSegment(int anodePin, int cathodePin) {   
  52.   pinMode(seg188_pins[anodePin], OUTPUT);digitalWrite(seg188_pins[anodePin], HIGH);  
  53.   pinMode(seg188_pins[cathodePin], OUTPUT);digitalWrite(seg188_pins[cathodePin], LOW);
  54.   delayMicroseconds(segDuration);
复制代码

上面的代码让AI移植为STC。
不过这次过程很折腾,一开始全是乱码,各种整改都不行,没道理啊,也没用到什么核心技术,引脚高高低低而已。
于是亲自看一下代码,原来AI把高阻模式写错了。。。
所以呢,用AI是可以省点力气,不过还是需要自己会的。

arduino nano


STC8G1K08-16pin







本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册 微信登录

x
发表于 2025-10-28 01:32:24 | 显示全部楼层
谢谢分享好东西
回复 支持 反对

使用道具 举报

发表于 2025-10-28 08:42:54 | 显示全部楼层
也有几个这样的没用起来
回复 支持 反对

使用道具 举报

发表于 2025-10-28 10:54:14 | 显示全部楼层
常用的188数码管
回复 支持 反对

使用道具 举报

发表于 2025-10-28 11:16:00 | 显示全部楼层
电压表用的就是这样的数码管。
回复 支持 反对

使用道具 举报

发表于 2025-10-28 11:22:21 | 显示全部楼层
谢谢分享~进来学习一下
回复 支持 反对

使用道具 举报

发表于 2025-10-28 17:24:21 | 显示全部楼层
本帖最后由 maidoo 于 2025-10-28 17:26 编辑

我之前写的程序已经用上了这种算法( 【AKClock】利旧两个零件,组装一个NTP对时电子钟 ),但是不知道他名字叫查理。多谢楼主布道!
这个名字的由来是怎样的呢?好奇。Bing一下:

“查理复用算法 (Charlieplexing ),具备控制多个LED的能力,即可以用较少数量的微控制器输出端来控制较多数量的 LED(微控制器输出端少于LED数量)。 在1995年, Maxim 公司的 Charles Allen 首次提出这一想法,并命名为 Charlieplexing 。 查理复用算法使用微控制器的所有三种逻辑状态以及LED的单向导电性来控制矩阵。”
回复 支持 反对

使用道具 举报

发表于 2025-10-28 20:07:47 | 显示全部楼层
这种数码管点亮不难,难的是如果芯片I/O没有恒流或限流功能,同一时间点亮不同点数时的亮度控制方法。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-10-28 21:03:02 | 显示全部楼层
本帖最后由 kpj001 于 2025-10-28 22:57 编辑
mmxx2015 发表于 2025-10-28 20:07
这种数码管点亮不难,难的是如果芯片I/O没有恒流或限流功能,同一时间点亮不同点数时的亮度控制方法。 ...

没什么难的,这个188共计2+7+7=16段,每个显示循环强制补足16个段时间即可 ,还可以在这16个段的时间外额外增加些时间用于调亮度。

showNumber 函数改为下面这样,就能很好地实现 亮度均衡和亮度控制(已实测):

void showNumber(int a) {
    int tens = (a % 100) / 10;
    int ones = a % 10;
    showDigit(3, ones);                                                         // 个位是肯定要显示的
    if(a > 9) showDigit(2, tens);else delay_ms(segDuration*7); //不显示十位也补足时间
    if(a > 99) showDigit(1, 1);else delay_ms(segDuration*2);   //不显示百位也补足时间
    delay_ms(segDuration*30); // 调整亮度
}


因为补了延时,lightSegment 里面的语句需要调整下顺序
回复 支持 1 反对 0

使用道具 举报

发表于 2025-10-28 22:42:45 | 显示全部楼层
kpj001 发表于 2025-10-28 21:03
没什么难的,这个188共计2+7+7=16段,每个显示循环强制补足16个段时间即可 ,还可以在这16个段的时间外额 ...

是不是每次只点亮一个点?如果是这样,因为点数较少,问题不大,刷新率=1000mS / (1mS * 16) = 62.5 Hz,如果点数多一些,这样做就容易觉察闪烁了。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-10-28 23:32:59 | 显示全部楼层
mmxx2015 发表于 2025-10-28 22:42
是不是每次只点亮一个点?如果是这样,因为点数较少,问题不大,刷新率=1000mS / (1mS * 16) = 62.5 Hz, ...

上面代码只是测试点亮。实际上每段亮的时间是可以大幅度减少的,比如到1us。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册 微信登录

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-11-9 04:54 , Processed in 0.265200 second(s), 8 queries , Gzip On, Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

快速回复 返回顶部 返回列表