1

《I.MX6U嵌入式Linux驱动开发指南》第十八章 EPIT定时器实验

 2 years ago
source link: https://blog.51cto.com/u_15046463/5381344
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

EPIT定时器实验

定时器是最常用的外设,常常需要使用定时器来完成精准的定时功能,I.MX6U提供了多种硬件定时器,有些定时器功能非常强大。本章我们从最基本的EPIT定时器开始,学习如何配置EPIT定时器,使其按照给定的时间,周期性的产生定时器中断,在定时器中断里面我们可以做其它的处理,比如翻转LED灯。

18.1 EPIT定时器简介​

EPIT的全称是:Enhanced Periodic Interrupt Timer,直译过来就是增强的周期中断定时器,它主要是完成周期性中断定时的。学过STM32的话应该知道,STM32里面的定时器还有很多其它的功能,比如输入捕获、PWM输出等等。但是I.MX6U的EPIT定时器只是完成周期性中断定时的,仅此一项功能!至于输入捕获、PWM输出等这些功能,I.MX6U由其它的外设来完成。

EPIT是一个32位定时器,在处理器几乎不用介入的情况下提供精准的定时中断,软件使能以后EPIT就会开始运行,EPIT定时器有如下特点:

①、时钟源可选的32位向下计数器。

②、12位的分频值。

③、当计数值和比较值相等的时候产生中断。

EPIT定时器结构如图18.1.1所示:

《I.MX6U嵌入式Linux驱动开发指南》第十八章 EPIT定时器实验_#include

图18.1.1 EPIT定时器框图

图18.1.1中各部分的功能如下:

①、这是个多路选择器,用来选择EPIT定时器的时钟源,EPIT共有3个时钟源可选择,ipg_clk、ipg_clk_32k和ipg_clk_highfreq。

②、这是一个12位的分频器,负责对时钟源进行分频,12位对应的值是0~4095,对应着1~4096分频。

③、经过分频的时钟进入到EPIT内部,在EPIT内部有三个重要的寄存器:计数寄存器(EPIT_CNR)、加载寄存器(EPIT_LR)和比较寄存器(EPIT_CMPR),这三个寄存器都是32位的。EPIT是一个向下计数器,也就是说给它一个初值,它就会从这个给定的初值开始递减,直到减为0,计数寄存器里面保存的就是当前的计数值。如果EPIT工作在set-and-forget模式下,当计数寄存器里面的值减少到0,EPIT就会重新从加载寄存器读取数值到计数寄存器里面,重新开始向下计数。比较寄存器里面保存的数值用于和计数寄存器里面的计数值比较,如果相等的话就会产生一个比较事件。

④、比较器。

⑤、EPIT可以设置引脚输出,如果设置了的话就会通过指定的引脚输出信号。

⑥、产生比较中断,也就是定时中断。

EPIT定时器有两种工作模式:set-and-forget和free-running,这两个工作模式的区别如下:

set-and-forget模式:EPITx_CR(x=1,2)寄存器的RLD位置1的时候EPIT工作在此模式下,在此模式下EPIT的计数器从加载寄存器EPITx_LR中获取初始值,不能直接向计数器寄存器写入数据。不管什么时候,只要计数器计数到0,那么就会从加载寄存器EPITx_LR中重新加载数据到计数器中,周而复始。

free-running模式:EPITx_CR寄存器的RLD位清零的时候EPIT工作在此模式下,当计数器计数到0以后会重新从0XFFFFFFFF开始计数,并不是从加载寄存器EPITx_LR中获取数据。

接下来看一下EPIT重要的几个寄存器,第一个就是EPIT的配置寄存器EPITx_CR,此寄存器的结构如图18.1.2所示:

《I.MX6U嵌入式Linux驱动开发指南》第十八章 EPIT定时器实验_寄存器_02

图18.1.2 EPITx_CR寄存器结构图

寄存器EPITx_CR我们用到的重要位如下:

