[ESP8266/ESP32]FireBeetle 制作一个LED矩阵 精华

2776浏览
查看: 2776|回复: 1

[ESP8266/ESP32] FireBeetle 制作一个LED矩阵

[复制链接]
这次介绍的项目是通过 FireBeetle ESP32 实现一个 8*16的单色LED矩阵,可以在上面实现一些简单的图形和动画效果。
在开始之前,首先介绍LED的静态驱动和动态驱动的概念。当我们在一个发光二极管两端加上一个电压的时候,发光二极管即可工作。理论上,如果驱动N个共阴极的LED那么需要N个提供正电压。这种带来一个问题,如果需要驱动大量的LED,那么就同样需要同样数量的引脚作为正极。对于单片机来说,会遇到IO引脚不够的问题。
FireBeetle 制作一个LED矩阵图1
这种直接驱动的方式称作“静态显示驱动”。与之相对,还可以通过构成矩阵的方式来进行驱动。以3x3LED矩阵为例,通过6IO 引脚可以驱动9LED
FireBeetle 制作一个LED矩阵图2

可以看到这种电路,我们可以一次性点亮一行或者一列上的LED,但是如果要点亮的位于不同的行列就会出现问题。例如:我们希望在矩阵上点亮LED0LED6, 那么需要Y0Y2为高,X0 为低,这种情况比较简单;但是如果需要同时点亮LED0 LED4 问题就变得麻烦。因为 LED0 要求Y0为高X0为低才能点亮,LED4要求Y1为高X1为低,但是Y1为高X0为低时LED3也会同时亮起。因此,这里需要引入一个分时点亮的方法,比如,先设置Y0为高X0为低点亮LED0,再设置Y0为低熄灭LED0,设置Y1为高X1为低点亮LED4,只要点亮速度足够快眼睛无法分辨出他们不是同时点亮的。这就是所谓的“动态扫描”。
如果使用 Arduino 编写代码,通常需要使用一个定时中断:
  1. Void timerInterrupt()
  2. {
  3. 熄灭上一次的行,点亮第x行
  4. x=x+1
  5. }
复制代码

可以看出,这样的方法会使得程序复杂度上升,同样的,对于N个灯需要根号N IO。经过研究发现一个好玩的 IC: WCH CH423。它是ICI/O 扩展芯片,功能如下【参考1】:

FireBeetle 制作一个LED矩阵图3



  • 通过两线串行接口远程扩展出8 个通用输入输出引脚GPIO 16 个通用输出引脚GPO
  • 内置电流驱动级,连续驱动电流不小于15mAOC 引脚输出1/16 脉冲灌电流不小于120mA
  • 静态显示驱动方式支持24 只发光管LED 或者3 位共阳数码管。
  • 分时动态扫描显示驱动方式支持128 只发光管LED 或者16 位共阴数码管,支持亮度控制。
  • 双向I/O 引脚在输入方式下具有输入电平变化时产生中断的功能,中断输出低电平有效。
  • 16 个通用输出引脚可以选择推挽输出或者开漏输出。
  • 支持3V5V 电源电压,支持低功耗睡眠,可以被输入电平变化唤醒。
  • 高速2 线串行接口,时钟速度从0 1MHz,兼容两线I2C 总线,节约引脚。
  • 提供SDIP28 SOP28 两种无铅封装,兼容RoHS


这次的试验就使用这个芯片来实现一个 8x16LED点阵。
首先进行电路设计:

FireBeetle 制作一个LED矩阵图4

芯片是I2C 接口,控制线路非常简单:SClSDA就好了。CH423SOP28封装,为了便于试验我从淘宝购买了一个SOP28DIP的小PCB,焊接之后将CH423插入到PCB上。接下来是LED矩阵的设计:

FireBeetle 制作一个LED矩阵图5

其中 SE[N] 信号能够输出高低电平,DEG[M]只用作吸收电流使用。对CH423发送命令,告知我现在要做动态扫描使用,然后告知SE[N]DEG[M] 的组合即可。例如:告知SE1输出高,DIG0吸收电流和SE7输出高,DIG15吸收电流,之后芯片本身会动态控制,肉眼看起来就是 L00L7F 点亮【参考2】。
因为都是低速信号,没有太多限制,摆放好 LED后直接使用立创自动布线走的通即可。

FireBeetle 制作一个LED矩阵图6

焊接后的样子:

FireBeetle 制作一个LED矩阵图7

编写一个测试代码如下:

  1. #include <Wire.h>
  2. // CH423接口定义
  3. #define     CH423_I2C_ADDR1     0x20         // CH423的地址
  4. #define     CH423_I2C_MASK      0x3E         // CH423的高字节命令掩码
  5. #define CH423_SYSON1    0x0417    //开启自动扫描显示
  6. unsigned char CH423_buf[16];    //定义16个数码管的数据映象缓存区
  7. const unsigned char BCD_decode_tab[ 0x10 ] = { 0X3F, 0X06, 0X5B, 0X4F, 0X66, 0X6D, 0X7D, 0X07, 0X7F, 0X6F, 0X77, 0X7C, 0X58, 0X5E, 0X79, 0X71 };
  8. void CH423_Write( uint32_t cmd )    // 写命令
  9. {
  10.   Serial.print("Address ");
  11.   Serial.print(( unsigned char )(cmd >> 8), HEX);
  12.   Serial.print("  command  ");
  13.   Serial.print(( unsigned char ) (cmd & 0xff), HEX);
  14.   Wire.beginTransmission (( unsigned char )(cmd >> 8));
  15.   Wire.write( ( unsigned char ) (cmd & 0xff) );  // 发送数据
  16.   // 结束总线
  17.   if (Wire.endTransmission() == 0) {
  18.     Serial.println(" I2C Success!");
  19.   } else {
  20.     Serial.println("I2C error!");
  21.   }
  22. }
  23. // 向CH423输出数据或者操作命令,自动建立数据映象
  24. void CH423_buf_write( uint32_t cmd )
  25. {
  26.   if ( cmd & 0x1000 )
  27.   { // 加载数据的命令,需要备份数据到映象缓冲区
  28.     CH423_buf[ (unsigned char)( cmd >> 8 ) & 0x0F ] = (unsigned char)( cmd & 0xFF );    // 备份数据到相应的映象单元
  29.   }
  30.   CH423_Write( cmd );    // 发出
  31. }
  32. void setup() {
  33.   Serial.begin (115200);
  34.   Wire.begin (21, 22);   // sda= GPIO_21 /scl= GPIO_22.
  35.   /* INTENS [00-11]
  36.      OD_EN 使能开漏
  37.      X_INT 0x08
  38.      DEC_H 0x04
  39.      DEC_L 0x02
  40.      IO_OE 0x01
  41.   */
  42.   CH423_buf_write( 0x2417 );
  43.   /* OC_L_DAT  OC7-OC0 电平控制
  44.   */
  45.   CH423_buf_write( 0x2200 );
  46.   /* OC_H_DAT  OC15-OC8 电平控制
  47.   */
  48.   CH423_buf_write( 0x2300 );
  49.   // 初始化时保持全灭
  50.   uint32_t i;
  51.   for (i = 0; i < 16; i++) {
  52.     CH423_buf_write(((0x30 + i) << 8) + 0x00);
  53.   }
  54. }
  55. // 要显示的字符取模, DFRobot 字样
  56. // 来自 https://www.zhetao.com/fontarray.html
  57. const unsigned char bitmap_bit_bytes[] = {
  58.   0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
  59.   0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
  60.   0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
  61.   0b11111000, 0b11111100, 0b11111100, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
  62.   0b01000100, 0b01000010, 0b01000010, 0b00000000, 0b11000000, 0b00000000, 0b00000000,
  63.   0b01000010, 0b01001000, 0b01000010, 0b00000000, 0b01000000, 0b00000000, 0b00010000,
  64.   0b01000010, 0b01001000, 0b01000010, 0b00000000, 0b01000000, 0b00000000, 0b00010000,
  65.   0b01000010, 0b01111000, 0b01111100, 0b00111100, 0b01011000, 0b00111100, 0b01111100,
  66.   0b01000010, 0b01001000, 0b01001000, 0b01000010, 0b01100100, 0b01000010, 0b00010000,
  67.   0b01000010, 0b01001000, 0b01001000, 0b01000010, 0b01000010, 0b01000010, 0b00010000,
  68.   0b01000010, 0b01000000, 0b01000100, 0b01000010, 0b01000010, 0b01000010, 0b00010000,
  69.   0b01000010, 0b01000000, 0b01000100, 0b01000010, 0b01000010, 0b01000010, 0b00010000,
  70.   0b01000100, 0b01000000, 0b01000010, 0b01000010, 0b01100100, 0b01000010, 0b00010010,
  71.   0b11111000, 0b11100000, 0b11100011, 0b00111100, 0b01011000, 0b00111100, 0b00001100,
  72.   0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
  73.   0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
  74. };
  75. // 显示一个动画效果
  76. uint16_t buf[8] = {
  77.   0b0100000000000000,
  78.   0b0010000000000000,
  79.   0b0001000000000000,
  80.   0b0000100000000000,
  81.   0b0000010000000000,
  82.   0b0000100000000000,
  83.   0b0001000000000000,
  84.   0b0010000000000000,
  85. };
  86. void loop() {
  87.   uint16_t i, j, m;
  88.   char c, v;
  89.   while (Serial.available()) {
  90.     c = Serial.read();
  91.     // 显示卡面定义的字符
  92.     if (c == '1') {
  93.       for (i = 0; i < 7 ; i++) { //一共有7个字符
  94.         for (j = 0; j < 16; j++) { // 每个字符有16个1Byte数据
  95.           CH423_buf_write( ((0x30 + j) << 8) + bitmap_bit_bytes[i + j * 7] );
  96.         }
  97.         delay(500);
  98.       }
  99.     }
  100.     // 随机点亮测试
  101.     if (c == '2') {
  102.       for (i = 0; i < 16; i++) {
  103.         for (j = 0; j < 16; j++) {
  104.           CH423_buf_write( ((0x30 + j) << 8) + random(0, 256) );
  105.         }
  106.         delay(500);
  107.       }
  108.     }
  109.     // 移动的动画效果
  110.     if (c == '3') {
  111.       for (m = 0; m < 32; m++) {
  112.         // 显示 buf 定义的图形
  113.         for (i = 0; i < 16; i++)
  114.         {
  115.           v = 0;
  116.           for (j = 0; j < 8; j++) {
  117.             if ((buf[j] & (1 << i)) == 0) {
  118.               v = v << 1;
  119.             }
  120.             else {
  121.               v = (v << 1) + 1;
  122.             }
  123.           }
  124.           CH423_buf_write( ((0x30 + i) << 8) + v );
  125.         }
  126.         // 移动 buf 字符
  127.         for (i = 0; i < 8; i++) {
  128.             if ((buf[i]&1)!=0) {buf[i]=buf[i]|0x8000;}
  129.             buf[i]=buf[i]>>1;
  130.           }
  131.         delay(100);  
  132.         
  133.       }
  134.     }
  135.   }
  136. }
复制代码

根据串口输入,进行不同的测试:
1.     输入1会逐个显示 DFRobot 字样
2.     输入2会随机点亮
3.     输入3会显示一个方向的简单动画效果。
参考:
2.     这部分在DataSheet有描述 “8.2. 动态显示驱动CH423 的动态显示驱动方式用于驱动128 只 LED 或者 16 只共阴数码管,由IO7~IO0 引脚分别驱动共阴数码管的各个段引脚(各数码管并联),由OC15~OC0 引脚分别驱动各个共阴数码管的公共端。单片机在加载完所有字数据后,开启DEC_L 和 DEC_H 控制位由CH423 自动地进行分时动态显示扫描。如果只需要驱动8 只数码管,那么可以只开启DEC_L 或者 DEC_H其中的一个控制位,剩余的另CH423中文手册”





zoologist  高级技匠
 楼主|

发表于 2022-12-9 08:21:28

本文提到的电路图 下载附件Mboard.zip
回复

使用道具 举报

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

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail