单片机精确延时计算和定时中断 一.延时 1. 10ms延时程序(for循环嵌套) ********************************************************************* 文件名称:void delay_10ms() 功能:10ms延时 参数:单片机晶振12MHz ********************************************************************* void delay_10ms() { unsigned int i, j; for(i=0;i<10;i++) { for(j=0;j<124;j++) ; } } i 和 j 定义为int整型时,for循环执行时间为8个机器周期,当i 和j 定义为char 字符型时,for 循环执行时间3个机器周期。“;”一个机器周期,每次调用for循环2个机器周期。 则执行本段延时程序是内循环时间t1=8*124+3个机器周期,其中“8”执行for循环指令时间;“124”为for循环次数;“3”为每次调用for循环指令的时间。外循环t2=t1*10+8*10+3其中“10”为for循环次数;“8”为一次for循环指令调用和执行时间;“10”为调用for循环次数,3为调用for循环指令时间。 所以本程序延时t=((8*124)+3)*10+8*10+3=10033=10.033ms≈10ms。 注意:变量为整型时,每次调用for循环需要3个机器周期的调用时间,执行for循环判断需要8个机器周期的执行时间;字符型变量时,每次调用for循环需要2个机器周期的调用时间,执行for循环判断需要3个机器周期的执行时间。 程序运行到第一个断点所用时间0.00038900s,运行到第二个断点所用时间为0.01042800s,则执行delay_10ms()函数所用时间为0.010428-0.000389=0.010039s= 10.039ms≈10ms。由于断点的原因,执行P0=0xff;和P0=0xfe;指令也花费了时间所以这里时间是10.039ms而不是上面计算出的10.033ms。 2. 10μs短暂延时 *********************************************************************
文件名称:void delay10μs() 功能:10μs延时 参数:单片机晶振12MHz,机器周期1μs ********************************************************************* void delay10μs() { _NOP_( ); _NOP_( ); _NOP_( ); _NOP_( ); _NOP_( ); _NOP_( ); }
函数中共用了6个_NOP_( )语句,每个语句执行时间为1μs。主函数调用delay10μs()时,先执行一个LCALL指令(2μs),然后执行个_NOP_( )语句(6μs),最后执行了一个RET指令(2μs),所以执行上述函数时共需要10μs。可以把这一函数当作基本延时函数,在其他函数中调用,即嵌套调用,以实现较长时间的延时;但需要注意,如在delay40μs()中直接调用4次delay10μs()函数,得到的延时时间将是42μs,而不40μs。这是因为执行delay40μs()时,先执行了一次LCALL指令(2μs),然后开始执行第一个delay10μs(),执行完最后一个delay10μs()时,直接返回到主程序。依此类推,如果是两层嵌套调用,如在delay80μs()中两次调用delay40μs(),则也要先执行一次LCALL指令(2μs),然后执行两次delay40μs()函数(84μs),所以,实际延时时间为86μs。简言之,只有最内层的函数执行RET指令。该指令直接返回到上级函数或主函数。如在delay80μs()中直接调用8次delay10μs(),此时的延时时间为82μs。通过修改基本延时函数和适当的组合调用,上述方法可以实现不同时间的延时。 二 . 定时中断:
1. 定时器/计数器: 8051单片机有2个16为定时器/计数器(8052有3个),它可设置为计数方式,对外部事件(脉冲)进行计数;也可以设置为定时方式,对标准时钟脉冲进行定时计时。它有4中工作方式,定时和计数范围可以通过软件编程进行设定。一旦定时或计数到位,就会立即向CPU发送中断请求,CPU根据定时或计数结果对外设实行控制。 单片机具有5~8个中断源,2级中断优先级。它可接收外部中断请求、定时器/计数器中断请求和串行口中断请求。对紧急事件的实时控制、故障自动处理、单片机与外设之间的数据传输、人机对话等。 定时器/计数器的核心是两个16位的加法计数器,T0,T1分别由两个8位的特殊功能寄存器TH0,TL0和TH1,TL1构成。 定时器/计数器工作方式控制器TMOD,字节地址89H:
| 定时器/计数器T1 | 定时器/计数器T0 | GATE :GATE 为0时,定时器/计数器由软件控制位TRx(x为0或1)来控制启动或停止,1启动,0停止;GATE为1时,由外部中断引脚INT0非或INT1非和TRx共同控制,INT非为高电平和TRx为1启动,低电平和0停止。 C/T非: C/T非为0时,定时器/计数器为定时器方式,对晶振脉冲的12分频信号进行计数。为1时,定时器/计数器为计数器方式,对外部引脚T0(P3.4)或T1(P3.5)输入的脉冲进行计数。CPU在每个机器周期的S5P2期间,对T0或T1引脚进行采样,在前一个周期采得值为1,后一个周期采得值为0,则计数器加1。由于确认一次负跳变需要两个机器周期,因此最高计数频率为晶振频率的1/12。
GATE C/T非 M1 M0 GATE C/T非 M1 M0 工作方式: M1 M0 工作方式
0 0 方式0,为13位定时器/计数器,2^13=8192
0 1 方式1,为16位定时器/计数器,2^16=65536
1 0 方式2,为8位自动重装载计数初值定时器/计数器,2^8=256
1 1 方式3,定时器/计数器T0分成两个独立的8位定时器/计数器
定时器/计数器控制寄存器TCON,字节地址88H,可位寻址: TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0
TF1/TF0:定时器/计数器溢出标志位,溢出时由硬件置1,向CPU申请中断或查询,CPU响应中断后,由硬件自动清0。 TR1/TR0:定时器/计数器运行控制位,由软件置1或清0,置1时启动定时器/计数器,清0时停止计数。 IT0/IT1:外部中断0/外部中断1触发方式选择位。IT0/IT1=0为电平触发方式,IT0/IT1=1为跳变触发方式。 IE0/IE1:外部中断0/外部中断1中断请求标志位。IE0/IE1=1表示在向CPU申请中断。
2. 中断: 中断是指CPU正在处理某件事情时,外部发生的某一事件(如一个电平的变化,一个脉冲沿的发生或定时器/计数器溢出等)请求CPU迅速去处理。于是,CPU暂时中断当前的工作,转去处理所发生的事件。中断服务处理完成后,再回到原来被中止的地方,继续原来的工作,此过程即为中断。 8051中断系统共有5个中断源,分别为1.外部中断0请求 2.外部中断1请求 3.定时器/计数器T0溢出中断请求 4.定时器/计数器T1溢出中断请求 5.串行口中断请求 1)串行口控制寄存器SCON,字节地址98H,可位寻址。 SM0 SM1 SM2 REN TB8 RB9 TI RI 与中断系统有关的标志位时TI和RI。 TI:串行口的发送中断请求标志位。CPU将一字节的数据写入发送缓冲器SBUF时,就启动一帧串行数据的发送,没发送完一帧串行数据后,硬件自动置TI位为1。(发送完一帧数据,若还要接着发送,则为了下一次发送做准备,需要软件将TI清0) RI:串行口的接收中断请求标志位。在串行口允许接收时,每接收完一帧串行数据,硬件自动置RI位为1。(接收完一帧数据,若还要接着接收,则为了下一次接收做准备,需要软件将RI清0)
2)中断允许控制寄存器IE,字节地址A8H,可位寻址。 EA — — ES ET1 EX1 ET0 EX0
EA:中断允许总控制位。EA=0,CPU关中断。EA=1,CPU开中断。 ES:串行口中断允许控制位。ES=0,禁止中断。ES=1,允许中断。 ET0/ET1:定时器/计数器0/1中断允许控制位。0禁止中断,1允许中断。 EX0/EX1:外部中断0/1中断允许控制位。0禁止中断,1允许中断。
3)中断优先级控制寄存器IP,字节地址B8H,可位寻址。 — — — PS PT1 PX1 PT0 PX0
PS:串行口中断优先级控制位。1为高优先级,0为低优先级。 PT0/PT1:定时器/计数器0/1溢出中断优先级控制位。1为高优先级,0为低优先级。 PX0/PX1:外部中断0/1中断优先级控制位。1为高优先级,0为低优先级。 查询中断方式编程: 用定时器T1查询方式控制单片机发出1KHz音频 #include // 包含51单片机寄存器定义的头文件 sbit sound=P3^7; //将sound位定义为P3.7引脚 void main(void) { EA=1; //开总中断 ET0=1; //定时器T0中断允许 TMOD=0x10; //使用定时器T1的模式1 TH1=(65536-921)/256; //定时器T1的高8位赋初值 TL1=(65536-921)%256; //定时器T1的低8位赋初值 TR1=1; //启动定时器T1 TF1=0; while(1)//无限循环等待查询 { while(TF1==0) ; TF1=0; sound=~sound; //将P3.7引脚输出电平取反 TH1=(65536-921)/256; //定时器T1的高8位赋初值 TL1=(65536-921)%256; //定时器T1的低8位赋初值 } }