×
单片机 > 单片机程序设计 > 详情

如何利用单片机设计一个99码表

发布时间:2021-05-14 发布时间:
|

单片机设计:基于51单片机的99码表设计

软件环境:Proteus8.0 + Keil4

要求:1,开关按一下,数码管开始计时。2,按两下,数码管显示静止。3,按三下,数码管数值清零。

C语言程序如下:

#include

#define uint unsigned int

#define uchar unsigned char

uchar shi,ge,aa,keycount=0,temp;

sbit anjian=P1^7;

uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};

void display(shi,ge);

void key ();

void init();

void delay(uint z);

/*-----主程序-----*/

void main()

{

init(); //初始化

while(1)

{

key ();

if(keycount==1)

TR0=1; //开中断

if(keycount==2)

TR0=0;

if(keycount==3)

{

temp=0;

keycount=0;

}

if(aa==10){aa=0;

if(temp《=99)

{

temp++;display(shi,ge);

}

else

temp=0;}

}

}

/*------初始化程序-------*/

void init()

{

keycount=0;

temp=0;

TMOD=0x01;

TH0=(65536-50000)/256;

TL0=(65536-50000)%256;

EA=1;

ET0=1;

//TR0=0;

}

/*-----定时器中断-----*/

void timer0() interrupt 1

{

TH0=(65536-50000)/256;

TL0=(65536-50000)%256;

aa++;

}

/*-----显示子程序-----*/

void display(shi,ge)

{

shi=temp/10;

ge=temp%10;

P0=table[shi];;delay(70);

P2=table[ge]; ;delay(70);

}

/*-----按键检测子程序-----*/

void key ()

{

if(anjian==0)

{

delay(5); //消抖

if(anjian==0)

keycount++;

}

//while(anjian==0);

//display(shi,ge); //等待按键弹起

}

/*-----延时子程序-----*/

void delay(uint z) //延时约1ms

{

uint x,y;

for(x=z;x》0;x--)

for(y=100;y》0;y--);

}

电路仿真结果如下:

好了,那么接下来我们就开始C语言——》汇编语言之旅^_^

(1)C语言1-10行改为

ORG 0000H //汇编起始伪指令,功能是规定程序存储器中源程序或数据块存放的起始地址

ajmp STAR //ajmp无条件跳转指令

ORG 000bh

ajmp timer0

anjian equ P1.7 //位定义

keycount equ 40h

shi equ 41h

gewei equ 42h

aa equ 43h

temp equ 44h

tab: db 3fh,6h,5bh,4fh,66h //建表

db 6dh,7dh,7h,7fh,6fh

(2)C语言中的初始化函数 12-14行和39-49行改为

1 STAR: 2 acall init //子程序近程调用指令,功能是主程序调用子程序,调用子程序的范围为2kb

init:

mov keycount,#0 //keycount=0

mov temp,#0 //temp=1

mov tmod,#01h //TMOD=0x01

mov TH0,#60

mov TL0,#176

setb EA //位置位指令,对操作数所指出的位进行置1操作

setb ET0

setb TR0

ret

acall为子程序近程调用指令,返回用ret。

(3)C语言中15-35行是个while循环,逻辑比较繁琐,注意了!

START:

acall display

inc temp //加1指令,将操作数所指定的单元或寄存器中的内容加1

acall delay70 //近程调用delay70

x8: mov r0,keycount

cjne r0,#2,F1 //cjne比较跳转指令,若r0=2则跳转到x8,否则跳转到F1。

ajmp x8

F1: mov r0,temp

cjne r0,#99,START //若r0《99时,重复循环,否则temp=0

mov temp,#0

ajmp START

F9:

acall key

mov r0,keycount

cjne r0,#0,F2 //keycount=0顺序执行,否则跳转到F1

CLR P1.3 //清0

SETB TR0

F2: mov r0,keycount //第二次按键

cjne r0,#2,F2

clr TR0

reti

mov r0,keycount //第三次按键

cjne r0,#3,F3

mov temp,#0

mov keycount,#0

inc增量指令,功能是将操作数所指定的单元或寄存器中的内容加1,其结果返还回原操作数单元中。

clr位复位,功能是对操作数所指出的位进行清“0”操作。

或者在中断函数中

timer0:

w1:

acall key

mov TH0,#60

mov TL0,#176

cpl p1.0

JB keycount,x2

ajmp x3

x2:

ajmp START

clr p1.0

ajmp w1

ajmp w1

x3: mov r0,keycount

cjne r0,#3,w1 //若r0=3则顺序执行,否则跳转到w1

mov temp,#0

mov keycount,#0

ret

(4)C语言58-64行display函数改为

display:

mov a,temp

mov b,#10

div ab //除法指令,实现两个八位无符号数的除法操作。

mov r2,A

mov r3,B

mov dptr,#tab //16位数据传送使用方式

mov a,r2

movc a,@a+dptr //查表,先将累加器A的内容与数据指针寄存器DPTR的内容相加,再以其结果为地址,将该地址的结果送入A中

mov P0,a

acall delay70

nop //空指令

mov a,r3

movc a,@a+dptr

mov P2,a

nop

acall delay70

ret

div为除法指令,功能是实现两个8位无符号数的除法操作,一般被除数放在累加器A中,除数放在寄存器B中。指令执行后,商放在A中,余数放在B中。

movc为查表指令,先将累加器A的内容与数据指针寄存器DPTR的内容相加,再以其结果为地址,将该地址的内容送入A中。

nop为空操作指令,它不作任何操作,但要占用一个机器周期(即12个振荡周期)的时间,常用于延时或等待。(有些程序执行的效果由于延时时间太短,在人眼视觉暂时作用下无法辨认清楚)

此段程序的作用在于将一个两位数分别分在一个十位上的数码管和一个个位上的数码管显示。

(5)C语言66-76行key函数改为

1 key:2 jb anjian,F6 //若anjian=0则顺序执行,否则跳转到F63 ACALL delay54 inc keycount //keycount++5 F6: 6 ret

jb为位条件转移指令,功能是若直接寻址的位bit=1,则程序转移到指定的目标地址去执行,若bit=0,则程序顺序执行。

(6)C语言78-83行delay函数改为

delay70:

mov r6,#70

D2: mov R7,#248

d1: djnz R7,d1 //248*70次

djnz R6,D2

ret

delay5:

mov r6,#5 //消抖。

F7: mov R7,#248

F8: djnz r7,F8 //248*5次

djnz r6,F7

ret

注意:248=2 8 ,约等于1ms。delay为延时程序。

温馨提示???在汇编中程序代码的大小写不受影响,但在C语言中就有影响了。

思考1:ret和 reti都是程序返回指令,有什么区别?

我的回答:ret是子程序返回指令,reti是中断子程序返回指令。区别在于如果是acall、lcall指令调用的子程序,返回指令就用ret;如果地址是0003,0013,000B,001B,0023调用的子程序,返回指令就用reti。

思考2:mov 20h,#0h和 setb 20h都是加1,用什么区别?

我的回答:mov指令中的20h指字节,setb中的20h是位。


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
qemu+chroot构建arm aarch64虚拟机