来源:百问网_嵌入式Linux wiki_jz2440 新1期视频维基教程 (视频文字版)
作者:韦东山
本文字数:1353,阅读时长:3分钟
在后续我们对讲解多个传感器,这几个传感器对时序的要求都比较高,比如温湿度传感器DH11,查看芯片手册时序,至少就需要微秒级的延时函数。
延时函数的方式一般有两种:
- ①:使用for循环,利用示波器等工具测得精确值;
- ②:使用定时器,通过不断检测定时器的计数值获得精确时间;
使用for循环的方式,可能会因为硬件的差异,导致延时函数不准,因此这里我们使用定时器的方式。
打开之前的timers.c文件,修改timer_init函数的配置。
PCLK仍然等于50000000,将prescaler value改为4,divider value设置为2,
这样,每减1, 对应0.2us;每减5, 对应1us;从50000减到0,对应10ms。
修改对应的寄存器:
TCFG0 = 4; /* Prescaler 0 = 4, 用于timer0,1 */TCFG1 &= ~0xf; /* MUX0 : 1/2 *//* 设置TIMER0的初值 */TCNTB0 = 50000; /* 10Ms中断一次 */
我们先写一个us延时的函数,然后ms延时就调用us即可。
因此,us延时函数里,尽量少调用函数。
假如现在要延时nus,我们先将n*5,得到nus对应的“计数时钟数”。
然后如果传入“计数时钟周期”如果大于0,则一直计算过去了多少个“计数时钟数”,与传入的“计数时钟数”相减,直到为零,退出循环,也就实现了延时nus。
怎样计算过去了多少个“计算周期”呢?
自然是当前的值,减去一开始进入函数的值。
但还有一种情况是定时器里的计数记到0时,会自动变成5000,计数计数,这时候,计算方式就变成了pre+(5000-cur):
/* 尽量少调用函数 */void udelay(int n){int cnt = n * 5; /* u us 对应n*5个计数值 */int pre = TCNTO0;int cur;int delta;while (cnt > 0){cur = TCNTO0;if (cur <= pre)delta = pre - cur;elsedelta = pre + (50000 - cur);cnt = cnt - delta;pre = cur;}}
然后是ms延时函数:
void mdelay(int m){udelay(m*1000);}
我们可以写一个测试函数,简单的测试下是否可用,测试函数隔1分钟进行打印一下。
如果us不准的话,放大至s,会有比较大的偏差,这样可以进行粗略的检测,精确检测可以使用示波器等工具。
void hrtimer_test(void){int cnt = 0;while (1){printf("delay one min: ");mdelay(60000); /* 延时1分钟 */printf("%d", ++cnt);}}
前面延时里的计算还是比较耗费时间的,因此,我们尽量提高CPU的运行时钟,并且 将尽可能的启动icache、dcache和mmu。
此外,如果延时过程中,发生了中断,如果中断比较耗时的话,就会导致延时可能出现不准确,所以,我们可以延时之前关中断, 延时之后开中断;
课后作业:
- a. 禁止icache, 禁止mmu, 修改lds, 测试延时函数是否还准确;
- b. 测试延时之前关中断, 延时之后开中断;
「新品首发」STM32MP157开发板火爆预售!首批仅300套