正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-16讲 EPIT定时器

前言:

本文是根据哔哩哔哩网站上"正点原子第二期Linux之ARM(MX6U)裸机篇"视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。

引用:

正点原子IMX6U仓库 (GuangzhouXingyi) - Gitee.com

《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.2.pdf》

正点原子资料下载中心 --- 正点原子资料下载中心 1.0.0 文档

正文:

本文是 "正点原子第二期Linux之ARM(MX6U)裸机篇--第16 讲" 的读书笔记。第16讲主要是介绍I.MX6U处理器的EPIT定时器。本节将参考正点原子的视频教程第16讲和配套的正点原子开发指南文档进行学习。

0. 概述

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

1. EPIT定时器原理

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

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

  1. 时钟源可选的32位向下定时器
  2. 12位分频值
  3. 当计数值和比较值相等的时候产生中断

EPIT定时器的结构如下图所示:

  1. 这是一个多路选择器,用来选择EPIT定时器的时钟源,EPIT共有三个时钟源可以选择 ipg_clk, ipg_clk_32k, ipg_clk_highfrq
  2. 这是一个12位的分频器,负责对时钟源进行分频,12位对应的值是0~4095,对应着1~4096分频
  3. 经过分频的时钟进入到EPIT定时内部,在EPIT定时器内部有三个重要的寄存器:技术寄存器(EPIT_CR),加载寄存器(EPIT_LR)和比较寄存器(EPIT_CMPR),这3个寄存器都是32位的。EPIT是一个向下计数器,也就是说给它一个初始值,它就会从这个给定的初始值开始递减,直到减为0,计数寄存器里面保存的就是当前的计数值。如果EPIT工作在 set-and-forget 模式下,当计数寄存器里面的值减少到0,EPIT就会重新从加载寄存器读取数值到技术寄存器里面,重新开始向下计数。比较寄存器里面保存的数值用于和计数寄存器里面的计数值比较,如果相等的话就会产生一个比较事件。
  4. 比较器
  5. EPIT可设置引脚输出,如果设置了的话就会通过制定的引脚输出信号。
  6. 产生比较中断,也就是定时中断。

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中获取数据。

1.1 EPIT定时器关联的 EPITx_XX 寄存器如下

|------------|------------------------|
| 寄存器 | 描述 |
| EPITx_CR | Control Register 控制寄存器 |
| EPITx_SR | Status Regisetr 状态寄存器 |
| EPITx_LR | Load Register 加载寄存器 |
| EPITx_CMPR | Compare Register 比较寄存器 |
| EPITx_CNR | Counter Register 计数寄存器 |

1.2 EPIT 比较重要的几个寄存器

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

|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------|
| EPITx_CR控制寄存器 | 描述 |
| CLKSRC bit25:24 | EPIT时钟源选择位,为0时关闭时钟源,1时使用ipg_clk时钟源,2时使用ipg_clk_higrfreq时钟源,3时使用 ipg_clk_32k 时钟源。 在本例程中,我们设置为 1,也就是选择 ipg_clk 作为 EPIT 的时钟源, ipg_clk=66MHz。 |
| IOVW bit17 | EPIT计数值覆盖写使能。为0写EPIT LR加载寄存器不影响计数寄存器里的值,为1写EPIT LR加载寄存器会立即覆盖写计数寄存器。 |
| 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 寄存器结构如下图所示:

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

关于 EPIT 的寄存器就介绍到这里,关于这些寄存器详细的描述,请参考《I.MX6ULL 参考手册》第 1174 页的 24.6 小节。

2. EPIT定时器程序编写

本章我们使用EPIT产生功能定时中断,然后在中断服务函数里面翻转LED0,接下来以EPIT1为例,讲解需要哪些步骤来实现这个功能。EPIT1的配置步骤如下

  1. 设置EPIT1的时钟源
    设置EPIT1_CR寄存器的 CLKSEL bit25:24位,选择 EPIT1的时钟源。
  2. 设置分频值
    设置EPIT1_CR寄存器的 PRESCALER bit15:4位,设置分频值
  3. 设置工作模式
    设置EPIT1_CR寄存器的 RLD bit3 位,设置计数器的初始值来源。
  4. 设置计数值的初始值来源
    设置EPIT1_CR寄存器的 ENMODE 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.1 本节用到的硬件资源

  • LED0
  • 定时器EPIT1

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

EIPT定时器输出比较中断的中断ID号为 88=56+32:

2.2 实验程序编写

经过上面的分析EPIT1定时器的使用方法和配置EPIT1寄存器的步骤已经清楚,接下来实现正点原子I.MX6U ALPHA/Mini 开发板上的EPIT定时器驱动程序。

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


