RT1064 周期中断定时器简介
PIT模块是一组计时器,可用于引发中断和触发DMA通道。
PIT定时器框图如下图所示:
PIT定时器内部有4个计数器(Timer0~Timer3)
PIT定时器功能包括:
- 32 位计数器(CNT),仅支持递减计数方式。
- 支持四个通道。
- 可以级联,实现超长定时(最长可组成 128 位定时器)。
- 支持中断/触发功能。
本次任务
我们主要使用 PIT 定时器来做周期性的中断应用,以 PIT 定时器通道 0 为例,一旦开启 PIT 时钟(MCR[MDIS]=0),并使能通道 0 的计数(TCTRLx[TEN]=1,x=0~3,下同),则通道 0 的 CNT 计数器会从 LDVAL0 加载值开始,做递减计数,当 CNT 等于 0 的时候,就产生超时事件,触发中断,然后 CNT 的又会重新加载 LDVAL0 的值,进行下一次递减计数周期,依次循环。
PIT寄存器简介
PIT Module Control Register (MCR)
PIT控制寄存器
该寄存器用于设置 PIT 定时器的运行(开/关),只有最低 2 位有效
MDIS 位,用于控制PIT 的时钟使能,0,使能;1,禁止。
FRZ 位,用于控制 PIT 在 Debug 模式下是否继续运行,0,Debug 模式继续运行;1,Debug 模式停止运行。
Timer Load Value Register (LDVAL0 - LDVAL3)
通道加载值寄存器
该寄存器确定 PIT 定时器具体某个通道的加载值,4 个通道,总共有 4 个寄存器。每个通道的初始计数值就是从由该寄存器决定,然后进行递减计数,计数到 0 时产生中断/触发,然后自动重载 LDVALx 的值,然后开启下一次递减计数。
所以,在时钟频率确定的前提下(PIT 的时钟源来自 PERCLK_CLK_ROOT),中断周期由该寄存器确定,计算公式为:
T
o
u
t
=
L
D
V
A
L
x
P
E
R
C
L
K
_
C
L
K
_
R
O
O
T
Tout=\frac{LDVALx}{PERCLK\_CLK\_ROOT}
Tout=PERCLK_CLK_ROOTLDVALx
其中PERCLK_CLK_ROOT 的频率一般为 75Mhz
LDVALx的取值为1~4,294,967,295
Timer Control Register (TCTRL0 - TCTRL3)
通道控制寄存器
该寄存器用于设置 PIT 定时器的某个通道,4 个通道,总共有 4 个寄存器。
CHN 位[2]:用于设置是否使用级联模式。0,不使用;1,使用。如果使用级联模式(通道 0不能级联),该通道的计数,只有在前一个通道计数完成后,才减 1。
2 个通道级联就可以组成一个 64 位的计数器,按 75M 的计数时钟算,可以计数 7799 年才会溢出!!。虽然可以 4 路级联,组成 128 位定时器,但是延时时间实在是太久了,实际上一般用不到。
TIE 位[1]:用于设置是否使能中断功能。0,不使能;1,使能。如果使能,则在定时器通道计数到 0 的时候,将会产生中断。
TEN 位[2]:用于设置是否使能该通道。0,不使能;1,使能。本章我们需要用到通道 0,所以通道 0 的 TEN 位需要设置为 1。
Timer Flag Register (TFLG0 - TFLG3)
通道标志寄存器
该寄存器只有最低位(TIF)有效,用于表示对应通道是否产生了超时事件(计数到 0),该位为写 1 清零。同样的,4 个通道,有 4 个 TFLG 寄存器。
PIT库函数
PIT 相关的库函数在 fsl_pit.c 和 fsl_pit.h
1.PIT时钟使能
使用函数 CLOCK_EnableClock 使能 PIT 时钟,使用方法如下:
CLOCK_EnableClock(kCLOCK_Pit)
此函数会被 PIT 定时器初始化函数 PIT_Init 调用,所以不需要我们显示的调用。
2.初始化PIT定时器
用函数 PIT_Init 初始化 PIT 定时器,此函数原型如下:
void PIT_Init(PIT_Type *base, const pit_config_t *config)
第一个参数PIT_Type *base
:PIT,在MIMXRT1064.h中21006中30794
第二个参数pit_config_t *config
:
typedef struct _pit_config
{
bool enableRunInDebug; /*!< true: Timers run in debug mode; false: Timers stop in debug mode ,debug 的时候 PIT 是否可以使用*/
} pit_config_t;
可以看到这个结构体很简单只有一个成员变量,用来标记debug的时候PIT是否可以使用。
函数 PIT_Init 的一般使用方法如下:
PIT_GetDefaultConfig(&pit_config); //初始化为默认配置
pit_config.enableRunInDebug=true; //调试模式下 PIT 继续运行
PIT_Init(PIT,&pit_config); //初始化 PIT 定时器
3.设置通道0的加载值
使用函数 PIT_SetTimerPeriod 设置通道 0 的加载值,也就是寄存器 LADVAL0 的值,此函数原型如下:
static inline void PIT_SetTimerPeriod(PIT_Type *base, pit_chnl_t channel, uint32_t count)
{
base->CHANNEL[channel].LDVAL = count;
}
第一个参数PIT_Type *base
:PIT,在MIMXRT1064.h中21006中30794
第二个参数 pit_chnl_t channel
:可设置的通道如下
typedef enum _pit_chnl
{
kPIT_Chnl_0 = 0U, /*!< PIT channel number 0*/
kPIT_Chnl_1, /*!< PIT channel number 1 */
kPIT_Chnl_2, /*!< PIT channel number 2 */
kPIT_Chnl_3, /*!< PIT channel number 3 */
} pit_chnl_t;
第三个参数 uint32_t count
:要设置的加载值,此加载值就确定了 PIT 定时器的定时时间。
4.使能通道0的中断
使能通道 0 的中断以后,每当设置好的加载值倒计数到 0 就会产生相应的中断,这样就实现了定时器功能,我们可以在中断中做具体的处理。
使用函数 PIT_EnableInterrupts 使能通道 0中断,此函数原型如下:
static inline void PIT_EnableInterrupts(PIT_Type *base, pit_chnl_t channel, uint32_t mask)
{
base->CHANNEL[channel].TCTRL |= mask;
}
第一个参数PIT_Type *base
:PIT,在MIMXRT1064.h中21006中30794
第二个参数 pit_chnl_t channel
:可设置的通道如下
typedef enum _pit_chnl
{
kPIT_Chnl_0 = 0U, /*!< PIT channel number 0*/
kPIT_Chnl_1, /*!< PIT channel number 1 */
kPIT_Chnl_2, /*!< PIT channel number 2 */
kPIT_Chnl_3, /*!< PIT channel number 3 */
} pit_chnl_t;
第三个参数 uint32_t mask
:要使能的中断类型,可选的中断类型如下:
/*! @brief List of PIT interrupts */
typedef enum _pit_interrupt_enable
{
kPIT_TimerInterruptEnable = PIT_TCTRL_TIE_MASK, /*!< Timer interrupt enable*/
} pit_interrupt_enable_t;
只有一个中断类型kPIT_TimerInterruptEnable,所以只能选它了[sad]
5.开启PIT定时器
配置好 PIT 定时器以后需要开启定时器,否则 PIT 定时器不会工作,开启 PIT 定时器的函数为 PIT_StartTimer,此函数原型如下;
static inline void PIT_StartTimer(PIT_Type *base, pit_chnl_t channel)
{
base->CHANNEL[channel].TCTRL |= PIT_TCTRL_TEN_MASK;
}
第一个参数PIT_Type *base
:PIT,在MIMXRT1064.h中21006中30794
第二个参数 pit_chnl_t channel
:可设置的通道如下
typedef enum _pit_chnl
{
kPIT_Chnl_0 = 0U, /*!< PIT channel number 0*/
kPIT_Chnl_1, /*!< PIT channel number 1 */
kPIT_Chnl_2, /*!< PIT channel number 2 */
kPIT_Chnl_3, /*!< PIT channel number 3 */
} pit_chnl_t;
6.开启PIT中断并设置优先级
在定时器配置完了之后,因为要产生中断,必不可少的要设置 NVIC 相关寄存器,以使能GPT1 的中断。
NVIC_SetPriority(PIT_IRQn,10);
因为是基于逐飞库写的,所以只需要指定中断优先级,最后会开启中断
7.编写中断服务函数
编写定时器中断服务函数,通过该函数来处理定时器产生的相关中断。
在中断产生后使用函数 PIT_GetStatusFlags 来获取中断状态,此函数就是获取 TFLG0 的 TIF 位状态,通过 TIF 位状态判断是否是通道 0 的中断。
如果是通道 0 中断的话就执行相关操作,最后调用函数 PIT_ClearStatusFlags 来清除相应的中断标志位,就是往 TIF 位写 1。
中断状态获取函数 PIT_GetStatusFlags 原型如下:
static inline uint32_t PIT_GetStatusFlags(PIT_Type *base, pit_chnl_t channel)
{
return (base->CHANNEL[channel].TFLG & PIT_TFLG_TIF_MASK);
}
第一个参数PIT_Type *base
:PIT,在MIMXRT1064.h中21006中30794
第二个参数 pit_chnl_t channel
:可设置的通道如下
typedef enum _pit_chnl
{
kPIT_Chnl_0 = 0U, /*!< PIT channel number 0*/
kPIT_Chnl_1, /*!< PIT channel number 1 */
kPIT_Chnl_2, /*!< PIT channel number 2 */
kPIT_Chnl_3, /*!< PIT channel number 3 */
} pit_chnl_t;
此函数其实就是读取寄存器 TFLG 的值,然后做简单的处理并将处理后的值返回给调用者,通过这个返回值就可以知道中断是否发生。
中断状态(标志位)清除函数 PIT_ClearStatusFlags 原型如下:
static inline void PIT_ClearStatusFlags(PIT_Type *base, pit_chnl_t channel, uint32_t mask)
{
base->CHANNEL[channel].TFLG = mask;
}
第一个参数PIT_Type *base
:PIT,在MIMXRT1064.h中21006中30794
第二个参数 pit_chnl_t channel
:可设置的通道如下
typedef enum _pit_chnl
{
kPIT_Chnl_0 = 0U, /*!< PIT channel number 0*/
kPIT_Chnl_1, /*!< PIT channel number 1 */
kPIT_Chnl_2, /*!< PIT channel number 2 */
kPIT_Chnl_3, /*!< PIT channel number 3 */
} pit_chnl_t;
第三个参数uint32_t mask
:清除的中断标志位,这里只有 kPIT_TimerFlag 可选择.
通过向寄存器内写1,去清除标志位
typedef enum _pit_status_flags
{
kPIT_TimerFlag = PIT_TFLG_TIF_MASK, /*!< Timer flag */
} pit_status_flags_t;
示例
void PIT_CH0_Int_Init(uint32 ldval)
{
pit_config_t PIT_Config;
//初始化PIT定时器
PIT_GetDefaultConfig(&PIT_Config);
PIT_Config.enableRunInDbg=true;
PIT_Init(PIT, &PIT_Config);
//设置通道0的加载值,即LADVAL0的值
PIT_SetTimerPeriod(PIT,kPIT_Chnl_0,ldval);
//使能通道0中断
PIT_EnableInterrupts(PIT,kPIT_Chnl_0,kPIT_TimerInterruptEnable);
//开启PIT中断并设置优先级
NVIC_SetPriority(PIT_IRQn,10);
//开启PIT定时器
PIT_StartTimer(PIT,kPIT_Chnl_0);
}
void PIT_IRQHandler(void)
{
if(PIT_GetStatusFlags(PIT,kPIT_Chnl_0)==kPIT_TimerFlag)
{
//do what
PIT_ClearStatusFlags(PIT,kPIT_Chnl_0,kPIT_TimerFlag);
}
__DSB();
}
本文参照正点原子RT1052 开发指南修改编辑。
作者的软件基于逐飞部分库和fsl库开发