初学者制作,把自己的制作过程记录下来与大家分享。用了一个树莓派的扩展版只是方便一些,没有扩展版的直接把线接到树莓派上就好了。

八段数码管的原理和接线

这里就不赘述了,可以参考这篇文章:https://www.h5jun.com/post/pi-num8.html

我们来着重说一下电子时钟的代码和原理。

电子时钟的原理

八段数码管其实我们只能让这四个数字同时显示一样的数字,那怎么才能实现显示不同的数字呢?

答:每次只显示一个数字,让他们轮番显示,当达到一定的速度的时候,肉眼是看不出来的。

为了方便大家理解,下面的三个视频分别是,1000毫秒,100毫秒,1毫秒的效果:

可以看到,当扫描速度到1毫秒的时候,肉眼已经识别不出来了。

电子时钟的代码

我们接好线以后,就可以定义各个引脚对应的Gpio的值:

1
2
const digitGroup = [29, 31, 33, 35, 37, 36, 38, 40]; //数码管输出端口
const portGroup = [15, 16, 18, 32]; //数码管选择端口,注意这里的顺序要对应从左到右的显示

然后需要自己测试一下8个输出引脚所对应的值,我自己对应的值如下:

1
2
3
4
5
6
7
8
9
5
|------------|
6| |7
| 4 |
|------------|
0| |3
| |
|------------| . 2
1

通过得出对应的值,我们来设置1-9数字应该显示的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
const NUMS = [
[0,1,3,5,6,7], //0
[3,7], //1
[0,1,4,5,7], //2
[1,3,4,5,7], //3
[3,4,6,7], //4
[1,3,4,5,6], //5
[0,1,3,4,5,6], //6
[3,5,7], //7
[0,1,3,4,5,6,7], //8
[1,3,4,5,6,7] //9
];
const DP = [2]; //.

好了,我们现在有了对应的值,怎么才能让他显示呢。。

1
2
3
digitGroup.concat(portGroup).forEach(n => {
rpio.open(n, rpio.OUTPUT, 1);
});

遍历他们所有的GPIO,把他们设置为OUTPUT并设置高电平1.

这样他们就全都亮了 ====> 8.8.8.8.

接下来,来设置一下让他们显示某个数字:

我想,如果我直接执行 setNum(3) 然后就屏幕就显示3,这样就方便多了。 那么我们需要一个setNum的函数。

1
2
3
4
5
function setNum(num, dp) { //(num:number, dp:boole) : string
digitGroup.forEach((n, i)=> {
rpio.write(n, NUMS[num].indexOf(i) > -1 ? rpio.LOW : rpio.HIGH); //判断当前的灯是否要显示
});
}

我们来执行 setNum(3),全部显示3了。看起来不错,但是有个问题,我要一起显示小数点怎么办呢?

答:用当前的num和小数点合并一下就好了。代码如下:

1
2
3
4
5
6
7
function setNum(num, dp) { //(num:number, dp:boole) : string
const rule = dp ? NUMS[num].concat(DP) : NUMS[num]; //判断dp和DP数组合并
digitGroup.forEach((n, i)=> {
rpio.write(n, rule.indexOf(i) > -1 ? rpio.LOW : rpio.HIGH);
});
}

setNum(3, true); //显示 3. ok,成功了

到现在为止,我们已经基本上成功一大半了!

接下来,我们根据上面的视频,让他们每隔1毫秒就轮流亮一次,然后切换不同的值。

例如,获取当前的时间为22:10,这样为们就可以把他们合并放到数组里面[2,2,1,0];

然后对应上面的数码管选择端口portGroup,这样就知道每个port对应的数值了。

这样看起来不错,其实还有一个问题,如果是凌晨02:03呢?用js的new Date().getHours()获取的是 2.这样的话就对应不上去了。

看来还需要一个辅助函数,来补全不足十位的数字

