当前位置:首页 > 单片机 > 单片机
[导读]看到了个好帖,我在此在它得基础上再抛抛砖!有个好帖,从精度考虑,它得研究结果是: void delay2(unsigned char i) { while(-i); } 为最佳方法。 分析:假设外挂12M(之后都是在这基础上讨论) 我编译了下,传

看到了个好帖,我在此在它得基础上再抛抛砖!

有个好帖,从精度考虑,它得研究结果是:
void delay2(unsigned char i)
{
while(-i);
}
为最佳方法。
分析:假设外挂12M(之后都是在这基础上讨论)
我编译了下,传了些参数,并看了汇编代码,观察记录了下面的数据:
delay2(0):延时518us 518-2*256=6
delay2(1):延时7us(原帖写“5us”是错的,^_^)
delay2(10):延时25us 25-20=5
delay2(20):延时45us 45-40=5
delay2(100):延时205us 205-200=5
delay2(200):延时405us 405-400=5
见上可得可调度为2us,而最大误差为6us。
精度是很高了!
但这个程序的最大延时是为518us显然不
能满足实际需要,因为很多时候需要延迟比较长的时间。
那么,接下来讨论将t分配为两个字节,即uint型的时候,会出现什么情况。
void delay8(uint t)
{
while(-t);
}
我编译了下,传了些参数,并看了汇编代码,观察记录了下面的数据:
delay8(0):延时524551us 524551-8*65536=263
delay8(1):延时15us
delay8(10):延时85us 85-80=5
delay8(100):延时806us 806-800=6
delay8(1000):延时8009us 8009-8000=9
delay8(10000):延时80045us 80045-8000=45
delay8(65535):延时524542us 524542-524280=262
如果把这个程序的可调度看为8us,那么最大误差为263us,但这个延时程序还是不能满足要求的,因为延时最大为524.551ms。
那么用ulong t呢?
一定很恐怖,不用看编译后的汇编代码了。。。
那么如何得到比较小的可调度,可调范围大,并占用比较少得RAM呢?请看下面的程序:
/*-
程序名称:50us延时
注意事项:基于1MIPS,AT89系列对应12M晶振,W77.W78系列对应3M晶振
例子提示:调用delay_50us(20),得到1ms延时
全局变量:无
返回:无
-*/
void delay_50us(uint t)
{
uchar j;
for(;t>0;t-)
for(j=19;j>0;j-)
;
}
我编译了下,传了些参数,并看了汇编代码,观察记录了下面的数据:
delay_50us(1):延时63us 63-50=13
delay_50us(10):延时513us 503-500=13
delay_50us(100):延时5013us 5013-5000=13
delay_50us(1000):延时50022us 50022-50000=22
赫赫,延时50ms,误差仅仅22us,作为C语言已经是可以接受了。再说要求再精确的话,就算是用汇编也得改用定时器了。
/*-
程序名称:50ms延时
注意事项:基于1MIPS,AT89系列对应12M晶振,W77.W78系列对应3M晶振
例子提示:调用delay_50ms(20),得到1s延时
全局变量:无
返回:无
-*/
void delay_50ms(uint t)
{
uint j;
/****
可以在此加少许延时补偿,以祢补大数值传递时(如delay_50ms(1000))造成的误差,
但付出的代价是造成传递小数值(delay_50ms(1))造成更大的误差。
因为实际应用更多时候是传递小数值,所以补建议加补偿!
****/
for(;t>0;t-)
for(j=6245;j>0;j-)
;
}
我编译了下,传了些参数,并看了汇编代码,观察记录了下面的数据:
delay_50ms(1):延时50 010 10us
delay_50ms(10):延时499 983 17us
delay_50ms(100):延时4 999 713 287us
delay_50ms(1000):延时4 997 022 2.978ms
赫赫,延时50s,误差仅仅2.978ms,可以接受!
上面程序没有才用long,也没采用3层以上的循环,而是将延时分拆为两个程序以提高精度。应该是比较好的做法了。


C51延时从精度考虑,下面的方法为最佳方法:
voiddelay(unsignedchari)
{
while(--i);
}

功能:延时
参数:c的范围为:0 -255
当 C=0时,延时为 2 *256+6=518个机器周期
C为其它值时,延时为 2* C+5个机器周期
注释:① 以上是以常数调用的计算值,如果用变量调用,根据变量的存
储类型的不同可能要增加一到三个周期
② 编译环境:KeilC

voiddelay(unsigned char i)
{
while(--i); //不可写成i--,否则生成的代码很难看
}

