us级延时可调用系统函数gettimeofday来实现,也可以用时钟计数器来实现
arm的要先使能用户态访问权限,v6 v7 v8都不一样 读取时钟计数器需先内核态使能,x86可以直接读
利用cpu时钟计数器可以ns级延时
以下代码
#include <sys/time.h>
#include <stddef.h>
#include <stdio.h>
#define CPUFREQ 7e8 // 700M
typedef void (*Delay)(unsigned int);
Delay delay;
//arm11 reset cycle counter
void rstccnt()
{
unsigned int tmp;
asm volatile ( "mrc p15, 0, %0, c15, c12, 0 \n"
"orr %0,%0,%1 \n"
"mcr p15, 0, %0, c15, c12, 0 \n"
:"+r" (tmp) : "r"(0x4));
}
unsigned int readccnt()
{
unsigned int tmp;
asm volatile ("mrc p15, 0, %0, c15, c12, 1 \n":"=r" (tmp));
return tmp;
}
void ccdelay(unsigned int cnt) //cycle counter delay
{
//cnt = ((CPUFREQ>>6) * cnt) >> 3 ;// avoid overflow
unsigned long long ct = cnt;
cnt = (CPUFREQ*ct)/1e6;
unsigned int tmp;
rstccnt();
tmp = readccnt();
while(tmp < cnt)
tmp = readccnt();
}
void tmdelay(unsigned int cnt)
{
struct timeval cur;
gettimeofday(&cur, NULL);
unsigned long long now = cur.tv_sec * 1e6 + cur.tv_usec;
unsigned long long end = now + cnt;
while(now < end){
gettimeofday(&cur, NULL);
now = cur.tv_sec * 1e6 + cur.tv_usec;
}
}
int main()
{
unsigned int t[50];
delay = ccdelay;
(*delay)(100);
t[0] = readccnt()*1e6/CPUFREQ;
delay = tmdelay;
for(int i = 1; i < 20; i++){
rstccnt();
(*delay)(20*i);
t[i] = readccnt()*1e6/CPUFREQ;
}
printf("tccnt 100 %u \n",t[0]);
for(int i = 1; i < 20; i++)
printf(" %u-%u \n",20*i,t[i]);
}
输出
tccnt 100 100
20-231
40-42
60-61
80-82
100-102
120-124
140-142
160-162
180-181
200-201
220-221
240-241
260-261
280-321
300-302
320-322
340-342
360-361
380-382
相差不大, gettimeofday只在初次延时较大。
bcm2835测试,其它需改写前面的汇编代码。
附 其它几个读取时钟计数器的汇编代码
arm64
asm volatile("mrs %0, cntvct_el0" :"=r"(ct));
arm32 v7
asm volatile("mrc p15,0,%0, c9, c13, 0":"=r"(ct));
x86 64
asm volatile("rdtsc ; shl $32, %%rdx ; or %%rdx, %0": "=a"(ct));