RT1064学习笔记-PIT定时器中断

RT1064 周期中断定时器简介

PIT模块是一组计时器,可用于引发中断和触发DMA通道。
PIT定时器框图如下图所示:

RT1064学习笔记-PIT定时器中断_第1张图片
PIT定时器内部有4个计数器(Timer0~Timer3)
PIT定时器功能包括:

  1. 32 位计数器(CNT),仅支持递减计数方式。
  2. 支持四个通道。
  3. 可以级联,实现超长定时(最长可组成 128 位定时器)。
  4. 支持中断/触发功能。

本次任务
我们主要使用 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控制寄存器
RT1064学习笔记-PIT定时器中断_第2张图片
该寄存器用于设置 PIT 定时器的运行(开/关),只有最低 2 位有效

MDIS 位,用于控制PIT 的时钟使能,0,使能;1,禁止。

FRZ 位,用于控制 PIT 在 Debug 模式下是否继续运行,0,Debug 模式继续运行;1,Debug 模式停止运行。

Timer Load Value Register (LDVAL0 - LDVAL3)

通道加载值寄存器
RT1064学习笔记-PIT定时器中断_第3张图片
该寄存器确定 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)

通道控制寄存器
RT1064学习笔记-PIT定时器中断_第4张图片
该寄存器用于设置 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)

通道标志寄存器

RT1064学习笔记-PIT定时器中断_第5张图片
该寄存器只有最低位(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库开发

你可能感兴趣的:(学习,单片机,arm,c语言,硬件工程)