【微蓝课堂】机器人编程|树莓派系列|13-从零开始编写TM1637驱动程序

一、课堂目标

  • 了解数码管的显示原理
  • 掌握tm1637的数据显示

二、原理分析

2.1 TM1637模块介绍

TM1637四位数码管模块是一种用于数字显示的模块。它可以通过接口与微控制器进行通信,并将数字信息显示在四个8段数码管上。 该模块具有以下功能:

  1. 显示数字0-9:模块能够将微控制器发送的数字信息显示在数码管上,可以显示0-9的任何数字。
  2. 显示符号:模块支持显示符号,如负号、小数点等。
  3. 亮度调整:可以通过接口控制模块的亮度,调整数码管的显示亮度。
  4. 多位数显示:模块支持同时显示多位数,最多可以同时显示四位数。
  5. 节约IO资源:使用TM1637芯片,模块只需要两个IO口即可完成与微控制器的通信,相比其他数码管模块节省了IO资源。

低功耗 :模块具有低功耗特性,可以有效节省电力。 通过对TM1637四位数码管模块的使用,可以方便地实现数字显示功能,适用于各种需要数字显示的应用场景,如计数器、计时器、电子钟等

2.2 7段/8段数码管

我们先来了解什么是7段数码管?上面图片看到正面是4个'8',它是利用发光二极管的原理,组成一个'8'的形状,这个'8'正好是七段组成,所以一般称为七段数码管。

而市面上有数字后面带有小数点的,称为8段数码管,由8个发光二极管组成。还有其他特殊的数码管,原理都相同。

这7段数码管分别命令为:A、B、C、D、E、F、G。如果显示数字0,那么亮起A、B、C、D、E、F这6段;如果显示数字1,那么亮起B、C这两段。

数字对应的段数据

0 -> 1111110

1 -> 0110000

2 -> 1101101

3 -> 1111001

2.3 引脚说明

TM1637焊接了4个引脚:

  • GND:接地引脚
  • VCC:电源引脚,3.3V和5V都可以
  • DIO:数据输入输出引脚
  • CLK:时钟信号引脚

仔细看背面的电路线,发现内部有12个针脚!

前面我们已经知道:一个LED灯有两个引脚,正极针脚和负极针脚,一个7段数码管就需要14个针脚,那TM1637模块有4个7段的数码管,为什么只有12个针脚了?

这里我们用到两种技术:共阳/共阴动态扫描显示

2.4 共阳/共阴

LED灯只要有电压差就会被点亮,我们可以把所有LED灯的正极都联通在一起,只控制各段发光二极管的负极通电即可,这种叫做共阳 。反之亦然,叫做共阴。这样一个7段的数码管就只需要引出8个引脚就行了,1个阳(阴)级接到树莓派vcc(gnd)上,另外7个分别连到gpio口上,通过控制io口高低电平即可显示所需数字。比如说数字1,我们只需要点亮B段和C段就行了,其他全灭。那么连到共阳端引脚的io口输出高电平,连到引脚b、c的io口输出低电平,连到引脚a、d、e、f、g、dp的io口均输出高电平即可。

照上面的算法,我们需要一个共阳引脚,7个控制引脚,至少需要7*4+1=29个,数量还是对不上!再看下面的"动态扫描显示"技术。

2.5 动态扫描显示

先看看什么叫做静态显示?

静态显示,就是前面说的每一个数字需要占用8个io口,每多一个数字就需要额外的8个io口,如果数字位数不多,io口够用的话,这样做完全没问题。实际应用中往往需要显示多个数字,io口基本上是不够用的。这就需要动态扫描显示了。

再了解什么叫做动态显示?

动态驱动是将所有数码管的7个显示笔划"a,b,c,d,e,f,g"的同名端连在一起引出7个引脚,每个数字再单独引出共阳(阴)端,这样总引脚数就只要7 + 数字个数即可。当树莓派输出7个段信号时,所有数码管都会接收到相同的信号,但究竟是哪个数码管会显示出字形,取决于这个数码管对应的共阳(阴)极(后统称位选端)有无导通。所以我们只要将需要显示的数码管的位选端选通,该位就显示出字形,没有选通的数码管就不会亮。通过分时轮流控制各个数码管的的位选端,就使各个数码管轮流受控显示,这就是动态驱动。在轮流显示过程中,每位数码管的点亮时间为1~2ms,由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位数码管并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感,动态显示的效果和静态显示是一样的,能够节省大量的I/O端口,而且功耗更低。

所以接线图就是下面这个样子:

  • DIG1~DIG4:代表4个数码管
  • 一共有12个针脚。每个数码管由7段组成,每一段都是一个发光二极管。
  • A-G 这7个针脚对应了数码管的7段。
  • DP针脚对应的是中间的冒号。

A、B、C、D、E、F、G、DP就是8个针脚,再加上4个数字单独的针脚,就是12个了。

三、官方文档

微处理器怎么用两个管脚和12个针脚通信的?参考官方提供的文档

硬件接线图

显示寄存器

接口说明

