用C语言实现精确的延时
C语言里如何编写精确的微量延时
C 语言里如何编写精确的微量延时
众所周知,相比于其他编程语言,C 语言在编写嵌入式编程中有着绝对的优势。
但它总也有缺点的:它的时序性比较差,不容易编写精准的延时。
而在编写嵌入系统驱动程序时,常常需要比较精确的软件延时,这使得C 语言的劣势暴露了出来,一般都只能通过嵌入汇编的方式实现。
例如,在1MHZ 工作频率下需要延时10us,就需要嵌入10 句空操作指令,显然在书写上比较难堪。
本文提出一种简化书写的延时方案,使用带参数的宏构来造微小时间片,可以实现完全精确的软件延时,大大方便了驱动程序及软件模拟通信协议的编写。
说明:以下皆为ICC AVR 平台下的讨论,对AVR 系列所有型号的单片机皆有效。
至于其他平台,可据此方案自行修改和移值。
该方案的实现方法其实很简单:
首先定义N 个宏,分别调用1 ~ N 个汇编空操作指令,如:
#define NOP_1 asm(nop)//延时一个时钟周期。
如何用C语言写精确的延时程序
如何用C语言写精确的延时程序/s/blog_7ceb2d560102v0ds.html2014假设一个延时函数如下:void delay(){uint i;for(i=0;i<20000;i++);}我们怎么来算呢,先将20000转成16进制,得到4E20,然后将高字节4E乘以6得到468D,再将468+2=470,然后将470D*20HEX (即32D)=15040;所心这个延时函数总共要执行15040个周期,假设所用的晶振为12MHZ,则该段函数总延时:15.04ms。
有了这个公式我们如果想要设定指定的延时长度就可以用它的己知公式确定。
即:总时间=[(6*i值的高8位)+2]*i的低8位。
例如:设要延时125ms。
我们将低字节先赋值200D*(即:C8),然后再算高低节的定值,由式中可知125ms=200*((i值的高字节*6)+2),又可推算出(高低节*6)+2的总延迟时间应等于625us,将625/6=104.1666,取整数得到104,将104转成16进制得到68H,再将高字节和低字节组合起来即得到了定值,即:68C8HEX,代入函数即如下:void delay(){uint i;for(i=0;i<0x68C8;i++);}如果写直进行就要将68C8转成10进制,即:26824,代入后得到:void delay(){uint i;for(i=0;i<0x26824;i++);在c语言中嵌入一段汇编的延时程序呀,你自己看看书,很简单的用在单片机里的可以C和汇编混合使用,因为用汇编编写延时要准确些,所以你不妨写一个汇编程序,然后再调用它好了,要用C来编精确的延时确实是比较难的哦.呵呵谁说C语言不能精确延时,用51单片机的定时/计数器一或者用定时/计数器2的工作方式2,自动再装入8位计数器,就可以精确延时,别说1MS了就是100um也能精确做到。
其实很简单,采用定时器,将需要定时时间算好后写入单片机中断程序中即可,这种方法可以实现精确定时,最终的误差仅由晶振来确定了。
用Keil C语言实现精确延时的技术研究
嵌 入式 汇编 , 即在 C语 言程 序 中嵌入 一段 汇编语
言程序 ; 二是 汇 编程序 部 分作 为 c语 言程 序的 一
个 子 函数 , 过 调 用 汇 编子 函数 实 现相 应 操 作 。 通 这两种 方法都 涉 及 到 C语 言与 汇 编 语 言混 合 编 程 的 问题 。混 合 编程 的关 键 主要有 两点 : 1 5 )C 1函数 名 的转换 及 其命 名规 则 ;
宏 的执行 时 间 以及 每 段 子程 序 加 调 用 语 句 所 消 耗 的 时间[ 。在 Ke 5 1 ] iC 1的 C编译 系统 中 , l C语
直接 采用 c语 言 实现 延 时的方 法 , 就是 通 过 C 1 5 编译 器预 先编译 c语 言程 序 , 后研 究它 生成 的 然
邓 全 李 磊 彭凤超
中 国电子 科技 集 团公 司第二 十七研 究所 郑 州 4 0 1 50 5
摘
要: 针对 C语言如何有效实现精确延时 的问题 , 介绍了两种基 于 Ke iC语言环境 的解决方 法 , 结合 l 并
工 程 实 践 , 证 了本 文 方 法 的 有 效 性 。 验
反汇 编语 言代码 , 而保 证延 时 准确性 的方 法 。 从
在汇 编程 序 中 实 现 1 / 0 s延 时 , 常 采用 的  ̄ 通
汇编 代码 为 :
M 0V , R7 #0 H 5 DEL Y:DI Z R7 DELAY A N ,
言程序 能够 和汇 编程 序 实 现方 便 灵 活 的接 口, C
为程序 调试 提 供 必 要 的符 号 信 息 。在 使 用 汇 编
Kel 5 iC 1编译器 S l模式 下 进 行 编译 , 时 程 mal 这
C8051F单片机C程序精确延时的方法
编译情况相同。3种循环语句在Keil C51中具有不同编 译特点的原因在于它们的流程不同。do—while语句是先 执行后判断,while和for语句都是先判断后执行。进行
多层循环时,使用do—while语句具有更大的优势:编译的 汇编代码就如直接用汇编语言编写的程序,结构紧凑,编
译效率高,条件转移控制循环次数的过程简单。因此,虽
C8051F单片机是完全集成的混合信号系统级芯片 (S0c),其MCU系统控制器的内核是CIP一51微控制器。 CIP一51的指令集与标准8051指令集完全兼容。CIP一 51采用流水线指令结构,指令时序与标准805l不同: 70%指令的执行时间为l或2个系统时钟周期;所有指令 时序都以时钟周期计算;大多数指令执行所需的时钟周期 数与指令的字节数一致;条件转移指令在不发生转移时和 发生转移时的时钟周期数不同。同标准8051相比, C8051F单片机实现程序延时的方法更复杂些。
void SingleCircle(unsigned char t){ unsigned char X=t,Y 5 tl
t基金项目:福建省教育厅科技硬目(jB07277)。
do(
;
}while(一一t); while(x一一);
for(;y--一;); }
使用Keil C51 V7.50编译器编泽,得到的汇编代码如 下:
2丁+(7l+3)T·(X一1)+(,l+2)T=ZT+(行+3)T·X一1’
同理,可得Delay函数的延时时间: {2T+[2T+(咒+3)T·x—T+3T]·y一丁+3丁)·
£一1’+5丁={[4y+(咒+3)X·y+4]·£+4}·T 其中,5丁为返回指令RET的时钟周期数。考虑调用De- lay函数的LCALL指令(时钟周期数为4T)和参数传递的 MOV指令(时钟周期数为2n,则总延时时间t且为
.c延时函数 纳秒级别
.c延时函数纳秒级别在C语言中,一般使用以下两种方法实现纳秒级别的延时函数:方法一:使用“空循环”来实现延时空循环指的是在函数中使用一个循环,但循环内部不进行任何有意义的操作,而是使循环的迭代次数达到一定数量,从而达到一定的延时效果。
例如,假设我们需要实现一个延时1纳秒的函数,可以使用下面的代码实现:cvoid delay_ns(unsigned int ns) {volatile int i,j; volatile表示变量可能被意外修改,防止被编译器优化掉for (i = 0; i < ns; i++) {for (j = 0; j < 12; j++); 循环12次,大约需要1纳秒的时间(具体需要根据CPU速度进行调整)}}需要注意的是,空循环的实现方式虽然简单,但有两个明显的缺点:1. 循环的迭代次数需要手动调整,且不同的CPU速度需要使用不同的迭代次数,不方便移植。
2. 空循环会消耗CPU的资源,并且不能保证在不同的CPU架构下具有相同的延时精度。
方法二:使用CPU提供的硬件定时器来实现延时现代的CPU一般都会提供一些硬件定时器,可以通过编程的方式来设置定时器的计数器和预分频器,从而实现精确的延时。
例如,假设我们需要实现一个延时1纳秒的函数,可以使用在Linux下使用POSIX定时器API实现:c#include <time.h>void delay_ns(unsigned int ns) {struct timespec t = {0, ns};nanosleep(&t, NULL);}需要注意的是,使用硬件定时器来实现延时通常比空循环的方式更准确,但在实现上需要考虑平台的差异性、可移植性和系统开销等问题。
FOR循环实现C语言精确延时
for实现C语言精确延时C语言最大的缺点就是实时性差,我在网上到看了一些关于延时的讨论,其中有篇文章51单片机Keil C延时程序的简单研究,写得不错,他是用while(--i);产生DJNZ来实现精确延时,后来有人说如果while里面不能放其它语句,否则也不行,用do-while就可以,具体怎样我没有去试.所有这些都没有给出具体的实例程序来.还看到一些延时的例子多多少少总有点延时差.为此我用for循环写了几个延时的子程序贴上来,希望能对初学者有所帮助.(晶振12MHz,一个机器周期1us.)一.500ms延时子程序程序:void delay500ms(void){unsigned char i,j,k;for(i=15;i>0;i--)for(j=202;j>0;j--)for(k=81;k>0;k--);}产生的汇编:C:0x08007F0F MOV R7,#0x0FC:0x08027ECA MOV R6,#0xCAC:0x08047D51MOV R5,#0x51C:0x0806DDFE DJNZ R5,C:0806C:0x0808DEFA DJNZ R6,C:0804C:0x080A DFF6DJNZ R7,C:0802C:0x080C22RET计算分析:程序共有三层循环一层循环n:R5*2=81*2= 162us DJNZ2us二层循环m:R6*(n+3)=202*165= 33330us DJNZ2us+R5赋值1us=3us三层循环:R7*(m+3)=15*33333= 499995us DJNZ2us+R6赋值1us=3us 循环外:5us子程序调用2us+子程序返回2us+R7赋值1us=5us延时总时间=三层循环+循环外=499995+5= 500000us=500ms计算公式:延时时间=[(2*R5+3)*R6+3]*R7+5二.200ms延时子程序程序:void delay200ms(void){unsigned char i,j,k;for(i=5;i>0;i--)for(j=132;j>0;j--)for(k=150;k>0;k--);}产生的汇编C:0x08007F05MOV R7,#0x05C:0x08027E84MOV R6,#0x84C:0x08047D96MOV R5,#0x96C:0x0806DDFE DJNZ R5,C:0806C:0x0808DEFA DJNZ R6,C:0804C:0x080A DFF6DJNZ R7,C:0802C:0x080C22RET三.10ms延时子程序:void delay10ms(void){unsigned char i,j,k;for(i=5;i>0;i--)for(j=4;j>0;j--)for(k=248;k>0;k--);}产生的汇编C:0x08007F05MOV R7,#0x05C:0x08027E04MOV R6,#0x04C:0x08047DF8MOV R5,#0xF8C:0x0806DDFE DJNZ R5,C:0806C:0x0808DEFA DJNZ R6,C:0804C:0x080A DFF6DJNZ R7,C:0802C:0x080C22RET四.1s延时子程序:void delay1s(void){unsigned char h,i,j,k;for(h=5;h>0;h--)for(i=4;i>0;i--)for(j=116;j>0;j--)for(k=214;k>0;k--);}产生的汇编C:0x08007F05MOV R7,#0x05C:0x08027E04MOV R6,#0x04C:0x08047D74MOV R5,#0x74C:0x08067CD6MOV R4,#0xD6C:0x0808DCFE DJNZ R4,C:0808C:0x080A DDFA DJNZ R5,C:0806C:0x080C DEF6DJNZ R6,C:0804C:0x080E DFF2DJNZ R7,C:0802C:0x081022RET在精确延时的计算当中,最容易让人忽略的是计算循环外的那部分延时,在对时间要求不高的场合,这部分对程序不会造成影响.。
Keil C51精确延时程序(C语言)
Keil C51精确延时程序程序说明如下:振荡频率:12MHz机器周期=12/振荡频率=12/12000000=1us#include <reg52.h>void delay1(unsigned char i){ while(--i);}说明:delay1程序为:延时时间=(2*i+2)*机器周期。
i=1~255。
void delay2(unsigned char i){ while(i--);}说明:delay2程序为:延时时间=(6*i+2)*机器周期。
i=1~255。
void main (void){unsigned char m;delay1(10); //赋值并调延时程序delay1说明:本句为赋值并调用Delayus1:延时时间=(1+2)*机器周期。
全部延时时间为:延时时间=(1+2+2*i+2)*机器周期。
i=1~255。
本例:延时时间=(1+2+2*10+2)*1us=25usdelay2(10); //赋值并调延时程序delay2说明:本句为赋值并调用Delayus2:延时时间=(1+2)*机器周期。
全部延时时间为:延时时间=(1+2+6*i+2)*机器周期。
i=1~255。
本例:延时时间=(1+2+6*10+2)*1us=65usm=10; //赋值,m=1~255while(--m) ; //计算,延时时间=2*m*机器周期说明:本两句为赋值并计算。
全部延时时间为:延时时间=(1+2*m)*机器周期。
m=1~255。
本例:延时时间=(1+2*10)*1us=25uswhile(1);}。
c语言中延时的方法
c语言中延时的方法C语言中实现延时的方法有多种方式。
下面将介绍两种常用的延时方法:方法一:使用循环实现延时使用循环来进行延时是C语言中常用的方法之一。
通过循环次数来控制延时的时间,代码如下:```#include <stdio.h>void delay(int milliseconds) {for (int i = 0; i < milliseconds * 10000; i++) {// 延时}}int main() {printf("开始延时\n");delay(1000); // 延时1秒printf("延时结束\n");return 0;}```在上述代码中,delay函数使用了一个for循环来实现延时,其中循环次数通过乘以10000与延时时间相乘得到。
这种方法的缺点是无法精确控制延时时间,受系统执行速度的影响较大。
方法二:使用<time.h>库函数实现延时另一种常用的延时方法是利用<time.h>头文件中的库函数,如sleep函数。
代码如下:```#include <stdio.h>#include <time.h>void delay(int seconds) {sleep(seconds);}int main() {printf("开始延时\n");delay(1); // 延时1秒printf("延时结束\n");return 0;}```在上述代码中,delay函数通过调用sleep函数来实现延时,参数表示延时的秒数。
这种方法的优点是延时时间较为精确,但缺点是无法实现毫秒级的延时。
以上是C语言中实现延时的两种常用方法。
开发者可以根据具体需求选择合适的延时方法。
C语言精确微秒级的延时
65535-(UБайду номын сангаасNT)(SYSCLK/1000000) * (UINT)(time_us) 定 时 器 需 要 TM_LODAE 指令周期才会溢出。该单片机的一个指令周期就是一个时 钟周期
TMR2H = TM_LODAE>>8; TMR2L = TM_LODAE&0x00FF;置定时器寄存器 的初值
TR2 = 1; 启动单片机计时
while (!TF2H); 等待定时器 2 寄存器溢出
TR2 = 0;停止计时
//-----------------------------------------------------------------------------
void Delay_us (unsigned char time_us)
{ unsigned long int TM_LODAE; TR2 = 0; // Stop timer TF2H = 0; // Clear timer overflow flag TM_LODAE = 65535-(UINT)(SYSCLK/1000000) * (UINT)(time_us); // TMR2 = -( (UINT)(SYSCLK/1000000) * (UINT)(time_us) ); TMR2H = TM_LODAE>>8; TMR2L = TM_LODAE&0x00FF; TR2 = 1; // Start timer while (!TF2H); // Wait till timer overflow occurs TR2 = 0; // Stop timer
C 语言精确微秒级的延时
在使用 C 语言编程时延时程序是非常常见的,但是实现一个精确 的延时是不太容易的,在给一个朋友的公司产品做维护时,发现一段 代码,可以实现微妙级的延时。看起来代码非常简单。但是我以前没 有想到过。我们一起来看看这段代码。
C语言延时子程序准确设置
C语言延时子程序准确设置
在给单片机写程序的时候往往需要一个微秒或者毫秒的延时子程序,但是C语言不同于汇编,很难根据语句确定准确的延时时间,经过各种查资料得到一种简单实用的方法:通过keil下断点调试确定时间。
就是编写一个延时子程序,然后在延时程序处和while(1)设置断点,运行程序,在调试模式下就可以获得该延时子程序的准确延时时间。
1,编写如下源程序:
#include
void delay_ms(unsigned intms)
{
unsigned int i;
unsigned char j;
for(i=0;i
{
for(j=0;j<200;j++);
for(j=0;j<102;j++);
}
}
void main (void)
{
delay_ms(1);
while(1);
}
2,下图位置设置断点
3,进入运行模式,开始运行(1处所示),程序会在延时子程序前的断点处停下,此时记录程序运行时间,2处所示。
4,再次点运行(1处所示),此时程序会在第二个断点处,也就是while(1)停下,这时候程序刚刚运行完延时子程序,读取此时的程序运行时间,如2所示,,二者想减就是延时子程序运行时间。
如下图:
0.00143989-0.00042209=0.0010178,约等于1mS,ok,这样就得到你所写的延时子程序的延时时间,准确的哦,可以在你得到的运行时间的基础上更改数值来得到希望的延时时间。
当然记得设置晶振为你所需要的,我这里是11.0592。
c语言精确delay函数
c语言精确delay函数C语言中的精确延时函数是指能够以指定的时间精确延迟程序执行的函数。
在实际开发中,我们经常会遇到需要程序暂停一段时间的情况,这时就需要使用延时函数来实现。
在C语言中,常用的延时函数有两种,一种是使用循环来实现延时,另一种是使用系统提供的延时函数。
下面将详细介绍这两种方法的实现原理及其使用场景。
1. 循环延时法循环延时法是通过循环来实现延时的方法,其原理是让程序在一个循环中空转一段时间,从而达到延时的效果。
具体实现代码如下:```cvoid delay(unsigned int ms){unsigned int i, j;for(i = 0; i < ms; i++)for(j = 0; j < 10000; j++);}```上述代码中,delay函数接收一个参数ms,表示要延时的毫秒数。
函数内部使用两个嵌套的循环,外层循环控制延时的次数,内层循环则是一个空转的过程,通过调整内层循环的次数来控制延时的时间。
循环延时法的优点是简单易懂,适用于较简单的延时场景。
但是由于循环的执行时间受到处理器的影响,不同的处理器执行相同的循环时间可能会有差异,因此不能保证延时的精确性。
2. 系统延时函数系统延时函数是通过调用系统提供的函数来实现延时的方法,其原理是让程序暂停一段时间,从而达到延时的效果。
具体实现代码如下:```c#include <unistd.h>void delay(unsigned int ms){usleep(ms * 1000);}```上述代码中,delay函数接收一个参数ms,表示要延时的毫秒数。
函数内部使用usleep函数来实现延时,usleep函数的参数是微秒,所以需要将毫秒转换为微秒。
系统延时函数的优点是精确可靠,适用于对延时精度要求较高的场景。
由于系统延时函数是由操作系统提供的,可以保证延时的精确性,不受处理器的影响。
C语言中的精确延时函数有循环延时法和系统延时函数两种方法,根据实际需求选择合适的方法来实现延时。
单片机C语言的精确延时程序设计
PUBLIC _delay3
RSEG ?PR?_delay3?DLY
_delay3:
DJNZ R7, ¥ 4 s
RET
4s
END
嵌入汇编的方法如下
void delay4(unsigned char vd)
{ #pragma asm
DJNZ R7, ¥
#pragma endasm
}
编译后的形式
汇编时代常用的延时指令为
MOV R7 DDLY
DJNZ R7 ¥
产生最小 4 s 的延时 仿造的 C 程序如下
void delay1(ndly)
{for( ndly>0 ndly--)
}
生成的汇编代码与 D J N Z 无关 是如下形式的 其
中无关的编译注释已删除
RSEG ?PR?_delay1?DLY2
法 在生成目标代码时 会有所不同 开发人员必须研 究它生成的汇编语言代码 来保证时间的准确性 这也 许是除了使用嵌入汇编或直接编写汇编函数的唯一方 法 其实在单片机的 C 编译器中 已经有足够底层操作 方面的扩展 所以这里只考虑纯 C 语言的方法
3 延时程序设计
以德国 Keil 公司的 C 5 1 编译器为例 目前它已被公 认为业界的标准 以下讨论均假设 5 1 单片机时钟晶振 为 6 M H z 以小模式下编译 这时程序指令执行的最小 单位是 2 s 如果使用非英特尔且内核优化过的单片 机 应切换回普通模式 或仔细研究它的时序 以 D S 1 8 B 2 0 为例 临时在程序中需要延时 2 s 那么可以 用下述程序
以上代码调用一次也有 14 s 之多 还是无法使用
也就是说 采用 f o r 形式的语句 生成的汇编代码都是
Keil C中软件实现精确延时的几种方法
两种循环可以单独使用,也可以嵌套使用,即可以编写成延时函数使用,也可以在程序中直接使用,但是会有微小的差别,即增加了调用函数的时间。下面的函数可实现1ms到1s的延时,误差为16us,基本满足一般应用的要求。需要注意的是使用不同的循环变量类型,得到的延时也会不同。
[3]徐爱钧.Keil Cx51 V7.0单片机高级语言编程与uVision2应用实践[M].北京:电子工业出版社,2004.
void Delayl0us()
{
_nop_();
_nop_();
_nop_();
_nop_();
_p_();
_nop_();
}
每个语句执行时间为1us,主函数调用Delayl0us()时,先执行一个LCALL指令(2us),然后执行6个_nop_()语句(6us),最后执行了一个RET指令(2s),所以执行上述函数时共需要10us。可以把这一函数当作基本延时函数,在其他函数中调用,即嵌套调用,以实现较长时间的延时;但需要注意,如在Delay40us()中直接调用4次Delayl0us()函数,得到的延时时间将是42us,而不是40us。这是因为执行Delay40us()时,先执行了一次LCALL指令(2us),然后开始执行第一个Delayl0us(),执行完最后一个Delayl0us()时,直接返回到主程序。依此类推,如果是两层嵌套调用,如在Delay80us()中两次调用Delay40us(),则也要先执行一次LCALL指令(2us),然后执行两次Delay40us()函数(84us),所以,实际延时时间为86us。简言之,只有最内层的函数执行RET指令。该指令直接返回到上级函数或主函数。如在Delay80us()中直接调用8次Delayl0us(),此时的延时时间为82us。通过修改基本延时函数和适当的组合调用,上述方法可以实现不同时间的延时。在KeilC语言中,使用_nop_()语句需要在程序的前面加上“#include<intrins.h>”语句。每一个_nop()_语句编译后需要一个字节的程序空间。因此在短暂延时的场合使用较多。
C语言延时计算
C语言延时计算C语言的延时计算C51中精确的延时与计算的实现C51由于其可读性和可移植性很强,在单片机中得到广泛的应用,但在某些时候由于C51编写的程序对在有精确时间要求下,可能就得要用汇编语言来编写,但在C51是否也能实现时间的精确控制呢,答案是肯定的。
在C51中要实现对时间的精确延时有以下几种方法其一:对于延时很短的,要求在us级的,采用“_nop_”函数,这个函数相当汇编NOP指令,延时几微秒,就插入个这样的函数。
NOP指令为单周期指令,可由晶振频率算出延时时间,对于12M 晶振,延时1uS。
其二:对于延时比较长的,要求在大于10us,采用C51中的循环语句来实现。
在选择C51中循环语句时,要注意以下几个问题第一、定义的C51中循环变量,尽量采用无符号字符型变量。
第二、在FOR循环语句中,尽量采用变量减减来做循环。
第三、在do…while,while语句中,循环体内变量也采用减减方法。
这因为在C51编译器中,对不同的循环方法,采用不同的指令来完成的。
下面举例说明:unsigned char I;for(i=0;i<255;i++);unsigned char I;for(i=255;i>0;i--);其中,第二个循环语句C51编译后,就用DJNZ指令来完成,相当于如下指令: MOV 09H,,0FFHLOOP: DJNZ 09H,LOOP指令相当简洁,也很好计算精确的延时时间。
同样对do…while,while循环语句中,也是如此例:unsigned char n;n=255;do{n--}while(n);或n=255;while(n){n--};这两个循环语句经过C51编译之后,形成DJNZ来完成的方法,故其精确时间的计算也很方便。
其三:对于要求精确延时时间更长,这时就要采用循环嵌套的方法来实现,因此,循环嵌套的方法常用于达到ms级的延时。
对于循环语句同样可以采用for,do…while,while结构来完成,每个循环体内的变量仍然采用无符号字符变量。
C语言精确延时方法
C语言精确延时方法有些特殊的应用会用到比较精确的延时(比如DS18B20等),而C不像汇编,延时精准度不好算。
本人经过反复调试,对照KEIL编译后的汇编源文件,得出了以下几条精确延时的语句(绝对精确!本人已通过实际测试),今天贴上来,希望对需要的朋友有所帮助。
sbit LED = P1^0; // 定义一个管脚(延时测试用)unsigned int i = 3; // 注意i,j的数据类型,unsigned char j = 3; // 不同的数据类型延时有很大不同//-----------------各种精确延时语句-----------------------------------while( (i--)!=1 ); // 延时10*i个机器周期i = 10; while( --i ); // 延时8*i+2个机器周期i = 10; while( i-- ); // 延时(i+1)*9+2个机器周期j = 5; while( --j ); // 延时2*j+1个机器周期j = 5; while( j-- ); // 延时(j+1)*6+1个机器周期i = 5;while( --i ) // 延时i*10+2个机器周期,在i*10+2个机器周期if( LED==0 ) break; // 内检测到LED管脚为低电平时跳出延时i = 5;while( LED ) // 每隔10个机器周期检测一次LED管脚状态,当LEDif( (--i)==0 ) break;// 为低时或者到了10*i+2个机器周期时跳出延时//--------------------------------------------------------------------例如18b20的复位函数(12M晶振)://************************************************************* **********// 函数功能:18B20复位// 入口参数:无// 出口参数:unsigned char x: 0:成功 1:失败//************************************************************* **********unsigned char ow_reset(void){unsigned char x=0; // 12M晶振 1个机器周期为1usDQ = 1; // DQ复位j = 10; while(--j);// 稍做延时(延时10*2+1=21个机器周期,21us)DQ = 0; // 单片机将DQ拉低j = 85; while(j--);// 精确延时(大于480us) 85*6+1=511usDQ = 1; // 拉高总线j = 10; while(j--);// 精确延时10*6+1=61usx = DQ; // 稍做延时后,return x; // 如果x=0则初始化成功 x=1则初始化失败j = 25; while(j--);// 精确延时25*6+1=151us}//************************************************************* ********************再如红外解码程序:(先说传统红外解码的弊端:程序中用了while(IR_IO);while(!IR_IO);这样的死循环,如果管脚一直处于一种状态,就会一直执行while,造成“死机”现象。
关于单片机C语言的精确延时
for(i = 0; i < 600; i++); } } /********************************************************************************************
/******************************************************************************************** void main (void){ //主程序 while(1){
LED = ~LED; //取LED相反状态 DELAY_MS(1000); //修改这里的数值看看会有什么变化 }
} /******************************************************************************************** - 应用实例
可将其模板下载到本地硬盘,在编写新程序的时候复制、粘贴到工程当 中,然后根据情况写入具体内容。
- 使用说明
data unsigned char cou = 0; // 定义软计数器,放在程序最前面的寄存 器定义部分
/********************************************************************************************
用单片机C语言精确延时(定时)的方法
用单片机C语言精确延时(定时)的方法
本人在闲暇的时候对单片机C语言下的各类延时程序做了下总结。
由于单
片机C语言下利用软件延时不容易做到精确的定时,所以很多人在编写延时子
程序的时候不能好好的把握延时的具体时间。
C语言下,延时程序主要有以下
几种:
一:
void delay(unsigned char k){
unsigned char i,k; //定义变量 for(i=0;ik;i++); //for循环语句
}
该程序在Keil环境下,会先将C语言转化成汇编语言,那么我们就可以根据
汇编语言来计算出精确的时间,转化具体步骤如下:
CLR A ;指令1 MOV R7,A ;指令2 LOOP: INC R7 ;指令3 CJNE R7,k,LOOP ;指令4
这里,指令1,指令2和指令3各消耗1个机器周期,指令4消耗两个机器
周期(可查此表得知:51hei/mcuteach/1312.html),而在12M的晶振下一个机
器周期是1us,在这个过程中,指令1和指令2分别执行1次,即消耗1+1us,
而指令3和指令4分别执行了k次,那么这样加起来,这个延时子程序所消耗
的具体时间就是t=1+1+(1+2)*k=3k+2us。
呵呵,这样说来,如果我们定义的k为100的话,这个延时子程序的精确时
间就是302us。
二:
void delay(unsigned char i){while(--i){;}}
同样的道理,将其反汇编,可以看到,只有一条语句:DJNZ i,$;。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
怎么用C语言做单片机的精确延时在单片机应用中,经常会遇到需要短时间延时的情况,一般都是几十到几百μs,并且需要很高的精度(比如用单片机驱动DS18B20时,误差容许的范围在十几μs以内,不然很容易出错);而某些情况下延时时间较长,用计时器往往有点小题大做。
另外在特殊情况下,计时器甚至已经全部用于其他方面的定时处理,此时就只能使用软件定时了[1]。
1C语言程序延时
Keil C51的编程语言常用的有2种:一种是汇编语言;另一种是C语言。
用汇编语言写单片机程序时,精确时间延时是相对容易解决的。
比如,用的是晶振频率为12MHz 的AT89C51,打算延时20μs,51单片机的指令周期是晶振频率的1/12,即一个机器周期为1μs;“MOV R0,#X”需要2个机器周期,DJNZ也需要2个机器周期,单循环延时时间t=2X+3(X为装入寄存器R0的时间常数)[2]。
这样,存入R0里的数初始化为8即可,其精度可以达到1μs。
用这种方法,可以非常方便地实现512μs以下时间的延时。
如果需要更长时间,可以使用两层或更多层的嵌套,当然其精度误差会随着嵌套层的增加而成倍增加。
虽然汇编语言的机器代码生成效率很高,但可读性却并不强,复杂一点的程序就更难读懂;而C语言在大多数情况下,其机器代码生成效率和汇编语言相当,但可读性和可移植性却远远超过汇编语言,且C语言还可以嵌入汇编程序来解决高时效性的代码编写问题。
就开发周期而言,中大型软件的编写使用C语言的开发周期通常要比汇编语言短很多,因此研究C语言程序的精确延时性能具有重要的意义。
C程序中可使用不同类型的变量来进行延时设计。
经实验测试,使用unsigned char类型具有比unsigned int更优化的代码,在使用时应该使用unsigned char作为延时变量。
2单层循环延时精度分析
下面是进行μs级延时的while程序代码。
延时函数:
void delay1(unsigned char i){
while(i);}
主函数:
void main(){
while(1){
delay1(i);
}
}
使用Keil C51的反汇编功能,延时函数的汇编代码如下:
C:0x00E6AE07MOVR6,0x07
C:0x00E81FDECR7
C:0x00E9EEMOVA,R6
C:0x00EA70FAJNZC:00E6
C:0x00EC22RET
图1断点设置位置图
通过对i赋值为10,在主程序中图1所示的位置设置断点。
经过测试,第1次执行到断点处的时间为457μs,再次执行到该处的时间为531μs,第3次执行到断点处的时间为605μs,10次while循环的时间为74μs,整个测试结果如图2所示。
图2使用i--方式测试仿真结果图
通过对汇编代码分析,时间延迟t=7X+4(其中X为i的取值)。
测试表明,for循环方式虽然生成的代码与用while语句不大一样,但是这两种方法的效率几乎相同。
C语言中的自减方式有两种,前面都使用的是i--的方式,能不能使用--i方式来获得不同的效果呢?将前面的主函数保持不变,delay1函数修改为下面的方式:void delay1(unsigned char i){
while(--i);}
同样进行反汇编,得到如下结果:
C:0x00E3DFFEDJNZR7,
C:00E3C:0x00E522RET
比较发现,--i的汇编代码效率明显高于i--方式。
由于只有1条语句DJNZ,执行只需要2个时钟周期,1个时钟周期按1μs计算,其延时精度为2μs;另外,RET
需要2个时钟周期,能够达到汇编语言代码的效率。
按前面的测试条件进行测试,第1次执行到断点处的时间为437μs,再次执行到该处的时间为465μs,第3次执行到断点处的时间为493μs,10次while循环的时间为28μs,整个测试结果如图3所示。
图3使用--i方式测试仿真结果图
调整i的取值,i取8时延时时间为24μs,i取9时延时时间为26μs。
通过分析得出,10次循环为28μs是由于外层循环造成的,其精度可以达到2μs。
在设计时应该考虑参数传递和RET语句执行所需要的时间周期。
实验分析发现,for语句使用--i方式,同样能够达到与汇编代码相同的精度。
i取不同值时延时仿真结果如图4所示。
图4i取不同值时延时仿真结果图
3多重嵌套下的C程序延时
在某些情况下,延时较长,仅使用单层循环方式是不能完成的。
此时,只能使用多层循环方式,那么多重循环条件下,C程序的精度如何呢?下面是一个使用for语句实现1s延时的函数。
延时函数
void delay1s(void){
for(k=100;k>0;k--)//定时1s
for(i=20;i>0;i--)
for(j=248;j>0;j--);
}
主函数调用延时函数代码段:
while(1){
delay1s();
scond+=1;
}
为了直接衡量这段代码的效果,利用Keil C找出这段代码产生的汇编代码:
C:0x00B37002JNZ
C:00B7C:0x00B5150CDEC0x0C
C:0x00B7E50DMOVA,0x0D
C:0x00B9450CORLA,0x0C
C:0x00BB70DEJNZC:009B
C:0x00BDE50BMOVA,0x0B
C:0x00BF150BDEC0x0B
C:0x00C17002JNZC:00C5
C:0x00C3150ADEC0x0A
C:0x00C5E50BMOVA,0x0B
C:0x00C7450AORLA,0x0A
C:0x00C970CAJNZC:0095
C:0x00CB22RET
分析汇编代码,其他汇编代码使用的不是DJNZ跳转方式,而是DEC和JNZ语句来实现循环判断。
1条JNZ指令要花费2个时钟周期,3条指令就需要6个机器周期,MOV指令和DEC指令各需要1小时钟周期,1个时钟周期按1μs算,其精度最多达到8μs,最后加上一条LCALL和一条RET语句,所以整个延时精度较差[4]。
利用Keil C的测试工具,在一处设置一个断点。
第1次执行到中断处的时间为0.000513s,第2次执行到中断处的时间为1.000922s,时间延迟为1.000409s,测试结果如图5所示。
对于上面的3种循环嵌套,循环次数为100×20×248=496000,每次循环的时间约为2μs。
图5三重嵌套循环1s实现时间测试结果
为获取与汇编语言延时的差距,同样进行1s的延时,程序代码段如下:
LCALL DELY1S
INC Second
DELY1S:MOV R5,#100
D2:MOV R6,#20
D1:MOV R7,#248
DJNZ R7,$
DJNZ R6,D1
DJNZ R5,D2
RET
通过Keil C51测试,其实际延迟时间为0.997943s。
虽然C语言实现延时方式的汇编代码复杂度增加,但是与汇编语言实现的方式性能差距并不大。
4总结
汇编语言在实时性方面具有较大的优越性,虽然使用Keil C51可以在C语言程序中嵌入汇编代码,但是复杂度明显提高。
实验证明,只要合理地运用C语言,在延时编程方面就可以达到与汇编语言相近的精度。
为了获得精确的时间延迟,可通过Keil C工具的仿真功能,调整延迟量,从而得到较理想的结果。