1. delay_us()函数:你的微秒级“秒表”
在PIC单片机的世界里,delay_us() 函数就像是你手腕上那块精准的秒表,只不过它计时的单位是微秒(百万分之一秒)。很多刚接触PIC和CCS编译器的朋友,第一个调用的库函数往往就是它。你可能用它来让一个LED灯闪烁,或者等待传感器稳定。但你真的了解这个看似简单的函数背后是怎么运作的吗?它真的是“死等”吗?在不同的应用场景下,直接用它会不会出问题?今天,我就结合自己这些年踩过的坑和积累的经验,带你彻底搞懂PIC CCS编译器中的这个延时利器。
简单来说,delay_us() 是CCS C编译器为PIC单片机提供的一个内置延时函数。它的核心任务就是让CPU“原地等待”你指定的微秒数。和很多新手自己用for循环写的延时不同,这个函数是编译器“算”出来的。你只需要通过 #use delay(clock=20000000) 这样的指令告诉编译器你的单片机主频(比如这里用的是20MHz),编译器就会在编译阶段,根据这个时钟频率,精确计算出需要插入多少个空操作指令(NOP)或者循环,来凑够你要求的延时时间。所以,它的精度直接依赖于你告诉编译器的时钟频率是否准确。
这个函数用起来非常简单:delay_us(100); 就是延时100微秒。参数可以是一个常数(比如100),也可以是一个变量。但这里有个细节需要注意:根据官方资料,如果使用变量作为参数,这个变量的值范围是0到255;如果直接使用常数,范围则可以大到0到65535。这意味着,如果你需要延时超过255微秒但又是变量控制的情况,就得动点脑筋了,我们后面会讲到如何优化。
2. 庖丁解牛:delay_us()是如何工作的?
2.1 编译器的“魔法”:从代码到机器周期
当你写下 delay_us(50) 并编译时,编译器其实在背后干了一件非常“机械”但精确的活儿。它并没有调用任何硬件定时器,而是通过插入特定数量的汇编指令来实现延时。这些指令通常是执行时间已知的 NOP(空操作)或者短循环。
举个例子,假设你的PIC单片机在20MHz晶振下,一个指令周期是0.2微秒(因为PIC大多数指令是4个时钟周期,20MHz下1个时钟周期50ns,4个就是200ns,即0.2us)。那么,要实现50微秒的延时,编译器就需要插入足够多的指令,让它们的总执行时间达到50us。它会精确计算需要多少个 NOP 或者循环体。对于非常短的延时(比如几个微秒),编译器可能会直接生成内联(INLINE)的 NOP 指令序列。对于较长的延时或者变量延时,则会生成一个函数调用,在函数内部进行循环。
这就是为什么 #use delay(clock=20000000) 这条指令至关重要。它是编译器进行所有计算的基准。如果你实际电路上的晶振是16MHz,但你在代码里写的是 clock=20000000,那么所有的延时都会比预期长,因为编译器按照20MHz的节奏来“数拍子”,而你的单片机实际跑得慢。
2.2 中断:精准延时的“头号杀手”
这是理解 delay_us() 行为的关键点,也是很多新手程序出Bug的根源。官方说明里明确提到:“如果有中断服务程序被执行,由于时间花费在中断服务程序上而使时间不向前计数。” 这句话是什么意思呢?
想象一下,你让单片机延时100微秒,单片机开始在心里默数。数到第50微秒时,突然一个中断(比如定时器溢出中断、外部引脚中断)发生了。CPU会立刻放下手头的“数数”工作,转头去执行中断服务程序(I

9108

被折叠的 条评论
为什么被折叠?



