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 中输入如下内容:
#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 中输入如下内容:
#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 里面输入如下内容:
#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 如下:
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文件,命令如下:

make
./imxdownload2 int.bin

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

chmod 777 imxdownload2

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值