找回密码
 立即注册

QQ登录

树莓派GPIO入门第八课 SPI数码管显示

来源: 奥松-Neil 2016-8-25 11:07 显示全部楼层 |阅读模式
如果使用树莓派直接去驱动一个数码管来显示数字,这样做会有两个缺点
1. 占用大量的 GPIO 接口,例如驱动一个八位数码管需要占用 8 个 GPIO 接口,如果要驱动两个就要占用 16 个 GPIO 接口。
2.系统资源占用过多。还是在上一篇文章里,为了实现数码管的动态扫描显示,需要不停地高速操作IO口,这对单任务的单片机比如51单片机来说问题并不太大,因为单任务的单片机的执行时序是由晶振来决定的,动态扫描的时间间隔可以控制地非常精确,数码管的显示会很稳定,不会出现闪烁的现象。但对于运行着多任务的Linux操作系统的树莓派来说,对IO口的大量反复操作以及大量使用sleep语句会导致CPU占用过多,最后导致动态扫描的间隔时间不均匀,体现为数码管显示不稳定,有明显闪烁的现象。
使用GPIO直接驱动数码管时,上面这两个问题是不可避免的。想解决这两个问题,我们需要借助外部芯片来解决问题。
这一篇文章中,我们使用一款 SPI 数码管,模块使用了显示驱动芯片 74HC595 来驱动数码管的方法。
一. 实验硬件
1. SPI LED 数码管模块 * 1 个
2. 杜邦线 * 若干
二. 硬件接线
QQ图片20160313202836.png


(1)SPI 模块 D(DATA / 74HC595 DS 引脚)连接树莓派 GPIO15 引脚
(2)SPI 模块 L (LATCH / 74HC595 STCP 引脚)连接树莓派 GPIO14 引脚
(3)SPI 模块 C (CLOCK / 74HC595 SHCP 引脚)连接树莓派 GPIO18 引脚
(4)SPI 模块 + (电源正)连接树莓派 VCC(+5V)
(5)SPI 模块 - (电源负)连接树莓派 GND
QQ图片20160313201020.png

三. 芯片简介
首先,看一下 74HC595 的芯片引脚图
QQ图片20160313202103.png

引脚介绍:
(1)GND接地,VCC接5V电源
(2)Q0-Q7 这8根引脚是芯片的输出引脚,直接跟数码管的8段引脚相连。
(3)DS是串行输入引脚,所谓串行就是使数据在一根信号线上按顺序一位一位地传输。
(4)SHCP是移位寄存器的时钟引脚。74HC595 内部有一个 8 位的移位寄存器用来保存从 DS 引脚输入的数据。那么 74HC595 怎么知道什么时候该从 DS 引脚上取数据了呢?正是通过SHCP 这个时钟引脚来实现的。只有在 SHCP 发生一次上升沿的时候,74HC595 才会从 DS 引脚上取得当前的数据(高/低电平)并把取到的这一位数据保存到移位寄存器里。同样的这个引脚也接到树莓派任意一个 GPIO 口上。当我们向芯片发送数据时,要先在DS引脚上准备好要传送的数据,然后制造一次 SHCP 引脚的上升沿(先拉低电平再拉高电平),74HC595 会在这个上升沿将 DS 引脚上的数据存入移位寄存器 D0,同时 D0 原来的数据会顺移到 D1,D1 的数据位移到 D2。。。D6 的数据位移到 D7。而原先 D7 的数据已经没有地方储存了,这一位数据会被输出到引脚Q7S上。
(5)STCP 是芯片内部另外一个 8 位锁存寄存器的时钟引脚。当移位寄存器的 8 位数据全部传输完毕后,制造一次锁存器时钟引脚的上升沿(先拉低电平再拉高电平)。74HC595会在这个上升沿将移位寄存器里的8位数据复制到锁存器中(锁存器里原来的数据将被替换)。注意,到这里为止,这8位数据还只是被保存在锁存器里,并没有输出到数码管上。这个引脚同样连接到树莓派任意一个GPIO口上即可。
(6)OE 是输出使能引脚,作用是控制锁存器里的数据是否最终输出到 Q0-Q7 输出引脚上。低电平时输出,高电平时不输出(既不是高电平,也不是低电平而是高阻态,不通电)
(7)MR是用来重置内部寄存器的引脚。低电平时重置内部寄存器。
(8)Q7S引脚,串行输出引脚,将Q7S引脚连接上另一块74HC595的DS引脚并保证两块芯片上的移位时钟的上升沿同时发生(将两块芯片的SHCP连在一起即可)的话,第一块74HC595的串行输出引脚Q7S就变成了第二块74HC595的串行输入数据源。Q7S上的数据会继续移位到第二块74HC595芯片的移位寄存器D0里(同样的,D0→D1→D2→…→D6→D7→Q7S)。
       关于锁存器。就是将数据保存并锁定。一旦进入了锁存器,除非断电或重置数据(MR 口设置为低电平),锁存器的数据不会再改变。好处是,当你需要更新数据时,将数据串行输入移位寄存器的过程中,锁存器里的数据不会有任何影响,也就不会有闪烁了。一直到移位寄存器8位数据准备完毕,再制造一次STCP的上升沿一次性更新锁存器的数据,更新输出。
