我使用的是普中51单片机开发板A2套件(2022),驱动数码管可能需要参考电路原理图。开发环境的搭建教程在本专栏的 51单片机开发环境搭建 - VS Code 从编写到烧录 有过介绍。
关于我的软硬件环境信息:
- Windows 10
- STC89C52RC
- SDCC (构建HEX文件)
- stcgal 1.6 (向STC单片机烧录)
点亮 1 位数码管
写个 python 脚本把驱动数字段选转换成 16 进制:
d = ["abcdef", "bc", "abged", "abcdg", "fgbc", "afgcd", "afedcg", "abc", "abcdefg", "abcdfg"] # 0-9
def solve(seg):
ans = 0
for i in seg:
ans ^= (1 << (ord(i) - ord("a")))
return ans
a = "{" + ", ".join([hex(solve(seg)) for seg in d]) + "}"
print(a)
# {0x3f, 0x6, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x7, 0x7f, 0x6f}
这样就得到了驱动一位数码管显示0-9的映射表:{0x3f, 0x6, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x7, 0x7f, 0x6f},也就是给 P0 置 0x3f 时,数码管会显示数字 0 (段选)。
先选择一位数码管进行点亮(选择第 8 位数码管,显示数字 6):
#include <8051.h>
#define decoder_in_1 P2_2 // 译码器的 3 位输入,用于位选
#define decoder_in_2 P2_3
#define decoder_in_3 P2_4
#define NUMBER P0
unsigned int LED_MAP[11] = {0x3f, 0x6, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x7, 0x7f, 0x6f};
void send_to_decoder(unsigned int position) { // position: 1 ~ 8
position--; // position: 0 ~ 7 (000 ~ 111)
decoder_in_1 = position & 1; // low bit of position (position & 001)
decoder_in_2 = position & 2; // middle bit of position (position & 010)
decoder_in_3 = position & 4; // high bit of position (position & 100)
}
void main() {
send_to_decoder(8);
NUMBER = LED_MAP[6];
}
点亮多位数码管
由于位选是通过 38 译码器来让 3 个引脚来控制 8 个引脚( 2 3 = 8 2^3=8 23=8),所以同一时间只能亮起1位,但是单片机可以快速切换亮起的数码管,利用视觉暂留可以让其看起来像是同时亮起。
#include <8051.h>
#define decoder_in_1 P2_2 // 译码器的 3 位输入,用于位选
#define decoder_in_2 P2_3
#define decoder_in_3 P2_4
#define NUMBER P0
unsigned int LED_MAP[11] = {0x3f, 0x6, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x7, 0x7f, 0x6f};
void send_to_decoder(unsigned int position) { // position: 1 ~ 8
position--; // position: 0 ~ 7 (000 ~ 111)
decoder_in_1 = position & 1; // low bit of position (position & 001)
decoder_in_2 = position & 2; // middle bit of position (position & 010)
decoder_in_3 = position & 4; // high bit of position (position & 100)
}
void main() {
unsigned int i;
while (1) {
for (i = 0; i < 8; i++) {
NUMBER = 0x00;
send_to_decoder(8 - i);
NUMBER = LED_MAP[i];
}
}
}
下一次位选之前,要先给段选清零(熄灭数码管),不然上次段选的值还没更改,导致这次新的一次位选还在显示上一次显示的内容。