树莓派GPIO入门05-驱动数码管显示数字http://blog.mangolovecarrot.net/2015/05/05/raspi-study05/
这次我们用树莓派的GPIO口驱动数码管来显示数字,进而制作一个简单的电子钟,通过按钮来切换显示时间或日期。
# 定义各段发光二极管对应的GPIO口
LED_A = 26
LED_B = 19
LED_C = 13
LED_D = 6
LED_E = 5
LED_F = 11
LED_G = 9
LED_DP = 10
# 定义数码管共阳极对应的GPIO口
VCC = 12
# 避免闪烁,在输出数字字形信号前先拉低共阳端,关闭显示
RPi.GPIO.output(VCC, False)
# 输出数字1的字形信号
RPi.GPIO.output(LED_A, True)
RPi.GPIO.output(LED_B, False)
RPi.GPIO.output(LED_C, False)
RPi.GPIO.output(LED_D, True)
RPi.GPIO.output(LED_E, True)
RPi.GPIO.output(LED_F, True)
RPi.GPIO.output(LED_G, True)
RPi.GPIO.output(LED_DP, True)
# 最后拉高共阳段,显示数字
RPi.GPIO.output(VCC, True)
数码管动态显示接口是单片机中应用最为广泛的一种显示方式之一,动态驱动是将所有数码管的8个显示笔划"a,b,c,d,e,f,g,dp"的同名端连在一起引出8个引脚,每个数字再单独引出共阳(阴)端,这样总引脚数就只要8 + 数字个数即可,本文使用的8段4位数码管正是引出了12个引脚。至于哪个引脚对应哪一段,哪几个引脚分别对应各数字的共阳(阴)端,就需要商家提供电路图了。当然也可以自己慢慢试,这不在本文讨论范围,大家可以自己摸索。当树莓派输出8个段信号时,所有数码管都会接收到相同的信号,但究竟是哪个数码管会显示出字形,取决于这个数码管对应的共阳(阴)极(后统称位选端)有无导通。所以我们只要将需要显示的数码管的位选端选通,该位就显示出字形,没有选通的数码管就不会亮。通过分时轮流控制各个数码管的的位选端,就使各个数码管轮流受控显示,这就是动态驱动。在轮流显示过程中,每位数码管的点亮时间为1~2ms,由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位数码管并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感,动态显示的效果和静态显示是一样的,能够节省大量的I/O端口,而且功耗更低。
#!/usr/bin/env python
# encoding: utf-8
import RPi.GPIO
import time
# 定义单个数码管各段led对应的GPIO口
LED_A = 26
LED_B = 19
LED_C = 13
LED_D = 6
LED_E = 5
LED_F = 11
LED_G = 9
LED_DP = 10
# 定义1到4号数码管阳极对应的GPIO口
DIGIT1 = 12
DIGIT2 = 16
DIGIT3 = 20
DIGIT4 = 21
# 定义按钮输入的GPIO口
btn = 27
RPi.GPIO.setmode(RPi.GPIO.BCM)
RPi.GPIO.setup(LED_A, RPi.GPIO.OUT)
RPi.GPIO.setup(LED_B, RPi.GPIO.OUT)
RPi.GPIO.setup(LED_C, RPi.GPIO.OUT)
RPi.GPIO.setup(LED_D, RPi.GPIO.OUT)
RPi.GPIO.setup(LED_E, RPi.GPIO.OUT)
RPi.GPIO.setup(LED_F, RPi.GPIO.OUT)
RPi.GPIO.setup(LED_G, RPi.GPIO.OUT)
RPi.GPIO.setup(LED_DP, RPi.GPIO.OUT)
RPi.GPIO.setup(DIGIT1, RPi.GPIO.OUT)
RPi.GPIO.setup(DIGIT2, RPi.GPIO.OUT)
RPi.GPIO.setup(DIGIT3, RPi.GPIO.OUT)
RPi.GPIO.setup(DIGIT4, RPi.GPIO.OUT)
RPi.GPIO.output(DIGIT1, True)
RPi.GPIO.output(DIGIT2, True)
RPi.GPIO.output(DIGIT3, True)
RPi.GPIO.output(DIGIT4, True)
RPi.GPIO.setup(btn, RPi.GPIO.IN, pull_up_down=RPi.GPIO.PUD_UP)
# 指定no(1-4)号数码管显示数字num(0-9),第三个参数是显示不显示小数点(true/false)
def showDigit(no, num, showDotPoint):
# 先将正极拉低,关掉显示
RPi.GPIO.output(DIGIT1, False)
RPi.GPIO.output(DIGIT2, False)
RPi.GPIO.output(DIGIT3, False)
RPi.GPIO.output(DIGIT4, False)
if (num == 0) :
RPi.GPIO.output(LED_A, False)
RPi.GPIO.output(LED_B, False)
RPi.GPIO.output(LED_C, False)
RPi.GPIO.output(LED_D, False)
RPi.GPIO.output(LED_E, False)
RPi.GPIO.output(LED_F, False)
RPi.GPIO.output(LED_G, True)
RPi.GPIO.output(LED_DP, not showDotPoint)
elif (num == 1) :
RPi.GPIO.output(LED_A, True)
RPi.GPIO.output(LED_B, False)
RPi.GPIO.output(LED_C, False)
RPi.GPIO.output(LED_D, True)
RPi.GPIO.output(LED_E, True)
RPi.GPIO.output(LED_F, True)
RPi.GPIO.output(LED_G, True)
RPi.GPIO.output(LED_DP, not showDotPoint)
elif (num == 2) :
RPi.GPIO.output(LED_A, False)
RPi.GPIO.output(LED_B, False)
RPi.GPIO.output(LED_C, True)
RPi.GPIO.output(LED_D, False)
RPi.GPIO.output(LED_E, False)
RPi.GPIO.output(LED_F, True)
RPi.GPIO.output(LED_G, False)
RPi.GPIO.output(LED_DP, not showDotPoint)
elif (num == 3) :
RPi.GPIO.output(LED_A, False)
RPi.GPIO.output(LED_B, False)
RPi.GPIO.output(LED_C, False)
RPi.GPIO.output(LED_D, False)
RPi.GPIO.output(LED_E, True)
RPi.GPIO.output(LED_F, True)
RPi.GPIO.output(LED_G, False)
RPi.GPIO.output(LED_DP, not showDotPoint)
elif (num == 4) :
RPi.GPIO.output(LED_A, True)
RPi.GPIO.output(LED_B, False)
RPi.GPIO.output(LED_C, False)
RPi.GPIO.output(LED_D, True)
RPi.GPIO.output(LED_E, True)
RPi.GPIO.output(LED_F, False)
RPi.GPIO.output(LED_G, False)
RPi.GPIO.output(LED_DP, not showDotPoint)
elif (num == 5) :
RPi.GPIO.output(LED_A, False)
RPi.GPIO.output(LED_B, True)
RPi.GPIO.output(LED_C, False)
RPi.GPIO.output(LED_D, False)
RPi.GPIO.output(LED_E, True)
RPi.GPIO.output(LED_F, False)
RPi.GPIO.output(LED_G, False)
RPi.GPIO.output(LED_DP, not showDotPoint)
elif (num == 6) :
RPi.GPIO.output(LED_A, False)
RPi.GPIO.output(LED_B, True)
RPi.GPIO.output(LED_C, False)
RPi.GPIO.output(LED_D, False)
RPi.GPIO.output(LED_E, False)
RPi.GPIO.output(LED_F, False)
RPi.GPIO.output(LED_G, False)
RPi.GPIO.output(LED_DP, not showDotPoint)
elif (num == 7) :
RPi.GPIO.output(LED_A, False)
RPi.GPIO.output(LED_B, False)
RPi.GPIO.output(LED_C, False)
RPi.GPIO.output(LED_D, True)
RPi.GPIO.output(LED_E, True)
RPi.GPIO.output(LED_F, True)
RPi.GPIO.output(LED_G, True)
RPi.GPIO.output(LED_DP, not showDotPoint)
elif (num == 8) :
RPi.GPIO.output(LED_A, False)
RPi.GPIO.output(LED_B, False)
RPi.GPIO.output(LED_C, False)
RPi.GPIO.output(LED_D, False)
RPi.GPIO.output(LED_E, False)
RPi.GPIO.output(LED_F, False)
RPi.GPIO.output(LED_G, False)
RPi.GPIO.output(LED_DP, not showDotPoint)
elif (num == 9) :
RPi.GPIO.output(LED_A, False)
RPi.GPIO.output(LED_B, False)
RPi.GPIO.output(LED_C, False)
RPi.GPIO.output(LED_D, False)
RPi.GPIO.output(LED_E, True)
RPi.GPIO.output(LED_F, False)
RPi.GPIO.output(LED_G, False)
RPi.GPIO.output(LED_DP, not showDotPoint)
if (no == 1) :
RPi.GPIO.output(DIGIT1, True)
elif (no == 2) :
RPi.GPIO.output(DIGIT2, True)
elif (no == 3) :
RPi.GPIO.output(DIGIT3, True)
elif (no == 4) :
RPi.GPIO.output(DIGIT4, True)
try:
t=0.005
while True:
# 按钮按下时显示日期,否则显示时间
# 为了区别左右的数字,让第二个数码管的小数点显示出来
#(本来应该是一个冒号,我们这个数码管没有,就用小数点代替了)
if (RPi.GPIO.input(btn) == 1):
time.sleep(t)
showDigit(1, int(time.strftime("%H",time.localtime(time.time()))) / 10, False)
time.sleep(t)
showDigit(2, int(time.strftime("%H",time.localtime(time.time()))) % 10, True)
time.sleep(t)
showDigit(3, int(time.strftime("%M",time.localtime(time.time()))) / 10, False)
time.sleep(t)
showDigit(4, int(time.strftime("%M",time.localtime(time.time()))) % 10, False)
else:
time.sleep(t)
showDigit(1, int(time.strftime("%m",time.localtime(time.time()))) / 10, False)
time.sleep(t)
showDigit(2, int(time.strftime("%m",time.localtime(time.time()))) % 10, True)
time.sleep(t)
showDigit(3, int(time.strftime("%d",time.localtime(time.time()))) / 10, False)
time.sleep(t)
showDigit(4, int(time.strftime("%d",time.localtime(time.time()))) % 10, False)
except KeyboardInterrupt:
pass
# 最后清理GPIO口(不做也可以,建议每次程序结束时清理一下,好习惯)
RPi.GPIO.cleanup()
当你照着这篇文章成功在数码管上显示出数字后,你可能会郁闷的发现数字有一点点闪烁,显示的不是非常稳定,这种情况在树莓派1代上更明显。
#include
#include
#include
// 定义单个数码管各段led对应的GPIO口
// 使用命令 "gpio readall" 来获取当前pi版本对应的各引脚的wiringPi和BCM的编号
// 再本程序中应该使用wiringPi编号
// 我的pi2 Mode B执行结果如下:(wPi列就是wiringPi编号)
// 之前Python版本的代码使用的是BCM编号,所以在不改变硬件接线的情况下,我们需要把原来BCM编号改成对应的wiringPi编号。
/*
+-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+
| BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM |
+-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
| | | 3.3v | | | 1 || 2 | | | 5v | | |
| 2 | 8 | SDA.1 | IN | 1 | 3 || 4 | | | 5V | | |
| 3 | 9 | SCL.1 | IN | 1 | 5 || 6 | | | 0v | | |
| 4 | 7 | GPIO. 7 | IN | 1 | 7 || 8 | 1 | ALT0 | TxD | 15 | 14 |
| | | 0v | | | 9 || 10 | 1 | ALT0 | RxD | 16 | 15 |
| 17 | 0 | GPIO. 0 | OUT | 0 | 11 || 12 | 0 | IN | GPIO. 1 | 1 | 18 |
| 27 | 2 | GPIO. 2 | IN | 0 | 13 || 14 | | | 0v | | |
| 22 | 3 | GPIO. 3 | IN | 0 | 15 || 16 | 0 | IN | GPIO. 4 | 4 | 23 |
| | | 3.3v | | | 17 || 18 | 0 | IN | GPIO. 5 | 5 | 24 |
| 10 | 12 | MOSI | IN | 0 | 19 || 20 | | | 0v | | |
| 9 | 13 | MISO | IN | 0 | 21 || 22 | 0 | IN | GPIO. 6 | 6 | 25 |
| 11 | 14 | SCLK | IN | 0 | 23 || 24 | 1 | IN | CE0 | 10 | 8 |
| | | 0v | | | 25 || 26 | 1 | IN | CE1 | 11 | 7 |
| 0 | 30 | SDA.0 | IN | 1 | 27 || 28 | 1 | IN | SCL.0 | 31 | 1 |
| 5 | 21 | GPIO.21 | IN | 1 | 29 || 30 | | | 0v | | |
| 6 | 22 | GPIO.22 | IN | 1 | 31 || 32 | 0 | IN | GPIO.26 | 26 | 12 |
| 13 | 23 | GPIO.23 | IN | 0 | 33 || 34 | | | 0v | | |
| 19 | 24 | GPIO.24 | IN | 0 | 35 || 36 | 0 | IN | GPIO.27 | 27 | 16 |
| 26 | 25 | GPIO.25 | IN | 0 | 37 || 38 | 0 | IN | GPIO.28 | 28 | 20 |
| | | 0v | | | 39 || 40 | 0 | IN | GPIO.29 | 29 | 21 |
+-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
| BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM |
+-----+-----+---------+------+---+---Pi 2---+---+------+---------+-----+-----+
*/
#define LED_A 25 //BCM:26
#define LED_B 24 //BCM:19
#define LED_C 23 //BCM:13
#define LED_D 22 //BCM:6
#define LED_E 21 //BCM:5
#define LED_F 14 //BCM:11
#define LED_G 13 //BCM:9
#define LED_DP 12 //BCM:10
// 定义1到4号数码管阳极对应的GPIO口
#define DIGIT1 26 //BCM:12
#define DIGIT2 27 //BCM:16
#define DIGIT3 28 //BCM:20
#define DIGIT4 29 //BCM:21
// 定义按钮输入的GPIO口
#define btn 2 //BCM:27
#define FALSE 0
#define TRUE 1
#define t 5000 //usleep延时长度(单位um微秒,1000um=1ms,1000ms=1s)
// 指定no(1-4)号数码管显示数字num(0-9),第三个参数是显示不显示小数点(1/0)
void showDigit(int no, int num, int showDotPoint);
time_t now;
struct tm *tm_now;
int main (void) {
wiringPiSetup () ;
pinMode (LED_A, OUTPUT) ;
pinMode (LED_B, OUTPUT) ;
pinMode (LED_C, OUTPUT) ;
pinMode (LED_D, OUTPUT) ;
pinMode (LED_E, OUTPUT) ;
pinMode (LED_F, OUTPUT) ;
pinMode (LED_G, OUTPUT) ;
pinMode (LED_DP, OUTPUT) ;
pinMode (DIGIT1, OUTPUT) ;
pinMode (DIGIT2, OUTPUT) ;
pinMode (DIGIT3, OUTPUT) ;
pinMode (DIGIT4, OUTPUT) ;
pinMode (btn, INPUT) ;
pullUpDnControl (btn, PUD_UP) ;
digitalWrite (DIGIT1, HIGH) ;
digitalWrite (DIGIT2, HIGH) ;
digitalWrite (DIGIT3, HIGH) ;
digitalWrite (DIGIT4, HIGH) ;
for (; ; )
{
time(&now);
tm_now=localtime(&now);
// 按钮按下时显示日期,否则显示时间
// 为了区别左右的数字,让第二个数码管的小数点显示出来
//(本来应该是一个冒号,我们这个数码管没有,就用小数点代替了)
if (digitalRead(btn) == HIGH) {
usleep(t);
showDigit(1, tm_now->tm_hour / 10, FALSE);
usleep(t);
showDigit(2, tm_now->tm_hour % 10, TRUE);
usleep(t);
showDigit(3, tm_now->tm_min / 10, FALSE);
usleep(t);
showDigit(4, tm_now->tm_min % 10, FALSE);
} else {
// 取得的月份和日期都是从0开始的,所以显示前需要加1
usleep(t);
showDigit(1, (tm_now->tm_mon+1) / 10, FALSE);
usleep(t);
showDigit(2, (tm_now->tm_mon+1) % 10, TRUE);
usleep(t);
showDigit(3, (tm_now->tm_mday+1) / 10, FALSE);
usleep(t);
showDigit(4, (tm_now->tm_mday+1) % 10, FALSE);
}
}
return 0 ;
}
void showDigit(int no, int num, int showDotPoint) {
// 先将正极拉低,关掉显示
digitalWrite (DIGIT1, LOW) ;
digitalWrite (DIGIT2, LOW) ;
digitalWrite (DIGIT3, LOW) ;
digitalWrite (DIGIT4, LOW) ;
if (num == 0) {
digitalWrite (LED_A, LOW) ;
digitalWrite (LED_B, LOW) ;
digitalWrite (LED_C, LOW) ;
digitalWrite (LED_D, LOW) ;
digitalWrite (LED_E, LOW) ;
digitalWrite (LED_F, LOW) ;
digitalWrite (LED_G, HIGH) ;
} else if (num == 1) {
digitalWrite (LED_A, HIGH) ;
digitalWrite (LED_B, LOW) ;
digitalWrite (LED_C, LOW) ;
digitalWrite (LED_D, HIGH) ;
digitalWrite (LED_E, HIGH) ;
digitalWrite (LED_F, HIGH) ;
digitalWrite (LED_G, HIGH) ;
} else if (num == 2) {
digitalWrite (LED_A, LOW) ;
digitalWrite (LED_B, LOW) ;
digitalWrite (LED_C, HIGH) ;
digitalWrite (LED_D, LOW) ;
digitalWrite (LED_E, LOW) ;
digitalWrite (LED_F, HIGH) ;
digitalWrite (LED_G, LOW) ;
} else if (num == 3) {
digitalWrite (LED_A, LOW) ;
digitalWrite (LED_B, LOW) ;
digitalWrite (LED_C, LOW) ;
digitalWrite (LED_D, LOW) ;
digitalWrite (LED_E, HIGH) ;
digitalWrite (LED_F, HIGH) ;
digitalWrite (LED_G, LOW) ;
} else if (num == 4) {
digitalWrite (LED_A, HIGH) ;
digitalWrite (LED_B, LOW) ;
digitalWrite (LED_C, LOW) ;
digitalWrite (LED_D, HIGH) ;
digitalWrite (LED_E, HIGH) ;
digitalWrite (LED_F, LOW) ;
digitalWrite (LED_G, LOW) ;
} else if (num == 5) {
digitalWrite (LED_A, LOW) ;
digitalWrite (LED_B, HIGH) ;
digitalWrite (LED_C, LOW) ;
digitalWrite (LED_D, LOW) ;
digitalWrite (LED_E, HIGH) ;
digitalWrite (LED_F, LOW) ;
digitalWrite (LED_G, LOW) ;
} else if (num == 6) {
digitalWrite (LED_A, LOW) ;
digitalWrite (LED_B, HIGH) ;
digitalWrite (LED_C, LOW) ;
digitalWrite (LED_D, LOW) ;
digitalWrite (LED_E, LOW) ;
digitalWrite (LED_F, LOW) ;
digitalWrite (LED_G, LOW) ;
} else if (num == 7) {
digitalWrite (LED_A, LOW) ;
digitalWrite (LED_B, LOW) ;
digitalWrite (LED_C, LOW) ;
digitalWrite (LED_D, HIGH) ;
digitalWrite (LED_E, HIGH) ;
digitalWrite (LED_F, HIGH) ;
digitalWrite (LED_G, HIGH) ;
} else if (num == 8) {
digitalWrite (LED_A, LOW) ;
digitalWrite (LED_B, LOW) ;
digitalWrite (LED_C, LOW) ;
digitalWrite (LED_D, LOW) ;
digitalWrite (LED_E, LOW) ;
digitalWrite (LED_F, LOW) ;
digitalWrite (LED_G, LOW) ;
} else if (num == 9) {
digitalWrite (LED_A, LOW) ;
digitalWrite (LED_B, LOW) ;
digitalWrite (LED_C, LOW) ;
digitalWrite (LED_D, LOW) ;
digitalWrite (LED_E, HIGH) ;
digitalWrite (LED_F, LOW) ;
digitalWrite (LED_G, LOW) ;
}
if (showDotPoint == 1) {
digitalWrite (LED_DP, LOW) ;
} else {
digitalWrite (LED_DP, HIGH) ;
}
if (no == 1) {
digitalWrite (DIGIT1, HIGH) ;
} else if (no == 2) {
digitalWrite (DIGIT2, HIGH) ;
} else if (no == 3) {
digitalWrite (DIGIT3, HIGH) ;
} else if (no == 4) {
digitalWrite (DIGIT4, HIGH) ;
}
}
c的wiringPi库的安装和代码的编译执行方法请自行百度。
c语言测试结果是显示稳定了很多,但仍然有一点不稳定不仔细观察基本看不出来了。这个应该跟我代码的效率有关,逻辑应该还可以精简节省cpu资源。这个以后再试了。我们这个系列主要是让大家了解各种基本外设传感器的原理和使用方法。用c语言也只是做一点延伸,以后的教程还是以python为主。
点击下载Python源码
点击下载c源码