四. 实验代码
  1. import RPi.GPIO
  2. import time
  3. # 串行数据输入引脚连接的GPIO口
  4. DS = 15
  5. # 移位寄存器时钟控制引脚连接的GPIO口——上升沿有效
  6. SHCP = 18
  7. # 数据锁存器时钟控制引脚连接的GPIO口——上升沿有效
  8. STCP = 14
  9. RPi.GPIO.setmode(RPi.GPIO.BCM)

  10. RPi.GPIO.setup(DS, RPi.GPIO.OUT)
  11. RPi.GPIO.setup(STCP, RPi.GPIO.OUT)
  12. RPi.GPIO.setup(SHCP, RPi.GPIO.OUT)

  13. RPi.GPIO.output(STCP, False)
  14. RPi.GPIO.output(SHCP, False)

  15. # 通过串行数据引脚向74HC595的传送一位数据
  16. def setBitData(data):
  17.         # 准备好要传送的数据
  18.         RPi.GPIO.output(DS, data)
  19.         # 制造一次移位寄存器时钟引脚的上升沿(先拉低电平再拉高电平)
  20.         # 74HC595会在这个上升沿将DS引脚上的数据存入移位寄存器D0
  21.         # 同时D0原来的数据会顺移到D1,D1的数据位移到D2。。。D6的数据位移到D7
  22.         # 而D7的数据已经没有地方储存了,这一位数据会被输出到引脚Q7S上
  23.         # 如果Q7S引脚没有被使用,那么这一位的数据就被丢掉了。
  24.         # 而如果将Q7S引脚连接到另一块74HC595上的DS引脚,
  25.         # 那么这一位数据就会继续位移到第二块595芯片的位移寄存器里去。
  26.         # 这就是多块595芯片级联的原理。
  27.         RPi.GPIO.output(SHCP, False)
  28.         RPi.GPIO.output(SHCP, True)

  29. # 指定数码管显示数字num(0-9),第2个参数是显示不显示小数点(true/false)
  30. # 由于我使用的数码管是共阳数码管,所以设置为低电平的段才会被点亮
  31. # 如果你用的是共阴数码管,那么要将下面的True和False全部颠倒过来,或者统一在前面加上not
  32. def showDigit(num, showDotPoint):
  33.         
  34.         if (num == 0) :
  35.                 setBitData(not showDotPoint) # DP
  36.                 setBitData(True)  # G
  37.                 setBitData(False) # F
  38.                 setBitData(False) # E
  39.                 setBitData(False) # D
  40.                 setBitData(False) # C
  41.                 setBitData(False) # B
  42.                 setBitData(False) # A
  43.         elif (num == 1) :
  44.                 setBitData(not showDotPoint)
  45.                 setBitData(True)
  46.                 setBitData(True)
  47.                 setBitData(True)
  48.                 setBitData(True)
  49.                 setBitData(False)
  50.                 setBitData(False)
  51.                 setBitData(True)
  52.         elif (num == 2) :
  53.                 setBitData(not showDotPoint)
  54.                 setBitData(False)
  55.                 setBitData(True)
  56.                 setBitData(False)
  57.                 setBitData(False)
  58.                 setBitData(True)
  59.                 setBitData(False)
  60.                 setBitData(False)
  61.         elif (num == 3) :
  62.                 setBitData(not showDotPoint)
  63.                 setBitData(False)
  64.                 setBitData(True)
  65.                 setBitData(True)
  66.                 setBitData(False)
  67.                 setBitData(False)
  68.                 setBitData(False)
  69.                 setBitData(False)
  70.         elif (num == 4) :
  71.                 setBitData(not showDotPoint)
  72.                 setBitData(False)
  73.                 setBitData(False)
  74.                 setBitData(True)
  75.                 setBitData(True)
  76.                 setBitData(False)
  77.                 setBitData(False)
  78.                 setBitData(True)
  79.         elif (num == 5) :
  80.                 setBitData(not showDotPoint)
  81.                 setBitData(False)
  82.                 setBitData(False)
  83.                 setBitData(True)
  84.                 setBitData(False)
  85.                 setBitData(False)
  86.                 setBitData(True)
  87.                 setBitData(False)
  88.         elif (num == 6) :
  89.                 setBitData(not showDotPoint)
  90.                 setBitData(False)
  91.                 setBitData(False)
  92.                 setBitData(False)
  93.                 setBitData(False)
  94.                 setBitData(False)
  95.                 setBitData(True)
  96.                 setBitData(False)
  97.         elif (num == 7) :
  98.                 setBitData(not showDotPoint)
  99.                 setBitData(True)
  100.                 setBitData(True)
  101.                 setBitData(True)
  102.                 setBitData(True)
  103.                 setBitData(False)
  104.                 setBitData(False)
  105.                 setBitData(False)
  106.         elif (num == 8) :
  107.                 setBitData(not showDotPoint)
  108.                 setBitData(False)
  109.                 setBitData(False)
  110.                 setBitData(False)
  111.                 setBitData(False)
  112.                 setBitData(False)
  113.                 setBitData(False)
  114.                 setBitData(False)
  115.         elif (num == 9) :
  116.                 setBitData(not showDotPoint)
  117.                 setBitData(False)
  118.                 setBitData(False)
  119.                 setBitData(True)
  120.                 setBitData(False)
  121.                 setBitData(False)
  122.                 setBitData(False)
  123.                 setBitData(False)

  124.         # 移位寄存器的8位数据全部传输完毕后,制造一次锁存器时钟引脚的上升沿(先拉低电平再拉高电平)
  125.         # 74HC595会在这个上升沿将移位寄存器里的8位数据复制到8位的锁存器中(锁存器里原来的数据将被替换)
  126.         # 到这里为止,这8位数据还只是被保存在锁存器里,并没有输出到数码管上。
  127.         # 决定锁存器里的数据是否输出是由“输出使能端口”OE决定的。当OE设置为低电平时,锁存器里数据才会被输出到Q0-Q7这8个输出引脚上。
  128.         # 在我的硬件连接里,OE直接连接在了GND上,总是保持低电平,所以移位寄存器的数据一旦通过时钟上升沿进入锁存器,也就相当于输出到LED上了。
  129.         RPi.GPIO.output(STCP, True)
  130.         RPi.GPIO.output(STCP, False)

  131. try:
  132.         # 测试代码
  133.         # 从1显示到8,不显示小数点
  134.         for x in range(0,9):
  135.                 showDigit(x, False)
  136.                 time.sleep(0.002)

  137.         # 再从1显示到8,显示小数点
  138.         for y in range(0,9):
  139.                 showDigit(y, True)
  140.                 time.sleep(0.002)
  141.                                        
  142. except KeyboardInterrupt:
  143.         pass

  144. # 最后清理GPIO口
  145. # 清理了IO是将所有使用中的IO口释放,并全部设置为输入模式
  146. # 你会发现最后设置的数据在清理了IO口以后还会继续正常显示
  147. # 这是因为数据一旦存入锁存器,除非断电或重置数据(MR口设置为低电平),
  148. # 否则最后设置的数据会一直保留在74HC595芯片中。也就是被“锁存”了。
  149. RPi.GPIO.cleanup()<font size="3" color="#000000">
  150. </font>
