8位8段数码管飞入显示时/分/秒(51单片机)

8位8段数码管飞入显示时/分/秒(51单片机)

使用51单片机在8位数码管上以飞入效果显示动态的时/分/秒信息,每次飞入一位,不能覆盖已飞入数据,直到时/分/秒信息完整显示到8位数码管上,使编译产生的可执行文件尽量小。
效果入图所示:
8位数码管飞入显示时/分/秒

问题分析

实现飞入效果并不难,难点在于如何使编译产生的可执行文件尽量小。

算法分析

动态时/分/秒信息就直接采用51单片机内部定时器实现。

typedef struct {            // 时间
     unsigned char hour;    // 时
     unsigned char miunte;  // 分
     unsigned char second;  // 秒
     unsigned char count;   // 50ms
} time_t;

使用pos记录飞入数据的位置,len记录已飞入数据的数量。8位8段数码管从左至右编号7-0,使用pos记录当前正在飞入的字符位置,len的初始值为0,表示还未飞入数据,len的最大值为7,表示所有数据都已经飞入。代码篇幅比较短,就不多赘述细节了(代码脑内运行!!!),飞入效果的实现主要都在定时中断函数t0()内的LED8_FLY标签段,主函数主要负责将pos和len进行显示前的映射并显示。
本算法用C语言写编译产生的可执行文件是695字节,使用汇编能降到485字节。
欢迎感兴趣的伙伴一起思考更优秀的算法或实现方式。

typedef struct {        // 控制飞入
    unsigned char pos;  // 飞入位置
    unsigned char len;  // 飞入字符数
} fly_t;

C代码

屏蔽掉的代码基本是初始化寄存器,不是废代码,只是在不影响飞入效果的情况下,使编译产生的可执行文件尽量小。

#include <reg51.h>

#define LED8_CTRL_PORT P2  // 位码端口
#define LED8_DATA_PORT P0  // 段码端口
#define CLEAR          11  // 熄灭数码管
#define CONNECT        10  // 连接符号'-'

typedef struct {        // 控制飞入
    unsigned char pos;  // 飞入位置
    unsigned char len;  // 飞入字符数
} fly_t;

typedef struct {            // 时间
     unsigned char hour;    // 时
     unsigned char miunte;  // 分
     unsigned char second;  // 秒
     unsigned char count;   // 50ms
} time_t;

fly_t data fly;
time_t data time;

/*******************************************************************************
* 函 数 名:main
* 函数功能:主函数
* 输    入:void
* 输    出:void
* 说    明:none
*******************************************************************************/
void main(void)
{
    unsigned char data buf[8];  // 显示内容
    unsigned char data i, j;
    unsigned char data ctrl_code;   // 位码
    unsigned char code LED8_DATA_CODE[] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x40, 0x00 };  // 共阴段码0~9,'-','NULL'

    fly.pos = 7;  // 初始化飞入相关信息
//    fly.len = 0;  // 屏蔽赋初值代码,默认初值为0

//    time.hour = 13;  // 初始化时间
//    time.miunte = 14;
//    time.second = 52;
//    time.count = 0;

//    TL0 = 0xb0;   // 计时50ms
//    TH0 = 0x3c;
    TMOD = 0x01;  // 定时器T0工作与方式1
    IE = 0x82;
    TR0 = 1;      // 启动定时器

    while (1) {
        ACC = time.second;  // 数据拆分
        B = 10;
        buf[1] = ACC / B;
        buf[0] = B;
        buf[2] = CONNECT;
        ACC = time.miunte;
        B = 10;
        buf[4] = ACC / B;
        buf[3] = B;
        buf[5] = CONNECT;
        ACC = time.hour;
        B = 10;
        buf[7] = ACC / B;
        buf[6] = B;

        buf[fly.pos] = buf[fly.len];  // 取飞入的字符
        for (i = fly.len; i != 8; i++) {  // 清除
            if(i != fly.pos) {
                buf[i] = CLEAR;
            }
        }

        ctrl_code = 0x80;  // 数码管显示
        for (i = 0; i != 8; i++) {
            LED8_CTRL_PORT = ~ctrl_code;  // 给位码
            LED8_DATA_PORT = LED8_DATA_CODE[buf[i]];  // 给段码
            ctrl_code >>= 1;
            while(j++);
//            LED8_DATA_PORT = 0x00;  // 消隐
        }
    }
}