CLKSRC(bit25:24):EPIT时钟源选择位,为0的时候关闭时钟源,1的时候选择选择Peripheral时钟(ipg_clk),为2的时候选择High-frequency参考时钟(ipg_clk_highfreq),为3的时候选择Low-frequency参考时钟(ipg_clk_32k)。在本例程中,我们设置为1,也就是选择ipg_clk作为EPIT的时钟源,ipg_clk=66MHz。

PRESCALAR(bit15:4):EPIT时钟源分频值,可设置范围0~4095,分别对应1~4096分频。

RLD(bit3):EPIT工作模式,为0的时候工作在free-running模式,为1的时候工作在set-and-forget模式。本章例程设置为1,也就是工作在set-and-forget模式。

OCIEN(bit2):比较中断使能位,为0的时候关闭比较中断,为1的时候使能比较中断,本章试验要使能比较中断。

ENMOD(bit1):设置计数器初始值,为0时计数器初始值等于上次关闭EPIT定时器以后计数器里面的值,为1的时候来源于加载寄存器。

EN(bit0):EPIT使能位,为0的时候关闭EPIT,为1的时候使能EPIT。

寄存器EPITx_SR结构体如图18.1.3所示:

《I.MX6U嵌入式Linux驱动开发指南》第十八章 EPIT定时器实验_#include_03

图18.1.3 EPITx_SR寄存器结构图

寄存器EPITx_SR只有一个位有效,那就是OCIF(bit0),这个位是比较中断标志位,为0的时候表示没有比较事件发生,为1的时候表示有比较事件发生。当比较中断发生以后需要手动清除此位,此位是写1 清零的。

寄存器EPITx_LR、EPITx_CMPR和EPITx_CNR分别为加载寄存器、比较寄存器和计数寄存器,这三个寄存器都是用来存放数据的,很简单。

关于EPIT的寄存器就介绍到这里,关于这些寄存器详细的描述,请参考《I.MX6ULL参考手册》第1174页的24.6小节。本章我们使用EPIT产生定时中断,然后在中断服务函数里面翻转LED0,接下来以EPIT1为例,讲解需要哪些步骤来实现这个功能。EPIT的配置步骤如下:

1、设置EPIT1的时钟源

设置寄存器EPIT1_CR寄存器的CLKSRC(bit25:24)位,选择EPIT1的时钟源。

2、设置分频值

设置寄存器EPIT1_CR寄存器的PRESCALAR(bit15:4)位,设置分频值。

3、设置工作模式

设置寄存器EPIT1_CR的RLD(bit3)位,设置EPTI1的工作模式。

4、设置计数器的初始值来源

设置寄存器EPIT1_CR的ENMOD(bit1)位,设置计数器的初始值来源。

5、使能比较中断

我们要使用到比较中断,因此需要设置寄存器EPIT1_CR的OCIEN(bit2)位,使能比较中断。

6、设置加载值和比较值

设置寄存器EPIT1_LR中的加载值和寄存器EPIT1_CMPR中的比较值,通过这两个寄存器就可以决定定时器的中断周期。

7、EPIT1中断设置和中断服务函数编写

使能GIC中对应的EPIT1中断,注册中断服务函数,如果需要的话还可以设置中断优先级。最后编写中断服务函数。

8、使能EPIT1定时器

配置好EPIT1以后就可以使能EPIT1了,通过寄存器EPIT1_CR的EN(bit0)位来设置。

通过以上几步我们就配置好EPIT了,通过EPIT的比较中断来实现LED0的翻转。

18.2 硬件原理分析​

本试验用到的资源如下:

①、LED0。

②、 定时器EPTI1。

本实验通过EPTI1的中断来控制LED0的亮灭,LED0的硬件原理前面已经介绍过了。

18.3 实验程序编写​

本实验对应的0例程路径为:开发板光盘-> 1、裸机例程-> 10_epit_timer。

本章实验在上一章例程的基础上完成,更改工程名字为“epit_timer”,然后在bsp文件夹下创建名为“epittimer”的文件夹,然后在bsp/epittimer中新建bsp_epittimer.c和bsp_epittimer.h这两个文件。在bsp_epittimer.h中输入如下内容:

