树莓派折腾日记-数码管显示 ip 地址

材料清单

  • 四位七段数码管一个(我用的是共阳极的)
  • 杜邦线若干(至少要 10 根)

电路连接

七段数码管的原理图

数码管

如何驱动

  • 6 8 9 12 四个引脚用于选择数码管
  • 11 7 4 2 1 10 5 分别对应数码管的的 A B C D E F G 七个led灯
  • 通过树莓派的 GPIO 不停地改变这些引脚的状态来使数码管显示数字

连线图

树莓派 GPIO 参考图
数码管

以下表所示的对应关系连线,(ps: 具体可自行分配,仅供参考)

树莓派 GPIO 功能名 对应数码管的引脚
GPIO.0 6
GPIO.1 8
GPIO.2 9
GPIO.21 11
GPIO.22 7
GPIO.23 4
GPIO.24 2
GPIO.25 1
GPIO.26 10
GPIO.27 5

驱动代码

用 C 语言写的驱动程序,使用了 wiringPi 库

wiringPi 库用如下方式下载

1
git clone git://git.drogon.net/wiringPi

也可以到 http://wiringpi.com 进行下载

下面是驱动代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// File Name: led.c

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<wiringPi.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<netdb.h>
#include<sys/ioctl.h>
#include<sys/types.h>
#include<net/if.h>

/*
*
* __1__
* | |
* 6 2
* |--7--|
* | |
* 5 3
* |__4__|
*

* 0 -- 0111111 -- 0x3f
* 1 -- 0000110 -- 0x06
* 2 -- 1011011 -- 0x5b
* 3 -- 1001111 -- 0x4f
* 4 -- 1100110 -- 0x66
* 5 -- 1101101 -- 0x6d
* 6 -- 1111101 -- 0x7d
* 7 -- 0000111 -- 0x07
* 8 -- 1111111 -- 0x7f
* 9 -- 1101111 -- 0x6f
*/

#define PIN_NUM 7
#define SEL_NUM 3

#define IP_SIZE 16

int pins[] = {21, 22, 23, 24, 25, 26, 27};
int sels[] = {0, 1, 2};
int nums[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};

// 处理 SIGINT 信号,保证程序退出时 GPIO 复位,使得数码管熄灭
void signal_handle(int signum)
{
int i = 0;
for(i = 0; i < PIN_NUM; ++i)
digitalWrite(pins[i], LOW);
for(i = 0; i < SEL_NUM; ++i)
digitalWrite(sels[i], LOW);
exit(EXIT_SUCCESS);
}

void init()
{
wiringPiSetup();
int i = 0;
for(i = 0; i < PIN_NUM; ++i)
pinMode(pins[i], OUTPUT);
for(i = 0; i < SEL_NUM; ++i)
pinMode(sels[i], OUTPUT);
for(i = 0; i < PIN_NUM; ++i)
digitalWrite(pins[i], LOW);
for(i = 0; i < SEL_NUM; ++i)
digitalWrite(sels[i], LOW);
signal(SIGINT, signal_handle);
printf("Use Ctrl+c to exit.\n");
}

void flush(int sel, int num)
{
int i = 0;
int n = nums[num];
digitalWrite(sels[sel], HIGH);
for(i = 0; i < SEL_NUM; ++i)
if(sel != i)
digitalWrite(sels[i], LOW);

for(i = 0; i < PIN_NUM; ++i)
{
if(n & (1 << i))
digitalWrite(pins[i], LOW);
else
digitalWrite(pins[i], HIGH);
}
}

// 获取某个网卡的ip地址
int getlocalip(const char * eth_inf, char *ip)
{
int sd;
struct sockaddr_in sin;
struct ifreq ifr;

sd = socket(AF_INET, SOCK_DGRAM, 0);
if( -1 == sd )
{
perror("Socket error.");
return -1;
}

strncpy(ifr.ifr_name, eth_inf, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ - 1] = 0;

if( ioctl(sd, SIOCGIFADDR, &ifr) < 0 )
{
perror("ioctl error.");
close(sd);
return -1;
}

memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
snprintf(ip, IP_SIZE, "%s", inet_ntoa(sin.sin_addr));

close(sd);
return 0;
}

int main()
{
init();
int i = 0 ;

char ip[16] = {0};
// 此处获取哪个网卡的地址由实际情况而定
// 我的树莓派是用无线联网的,所以用的 wlan0
// 网卡名可通过 ifconfig 命令查看
int ret = getlocalip("wlan0", ip);
printf("%s\n", ip);

// 在局域网下,ip 地址一般只有最后一位不同
// 只获取点分十进制 ip 地址的最后一位
int led0, led1, led2;
if(ret < 0)
{
led0 = 8;
led1 = 8;
led2 = 8;
}
else
{
int ip_len = strlen(ip);
led0 = ip[ip_len-1] - '0';
led1 = ip[ip_len-2] - '0';
led2 = ip[ip_len-3] - '0';
}

for( ; ; )
{
flush(0, led0);
delay(5); // 如果不加延时,显示数字会不清晰
flush(1, led1);
delay(5);
flush(2, led2);
delay(5);
}

return 0;
}

配置自启动

/etc/init.d/ 下新建文件 led_show_ip 并给上执行权限

文件内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
# /etc/init.d/temp-ctrl
# Default-Start: 2 3 4 5

case "$1" in
start)
echo "start temp ctrl"
nohup /home/pi/gpio/led &
;;
stop)
echo "stop temp ctrl"
kill -2 $(ps aux | grep -m 1 '/home/pi/gpio/led' | awk '{ print $2 }')
;;
*)
echo "Usage : service led-show-ip start/stop"
exit 1
;;
esac
exit 0

/etc/rc.localexit 0 之前添加一行

1
2
3
/etc/init.d/led_show_ip start

exit 0

最终效果

结果