编译后的子程序内是一条DJNZ指令,耗时2个周期,由于准备到R7中,调用LCALL,还有返回RET等要消耗时间,所以要加上5个周期。
用0调用时有点特别,0减1后变作0xFF,所以循环次数为256次,另外一点很有意思:
当用 1-255的常数调用时,生成的代码如下

MOVR7, #1 ; #1 - 255
LCALL _Delay

0 调用则多了条指令

CLR A
MOV R7, A
LCALL _Delay
所以参数为 0 时,要多加一个周期

用变量调用时(如Delay(i)之类的形式)根据变量i的存储类型加上相应的访问时间, 该函数的分辨率为 2个机器周期,最小延时为7个机器周期。
写延时程序时一定要考虑系统使用的晶振的频率。对于你所说的微秒级的延时,如果系统使用12MHZ的晶振,则一条NOP汇编指令便是延时1微秒。

在做ms级以下的延时时,一定要用汇编,那样才能够很精确的控制!其他主控程序可以使用c语言!

在C中嵌入汇编用下面格式
_asm
{
//在这里加入汇编的程序段
}

如果是单片机,我想可以用_nop_指令!一个_nop指令是12*1/12MHz=1us,所以,当然如果采用循环这也会消耗掉一定的时间,可以实机调试,用示波器观察!

有些特殊的应用会用到比较精确的延时(比如DS18B20等),而C不像汇编,延时精准度不好算。本人经过反复调试,对照KEIL编译后的汇编源文件,得出了以下几条精确延时的语句(绝对精确!本人已通过实际测试),今天贴上来,希望对需要的朋友有所帮助。

sbitLED=P1^0;//定义一个管脚(延时测试用)
unsignedinti=3;//注意i,j的数据类型,
unsignedcharj=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管脚状态,当LED
if((--i)==0)break;//为低时或者到了10*i+2个机器周期时跳出延时
//--------------------------------------------------------------------

例如18b20的复位函数(12M晶振):
//***********************************************************************
//函数功能:18B20复位
//入口参数:无
//出口参数:unsignedcharx:0:成功1:失败
//***********************************************************************
unsignedcharow_reset(void)
{
unsignedcharx=0;//12M晶振1个机器周期为1us
DQ=1; //DQ复位
j=10;while(--j);//稍做延时(延时10*2+1=21个机器周期,21us)
DQ=0; //单片机将DQ拉低
j=85;while(j--);//精确延时(大于480us)85*6+1=511us
DQ=1; //拉高总线
j=10;while(j--);//精确延时10*6+1=61us
x=DQ; //稍做延时后,
returnx; //如果x=0则初始化成功x=1则初始化失败
j=25;while(j--);//精确延时25*6+1=151us
}
//*********************************************************************************
再如红外解码程序:
(先说传统红外解码的弊端:
程序中用了while(IR_IO);while(!IR_IO);这样的死循环,如果管脚一直处于一种状态,就会一直执行while,造成“死机”现象。当然这种情况很少,但我们也的考虑到。而用以下程序则不会,在规定的时间内没有正确的电平信号就会返回主程序,这样就不会出现“死机”了)
//***************************外部中断0*******************************
voidint0(void)interrupt0
{
unsignedchari,j;
unsignedintcount=800;
//--------------8.5ms低电平引导码-------------------------------------
while(--count)
if(IR_IO==1)return;//在小于8ms内出现高电平,返回
count=100;//延时1ms
while(!IR_IO)//等待高电平
if((--count)==0)return;//在9ms内未出现高电平,返回
//-------------4.5ms高电平引导码------------------------------------
count=410;//延时4.1ms
while(--count)//...
if(IR_IO==0)return;//在4.1ms内出现低电平,返回
count=50;//延时0.5ms
while(IR_IO)//等待低电平
if((--count)==0)return;//在4.7ms内未出现低电平,返回
//-----------------------------------------------------------------
//------------4个数据码------------------------------------
for(j=0;j<4;j++)
{
for(i=0;i<8;i++)
{
IR_data[j]<<=1;//装入数据
count=60;//延时0.6ms
while(!IR_IO)//等待高电平
if((--count)==0)return;//在0.6ms内未出现高电平,返回
count=40;//低电平结束,继续
while(--count)//延时0.4ms
if(IR_IO==0)return;//在0.4ms内出现低电平,返回
count=100;//延时1.4ms
while(IR_IO)//检测IO状态
if((--count)==0)//等待1.4ms到来
{//在1.4ms内都是高电平
IR_data[j]|=1;//两个单位高电平,为数据1
break;//跳出循环
}
count=20;//延时0.2ms
while(IR_IO)//等待低电平跳出
if((--count)==0)return;//0.2ms内未出现低电平,返回
}
}
//-------------------------------------------------------------------
flag_IR=1;//置位红外接收成功标志
}

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