1
2
3
function fillNum(num, length = 2){ //默认补全2位,为了以后扩展用
return num < Math.pow(10, length-1) ? Array(length-1).fill(0).join("") + num : num.toString();
}

来执行一下函数:fillNum(0); // “00”.

现在没问题了,我们来写一个无限循环:

1
2
3
4
5
6
7
8
9
10
while(1) {
portGroup.forEach((port, i)=>{
let showHours = (fillNum(new Date().getHours()) + fillNum(new Date().getMinutes())).split("");
rpio.write(port, 1); //点亮
setNum(showHours[i]);
rpio.msleep(1); //暂停1毫秒
rpio.write(port, 0); //熄灭
});
}

现在可以显示小时和分钟数了,但是有种怪怪的感觉,为什么感觉不到它在走动。。

让我们在中间加一个小数点。来表示它确实是时间。

把上面的setNum(showHours[i]);改造一下

1
setNum(date[i], i === 1 && new Date().getSeconds()%2);

判断在第二个数字后面并且秒数是双数的时候显示小数点。

ok, 现在没什么问题了。只不过我还想优化一下,我按一下按钮的话,可以显示分钟和秒数。

来让我们加一个四脚按钮,接两条线一个接到GPIO口,一个接到5V:

1
2
const buttonPin = 12; //按钮的接口
rpio.open(buttonPin, rpio.INPUT); //设置为输入模式

然后再在循环里加点料:

1
2
3
4
5
6
7
8
9
10
11
12
while(1) {
portGroup.forEach((port, i)=>{
let showHours = (fillNum(new Date().getHours()) + fillNum(new Date().getMinutes())).split("");
let showMinutes = (fillNum(new Date().getMinutes()) + fillNum(new Date().getSeconds())).split(""); //添加显示分钟和秒数
date = rpio.read(buttonPin) ? showMinutes : showHours; //通过读取button的状态来显示不同的形式
rpio.write(port, 1);
setNum(date[i], i === 1 && new Date().getSeconds()%2);
rpio.msleep(1);
rpio.write(port, 0);
});
}

大功告成,来看一下完整的视频:

完整代码

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
const rpio = require("rpio");
const NUMS = [
[0,1,3,5,6,7], //0
[3,7], //1
[0,1,4,5,7], //2
[1,3,4,5,7], //3
[3,4,6,7], //4
[1,3,4,5,6], //5
[0,1,3,4,5,6], //6
[3,5,7], //7
[0,1,3,4,5,6,7], //8
[1,3,4,5,6,7] //9
];
const DP = [2]; //.
const digitGroup = [29, 31, 33, 35, 37, 36, 38, 40]; //数码管输出端口
const portGroup = [15, 16, 18, 32]; //数码管选择端口,注意这里的顺序要对应从左到右的显示
const buttonPin = 12; //按钮的接口
function setNum(num, dp) {
const rule = dp ? NUMS[num].concat(DP) : NUMS[num];
digitGroup.forEach((n, i)=> {
rpio.write(n, rule.indexOf(i) > -1 ? rpio.LOW : rpio.HIGH);
});
}
function fillNum(num, length = 2){
return num < Math.pow(10, length-1) ? Array(length-1).fill(0).join("") + num : num.toString();
}
digitGroup.concat(portGroup).forEach(n => {
rpio.open(n, rpio.OUTPUT);
});
rpio.open(buttonPin, rpio.INPUT);
while(1) {
portGroup.forEach((port, i)=>{
let showHours = (fillNum(new Date().getHours()) + fillNum(new Date().getMinutes())).split("");
let showMinutes = (fillNum(new Date().getMinutes()) + fillNum(new Date().getSeconds())).split("");
date = rpio.read(buttonPin) ? showMinutes : showHours;
rpio.write(port, 1);
setNum(date[i], i === 1 && new Date().getSeconds()%2);
rpio.msleep(1);
rpio.write(port, 0);
});
}