历史上的今天
今天是:2025年04月17日(星期四)
2020年04月17日 | 51单片机之计时器
2020-04-17 来源:eefocus
一、实验目的:
让六个数码管动态显示数字,计算秒数
二、实验分析:
a.因为要显示秒数,所以我们首先应该定义一个数组,用来存放0-9数字在数码管上对应的 十六进制数
b.3/8译码器的使能设置
c.定时器的理解。首先应该知道定时器与TMOD和和TCON有关。对于TMOD值得我们特别注意的是TMOD的Gate位(门控位)和C/T位。Gate=0时,仅由运行控制位TR1/TR0来启动定时器运行。而当Gate=1时,仅由运行控制位TR1/TR0和外中断引脚(INT0和INT1)上的高电平共同来启动定时器运行。C/T=0为定时模式,C/T=1为计数模式。我这次用到的是定时器1的方式一,为了不影响TMOD上的其他位,我这次用了逻辑运算:TMOD=TMOD|0x10;MOD=TMOD&0x0DF。对于定时方式一实际上是有一个16位数(TH和TL组成,各占8位)进行累加。上接着看TCON,我们主要用到的TF(定时溢出标志)和TR(定时器运行控制位)。上面说的16位数溢出时,TF=1。(特别需要注意的是:当执行溢出中断时TF位可由硬件清零,当不执行溢出中断时TF位一定要由软件清零)。而对于TR位,当TR=1时开始计时,TR=0,停止计时。因为方式一最长定时是100多毫秒,所以可以每1ms进行一次中断,中断1000次就是1s。
d.定时器的初始化:
因为方式一是16位,所以用公式:(65536-X)*12/11.0592M=0.001,可以得到x=FC66H
所以我们令TH1=0xFC,TL1=66
e.中断的初始化。我们主要用到中断请求寄存器IE。为了能够响应定时器1中断,我们需要另总中断使能位EA=1,定时器1中断使能位ET1=1
f.中断服务程序。这里特别注意我们需要重新设置TH1=0xFC,TL1=66,因为如果不设置,那么TH1=0x00,TL1=0x00,这样下次进入中断的时间久不是1ms了。同时我们在这里每3ms进行一次刷新一个数码管。这样在18ms就可以将全部数码管刷新一个。(因为人视线残留为20ms,而20/6=3.33,s所以每3ms刷新一次,这样就不会占用太多cpu!!)。
g. 刷新函数
需要指出的是在用C和汇编还是有一些方面是不一样的,在进行汇编编写是遇到的一个
比较棘手的问题就是六位数的存放。因为C中有32数,但是汇编没有,最后我用到的是用R0--R5分别存放数码管显示的数字。
三、实验代码:
1.C语言实现:
#include typedef unsigned char uint8; typedef unsigned int uint16; typedef unsigned long uint32; //共阳数码管0-9 uint8 number[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; uint8 a[6]; uint32 sec=0; //用于记录秒数,因为六个数码管已经超过16位可以表示最大范围,所以用32 位 uint16 count=0;//用于记录中断次数,因为我设置每次中断时1ms,所以count=1000时就是一秒 sbit ENLED1 = P1^4; sbit ENLED2= P1^3; sbit ADDR0 = P1^0; sbit ADDR1 = P1^1; sbit ADDR2 = P1^2; //首先3/8译码器使能 void en() { ENLED1=0; ENLED2=1; } //定时器初始化 void init_timer() { TMOD=TMOD|0x10; TMOD=TMOD&0x0DF; TH1=0x0FC; TL1=0x66; TR1=1; } //中断初始化 void init_interrupr() { EA=1;//总中断使能位 ET1=1;//定时器1中断使能位 } void display() { static uint8 j = 0; switch(j) { case 0: ADDR0 = 0;ADDR1 = 0;ADDR2 = 0;j++;P0 = number[a[0]];break; case 1: ADDR0 = 1;ADDR1 = 0;ADDR2 = 0;j++;P0 = number[a[1]];break; case 2: ADDR0 = 0;ADDR1 = 1;ADDR2 = 0;j++;P0 = number[a[2]];break; case 3: ADDR0 = 1;ADDR1 = 1;ADDR2 = 0;j++;P0 = number[a[3]];break; case 4: ADDR0 = 0;ADDR1 = 0;ADDR2 = 1;j++;P0 = number[a[4]];break; case 5: ADDR0 = 1;ADDR1 = 0;ADDR2 = 1;j=0;P0 = number[a[5]];break; default: break; } } void ov_interrupt() interrupt 3 { count++; TF1=0;// 这里因为我们进行了中断,所以硬件会自动清零,所以不一定要加上这一句 TH1=0x0FC; TL1=0x66; if(count==1000)//说明是一秒 { sec++; count=0; a[0] = sec%10; a[1] = sec/10%10; a[2] = sec/100%10; a[3] = sec/1000%10; a[4] = sec/10000%10; a[5] = sec/100000%10; } if(count%3==0) display();//进行刷新,这里我们可以看出,我们是3ms进行一次刷新,这样就不会占用太多cpu了 } void main() { en(); //首先3/8译码器使能 init_timer();//定时器初始化 init_interrupr();//中断初始化 while(1); //让程序一直处于运行中 } 2.汇编语言实现: org 00H ljmp start org 1BH //定时器1中断入口地址 ljmp ov_interrupt org 30H start: mov R6,#00//用于记录产生几次中断,如果(R6)=100,说明为1s mov R0,#00//R0-R5用于存储数码管上数字 mov R1,#00 mov R2,#00 mov R3,#00 mov R4,#00 mov R5,#00 mov dptr,#numb lcall en lcall init_interrupt lcall init_timer here: sjmp here //好比是一个while(1)循环 ;首先3/8译码器使能 en: clr P1.4 setb P1.3 ret //中断初始化 init_interrupt: setb EA setb ET1 ret //定时器初始化 init_timer: /*orl TMOD,#0x10 anl TMOD,#0x0DF*///这两技能说明工作在T1的方式1 且计时有TR1启动 mov TMOD,#10H mov TH1,#0xDC mov TL1,#0x00//一个10ms的定时初值设置 setb TR1 //让定时器1开始工作 ret delay: mov 20H,#3 ;一个机器周期 Dd2: mov 30H,#10 ;一个机器周期 Dd1: mov 40H,#3 ;一个机器周期 djnz 40H,$ ;2个机器周期 djnz 30H,Dd1 ;2个机器周期 djnz 20H,Dd2 ;个机器周期 Ret //刷新函数 display: D0: mov A,R0 movc A,@A+dptr clr P1.0 clr P1.1 clr P1.2 mov P0,A lcall delay D1: mov A,R1 movc A,@A+dptr setb P1.0 clr P1.1 clr P1.2 mov P0,A lcall delay D2: mov A,R2 movc A,@A+dptr clr P1.0 setb P1.1 clr P1.2 mov P0,A lcall delay D3: mov A,R3 movc A,@A+dptr setb P1.0 setb P1.1 clr P1.2 mov P0,A lcall delay D4: mov A,R4 movc A,@A+dptr clr P1.0 clr P1.1 setb P1.2 mov P0,A lcall delay D5: mov A,R5 movc A,@A+dptr setb P1.0 clr P1.1 setb P1.2 mov P0,A lcall delay ret //TF1=1中断服务程序 ov_interrupt: push DPH push DPL push psw mov TH1,0xDC//一个0.01s的定时初值设置 mov TL1,0x00 inc R6 //中断计数器加1,当(R6)=100,就是1s clr TF1// 这里因为我们进行了中断,所以硬件会自动清零,所以不一定要加上这一句 cjne R6,#100,return //判断中断计数器R6,如果不为100则转移到here mov R6,#00H led0: inc R0//数码管0数字加1 cjne R0,#10,return //判断(R0),如果不为10,转移到display, //否则为10就变为0,然后转移到led1 mov R0,#00H led1: inc R1 cjne R1,#10,return mov R1,#00H led2: inc R2 cjne R2,#10,return mov R2,#00H led3: inc R3 cjne R4,#10,return mov R4,#00H led4: inc R4 cjne R4,#10,return mov R4,#00H led5: inc R5 cjne R5,#10,return mov R5,#00H //如果执行这一句说明超过了999999 return: pop psw pop DPL pop DPH dis: mov A,R6//当R6为偶数,进行display,也就是每两次中断(20ms)进行一次刷新 anl A,#01H cjne A,#0,quit call display quit: reti //0-9 numb:DB 0C0H,0F9H,0A4H,0B0H,99H,92H,82H,0F8H,80H,90H end 实验结果: 和自己实验前的分析结果一样。
上一篇:51单片机做一个计时器
史海拾趣
|
一. 在X86的目标机上能正常运行Createfile打开com1,但是用writefile发送数据失败.通过GetLastError()获得错误好1359. 二. 在该目标机器上,只能打开com3,其他的com1,com2,com4,com5都是Createfile失败.通过调试发现Com1用来wince作为调试信息输出.… 查看全部问答> |
|
long int i=32132121; void show_shuzi() { if (i>99999999) return; wei[7]=i/10000000; i=i-wei[7]*10000000; wei[6]=i/1000000; i=i-wei[6]*1000000;   ...… 查看全部问答> |
|
大家比较熟悉的应该是VS1003吧,这一款比较新而且比较强大。在官方网看了一下有样品申请就申请了,联合科技的销售经理直接从香港给我送样品来了,本来说要送两片的,我说要5片,他就送5片来了。和我聊了个把小时感 ...… 查看全部问答> |
|
请问msp430F1232如何产生13.56Mhz的频率 各位大侠,如何通过430单片机的引脚输出13.56Mhz的频率 谢谢!主要作用是想实现MF1卡检测功能,在平常无卡时低功耗待机每秒检测2次,有卡来到时可以通过430检测到卡片到来, 然后启动射频芯片RC531或TRF7 ...… 查看全部问答> |




