应用树莓派——使用逐行扫描驱动点阵屏

52 Views 

前言

本文章用到的设备:树莓派、杜邦线、面包板、220Ω电阻(8个)、8*8点阵屏(1088BS)。

我们看视频的时候,经常会说1080p,720p。这个“p”是什么意思呢?它的意思为:逐行(Progressive)扫描(Scanning)中的“逐行”。与之对应的是隔行(Interlace)扫描( Scanning )。为什么会有这两种扫描技术呢?

我们知道,一块屏幕由无数个像素点组成。以我们最常见的1920*1080的屏幕来说,它就有2,073,600‬个像素点。要显示内容的话,最理想的方式当然是每一个像素点对应一个独立的开关,这样反应灵敏、延迟低。但在实际中,这是不可能实现的——几百万个像素点,就意味着有几百万根线,根本无法在一小块PCB上排布。退而求其次的方法就是,每一行共享一个高电平(或低电平),每一列共享一个低电平(或高电平),这样可以显著的减少所需的连线。为了能够精确的控制每一个像素点的亮灭,每一个瞬间显然只能有一一行与一列被接通,这样他们相交的像素点就被点亮。 这种通过遍历行与列的显示方法,叫做扫描。由于人眼的视觉暂留效果,只要在足够短的时间内扫描完所有像素点,就能够显示完整的画面。

那么为什么会有逐行扫描与隔行扫描之分呢?原来在电视机刚刚诞生的时候,显像管的扫描速度不够快,屏幕的最上和最下存在着不小的时间差。如果使用逐行扫描的话,用户能看到屏幕上有一暗带,并且不断上下移动。隔行扫描就能解决这个问题。除此之外,隔行扫描还能够使得所需带宽减半(因为所需的输入线路减半)。当然它也有缺点。由于由于每一帧只刷新一半的行,人眼能看到明显的虚像,这在快速移动的画面中尤为明显。

后来随着显示技术的逐渐提高,逐行扫描基本取代了隔行扫描。

1088BS介绍

1088BS是一个共阳的8*8点阵LED显示屏,其规格如下:

引脚定义如下:

组装电路

本电路的原理非常简单,将所有引脚连接至GPIO接口,并且保证每一个正极(或负极)电路都串联一个220Ω的电阻即可。

由于1088BS的引脚都在背面,且其宽度无法在普通面包板上使用,我将其焊接到了万用板上:

同样我也将电阻焊在了一起:

最后连接起来效果如下:

编写代码

首先……点亮它!

在你连接电路的时候,会发现有的LED已经被点亮了。不要惊慌,这是正常的!

为了判断连接是否有误,首先运行如下程序:

#!/usr/bin/python
#上一行用于在terminal中执行文件
import RPi.GPIO as GPIO
import time

C = [2,3,4,17,27,22,10,9]  #列对应的GPIO编号,可以使用数组形式
R = [14,15,18,23,24,25,8,7]

GPIO.setmode(GPIO.BCM)
GPIO.setup(C,GPIO.OUT)
GPIO.setup(R,GPIO.OUT)

GPIO.output(C,GPIO.HIGH)
GPIO.output(R,GPIO.LOW)

time.sleep(1)
GPIO.cleanup()

效果如下:

如果有某一行或列没有亮,请检查其接线情况。

逐行扫描

逐行扫描技术,就是首先遍历所有行,在每行中又遍历所有的列。在任意一个时间点,都只有一行与一列是导通状态。

#!/usr/bin/python
import RPi.GPIO as GPIO
import time

C = [2,3,4,17,27,22,10,9]
R = [14,15,18,23,24,25,8,7]
SCAN_INTERVAL =  0.0001 #扫描间隔,添加此代码以防止CPU占用过高,同时也可以观察在不同扫描速度下的视觉表现

GPIO.setmode(GPIO.BCM)
GPIO.setup(C,GPIO.OUT)
GPIO.setup(R,GPIO.OUT)
GPIO.output(C,GPIO.LOW)
GPIO.output(R,GPIO.LOW)

def main():
  while True:
    for i in range(0,8):
      GPIO.output(R,GPIO.HIGH) #除了遍历行/列之外的均反接,使得二极管不导通。
      GPIO.output(R[i],GPIO.LOW)
      for j in range(0,8):
        GPIO.output(C,GPIO.LOW)
        GPIO.output(C[j],GPIO.HIGH)
        time.sleep(SCAN_INTERVAL)

if __name__ == '__main__':
  try:
    main()
  except KeyboardInterrupt:
    pass
  finally:
    GPIO.cleanup()

代码运行后,每个LED的亮度较上个代码稍暗,但视觉效果仍为全亮。

如果将间隔调为0.001,可以明显看到频闪;将间隔调为0.01,可以看到逐行扫描的过程。

显示内容

定义一个二维数组用于存储图像,1表示导通。只要在上述代码中加入判断程序即可。

#!/usr/bin/python
import RPi.GPIO as GPIO
import time

C = [2,3,4,17,27,22,10,9]
R = [14,15,18,23,24,25,8,7]
SCAN_INTERVAL =  0.00001

image = [ #这是一个心形
  [0,0,0,0,0,0,0,0],
  [0,1,1,0,0,1,1,0],
  [1,0,0,1,1,0,0,1],
  [1,0,0,0,0,0,0,1],
  [0,1,0,0,0,0,1,0],
  [0,0,1,0,0,1,0,0],
  [0,0,0,1,1,0,0,0],
  [0,0,0,0,0,0,0,0]
]

GPIO.setmode(GPIO.BCM)
GPIO.setup(C,GPIO.OUT)
GPIO.setup(R,GPIO.OUT)
GPIO.output(C,GPIO.LOW)
GPIO.output(R,GPIO.LOW)

def main():
  while True:
    for i in range(0,8):
      GPIO.output(R,GPIO.HIGH)
      GPIO.output(R[i],GPIO.LOW)
      for j in range(0,8):
        GPIO.output(C,GPIO.LOW)
        if image[i][j] == 1:
          GPIO.output(C[j],GPIO.HIGH)
        time.sleep(SCAN_INTERVAL)

if __name__ == '__main__':
  try:
    main()
  except KeyboardInterrupt:
    pass
  finally:
    GPIO.cleanup()

显示效果如下:

相关推荐

发表评论

电子邮件地址不会被公开。 必填项已用*标注

   
隐藏