延时最简单粗暴的方式就是使用空循环来延时,依赖的是时钟主频(默认是396M)来计数,一旦修改了 6ull 的时钟主频,延时效果就会存在偏差。
因此我们可以使用 EPIT 或者 GPT 的计数功能实现高精度延时,EPIT 是向下计数(自减),GPT 是向上计数(自增)。
目录
一、GPT 初始化
二、延时函数delayus(us 级)
1、原理解析
2、具体实现
三、延时函数delayms(ms级)
一、GPT 初始化
和前面定时中断不一样,如果只是要实现延时功能的话,无需初始化中断,因为延时只用到了 GPT 的计数功能。
- GPT 复位,并等待复位完成
- 配置GPT定时器(设置计数器初始值、选择时钟源、选择工作模式)
- 设置分频数(66 分频)
- 启动定时器
/** @description : gpt 定时初始化(不会产生中断,只是单纯的计数)* @param : 定时器时间间隔* @return : 无*/
void delay_init()
{// 禁用GPT定时器GPT1_CR = 0;GPT1_CR |= (1 << 15);while((GPT1_CR >> 15) & 0x01); // 等待复位完成/* * 配置GPT定时器* bit 1: 1 设置计数器初始值为0* bit 8-6: 001 选择时钟源ipg_clk* bit 9: 1 选择工作模式free-run*/GPT1_CR |= ((1 << 1) | (1 << 6) | (1 << 9));// 分频GPT1_PR &= ~(0xFFF << 0); // 低 12 bit 清零GPT1_PR |= (65 << 0); // 66 分频// 启动定时器GPT1_CR |= (1 << 0);
}
二、延时函数delayus(us 级)
比如我们要延时 500ms,66MHz时钟源,66分频的条件下,500ms 转换成计数值就是 500000,因此当 delta >= 500000 时,说明延时完毕,所以问题的关键就是求从开始到现在经过了多少计数值。
1、原理解析
Free-Run 模式下,当计数器到达 0xFFFF FFFF 时就会溢出,重新从 0 开始计数。如果计数器没有溢出,从开始到现在,计数的差值如下。(计数的差值是和时间关联的)
delta = newcount - oldcount;
如果计数器溢出了,从开始到现在,计数的差值如下
delta = delta1 + delta2 = 0xFFFFFFFF - oldcount + newcount;
注意:这里的 period 与时钟周期无关
2、具体实现
判断是否移除,需要用到 GPT1_SR 的 ROV 位(bit 5)。
/** @description : 延时函数(us级)* @param - n : 要延时的us数* @return : 无*/
void delayus(unsigned int val)
{// 获取到当前计数器的值unsigned int oldcount = GPT1_CNT;unsigned int delta = 0;while (1){unsigned int newcount = GPT1_CNT;// 判断是否溢出if ((GPT1_SR >> 5) & 0x01) // 说明溢出了{delta = 0xFFFFFFFF - oldcount + newcount;}else{delta = newcount - oldcount;}if (delta >= val) break; // 延时完毕,跳出循环}GPT1_SR &= ~(1 << 5); // 清除溢出标志位
}
解析:if (delta >= val)
66 MHz 的时钟源,66分频 =》一个时钟周期的间隔就是 1/1M = 1/10^6
val 的单位是 us:
- 转换成 s,结果 = val / 10^6
- 转换成对应的计数值 = 10^6 * val / 10^6 = val
因此,val (us)经过转换以后,最终的计数值就是 val
三、延时函数delayms(ms级)
/** @description : 延时函数(ms级)* @param - n : 要延时的ms数* @return : 无*/
void delayms(unsigned int val)
{delayus(val * 1000);
}