c语言打字时延,C语言里如何编写精确的微量延时

本文介绍了如何在AVR单片机的C语言编程中通过宏定义实现精确的软件延时,解决了C语言在时序性上的不足。作者提供了一种使用汇编空操作指令的宏构造方法,通过定义一系列NOP宏,实现了1到40个时钟周期的精确延时。同时,文中也注意到了宏在条件语句中的使用问题,并给出了解决方案,以确保代码的正确执行。此外,还给出了整理后的头文件`NOP.H`,供开发者在项目中直接引用。
摘要由CSDN通过智能技术生成

广西民族大学物理与电子工程学院    白羽

众所周知,相比于其他编程语言,C语言在编写嵌入式编程中有着绝对的优势。但它总也有缺点的:它的时序性比较差,不容易编写精准的延时。而在编写嵌入系统驱动程序时,常常需要比较精确的软件延时,这使得C语言的“劣势”暴露了出来,一般都只能通过嵌入汇编的方式实现。例如,在1MHZ工作频率下需要延时10us,就需要嵌入10句“空操作”指令,显然在书写上比较难堪。本文提出一种简化书写的延时方案,使用带参数的宏构来造微小时间片,可以实现完全精确的软件延时,大大方便了驱动程序及软件模拟通信协议的编写。

说明:以下皆为ICC AVR平台下的讨论,对AVR系列所有型号的单片机皆有效。至于其他平台,可据此方案自行修改和移值。

该方案的实现方法其实很简单:

首先定义N个宏,分别调用 1 ~ N 个汇编“空操作”指令,如:

#define   NOP_1      asm("nop")               //延时一个时钟周期

#define   NOP_2      NOP_1;   asm("nop")      //延时两个时钟周期

#define   NOP_3      NOP_2;   asm("nop")      //延时三个时钟周期

#define   NOP_4      NOP_3;   asm("nop")      //延时四个时钟周期

……

#define  NOP_40   NOP_40;  asm("nop")         //延时40个时钟周期

然后利用“##”操作符,实现带参数宏的延时:

#define   NOP(N)    NOP_##N                   //延时 N个时钟周期

操作符的作用是把两个部分的内容连成一个内容。就是说,NOP(3)展开后成为NOP_3,NOP(4)展开后成为NOP_4,等等。因此,定义上述宏之后,就可通过调用NOP(N)语句实现精确软件延时。例如:

NOP(4);   //延时4个时钟周期

上述语句展开过程如下:

NOP_3 ;   asm("nop");

NOP_2;   asm("nop");   asm("nop");

NOP_1;   asm("nop");   asm("nop");   asm("nop");

asm("nop");   asm("nop");   asm("nop");   asm("nop");

正好延时4个时钟周期

不过,上面的宏还不够完善,如果试图使用下面的语句,程序将会出现漏洞。

if(表达式)

NOP(3);

else

NOP(4);

这是因为NOP(N)宏展开之后,不是一个语句,而是变成N个语句。故必须用花括号括起来,程序才能运行正确。即应该改为:

if(表达式)

{

NOP(3);

}

else

{

NOP(4);

}

如果把NOP(N)宏的定义改为:

#define  NOP(N)      do{   NOP_##N ;   }while(0)

则NOP(N)宏展开之后只形成一个语句,将不会出现上面的问题。

但是要注意,“##”操作符只能按照原样把两边的内容连在一起。故NOP(N)的参数必须是具体的常量,即只能是数字,并且,与该数字相对应的宏NOP_N已必须已经定义。

例如:

“NOP(3+4);”语句展开之后,将将变成“NOP_3+4;”,出现语法错误;

又如:

“NOP(a);”语句展开之后,将将变成“NOP_a;”,而“NOP_a;”未定义。

只有这样的语句才是正确的调用:

NOP(20);      //延时20个时钟周期

将上述方案整理成一个头文件,以后就可以任意调用了。下面是整理好的头文件:

注意:该文件不宜作长时间的延时。长时间的延时将会调用大量的“空操作”指令,占用大量的程序指令空间。这个问题将在V2.0版本中解决。

NOP.H

/*********************************************************************

单    位:广西民族大学物理与电子工程学院

文件名称:NOP.H

文件标识:_NOP_H_

摘    要:精密延时头文件,可以精确延时40以内(包括40)的时钟周期

当前版本:V1.0

作    者:白  羽

完成日期:2010年5月9日

特别声明:您可以任意转载、复制本文件,但不能随便剔除本文件说明

*********************************************************************/

#ifndef  _NOP_H_

#define  _NOP_H_

#define  NOP(N)    do{ NOP_##N(); }while(0)

#define  NOP_0()

#define  NOP_1()   asm("nop")

#define  NOP_2()   NOP_1();asm("nop")

#define  NOP_3()   NOP_2();asm("nop")

#define  NOP_4()   NOP_3();asm("nop")

