定时器是最常用的外设,常常需要使用定时器来完成精准的定时功能,
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 周期闪烁,
723

被折叠的 条评论
为什么被折叠?