微处理器的数据通过两线总线接口和 TM1637 通信,在输入数据时当 CLK 是高电平时,DIO 上的信号必须保持不变;只有 CLK 上的时钟信号为低电平时,DIO 上的信号才能改变。数据输入的开始条件是 CLK 为高电平时,DIO 由高变低;结束条件是 CLK 为高时,DIO 由低电平变为高电平。

TM1637 的数据传输带有应答信号 ACK,当传输数据正确时,会在第八个时钟的下降沿,芯片内部会产生一个应答信号 ACK 将 DIO 管脚拉低,在第九个时钟结束之后释放 DIO 口线。

数据指令

指令用来设置显示模式和LED 驱动器的状态。

在CLK下降沿后由DIO输入的第一个字节作为一条指令。经过译码,取最高B7、B6两位比特位以区别不同的指令。

流程图

总结

上面有点复杂,下面是提炼的信息:

  • 通信是8位二进制数据
  • 显示协议为:1.发送写显存数据命令 --> 2.发送设置地址命令 --> 3.传输段数据 --> 4.控制显示命令
  • 传输的开始和结束标记
    • start :CLK 为1, DIO从1变为0
    • stop :CLK为1, DIO从0变为1
  • CLK作为时钟总线,通过高低电平控制时钟频率
  • 传输数据时,DIO数据在CLK高电平变化,CLK低电平时被传输
  • DIO上传输数据正常的话,第8个时钟下降沿会收到ACK, DIO会被拉为低电平

四、驱动程序

根据上面的技术文档,具体的驱动脚本如下,80行的代码,也没那么难:

复制代码
from RPi import GPIO
from time import time, sleep

class TM1637(object):
    I2C_COMM1 = 0x40  # 写显存数据命令: 0b01000000
    I2C_COMM2 = 0xC0  # 设置地址命令:0b11000000
    I2C_COMM3 = 0x88  # 控制显示命令:0b10001000

    def __init__(self, clk, dio, brightness=1.0):
        self.clk_pin = clk  # 时钟信号引脚
        self.dio_pin = dio  # 数据输入输出引脚
        self.brightness = brightness  # 明亮度
        # 引脚初始化
        GPIO.setup(self.clk_pin, GPIO.OUT)
        GPIO.setup(self.dio_pin, GPIO.OUT)

    def set_segments(self, segments, pos=0):
        self.start()
        self.write_byte(self.I2C_COMM1)  # 写入命令1:写显存数据
        self.br()
        self.write_byte(self.I2C_COMM2 | pos)  # 写入命令2:设置地址
        for seg in segments:
            self.write_byte(seg)
        self.br()
        self.write_byte(self.I2C_COMM3 + int(self.brightness))  # 写入命令3:显示控制,明暗度
        self.stop()

    def start(self):
        """ 开始条件:待确认 """
        GPIO.output(self.clk_pin, GPIO.HIGH)
        GPIO.output(self.dio_pin, GPIO.HIGH)
        GPIO.output(self.dio_pin, GPIO.LOW)
        GPIO.output(self.clk_pin, GPIO.LOW)

    def stop(self):
        """ 结束条件:clk为高电位,dio由低电位变为高电位 """
        GPIO.output(self.clk_pin, GPIO.LOW)
        GPIO.output(self.dio_pin, GPIO.LOW)
        GPIO.output(self.clk_pin, GPIO.HIGH)
        GPIO.output(self.dio_pin, GPIO.HIGH)

    def br(self):
        """ 多条命令封装实现换行效果 """
        self.stop()
        self.start()

    def write_byte(self, b):
        # 写入二进制数据:8个bit
        for i in range(8):
            GPIO.output(self.clk_pin, GPIO.LOW)
            if b & 0x01:
                GPIO.output(self.dio_pin, GPIO.HIGH)
            else:
                GPIO.output(self.dio_pin, GPIO.LOW)
            b = b >> 1
            GPIO.output(self.clk_pin, GPIO.HIGH)

        # wait for ACK
        GPIO.output(self.clk_pin, GPIO.LOW)
        GPIO.output(self.dio_pin, GPIO.HIGH)
        GPIO.output(self.clk_pin, GPIO.HIGH)
        GPIO.setup(self.dio_pin, GPIO.IN)
        GPIO.setup(self.dio_pin, GPIO.OUT)

    def show(self, colon=False):
        segments = [63,63,63,63]
        if colon:
            point_data = 0x80
        else:
            point_data = 0x00
        results = []
        for segment in segments:
            if segment == 0x7F:
                results.append(0)
            else:
                results.append(segment + point_data)
        self.set_segments(results)

完整功能参见github源码

https://github.com/tim2anna/micro-blue/blob/master/micro_blue/gpiozero_lib/tm1637.py

相关推荐
databook5 小时前
Manim实现闪光轨迹特效
后端·python·动效
Juchecar6 小时前
解惑:NumPy 中 ndarray.ndim 到底是什么?
python
用户8356290780516 小时前
Python 删除 Excel 工作表中的空白行列
后端·python
Json_6 小时前
使用python-fastApi框架开发一个学校宿舍管理系统-前后端分离项目
后端·python·fastapi
数据智能老司机13 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机14 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机14 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机14 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i14 小时前
drf初步梳理
python·django
每日AI新事件14 小时前
python的异步函数
python