#define  NOP_5()   NOP_4();asm("nop")

#define  NOP_6()   NOP_5();asm("nop")

#define  NOP_7()   NOP_6();asm("nop")

#define  NOP_8()   NOP_7();asm("nop")

#define  NOP_9()   NOP_8();asm("nop")

#define  NOP_10()  NOP_9();asm("nop")

#define  NOP_11()  NOP_10();asm("nop")

#define  NOP_12()  NOP_11();asm("nop")

#define  NOP_13()  NOP_12();asm("nop")

#define  NOP_14()  NOP_13();asm("nop")

#define  NOP_15()  NOP_14();asm("nop")

#define  NOP_16()  NOP_15();asm("nop")

#define  NOP_17()  NOP_16();asm("nop")

#define  NOP_18()  NOP_17();asm("nop")

#define  NOP_19()  NOP_18();asm("nop")

#define  NOP_20()  NOP_19();asm("nop")

#define  NOP_21()  NOP_20();asm("nop")

#define  NOP_22()  NOP_21();asm("nop")

#define  NOP_23()  NOP_22();asm("nop")

#define  NOP_24()  NOP_23();asm("nop")

#define  NOP_25()  NOP_24();asm("nop")

#define  NOP_26()  NOP_25();asm("nop")

#define  NOP_27()  NOP_26();asm("nop")

#define  NOP_28()  NOP_27();asm("nop")

#define  NOP_29()  NOP_28();asm("nop")

#define  NOP_30()  NOP_29();asm("nop")

#define  NOP_31()  NOP_30();asm("nop")

#define  NOP_32()  NOP_31();asm("nop")

#define  NOP_33()  NOP_32();asm("nop")

#define  NOP_34()  NOP_33();asm("nop")

#define  NOP_35()  NOP_34();asm("nop")

#define  NOP_36()  NOP_35();asm("nop")

#define  NOP_37()  NOP_36();asm("nop")

#define  NOP_38()  NOP_37();asm("nop")

#define  NOP_39()  NOP_38();asm("nop")

#define  NOP_40()  NOP_39();asm("nop")

#endif

由于不清楚具体实现的细节,以下仅提供一个简单的代码框架供参考: ``` #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <string.h> #include <unistd.h> #include <time.h> #include "nRF24L01.h" // 24L01库头文件 #define DELAY_TIME_MS 1000 // 时延时间,单位:毫秒 #define PAYLOAD_SIZE 4 // 数据有效载荷长度,单位:字节 int main(void) { // 初始化24L01模块 if (!nRF24L01_Init()) { printf("nRF24L01 module initialization failed.\n"); exit(EXIT_FAILURE); } // 设置通信参数 nRF24L01_SetChannel(0); // 设置通信信道 nRF24L01_SetDataRate(RATE_2M); // 设置数据率 nRF24L01_SetPower(TX_PWR_0dBm); // 设置发射功率 nRF24L01_SetAutoRetransmit(0x0F, 0x0F); // 设置自动重发参数 nRF24L01_SetCRCLength(CRC_16); // 设置CRC校验长度 nRF24L01_SetAddressWidth(5); // 设置地址宽度 // 设置分别设置发送端和接收端地址 uint8_t txAddr[] = { 0xE7, 0xE7, 0xE7, 0xE7, 0xE7 }; uint8_t rxAddr[] = { 0xC2, 0xC2, 0xC2, 0xC2, 0xC2 }; // 设置接收端地址 nRF24L01_SetRXAddress(rxAddr); while (true) { // 发送数据 uint8_t txBuffer[PAYLOAD_SIZE]; memset(&txBuffer, 0, sizeof(txBuffer)); // 清空数据数组 uint32_t timestamp = (uint32_t)time(NULL); // 获取当前时间戳 memcpy(&txBuffer, &timestamp, sizeof(timestamp)); // 将时间戳拷贝到数据数组中 nRF24L01_WritePayload(txBuffer, PAYLOAD_SIZE); // 发送数据 // 等待一段时间 usleep(DELAY_TIME_MS * 1000); // 接收数据 bool rxStatus = nRF24L01_ReadPayload(rxBuffer, PAYLOAD_SIZE); // 接收数据 if (rxStatus) { uint32_t rxTimestamp = 0; memcpy(&rxTimestamp, &rxBuffer, sizeof(rxTimestamp)); // 解析接收到的时间戳 uint32_t delay = time(NULL) - rxTimestamp; // 计算时延 printf("Delay is %d seconds\n", delay); // 输出时延 } } return 0; } ``` 值得注意的是,这使用了C标准库中的`time()`函数来获取当前时间戳。由于`time()`函数的精度较低,可能会影响到精度较高的时延计算,因此更好的实现方案是使用微秒级别的定时器来计时。此外,还需要对数据的传输进行一定的差错控制,确保数据的可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值