示例代码18.3.1 bsp_epittimer.h文件代码
1 #ifndef _BSP_EPITTIMER_H
2 #define _BSP_EPITTIMER_H
3 /***************************************************************
4 Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
5 文件名 : bsp_epittimer.h
6 作者 : 左忠凯
7 版本 : V1.0
8 描述 : EPIT定时器驱动头文件。
9 其他 : 无
日志 : 初版V1.0 2019/1/5 左忠凯创建
12 ***************************************************************/
13 #include "imx6ul.h"
14
15 /* 函数声明 */
16 void epit1_init(unsigned int frac, unsigned int value);
17 void epit1_irqhandler(void);
18
19 #endif

bsp_epittimer.h文件很简单,就是一些函数声明。然后在bsp_epittimer.c中输入如下内容:

示例代码18.3.2 bsp_epittimer.c文件代码
/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名 : bsp_epittimer.c
作者 : 左忠凯
版本 : V1.0
描述 : EPIT定时器驱动文件。
其他 : 配置EPIT定时器,实现EPIT定时器中断处理函数
日志 : 初版V1.0 2019/1/5 左忠凯创建
***************************************************************/
1 #include "bsp_epittimer.h"
2 #include "bsp_int.h"
3 #include "bsp_led.h"
4
5 /*
6 * @description : 初始化EPIT定时器.
7 * EPIT定时器是32位向下计数器,时钟源使用ipg=66Mhz
8 * @param – frac : 分频值,范围为0~4095,分别对应1~4096分频。
9 * @param - value : 倒计数值。
10 * @return : 无
11 */
12 void epit1_init(unsigned int frac, unsigned int value)
13 {
14 if(frac > 0XFFF)
15 frac = 0XFFF;
16 EPIT1->CR = 0; /* 先清零CR寄存器 */
17
18 /*
19 * CR寄存器:
20 * bit25:24 01 时钟源选择Peripheral clock=66MHz
21 * bit15:4 frac 分频值
22 * bit3: 1 当计数器到0的话从LR重新加载数值
23 * bit2: 1 比较中断使能
24 * bit1: 1 初始计数值来源于LR寄存器值
25 * bit0: 0 先关闭EPIT1
26 */
27 EPIT1->CR = (1<<24 | frac << 4 | 1<<3 | 1<<2 | 1<<1);
28 EPIT1->LR = value; /* 加载寄存器值 */
29 EPIT1->CMPR = 0; /* 比较寄存器值 */
30
31 /* 使能GIC中对应的中断 */
32 GIC_EnableIRQ(EPIT1_IRQn);
33
34 /* 注册中断服务函数 */
35 system_register_irqhandler(EPIT1_IRQn,
(system_irq_handler_t)epit1_irqhandler,
NULL);
36 EPIT1->CR |= 1<<0; /* 使能EPIT1 */
37 }
38
39 /*
40 * @description : EPIT中断处理函数
41 * @param : 无
42 * @return : 无
43 */
44 void epit1_irqhandler(void)
45 {
46 static unsigned char state = 0;
47 state = !state;
48 if(EPIT1->SR & (1<<0)) /* 判断比较事件发生 */
49 {
50 led_switch(LED0, state); /* 定时器周期到,反转LED */
51 }
52 EPIT1->SR |= 1<<0; /* 清除中断标志位 */
53 }

bsp_epittimer.c里面有两个函数epit1_init和epit1_irqhandler,分别是EPIT1初始化函数和EPIT1中断处理函数。epit1_init有两个参数frac和value,其中frac是分频值,value是加载值。在第29行设置比较寄存器为0,也就是当计数器倒计数到0以后就会触发比较中断,因此分频值frac和value就可以决定中断频率,计算公式如下:

Tout = ((frac +1 )* value) / Tclk;

Tclk:EPIT1的输入时钟频率(单位Hz)。

Tout:EPIT1的溢出时间(单位S)。

第38行设置了EPIT1工作模式为set-and-forget,并且时钟源为ipg_clk=66MHz。假如我们现在要设置EPIT1中断周期为500ms,可以设置分频值为0,也就是1分频,这样进入EPIT1的时钟就是66MHz。如果要实现500ms的中断周期,EPIT1的加载寄存器就应该为66000000/2=33000000。

