1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html 3)对正点原子Linux感兴趣的同学可以加群讨论:935446741 4)关注正点原子公众号,获取最新资料更新
第二十章 高精度延时实验
延时函数是很常用的API函数,在前面的实验中我们使用循环来实现延时函数,但是使用循环来实现的延时函数不准确,误差会很大。虽然使用到延时函数的地方精度要求都不会很严格(要求严格的话就使用硬件定时器了),但是延时函数肯定是越精确越好,这样延时函数就可以使用在某些对时序要求严格的场合。本章我们就来学习一下如何使用硬件定时器来实现高精度延时。
20.1 高精度延时简介 20.1.1 GPT定时器简介 学过STM32的同学应该知道,在使用STM32的时候可以使用SYSTICK来实现高精度延时。I.MX6U没有SYSTICK定时器,但是I.MX6U有其他定时器啊,比如第十八章讲解的EPIT定时器。本章我们使用I.MX6U的GPT定时器来实现高精度延时,顺便学习一下GPT定时器,GPT定时器全称为General Purpose Timer。 GPT定时器是一个32位向上定时器(也就是从0X00000000开始向上递增计数),GPT定时器也可以跟一个值进行比较,当计数器值和这个值相等的话就发生比较事件,产生比较中断。GPT定时器有一个12位的分频器,可以对GPT定时器的时钟源进行分频,GPT定时器特性如下: ①、一个可选时钟源的32位向上计数器。 ②、两个输入捕获通道,可以设置触发方式。 ③、三个输出比较通道,可以设置输出模式。 ④、可以生成捕获中断、比较中断和溢出中断。 ⑤、计数器可以运行在重新启动(restart)或(自由运行)free-run模式。 GPT定时器的可选时钟源如图20.1.1.1所示:
图20.1.1.1 GPT时钟源 从图20.1.1.1可以看出一共有五个时钟源,分别为:ipg_clk_24M、GPT_CLK(外部时钟)、ipg_clk、ipg_clk_32k和ipg_clk_highfreq。本例程选择ipg_clk为GPT的时钟源,ipg_clk=66MHz。 GPT定时器结构如图20.1.1.2所示:
图20.1.1.2 GPT定时器结构图 图20.1.1.2中各部分意义如下: ①、此部分为GPT定时器的时钟源,前面已经说过了,本章例程选择ipg_clk作为GPT定时器时钟源。 ②、此部分为12位分频器,对时钟源进行分频处理,可设置04095,分别对应14096分频。 ③、经过分频的时钟源进入到GPT定时器内部32位计数器。 ④和⑤、这两部分是GPT的两路输入捕获通道,本章不讲解GPT定时器的输入捕获。 ⑥、此部分为输出比较寄存器,一共有三路输出比较,因此有三个输出比较寄存器,输出比较寄存器是32位的。 ⑦、此部分位输出比较中断,三路输出比较中断,当计数器里面的值和输出比较寄存器里面的比较值相等就会触发输出比较中断。 GPT定时器有两种工作模式:重新启动(restart)模式和自由运行(free-run)模式,这两个工作模式的区别如下: 重新启动(restart)模式:当GPTx_CR(x=1,2)寄存器的FRR位清零的时候GPT工作在此模式。在此模式下,当计数值和比较寄存器中的值相等的话计数值就会清零,然后重新从0X00000000开始向上计数,只有比较通道1才有此模式!向比较通道1的比较寄存器写入任何数据都会复位GPT计数器。对于其他两路比较通道(通道2和3),当发生比较事件以后不会复位计数器。 自由运行(free-run)模式:当GPTx_CR(x=1,2)寄存器的FRR位置1时候GPT工作在此模式下,此模式适用于所有三个比较通道,当比较事件发生以后并不会复位计数器,而是继续计数,直到计数值为0XFFFFFFFF,然后重新回滚到0X00000000。 接下来看一下GPT定时器几个重要的寄存器,第一个就是GPT的配置寄存器GPTx_CR,此寄存器的结构如图20.1.1.3所示:
图20.1.1.3 寄存器GPTx_CR 寄存器GPTx_CR我们用到的重要位如下: SWR(bit15):复位GPT定时器,向此位写1就可以复位GPT定时器,当GPT复位完成以后此为会自动清零。 FRR(bit9):运行模式选择,当此位为0的时候比较通道1工作在重新启动(restart)模式。当此位为1的时候所有的三个比较通道均工作在自由运行模式(free-run)。 CLKSRC(bit8:6):GPT定时器时钟源选择位,为0的时候关闭时钟源;为1的时候选择ipg_clk作为时钟源;为2的时候选择ipg_clk_highfreq为时钟源;为3的时候选择外部时钟为时钟源;为4的时候选择ipg_clk_32k为时钟源;为5的时候选择ip_clk_24M为时钟源。本章例程选择ipg_clk作为GPT定时器的时钟源,因此此位设置位1(0b001)。 ENMOD(bit1):GPT使能模式,此位为0的时候如果关闭GPT定时器,计数器寄存器保存定时器关闭时候的计数值。此位为1的时候如果关闭GPT定时器,计数器寄存器就会清零。 EN(bit):GPT使能位,为1的时候使能GPT定时器,为0的时候关闭GPT定时器。 接下来看一下GPT定时器的分频寄存器GPTx_PR,此寄存器结构如图20.1.1.4所示:
图20.1.1.4 寄存器GPTx_PR寄存器 寄存器GPTx_PR我们用到的重要位就一个:PRESCALER(bit11:0),这就是12位分频值,可设置04095,分别对应14096分频。 接下来看一下GPT定时器的状态寄存器GPTx_SR,此寄存器结构如图20.1.1.5所示:
图20.1.1.5 GPTx_SR寄存器结构 寄存器GPTx_SR重要的位如下: ROV(bit5):回滚标志位,当计数值从0XFFFFFFFF回滚到0X00000000的时候此位置1。 IF2~IF1(bit4:3):输入捕获标志位,当输入捕获事件发生以后此位置1,一共有两路输入捕获通道。如果使用输入捕获中断的话需要在中断处理函数中清除此位。 OF3~OF1(bit2:0):输出比较中断标志位,当输出比较事件发生以后此位置1,一共有三路输出比较通道。如果使用输出比较中断的话需要在中断处理函数中清除此位。 接着看一下GPT定时器的计数寄存器GPTx_CNT,这个寄存器保存着GPT定时器的当前计数值。最后看一下GPT定时器的输出比较寄存器GPTx_OCR,每个输出比较通道对应一个输出比较寄存器,因此一个GPT定时器有三个OCR寄存器,它们的作都是相同的。以输出比较通道1为例,其输出比较寄存器为GPTx_OCR1,这是一个32位寄存器,用于存放32位的比较值。当计数器值和寄存器GPTx_OCR1中的值相等就会产生比较事件,如果使能了比较中断的话就会触发相应的中断。 关于GPT的寄存器就介绍到这里,关于这些寄存器详细的描述,请参考《I.MX6ULL参考手册》第1432页的30.6小节。 20.1.2 定时器实现高精度延时原理 高精度延时函数的实现肯定是要借助硬件定时器,前面说了本章实验使用GPT定时器来实现高精度延时。如果设置GPT定时器的时钟源为ipg_clk=66MHz,设置66分频,那么进入GPT定时器的最终时钟频率就是66/66=1MHz,周期为1us。GPT的计数器每计一个数就表示“过去”了1us。如果计10个数就表示“过去”了10us。通过读取寄存器GPTx_CNT中的值就知道计了个数,比如现在要延时100us,那么进入延时函数以后纪录下寄存器GPTx_CNT中的值为200,当GPTx_CNT中的值为300的时候就表示100us过去了,也就是延时结束。GPTx_CNT是个32位寄存器,如果时钟为1MHz的话,GPTx_CNT最多可以实现0XFFFFFFFFus=4294967295us≈4294s≈72min。也就是说72分钟以后GPTx_CNT寄存器就会回滚到0X00000000,也就是溢出,所以需要在延时函数中要处理溢出的情况。关于定时器实现高精度延时的原理就讲解到这里,原理还是很简单的,高精度延时的实现步骤如下: 1、设置GPT1定时器 首先设置GPT1_CR寄存器的SWR(bit15)位来复位寄存器GPT1。复位完成以后设置寄存器GPT1_CR寄存器的CLKSRC(bit8:6)位,选择GPT1的时钟源为ipg_clk。设置定时器GPT1的工作模式, 2、设置GPT1的分频值 设置寄存器GPT1_PR寄存器的PRESCALAR(bit111:0)位,设置分频值。 3、设置GPT1的比较值 如果要使用GPT1的输出比较中断,那么GPT1的输出比较寄存器GPT1_OCR1的值可以根据所需的中断时间来设置。本章例程不使用比较输出中断,所以将GPT1_OCR1设置为最大值,即:0XFFFFFFFF。 4、使能GPT1定时器 设置好GPT1定时器以后就可以使能了,设置GPT1_CR的EN(bit0)位为1来使能GPT1定时器。 5、编写延时函数 GPT1定时器已经开始运行了,可以根据前面介绍的高精度延时函数原理来编写延时函数,针对us和ms延时分别编写两个延时函数。 20.2 硬件原理分析 本试验用到的资源如下: ①、一个LED灯:LED0。 ②、定时器GPT1。 本实验通过高精度延时函数来控制LED0的闪烁,可以通过示波器来观察LED0的控制IO输出波形,通过波形的频率或者周期来判断延时函数精度是否正常。 20.3 实验程序编写 本实验对应的例程路径为:开发板光盘-> 1、裸机例程-> 12_highpreci_delay。 本章实验在上一章例程的基础上完成,更改工程名字为“delay”,直接修改bsp_delay.c和bsp_delay.h这两个文件,将bsp_delay.h文件改为如下所示内容:
示例代码20.3.1 bsp_delay.h文件代码
1 #ifndef __BSP_DELAY_H
2 #define __BSP_DELAY_H
3 /***************************************************************
4 Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
5 文件名 : bsp_delay.h
6 作者 : 左忠凯
7 版本 : V1.0
8 描述 : 延时头文件。
9 其他 : 无
10 论坛 : www.openedv.com
11 日志 : 初版V1.0 2019/1/4 左忠凯创建
12
13 V2.0 2019/1/15 左忠凯修改
14 添加了一些函数声明。
15 ***************************************************************/
16 #include "imx6ul.h"
17
18 /* 函数声明 */
19 void delay_init(void);
20 void delayus(unsigned int usdelay);
21 void delayms(unsigned int msdelay);
22 void delay(volatile unsigned int n);
23 void gpt1_irqhandler(void);
24
25 #endif
bsp_delay.h文件就是一些函数声明,很简单。在文件bsp_delay.c中输入如下内容:
示例代码20.3.2 bsp_delay.c文件代码
/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名 : bsp_delay.c
作者 : 左忠凯
版本 : V1.0
描述 : 延时文件。
其他 : 无
论坛 : www.openedv.com
日志 : 初版V1.0 2019/1/4 左忠凯创建
V2.0 2019/1/15 左忠凯修改
使用定时器GPT实现高精度延时,添加了:
delay_init 延时初始化函数
gpt1_irqhandler gpt1定时器中断处理函数
delayus us延时函数
delayms ms延时函数
***************************************************************/
1 #include "bsp_delay.h"
2
3 /*
4 * @description : 延时有关硬件初始化,主要是GPT定时器
5 GPT定时器时钟源选择ipg_clk=66Mhz
6 * @param : 无
7 * @return : 无
8 */
9 void delay_init(void)
10 {
11 GPT1->CR = 0; /* 清零 */
12 GPT1->CR = 1 > 15) & 0x01); /*等待复位完成 */
14
15 /*
16 * GPT的CR寄存器,GPT通用设置
17 * bit22:20 000 输出比较1的输出功能关闭,也就是对应的引脚没反应
18 * bit9: 0 Restart模式,当CNT等于OCR1的时候就产生中断
19 * bit8:6 001 GPT时钟源选择ipg_clk=66Mhz
20 */
21 GPT1->CR = (1OCR[0] = 0XFFFFFFFF;
38 GPT1->CR |= 1OCR[0] = 500000;
57
58 /*
59 * GPT的IR寄存器,使能通道1的比较中断
60 * bit0: 0 使能输出比较中断
61 */
62 GPT1->IR |= 1 = usdelay) /* 延时时间到了 */
113 break; /* 跳出 */
114 }
115 }
116 }
117
118 /*
119 * @description : 毫秒(ms)级延时
120 * @param - msdelay : 需要延时的ms数
121 * @return : 无
122 */
123 void delayms(unsigned int msdelay)
124 {
125 int i = 0;
126 for(i=0; iDR &= ~(1
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?