/*******************************************************************************
* 函 数 名:t0
* 函数功能:50ms中断计时
* 输    入:void
* 输    出:void
* 说    明:none
*******************************************************************************/
void t0(void) interrupt 1
{
//    TL0 = 0xb0;
//    TH0 = 0x3c;

    time.count++;             // 计时
    if (time.count != 15) {   // 约1秒
        return;
    }
    time.count = 0;
    time.second++;
    if (time.second != 60) {  // 1分
        goto LED8_FLY;
    }
    time.second = 0;
    time.miunte++;
    if (time.miunte != 60) {  // 1小时
        goto LED8_FLY;
    }
    time.miunte = 0;
    time.hour++;
    if(time.hour != 24) {     // 1天
        goto LED8_FLY;
    }
    time.hour = 0;

LED8_FLY:
    fly.pos--;  // 飞入字符移位
    if(fly.pos != (fly.len - 1)) {  // 飞入下一个字符
        return;
    }
    fly.pos = 7;
    fly.len++;
    if(fly.len == 8) {   // 所有字符飞入完成
        fly.len = 0;
    }
}

汇编代码

屏蔽掉的代码基本是初始化寄存器,不是废代码,只是在不影响飞入效果的情况下,使编译产生的可执行文件尽量小。

LED8_CTRL_PORT EQU  P2  ; 位码端口
LED8_DATA_PORT EQU  P0  ; 段码端口
CLEAR          EQU  11  ; 熄灭数码管
CONNECT        EQU  10  ; 连接符号'-'

pos            EQU 41H  ; 飞入位置
len            EQU 40H  ; 飞入字符数
hour           EQU  R7  ; 时
miunte         EQU  R6  ; 分
second         EQU  R5  ; 秒
count          EQU  R4  ; 50ms
; 50H    51H    52H      53H    54H    55H      56H    57H    58H(多余项)
; 秒个位 秒十位 连接符号 分个位 分十位 连接符号 时个位 时十位 连接符号
buf            EQU 50H  ; 显示内容

ORG 0000H
AJMP MAIN
ORG 000BH
AJMP T0_INTERRUPT
ORG 0030H
MAIN:
MOV pos, #7  ; 初始化
; MOV len, #00H  ; 屏蔽赋初值代码,默认初值为0
; MOV hour, #13
; MOV miunte, #14
; MOV second, #52
; MOV count, #0
; MOV TL0, #0B0H
; MOV TH0, #3CH
; MOV SP, #30H
MOV TMOD, #01H
MOV IE, #82H
SETB TR0

LOOP:  ; 循环
MOV R0, #buf  ; 数据拆分
MOV R1, #05H  ; second(R5)地址
DATA_SPLIT:
MOV A, @R1
MOV B, #10
DIV AB
MOV @R0, B
INC R0
MOV @R0, A
INC R0
MOV @R0, #CONNECT  ; 最末的CONNECT字符不影响
INC R0
INC R1
CJNE R1, #08H, DATA_SPLIT

MOV A, pos  ; 取飞入的字符
ADD A, #buf
MOV R0, A
MOV A, len
ADD A, #buf
MOV R1, A
MOV A, @R1
MOV @R0, A

MOV R0, len  ; 清除
CLEAR1:
MOV A, R0
CJNE A, pos, CLEAR2
CLEAR3:
INC R0
CJNE R0, #8, CLEAR1
AJMP DISPLAY
CLEAR2:
ADD A, #buf
MOV R1, A
MOV @R1, #CLEAR
AJMP CLEAR3

DISPLAY:  ; 数码管显示
MOV A, #7FH
MOV R0, #buf
OUTPUT:
MOV LED8_CTRL_PORT, A  ; 给位码
MOV A, @R0
INC R0
MOV DPTR, #LED8_DATA_CODE
MOVC A, @A+DPTR
MOV LED8_DATA_PORT, A  ; 给段码
DELAY:
DJNZ R2, DELAY
; MOV LED8_DATA_PORT, #00H  ; 消隐
MOV A, LED8_CTRL_PORT
RR A
CJNE R0, #58H, OUTPUT
AJMP LOOP

T0_INTERRUPT:  ; 50ms中断计时
PUSH ACC
PUSH B
; MOV TH0, #3CH
; MOV TL0, #0B0H
CLR A
INC count  ; 计时
CJNE count, #15, RETURN
MOV count, A
INC second  ; 约1秒
CJNE second, #60, LED8_FLY
MOV second, A
INC miunte  ; 1分
CJNE miunte, #60, LED8_FLY
MOV miunte, A
INC hour  ; 1小时
CJNE hour, #24, LED8_FLY
MOV hour, A

LED8_FLY:
DEC pos  ; 飞入字符移位
MOV A, pos  ; 飞入下一个字符
MOV B, len
DEC B
CJNE A, B, RETURN
MOV pos, #7
INC len
MOV A, len  ; 所有字符飞入完成
CJNE A, #8, RETURN
MOV len, #0
RETURN:
POP B
POP ACC
RETI

LED8_DATA_CODE:  ; 共阴段码0~9, '-', 'NULL'
DB 3FH, 06H, 5BH, 4FH, 66H, 6DH
DB 7DH, 07H, 7FH, 6FH, 40H, 00H

END
发布了24 篇原创文章 · 获赞 53 · 访问量 3988

猜你喜欢

转载自blog.csdn.net/weixin_44413515/article/details/105152349