复制代码
五. 实验效果
将代码输入到树莓派控制器,或者直接下载附件中的 Python 程序,在树莓派中使用以下语句执行:
  1. sudo python 7.py
复制代码
正确连线并执行程序后,SPI 数码管上会显示出 1 - 8 的数字。
六. 附件
7.py (4.96 KB, 下载次数: 7)
回复

使用道具 举报

大神点评21

huangyilele 2016-10-16 22:03 显示全部楼层
shumaguanxiansshi
回复 支持 反对

使用道具 举报

zhouran2016 2016-10-26 08:11 显示全部楼层
:D:D:D:D:D:D:D:D:D:D:D:D:D:D
回复 支持 反对

使用道具 举报

風雨谷 2016-11-11 23:38 显示全部楼层
dtddfnfdgndfndrnrf
回复 支持 反对

使用道具 举报

天威546 2016-11-12 07:16 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

TaoTaoCome 2016-11-15 20:00 显示全部楼层
灌灌灌灌灌灌灌灌灌灌
回复 支持 反对

使用道具 举报

leleluo123 2016-12-13 10:00 显示全部楼层
SFSDFSDFDSFFDSFSDFSDFDF
回复 支持 反对

使用道具 举报

whzzr 2017-1-5 15:07 显示全部楼层
看看。。。。。。。。。。。
回复

使用道具 举报

mquark 2017-1-8 21:25 显示全部楼层
继续学习
回复

使用道具 举报

666666666666666
回复 支持 反对

使用道具 举报

您可能感兴趣的文章

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

关注0

粉丝9

帖子147

发布主题
社区热门 MORE+
    社区热帖 MORE+