函数epit1_irqhandler是EPIT1的中断处理函数,此函数先读取EPIT1_SR寄存器,判断当前的中断是否为比较事件,如果是的话就翻转LED灯。最后在退出中断处理函数的时候需要清除中断标志位。

最后就是main.c文件了,在main.c里面输入如下内容:

示例代码18.3.3 main.c文件代码
/**************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名 : main.c
作者 : 左忠凯
版本 : V1.0
描述 : I.MX6U开发板裸机实验10 EPIT定时器实验
其他 : 本实验主要学习使用I.MX6UL自带的EPIT定时器,学习如何使用
EPIT定时器来实现定时功能,巩固Cortex-A的中断知识。
日志 : 初版V1.0 2019/1/4 左忠凯创建
**************************************************************/
1 #include "bsp_clk.h"
2 #include "bsp_delay.h"
3 #include "bsp_led.h"
4 #include "bsp_beep.h"
5 #include "bsp_key.h"
6 #include "bsp_int.h"
7 #include "bsp_epittimer.h"
8
9 /*
10 * @description : main函数
11 * @param : 无
12 * @return : 无
13 */
14 int main(void)
15 {
16 int_init(); /* 初始化中断(一定要最先调用!) */
17 imx6u_clkinit(); /* 初始化系统时钟 */
18 clk_enable(); /* 使能所有的时钟 */
19 led_init(); /* 初始化led */
20 beep_init(); /* 初始化beep */
21 key_init(); /* 初始化key */
22 epit1_init(0, 66000000/2); /* 初始化EPIT1定时器,1分频
23 * 计数值为:66000000/2,也就是
24 * 定时周期为500ms。
25 */
26 while(1)
27 {
28 delay(500);
29 }
30
31 return 0;
32 }

main.c里面就一个main函数,第22行调用函数epit1_init来初始化EPIT1,分频值为0,也就是1分频,加载寄存器值为66000000/2=33000000,EPIT1定时器中断周期为500ms。第26~29行的while循环里面就只有一个延时函数,没有做其他处理,延时函数都可以取掉。

18.4 编译下载验证​

18.4.1 编写Makefile和链接脚本

修改Makfile中的TARGET为epit,在INCDIRS和SRCDIRS中加入“bsp/epittimer”,修改后的Makefile如下:

示例代码18.4.1.1 Makefile文件代码
1 CROSS_COMPILE ?= arm-linux-gnueabihf-
2 TARGET ?= epit
3
4 /* 省略掉其它代码...... */
5
6 INCDIRS := imx6ul \
7 bsp/clk \
8 bsp/led \
9 bsp/delay \
10 bsp/beep \
11 bsp/gpio \
12 bsp/key \
13 bsp/exit \
14 bsp/int \
15 bsp/epittimer
16
17 SRCDIRS := project \
18 bsp/clk \
19 bsp/led \
20 bsp/delay \
21 bsp/beep \
22 bsp/gpio \
23 bsp/key \
24 bsp/exit \
25 bsp/int \
26 bsp/epittimer
27
28 /* 省略掉其他代码...... */
29
30 clean:
31 rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

第2行修改变量TARGET为“epit”,也就是目标名称为“epit”。

第15行在变量INCDIRS中添加EPIT1驱动头文件(.h)路径。

第26行在变量SRCDIRS中添加EPIT1驱动文件(.c)路径。

链接脚本保持不变。

18.4.2 编译下载

使用Make命令编译代码,编译成功以后使用软件imxdownload将编译完成的epit.bin文件下载到SD卡中,命令如下:

chmod 777 imxdownload //给予imxdownload可执行权限,一次即可
./imxdownload epit.bin /dev/sdd //烧写到SD卡中,不能烧写到/dev/sda或sda1设备里面!

烧写成功以后将SD卡插到开发板的SD卡槽中,然后复位开发板。程序运行正常的话LED0会以500ms为周期不断的亮、灭闪烁。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK