当前位置: 首页 > article >正文

【微蓝课堂】机器人编程|树莓派系列|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


http://www.kler.cn/a/407970.html

相关文章:

  • IDEA配置本地maven
  • React第六节 组件属性prop的propTypes类型使用介绍
  • Linux基本指令的使用
  • Thymeleaf模板引擎生成的html字符串转换成pdf
  • 安宝特分享 | 如何利用AR技术革新医疗实践:从远程急救到多学科协作
  • android MQTT使用示例
  • 蓝桥杯疑似例题解答方案(打印任意阶杨辉三角)
  • ubuntu 交叉编译arm架构的mysql
  • php常用伪协议整理
  • 从〇开始深度学习(0)——背景知识与环境配置
  • Spring框架深度剖析:特性、安全与优化
  • [代码随想录Day21打卡] 669. 修剪二叉搜索树 108.将有序数组转换为二叉搜索树 538.把二叉搜索树转换为累加树 总结篇
  • tomcat 后台部署 war 包 getshell
  • IntelliJ+SpringBoot项目实战(十)--常量类、自定义错误页、全局异常处理
  • 3D超声重建技术
  • C#里怎么样检测文件的属性?
  • 《Spring Boot:快速构建应用的利器》
  • 【青牛科技】 GC2803:白色家电与安防领域的卓越驱动芯片可替代ULN2803
  • stm32如何接收舵机的控制信号(而不是控制舵机)
  • 【AI】kimi深度版的荣誉之战(fail)
  • 软件测试面试之重要的名词解释
  • 大模型上层Agent系统思考
  • Perforce《2024游戏技术现状报告》Part3:生成式AI、版本控制、CI/CD等游戏技术的未来趋势与应用
  • FreeSWITCH 简单图形化界面34 - 网络环境安全的情况下,进行任意SIP注册
  • 案例研究|阿特斯的JumpServer分布式部署和多组织管理实践
  • 摄像机视频分析软件下载LiteAIServer视频智能分析平台玩手机打电话检测算法技术的实现