Linux学习笔记14---EPIT 定时器实验

定时器是最常用的外设,常常需要使用定时器来完成精准的定时功能, I.MX6U 提供了多种硬件定时器,有些定时器功能非常强大。

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 所示:

图 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 所示:

寄存器 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 所示:

寄存器 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 的翻转。

**2、**硬件原理分析

本试验用到的资源如下:
①、 LED0 。

②、 定时器 EPTI1 。
本实验通过 EPTI1 的中断来控制 LED0 的亮灭 。

**3、**实验程序编写

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

cpp 复制代码
#ifndef _BSP_EPITTIMER_H
#define _BSP_EPITTIMER_H
#include "imx6ul.h"


/* 函数声明 */
void epit1_init(unsigned int frac, unsigned int value);
void epit1_irqhandler(void);

#endif

然后在 bsp_epittimer.c 中输入如下内容:

cpp 复制代码
#include "bsp_epittimer.h"
#include "bsp_int.h"
#include "bsp_led.h"

/*
 * @description		: 初始化EPIT定时器.
 *					  EPIT定时器是32位向下计数器,时钟源使用ipg=66Mhz		 
 * @param - frac	: 分频值,范围为0~4095,分别对应1~4096分频。
 * @param - value	: 倒计数值。
 * @return 			: 无
 */
void epit1_init(unsigned int frac, unsigned int value)
{
	if(frac > 0XFFF)
		frac = 0XFFF;
		
	EPIT1->CR = 0;	/* 先清零CR寄存器 */
	
	/*
     * CR寄存器:
     * bit25:24 01 时钟源选择Peripheral clock=66MHz
     * bit15:4  frac 分频值
     * bit3:	1  当计数器到0的话从LR重新加载数值
     * bit2:	1  比较中断使能
     * bit1:    1  初始计数值来源于LR寄存器值
     * bit0:    0  先关闭EPIT1
     */
	EPIT1->CR = (1<<24 | frac << 4 | 1<<3 | 1<<2 | 1<<1);
	
	EPIT1->LR = value;	/* 倒计数值 */
	EPIT1->CMPR	= 0;	/* 比较寄存器,当计数器值和此寄存器值相等的话就会产生中断 */

	/* 使能GIC中对应的中断 			*/
	GIC_EnableIRQ(EPIT1_IRQn);

	/* 注册中断服务函数 			*/
	system_register_irqhandler(EPIT1_IRQn, (system_irq_handler_t)epit1_irqhandler, NULL);	

	EPIT1->CR |= 1<<0;	/* 使能EPIT1 */ 
}

/*
 * @description			: EPIT中断处理函数
 * @param				: 无
 * @return 				: 无
 */
void epit1_irqhandler(void)
{ 
	static unsigned char state = 0;

	state = !state;
	if(EPIT1->SR & (1<<0)) 			/* 判断比较事件发生 */
	{
		led_switch(LED0, state); 	/* 定时器周期到,反转LED */
	}
	
	EPIT1->SR |= 1<<0; 				/* 清除中断标志位 */
}

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 里面输入如下内容:

cpp 复制代码
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_epittimer.h"

/*
 * @description	: main函数
 * @param 		: 无
 * @return 		: 无
 */
int main(void)
{
	int_init(); 				/* 初始化中断(一定要最先调用!) */
	imx6u_clkinit();			/* 初始化系统时钟 			*/
	clk_enable();				/* 使能所有的时钟 			*/
	led_init();					/* 初始化led 			*/
	beep_init();				/* 初始化beep	 		*/
	key_init();					/* 初始化key 			*/
	epit1_init(0, 66000000/2);	/* 初始化EPIT1定时器,1分频
								 * 计数值为:66000000/2,也就是
								 * 定时周期为500ms。
								 */
	while(1)			
	{	
		delay(500);
	}

	return 0;
}

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

4、编写Makefile和链接脚本

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

cpp 复制代码
CROSS_COMPILE 	?= arm-linux-gnueabihf-
TARGET		  	?= epit

CC 				:= $(CROSS_COMPILE)gcc
LD				:= $(CROSS_COMPILE)ld
OBJCOPY 		:= $(CROSS_COMPILE)objcopy
OBJDUMP 		:= $(CROSS_COMPILE)objdump

INCDIRS 		:= imx6ul \
				   bsp/clk \
				   bsp/led \
				   bsp/delay  \
				   bsp/beep \
				   bsp/gpio \
				   bsp/key \
				   bsp/exit \
				   bsp/int \
				   bsp/epittimer
				   			   
SRCDIRS			:= project \
				   bsp/clk \
				   bsp/led \
				   bsp/delay \
				   bsp/beep \
				   bsp/gpio \
				   bsp/key \
				   bsp/exit \
				   bsp/int  \
				   bsp/epittimer
				   
				   
INCLUDE			:= $(patsubst %, -I %, $(INCDIRS))

SFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
CFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))

SFILENDIR		:= $(notdir  $(SFILES))
CFILENDIR		:= $(notdir  $(CFILES))

SOBJS			:= $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS			:= $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS			:= $(SOBJS) $(COBJS)

VPATH			:= $(SRCDIRS)

.PHONY: clean
	
$(TARGET).bin : $(OBJS)
	$(LD) -Timx6ul.lds -o $(TARGET).elf $^
	$(OBJCOPY) -O binary -S $(TARGET).elf $@
	$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis

$(SOBJS) : obj/%.o : %.S
	$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<

$(COBJS) : obj/%.o : %.c
	$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<
	
clean:
	rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

	

第 2 行修改变量 TARGET 为" epit ",也就是目标名称为" epit "。
第 15 行在变量 INCDIRS 中添加 EPIT1 驱动头文件 (.h) 路径。
第 26 行在变量 SRCDIRS 中添加 EPIT1 驱动文件 (.c) 路径。
链接脚本保持不变。

5、编译下载

使用 Make 命令编译代码,编译成功以后使用软件 imxdownload2 将编译完成的 bsp.bin 文件生成可执行的img文件,命令如下:

cpp 复制代码
make
./imxdownload2 int.bin

如果 imxdownload2无权限,可用以下命令添加权限

cpp 复制代码
chmod 777 imxdownload2

利用Win32DiskImager软件将load.img执行文件写入SD卡,SD卡插入开发板上即可正常运行。如果代码运行正常的话 LED0 会以大约 500ms 周期闪烁,

例程

【免费】Linux学习笔记14-EPIT定时器实验例程资源-CSDN文库

相关推荐
Y.O.U..2 小时前
STL学习-容器适配器
开发语言·c++·学习·stl·1024程序员节
TT哇3 小时前
【Java】数组的定义与使用
java·开发语言·笔记
黑叶白树4 小时前
包和模块(上) python复习笔记
开发语言·笔记·python
T_Y99434 小时前
selenium学习日记
学习·selenium·测试工具
L_Z_J_I4 小时前
超子物联网HAL库笔记:多指针定位+循环收发缓冲区方案设计
笔记
我是水怪的哥4 小时前
一些有用的科研数据网站
经验分享·笔记
zhilanguifang5 小时前
ERC论文阅读(02)--SAC;-LSTM论文阅读笔记
论文阅读·笔记·lstm
糊涂君-Q5 小时前
Python小白学习教程从入门到入坑------第十九课 异常模块与包【下】(语法基础)
开发语言·python·学习·程序人生·改行学it
爱编程的小新☆6 小时前
Java篇图书管理系统
java·开发语言·学习
致奋斗的我们6 小时前
RHCE的学习(7)
linux·服务器·网络·学习·redhat·rhce·rhcsa