当前位置:
文档之家› 单片机C语言的精确延时程序设计
单片机C语言的精确延时程序设计
法 在生成目标代码时 会有所不同 开发人员必须研 究它生成的汇编语言代码 来保证时间的准确性 这也 许是除了使用嵌入汇编或直接编写汇编函数的唯一方 法 其实在单片机的 C 编译器中 已经有足够底层操作 方面的扩展 所以这里只考虑纯 C 语言的方法
3 延时程序设计
以德国 Keil 公司的 C 5 1 编译器为例 目前它已被公 认为业界的标准 以下讨论均假设 5 1 单片机时钟晶振 为 6 M H z 以小模式下编译 这时程序指令执行的最小 单位是 2 s 如果使用非英特尔且内核优化过的单片 机 应切换回普通模式 或仔细研究它的时序 以 D S 1 8 B 2 0 为例 临时在程序中需要延时 2 s 那么可以 用下述程序
val=val>>1
delay(10)
//因为写时间必须匹配
D0 = 1;
}
delay(2);
}
完整的 1 8 B 2 0 数据采集程序见本刊网站 w w w . d p j .
c o m . c n 其中包含了 CRC 校验高级语言的运 用大大提高了开发效率 在用 C 等高级语言编写这类时 间要求严格的程序时 分析最终的目标汇编代码是无法 避免的 在现代带有集成开发环境的编译器中 有软件 仿真 或有硬件调试功能 都给精确定时程序的开发带 来方便 充分发挥软件工具的潜力 才能真正在各种环 境下发挥高级语言带来的最大好处 效率
生成的汇编语言代码不是 LCALL delay1; LCALL delay2;
它被编译器优化 在调用 delay1 进入并执行该函数 返回前直接跳入 d e l a y 2 执行 类似于 delay1
LJMP delay 2;
由 delay 2 执行 RET 返回 这样连续调用两个函数时
就变成一次调用加跳转 结果会省去 2 s 解决了延
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
}
编译后的形式
EXPERIENCE EXCHANGE 经验交流
单片机 C 语言的精确延时程序设计
东北电力学院 盛文利
现在单片机的开发中 C 语言以其方便 灵活 逻 辑性强 易于维护和可移植性好等强大优势 占据了单 片机开发的主要地位 过去 由于单片机 C 语言本身存 在的缺陷 加之单片机工程项目本身都不大 C 语言在 单片机中的应用没有被重视 在 C 语言环境下 只需关 心程序的逻辑关系 实现细节则由编译器完成 这为快 速开发提供了条件 也大大减小了开发工作量 但同 时 实现细节就不被控制了 这个问题对于经常重视实 现细节的单片机 就必须要得到解决 好在一般的 C 语 言编译器都提供嵌入汇编 与汇编互调用和汇编级的代 码察看等功能 现以 Keil C51(见参考文献[1])下的单总线 器件程序开发为例 说明解决方法
_nop_()
//2 s
_nop_()
//2 s
_nop_()
//2 s
D0 = 1
/ / 恢复高电平
_nop_()
//2 s
_nop_()
//2 s
if(D0) value|=0x80 _nop_(); //2 s
delay(10)
/ / 等待
}
} while(vd);
}
编译后
RSEG ?PR?_delay5?DLY3
_delay5:
USING0
?C0003:
D J N Z R7,?C0003
RET
看来 do-while 是和 DJNZ 相对应编译的
4 调用时间和某些细节
除了直接插入 _ n o p _ ( ) 语句的短时间延时 在稍长 时间的多变延时程序中 往往需要调用上节讨论的延时 函数来完成延时 其中以汇编语言函数和 do-wh i l e 最易
return(value);
}
写 1 字节的程序
void write_byte(unsigned char val)
{ unsigned char i
for (i=0 i<8 i++) {
D0 = 0
// 把DQ拉低启动
delay(1)
//为4时不能读
D0 = val&0x01
#include< intrins.h> void somefunc(void) { _nop_() }
如果要延时 64 s 甚至 640 s 那么不可能在程序 中重复上述 _ n o p _ ( ) 虽然这并不会出错 可以利用它 来构建延时程序 精确定时的问题就变为延时程序的精 度问题 首先讨论空操作延时
3.1 空操作延时及延时函数
注意到 D S 1 8 B 2 0 最小时隙除大于 1 s 的外 最小 的就是 1 5 s 而数据的读或写也包含在这 1 5 s 中 由于定时器的延时要对定时器进行初始化 不易得到小 的延时 只能用于复位脉冲 即在 4 8 0  ̄ 9 6 0 s 之间的 延时 程序实现接近 9 6 0 s 的延时 TH0 = -950/256; TL0 = -950%256; TMOD= TMOD|0x01 TR0 = 1;
_delay4:
M O V vd?040,R7
2s
DJNZ R7, ¥
RET
END OF _delay4
注意 这里用于参数传递的 R 7 被复制保存起来了
这样在调用时 比汇编书写的函数多出 2 s
除此之外 还是可以编出全 C 的类似函数
void delay5(unsigned char vd)
{ do {vd--;
汇编时代常用的延时指令为
MOV R7 DDLY
DJNZ R7 ¥
产生最小 4 s 的延时 仿造的 C 程序如下
void delay1(ndly)
{for( ndly>0 ndly--)
}
生成的汇编代码与 D J N Z 无关 是如下形式的 其
中无关的编译注释已删除
RSEG ?PR?_delay1?DLY2
{ for (vd=0 vd<10 vd++)
}
生成的汇编代码如下
RSEG ?PR?_delay2?DLY2
_delay2:
USING0
CLR A
2s
M O V R7,A
2s
?C0002:
INC R7
2s
C J N E R7,#0AH,?C0002 4 s
?C0005:
RET
4s
; END OF _ delay2
初始化时序时间裕度大 容易实现 读写脉冲对时 序要求相对严格 尤其在慢速的 M C S - 5 1 下 指令的运 行在 s 级 读写时序在 15 s 的后一小部分 大约 4 s 不同批次的芯片会有少许差距 有的会允许你的时序有 少许误差 有的则非常严格
2 C 语言编译器
在用汇编语言编写程序时 很容易控制时间 因为 我们知道每条语句的执行时间 每段宏的执行时间以及 每段子程序加调用语句所消耗的时间 在单片机的 C 语 言开发中 C 语言编译器都对标准 C 作了针对单片机特 点的扩展 但对于不同的单片机 不同的 C 语言编译器 在将源程序翻译成目标机器语言时 会有不同的编译方
参考文献
1 Keil Software Keil uVision2 Reference Keil Electronik GmbH/Keil Software Inc 1995
2 DALLAS MAXIM 产品资料光盘 6.0版, 2002 收稿日期 2004-06-01
时问题 再根据参考文献[ 2 ] 可写出读字节程序
unsigned char read_byte(void)
{ unsigned char i
unsigned char value = 0
for (i=0 i<8 i++)
{ value>>=1;
//2 s
D0 = 0
/ / 把DQ拉低启动
列单片机进行产品开发工程技术人员的硬件和软件设计参考书 部分内容对其它类型单片机的开发也具有一定的参考价值
69 2004.10 Microcontrollers & Embedded Systems
万方数据
单片机C语言的精确延时程序设计
定时器启动后 单片机可以作其它事情 如复位看 门狗或 L E D 扫描
调用的短延时函数必须精确地受到控制 先作一个
67 2004.10 Microcontrollers & Embedded Systems
万方数据
经验交流
EXPERIENCE EXCHANGE
几 s 的延时实验程序 以观察它实际的延时时间 在
以上代码调用一次也有 14 s 之多 还是无法使用
也就是说 采用 f o r 形式的语句 生成的汇编代码都是
先减 再比较 还是与 D J N Z 无关 也就不能产生短
延时
3.2 短延时程序编写
首先想到的是用汇编语言函数或嵌入汇编的方法 汇编语言函数实现方法如下
N A M E DLY
?PR?_delay3?DLY SEGMENT CODE
?PR?_delay4?DLY SEGMENT CODE
?DT?_delay4?DLY SEGMENT DATA OVERLAYABLE PUBLIC
_delay4
RSEG ?DT?_delay4?DLY2
?_delay4?BYTE:
vd?040: DS 1
RSEG ?PR?_delay4?DLY2
1 单总线协议器件