树莓派基于时钟信号的串行输出——TM1637数码管

之前的那个12脚扫描式4位数码管 要想显示1234这样的数字 一个扫描周期里需要四个动作 每个动作只显示一位 另外三位是不亮的 而且每次占用了大量的针脚 于是 淘宝上买了个带芯片的 4针脚的 4位数码管

本次的芯片是用的TM1637 查了一下 到处都是只有arduino的代码 而且貌似这个数码管也是标配给它的 不管 反正接口是一样的嘛

原本给arduino的代码是用c写的 还找到了另外一个python写给树莓派的 两者的代码的思路是一样的 但是还是没有看懂 说好的基本输入输出呢 两个的代码全都是直接修改pinMode来处理高低电平的 我擦泪 完全看不懂啊 为啥不用digitalWrite

反正就是这个数码管有4个脚 一个电源 一个接地 一个时钟信号 一个串行输入信号 然后看文档 文档 尼玛这什么狗屁文档 完全看不懂啊 一个是数码管的说明 一个是芯片的说明 一个是两者连接说明

输入一次数据

关键是芯片说明书

微处理器的数据通过两线总线接口和 TM1637 通信,在输入数据时当 CLK 是高电平时,DIO 上的信号 必须保持不变;只有 CLK 上的时钟信号为低电平时,DIO 上的信号才能改变。数据输入的开始条件是 CLK 为高电平时,DIO 由高变低;结束条件是 CLK 为高时,DIO 由低电平变为高电平。
TM1637 的数据传输带有应答信号 ACK,当传输数据正确时,会在第八个时钟的下降沿,芯片内部会 产生一个应答信号 ACK 将 DIO 管脚拉低,在第九个时钟结束之后释放 DIO 口线。

这特么都说的是啥 反正结合这段话 然后再看那两份源码 看的我真是有点傻逼了 后来搞了很久 看懂了 上面这两段话说了三个事情

  1. 时钟信号上升沿为信号输入
  2. 时钟信号高电平时, 数据信号下降沿表示输入开始, 上升沿表示输入结束
  3. 每次输入要经过9个时钟 前八个时钟为发送的数据 第九个时钟时 数据接口产生一个应答信号 低电平表示数据正确

起始的时候 两个口都为高电平

输入一组数据

上面这个基本上说了一次输入的流程 但是要现实出数字来一次输入是没用的 要输入多次

于是 芯片的文档还说了另外一个事 每组数据分为三部分

  1. 设置输入模式 输入一次数据 总共三种模式
    1. 使用按键输入数据 芯片口有 k1 和 k2 两个针脚 可以用来通过外部的开关闭合来作为输入信号 具体的我就不知道了 因为本次是使用同步信号来输入的
    2. 设置第一位显示的地址 后面的自增
    3. 每位需要单独设置地址
  2. 输入显示数据 需要输入多次数据
    1. 第二种模式: 地址1 数据1 数据2 …
    2. 第三种模式: 地址1 数据1 地址2 数据2 …
  3. 设置显示模式 输入一次数据 表示灯管的开关以及亮度

实现

然后因为很明显 第二种模式最方便 然后就选了这个来实现了

环境 为nodejs 然后还遇到一个问题 文档中虽然没说 但是前面两个源码里 两个信号之间有一定的时间间隔 应该是表示时钟每个周期时长

由于上面两个源码 一个是c一个是python 设置时钟高低电平的时长用sleep之类的很容易实现 但是js咋办了 想了两套方案

最容易想到的就是js模拟一个sleep出来 js里async还用不了 可以使用generator来做 实际上 还挺不喜欢这个的 需要一个辅助库co 之前也写了一个小轮子大概实现了这个功能 不过为了方便 还是直接用co了 这个方案的一个好处就是 代码结构和python这些使用sleep来实现延迟的基本一致 代码逻辑易理解 如果中途出问题比如第9时钟返回的ack异常这种 可以及时解决 有点点同步执行的意思

还有另外一个解决方案 既然没办法同步执行 那可以把所有的操作保存到队列 然后控制出队的时间间隔 也可以做到延时的操作 这个方案也有个好处 就是代码写起来方便易读 没有特别难懂的js语法 generatoryield啊这种 但是因为是异步执行 比如上面说的 如果ack返回有问题就得不到及时处理 只能数据发送完成之后才能知道

大概的逻辑是这样的

1
2
3
4
5
6
7
8
9
10
11
12
this.start(); // 数据命令设置
this.writeByte(0b01000000); // 普通模式, 自动地址增加, 写数据到显示寄存器
this.stop();

this.start(); // 地址命令设置
this.writeByte(0b11000000 + (0b11 & (4 - nums.length))); // 地址起始位
nums.forEach(n => this.writeByte(codigitToSegment[n])); // 数据
this.stop();

this.start(); // 显示控制
this.writeByte(0b10001111); // 显示控制命令设置, 开, 亮度为 111
this.stop();

代码 在这里

最终效果

然后最后是最后的效果

link2

back

front

demo1