第十集:数码管的动态显示
动态显示原理
1. 动态刷新概念
- 静态显示:数码管持续点亮,不熄灭。
- 动态显示:逐位快速轮流显示,利用视觉暂留(约20ms内)产生“同时点亮”的错觉。
2. 动态刷新机制
- 每个数码管显示一个短时间(如1ms),然后切换到下一个。
- 循环周期需小于20ms,刷新率高于50Hz,避免肉眼看到闪烁。
- 原理类似电影帧切换,通过快速切换形成连续画面。
3. 实现步骤(以八位数码管为例)
- 选择第一位(位选)
- 输出该位对应的段码(段选)
- 延时1ms
- 关闭当前位,选择下一位,重复步骤2-3
- 循环8位后回到第一步,形成连续显示
硬件连接与码表
1. 开发板连接(共阴数码管)
- 段选(a~g, dp):接P6.0~P6.7
- 位选(8位数码管):接P7.0
P7.7(K1K8)
2. 段码数组(共阴)
unsigned char code segmentCode[] = {
0xC0, // 0
0xF9, // 1
0xA4, // 2
0xB0, // 3
0x99, // 4
0x92, // 5
0x82, // 6
0xF8, // 7
0x80, // 8
0x90 // 9
};
3. 位码数组(开发板对应P7.0~P7.7)
unsigned char code bitCode[] = {
0x7F, // 第一位(P7.0=0)
0xBF, // 第二位(P7.1=0)
0xDF, // 第三位(P7.2=0)
0xEF, // 第四位(P7.3=0)
0xF7, // 第五位(P7.4=0)
0xFB, // 第六位(P7.5=0)
0xFD, // 第七位(P7.6=0)
0xFE // 第八位(P7.7=0)
};
4. 带小数点的段码
- 在原段码基础上减去0x80(点亮dp段)
- 例如:数字0的段码0xC0,带小数点则为0x40
程序实现
1. 基础动态显示程序
unsigned char num = 0; // 当前显示数字
while(1) {
for(int i = 0; i < 8; i++) {
P7 = bitCode[i]; // 选择第i位
P6 = segmentCode[num]; // 显示数字num
delay_ms(1); // 延时1ms
}
num++;
if(num > 9) num = 0;
}
2. 使用显示数组管理各位置
// 定义每个数码管要显示的数字(0-9)
unsigned char displayBuff[8] = {1, 2, 3, 4, 5, 6, 7, 8};
void displayScan() {
static unsigned char i = 0;
P7 = bitCode[i]; // 选择第i位
P6 = segmentCode[displayBuff[i]]; // 显示对应数字
i++;
if(i >= 8) i = 0;
delay_ms(1); // 每位移显示时间
}
3. 带小数点的显示
// 段码数组扩展,10-19为带小数点的0-9
unsigned char code segmentCodeWithDot[20] = {
0xC0, 0xF9, 0xA4, 0xB0, 0x99, // 0-4
0x92, 0x82, 0xF8, 0x80, 0x90, // 5-9
0x40, 0x79, 0x24, 0x30, 0x19, // 0.-4.(带小数点)
0x12, 0x02, 0x78, 0x00, 0x10 // 5.-9.(带小数点)
};
// 显示带小数点的数字(如显示"3.")
P6 = segmentCodeWithDot[13]; // 3在数组下标3,3.在下标13
五、十秒免单计数器
1. 项目功能
- 前四位显示目标时间:"10.00"
- 后四位显示当前计时(10ms单位)
- 按键控制开始/停止
- 目标:停在"10.00"时"免单"
2. 关键代码实现
// 显示缓冲区
unsigned char displayBuff[8] = {1, 10, 0, 0, 0, 0, 0, 0}; // 显示"10.00 0000"
// 计时变量
unsigned long timerCount = 0; // 计时变量(10ms单位)
unsigned char runFlag = 0; // 运行标志
// 主循环
while(1) {
// 扫描显示
for(int i = 0; i < 8; i++) {
P7 = bitCode[i];
P6 = segmentCodeWithDot[displayBuff[i]];
delay_ms(1);
}
// 按键检测
if(KEY1 == 0) {
delay_ms(10);
if(KEY1 == 0) {
BEEP = 0; // 蜂鸣器响
delay_ms(10);
BEEP = 1;
runFlag = !runFlag; // 切换运行状态
if(runFlag == 1) {
timerCount = 0; // 重新开始计时
}
while(KEY1 == 0) {
// 等待按键释放,同时保持显示刷新
displayScan();
}
}
}
// 计时更新
if(runFlag == 1) {
timerCount++;
// 更新显示缓冲区后4位
displayBuff[4] = timerCount / 10000 % 10; // 万位
displayBuff[5] = timerCount / 1000 % 10; // 千位
displayBuff[6] = timerCount / 100 % 10; // 百位
displayBuff[7] = timerCount / 10 % 10 + 10; // 十位(带小数点)
}
}
3. 数字提取技巧
// 提取数值的各位数字
unsigned long num = 12345;
// 方法1:除法和取余
ge = num % 10; // 个位:5
shi = num / 10 % 10; // 十位:4
bai = num / 100 % 10; // 百位:3
// 方法2:使用循环
while(num > 0) {
digit = num % 10; // 从个位开始取
num = num / 10;
}
六、代码优化技巧
1. 使用宏定义延时时间
#define DISPLAY_DELAY 1 // 每位移显示时间
// 使用时
delay_ms(DISPLAY_DELAY);
2. 封装显示函数
void displayRefresh() {
static unsigned char i = 0;
P7 = bitCode[i];
P6 = segmentCodeWithDot[displayBuff[i]];
i++;
if(i >= 8) i = 0;
}
// 主循环中调用
while(1) {
displayRefresh();
delay_ms(1);
}
3. 全局变量管理
displayBuff[8]:显示缓冲区
timerCount:计时变量
runFlag:运行标志
- 使用全局变量便于多个函数访问