void epittimer_init(int frac, int counterValue)
{
	if(frac<0 || frac > 0xFFF){
		return;
	}

	/* 首先清零EPIT1->CR 控制寄存器.
	 * I.MX6U手册要求在修改EPIT定时器时钟源之前必须先去使能EPIT定时器 */
	EPIT1->CR = 0x0;

	/* CLKSEL bit[25:24] EPIT时钟源选择, 1:ipg_clk */
	EPIT1->CR |= (1 << 24);

	/* PRESCALAR bit[15:4] EPIT分频值,0:1分频, 66MHz/1==66MHz */
	EPIT1->CR |= (frac << 4);

	/* RLD bit[3] EPIT工作模式,1:set-and-foret 模式 */
	EPIT1->CR |= (1 << 3);

	/* OCIEN bit[2] EPIT输出比较中断使能,1:使能比较中断 */
	EPIT1->CR |= (1 << 2);

	/* ENMODE bit[1] EPIT使能模式,1:EPIT使能是计数器值从LR寄存器获取 */
	EPIT1->CR |= (1 << 1);

	/* EPITx_LR 加载值寄存器设置 */
	EPIT1->LR = counterValue;

	/* EPIT_CMPR 比较值寄存器设置 */
	EPIT1->CMPR = 0;

	/* 使能GIC IRQn 中断 */
	GIC_EnableIRQ(EPIT1_IRQn);

	/* 注册EPIT1比较中断 EPIT1_IRQn 的中断处理函数 */
	system_irqhandler_register(EPIT1_IRQn, eptitimer_irq_handler, NULL);

	/* 使能EPIT1 EN bit[0], 1: 是能EPIT */
	EPIT1->CR |= (1 << 0);
}

void eptitimer_irq_handler(IRQn_Type irq, void *userparam)
{
	static int beep_state = 0;
	static int led_state = 0;


	if((EPIT1->SR & (1 << 0))){		/* 判断比较中断事件发生 */
		beep_state = !beep_state;
		beep_switch(beep_state);

		led_state = !led_state;
		led_switch(LED_0, led_state);
	}


	/* 清除EPITx_ISR 中断标志位 */
	EPIT1->SR |= (1 << 0);
}

3. 编译烧写SD卡验证按键EPIT定时器中断实验结果

译修改主频后源码烧录SD卡验证本节的EPIT定时器实验是否生效。预期烧录SD卡后正点原子I.MX6ULL ALPHA/Mini 开发板会周期性的每500ms鸣叫一次。

我本地验证的结果是EPIT定时器正常工作每500ms触发一次EPIT输出比较事件中断在EPIT定时器中断里翻转一次蜂鸣器的开关,蜂鸣器正常鸣叫。

4. 总结和实验遇到的问题记录

4.1 问题1:EPIT定时器驱动程序烧录SD,开发板上电需要等待大概1分钟之后蜂鸣器才会开始按照500ms的间隔鸣叫。

对照正点原子的示例源码找到了问题原因:

原因:忘记了配置EPITx->CR寄存器的ENMODE bit1 位置1,这样CR寄存器 ENMOE=0,EPIT计数寄存器就使用上一次残留的寄存器值开始向下递减,可能是从0XFFFFFFFF 开始递减的所以需要等待大概1分钟才能递减到 0.

5. 结束

本文至此结束

相关推荐
hongyucai4 小时前
随意学习-模型工程:因子、隐空间、采样策略
学习
Amazing_Cacao8 小时前
CFCA精品可可产区风土体系(亚洲):彻底拒绝应试背诵,将感官复核作为检验真实物理差异的唯一铁律
学习
FoldWinCard9 小时前
D5 Linux 网络及端口命令
linux·运维·服务器
零涂毕业设计10 小时前
毕业设计模块开发-OLED显示屏(IIC协议0.96寸)STM32 ESP32 FPGA Linux驱动免费分享
linux·stm32·单片机·嵌入式·esp32·fpga·oled
tedcloud12310 小时前
OmniRoute怎么部署?开源AI模型路由平台Linux部署教程
linux·服务器·人工智能·开源·音视频
霸道流氓气质11 小时前
KMS 密钥管理服务(Key Management Service)原理与实践
linux·服务器·数据库
SakitamaX11 小时前
LVS(Linux Virtual Server)概念详解
linux·运维·lvs
小张同学a.11 小时前
Linux系统管理
linux·运维
阿龙的工作记录11 小时前
深入理解Linux设备驱动模型
linux·嵌入式硬件·工作记录
代码村新手12 小时前
Linux的基本指令
linux·运维·服务器