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

RT1064通用定时器简介

RT1064内部包含4个四定时器(QTMR1~4),每个4四定时器又包含4个独立的通道,4个通道完全独立,不共用任何资源,单通道的框图如图所示

TMR模块设计包括这些独特的功能 :

  • 4个16位计数器/定时器
  • 向上/向下计数
  • 计数器是可串联的
  • 可编程计算模块
  • 对于外部时钟,最大计数速率等于外部时钟/2
  • 对于内部时钟,最大计数速率等于外部时钟
  • 单次计数或者重复计数
  • 计数器是可预加载的
  • 比较寄存器是可预加载的(可通过比较加载特性获得)
  • 计数器可以共享可用的输入引脚
  • 为每个计数器单独的预分频器
  • 每个计数器都具有捕获和比较功能
  • 调试模式下可编程操作
  • 输入可以作为错误输入
  • 可编程输入滤波器
  • 计数开始可以跨计数器同步

QTMR系统框图

在这里插入图片描述

定时器模式简介

我们使用计数模式(14 种模式中的 1 种)来实现 QTMR 的周期性中断应用。在计数模式(CTRL[CM]=001)下,QTMR 通道计数器在时钟(由 CTRL[PCS]设置)的上升沿计数,计数方向可以是递增/递减(由 CTRL[DIR]设置),非常适合做周期性中断、计时或者统计外部脉冲个数。
以 QTMR1 的通道 0 为例,我们设置好计数方向(CTRL[DIR]=0,向上计数),初始值(LOAD),匹配值(向上计数的匹配值一般为 COMP1)和计数模式(CTRL[CM]=001),通道 0 的 CNTR就会从 LOAD 值开始递增,当 CNTR 和 COMP1 相等时,发生比较匹配事件,产生中断,然后重新加载 LOAD 的值,再次开始递增计数,依次循环。
在这里插入图片描述
图中 t1、t2 和 t3 是 3 次比较匹配的时刻,会产生中断和重置 CNTR 寄存器(需要设置CTRL[LENTH]=1,否则 CNTR 将持续计数到 0XFFFF(溢出)),重置 CNTR 寄存器使用 LOAD寄存器的值来初始化,所以通道 0 的定时时间由 LOAD0 和 COMP1 以及输入时钟三个参数决定。特别的,当 LOAD0=0 的时候,定时时间就由 COMP1 和时钟频率决定了。

寄存器简介

Timer Channel Counter Register (TMRx_CNTRn)

QTMR 通道计数寄存器
x为QMTR编号,n为通道编号,x=0-3,n=0-3
在这里插入图片描述该寄存器用于存储通道计数器值,在时钟的驱动下执行递增/递减计数,与 COMP1 比较匹配后,可以产生比较匹配事件。通过读取该寄存器,可以获取当前该通道的计数值。

Timer Channel Load Register (TMRx_LOADn)

QTMR 通道加载值寄存器
在这里插入图片描述
该寄存器用于存储 CNTR 的重载值,如果设置 CTRL[LENGTH]位为 1,当 CNTR 与 COMPx(x=1/2),会产生比较匹配事件,然后,则 CNTR 的值会从 LOAD 加载(CNTR=LOAD),后继续进行计数,直到下次比较匹配事件。

Timer Channel Control Register (TMRx_CTRLn)

QTMR 通道控制寄存器

在这里插入图片描述
在这里插入图片描述
CM[15:13]位:用于设置计数模式。只要这 3 个位非零,CNTR 就可以进行某种计数(7 种模式可选)
当CM[15:13]=000时,表示当前通道禁止计数

PCS[12:9]位:用于选择第一时钟源的来源。
0000 ~ 0011,选择外部输入引脚 0 ~ 3 作为第一时钟源,可用于外部脉冲计数;
0100 ~ 00111,选择内部其他通道(0 ~ 3)计数器输出作为第一时钟源,可以用于计数器级联;
1000 ~ 1111,用于选择时钟源来自 IP_CLK_ROOT 的 1~128 分频;IP_CLK_ROOT =150MHz

LENGTH 位:用于设置 CNTR 的计数长度。
当 LENGTH=0 时,忽略 LOAD 的设置,CNTR总是从 0 计数到 0XFFFF;
当 LENGTH=1 时,CNTR 的计数范围分 2 个情况:
1,递增计数(DIR=0)时:LOAD≤CNTR≤COMP1;
2,递减计数时 COMP2≤CNTR≤LOAD。

DIR 位:用于控制计数方向。
当 DIR=0 时,CNTR 向上计数;
当 DIR=1 时,CNTR 向下计数。

OUTMODE[2:0]位:用于控制 OFLAG 输出信号的状态(输出模式)。
设置为 000 时,不会对输出产生影响。
如果设置为其他值,则会对 OFLAG 执行不同的操作,这个在输出 PWM 的时候,非常有用。
本章我们设置 OUTMODE[2:0]=000 即可。

Timer Channel Comparator Status and Control Register(TMRx_CSCTRLn)

QTMR 通道比较状态&控制寄存器
在这里插入图片描述
DBG_EN[15:14]位:用于设置调试模式下,QTMR 的工作状态。
我们一般设置这两个位为 0, 即 DBG_EN[1:0]=00,Debug 模式下,QTMR 正常工作。

TCF1EN 位:用于设置是否使能比较匹配 1 的中断。本章,我们设置该位为 1,当发生比较匹配 1 事件时,产生中断。

TCF1 位:表示是否产生了比较匹配 1 中断。如果该位为 1,则表示发生了比较匹配 1 中断。该位为写 0 清除。

CL1[1:0]位:用于设置 COMP1 的值,在发生比较匹配事件 1 时,是否从 CMPLD1/CMPLD2加载。
这个在精确控制波形的时候,非常有用,本章我们用不到,所以设置 CL1[1:0]=00 即可。

Timer Channel Compare Register 1 (TMRx_COMP1n)

QTMR 通道比较寄存器 1
在这里插入图片描述
该寄存器存储了通道 n 的比较匹配值 1。
当 CNTR 工作在向上计数模式时,如果 CNTR 与 COMP1 相等,则会产生比较匹配事件,还可触发中断(TCF1EN=1)。
当 QTMR 通道 y工作在向上计数( CTRLy[DIR]=1=0 ),且 CTRLy[LENGTH]=1 ,LOADy=0 ,
假设我们选择IPG_CLK_ROOT 作为时钟源(CTRLy[PCS]=1000),则通道 y 的定时溢出时间为:
T o u t = C O M P 1 n I P G C L K R O O T Tout =\frac{COMP1n}{IPG_CLK_ROOT } Tout=IPGCLKROOTCOMP1n
COMP1y:通道 y 的比较匹配值 1。
IPG_CLK_ROOT:IPG 根时钟,一般为 150Mhz。
通过控制 COMP1n 的值,就可以改变通道 n 的定时时间(中断时间)。

Timer Channel Enable Register (TMRx_ENBL)

QTMR 通道使能寄存器
在这里插入图片描述
该寄存器用于使能 QTMRx 的某个通道,共 4 个位,表示 4 个通道。
当对应通道的 ENBL位设置为 1 时,表示使能该通道;
设置 ENBL=0 时,表示禁止该通道。
默认四个通道都是开启的,所以一般我们不设置这个寄存器,也是可以的(默认就开启了)。
但是,如果我们在某些特殊场景,需要同时使能几个通道的时候,这个寄存器就非常有用了,它可以用于同步触发这些通道,达到 0 相位差。
注意:该寄存器只有通道 0 的有效,对通道 1~3 的 ENBL 寄存器设置是
无效的!!

fsl库函数及配置过程

QTMR 相关的库函数在 fsl_qtmr.c 和 fsl_qtmr.h 这两个文件中。
以四定时器 QTMR1 通道 0 为实例

1.QTMR1时钟使能

使用函数 CLOCK_EnableClock 使能 QTMR1 时钟,使用方法如下:

CLOCK_EnableClock(kCLOCK_Timer1)

此函数会被 QTMR 定时器初始化函数 QTMR_Init 调用,所以不需要我们显示的调用。

2.初始化QTMR1

使用函数 QTMR_Init 来初始化 QTMR,此函数原型如下:

void QTMR_Init(TMR_Type *base, qtmr_channel_selection_t channel, const qtmr_config_t *config)

第一个参数TMR_Type *base:指定初始化哪个 QTMR,可以选择:TMR1、TMR2、TMR3 和 TMR4
在MIMXRT1064.h中40148行

第二个参数qtmr_channel_selection_t channel:选择使用哪个通道,可选择的通道如下:

/*! @brief List of channel selection */
typedef enum _qtmr_channel_selection
{
    
    
    kQTMR_Channel_0 = 0U, /*!< TMR Channel 0 */
    kQTMR_Channel_1,      /*!< TMR Channel 1 */
    kQTMR_Channel_2,      /*!< TMR Channel 2 */
    kQTMR_Channel_3,      /*!< TMR Channel 3 */
} qtmr_channel_selection_t;

第三个参数const qtmr_config_t *config:为指向结构体 qtmr_config_t 的指针
此结构体为 QTMR 的配置结构体,结构体定义如下:

typedef struct _qtmr_config
{
    
    
    qtmr_primary_count_source_t primarySource; /*!< Specify the primary count source 指定第一时钟源*/
    qtmr_input_source_t secondarySource;       /*!< Specify the secondary count source 指定第二时钟源*/
    bool enableMasterMode;                     /*!< true: Broadcast compare function output to other counters;
                                                    false no broadcast */
    bool enableExternalForce;                  /*!< true: Compare from another counter force state of OFLAG signal
                                                    false: OFLAG controlled by local counter */
    uint8_t faultFilterCount;                  /*!< Fault filter count */
    uint8_t faultFilterPeriod;                 /*!< Fault filter period;value of 0 will bypass the filter */
    qtmr_debug_action_t debugMode;             /*!< Operation in Debug mode */
} qtmr_config_t;

这个结构体最常用的就是前两个成员变量,即设置定时器的时钟源
第一时钟源qtmr_primary_count_source_t primarySource;:可选参数如下

/*! @brief Quad Timer primary clock source selection*/
typedef enum _qtmr_primary_count_source
{
    
    
    kQTMR_ClockCounter0InputPin = 0, /*!< Use counter 0 input pin */
    kQTMR_ClockCounter1InputPin,     /*!< Use counter 1 input pin */
    kQTMR_ClockCounter2InputPin,     /*!< Use counter 2 input pin */
    kQTMR_ClockCounter3InputPin,     /*!< Use counter 3 input pin */
    kQTMR_ClockCounter0Output,       /*!< Use counter 0 output */
    kQTMR_ClockCounter1Output,       /*!< Use counter 1 output */
    kQTMR_ClockCounter2Output,       /*!< Use counter 2 output */
    kQTMR_ClockCounter3Output,       /*!< Use counter 3 output */
    kQTMR_ClockDivide_1,             /*!< IP bus clock divide by 1 prescaler */
    kQTMR_ClockDivide_2,             /*!< IP bus clock divide by 2 prescaler */
    kQTMR_ClockDivide_4,             /*!< IP bus clock divide by 4 prescaler */
    kQTMR_ClockDivide_8,             /*!< IP bus clock divide by 8 prescaler */
    kQTMR_ClockDivide_16,            /*!< IP bus clock divide by 16 prescaler */
    kQTMR_ClockDivide_32,            /*!< IP bus clock divide by 32 prescaler */
    kQTMR_ClockDivide_64,            /*!< IP bus clock divide by 64 prescaler */
    kQTMR_ClockDivide_128            /*!< IP bus clock divide by 128 prescaler */
} qtmr_primary_count_source_t;

第二时钟源qtmr_input_source_t secondarySource;:可选参数如下

/*! @brief Quad Timer input sources selection*/
typedef enum _qtmr_input_source
{
    
    
    kQTMR_Counter0InputPin = 0, /*!< Use counter 0 input pin */
    kQTMR_Counter1InputPin,     /*!< Use counter 1 input pin */
    kQTMR_Counter2InputPin,     /*!< Use counter 2 input pin */
    kQTMR_Counter3InputPin      /*!< Use counter 3 input pin */
} qtmr_input_source_t;

函数QTMR_GetDefaultConfig的具体内容如下:

void QTMR_GetDefaultConfig(qtmr_config_t *config)
{
    
    
    assert(NULL != config);

    /* Initializes the configure structure to zero. */
    (void)memset(config, 0, sizeof(*config));

    /* Halt counter during debug mode */
    config->debugMode = kQTMR_RunNormalInDebug;
    /* Another counter cannot force state of OFLAG signal */
    config->enableExternalForce = false;
    /* Compare function's output from this counter is not broadcast to other counters */
    config->enableMasterMode = false;
    /* Fault filter count is set to 0 */
    config->faultFilterCount = 0;
    /* Fault filter period is set to 0 which disables the fault filter */
    config->faultFilterPeriod = 0;
    /* Primary count source is IP bus clock divide by 2 */
    config->primarySource = kQTMR_ClockDivide_2;
    /* Secondary count source is counter 0 input pin */
    config->secondarySource = kQTMR_Counter0InputPin;
}

函数 QTMR_Init 的一般使用方法如下:

qtmr_config_t qtimer1_config;
QTMR_GetDefaultConfig(&qtimer1_config); //先设置为默认配置
qtimer1_config.primarySource= kQTMR_ClockDivide_128; //设置第一时钟源
QTMR_Init(TMR1, kQTMR_Channel_0, &qtimer1_config); //初始化 QTIMER

3.设置QTMR1通道0的定时周期

QTMR1 通道 0 的定时周期通过函数 QTMR_SetTimerPeriod 来设置,此函数原型如下:

void QTMR_SetTimerPeriod(TMR_Type *base, qtmr_channel_selection_t channel, uint16_t ticks)
{
    
    
    /* Set the length bit to reinitialize the counters on a match */
    base->CHANNEL[channel].CTRL |= TMR_CTRL_LENGTH_MASK;

    if ((base->CHANNEL[channel].CTRL & TMR_CTRL_DIR_MASK) != 0U)
    {
    
    
        /* Counting down */
        base->CHANNEL[channel].COMP2 = ticks;
    }
    else
    {
    
    
        /* Counting up */
        base->CHANNEL[channel].COMP1 = ticks;
    }
}

第一个参数TMR_Type *base:指定要设置的 QTMR,这里为 TMR1。
第二个参数qtmr_channel_selection_t channel:指定要设置的通道,这里是通道 0 所以应该设置为 kQTMR_Channel_0。
第三个参数uint16_t ticks:设置匹配比较值,此值就是用来决定定时器溢出时间的。
QTMR 的计数方向是可以修改的,当 CTRL 寄存器的 DIR 位为 0 的时候就是向上计数器,当 DIR 位为 1 的时候就是向下计数器,默认是向上计数器。
因此当定时器的计数值(CNTR0)达到匹配比较值的时候就会产生定时中断(如果开启了相应中断的话)。

4.使能QTMR1通道0的比较中断

使用函数 QTMR_EnableInterrupts 来使能 QTMR1 通道 0 的比较中断,这样当定时器计数值(CNTR0)达到我们设置的匹配比较值的时候就会产生相应中断,我们可以在中断中做具体的处理,此函数原型如下:

void QTMR_EnableInterrupts(TMR_Type *base, qtmr_channel_selection_t channel, uint32_t mask)

第一个参数TMR_Type *base:指定初始化哪个 QTMR,可以选择:TMR1、TMR2、TMR3 和 TMR4
在MIMXRT1064.h中40148行

第二个参数qtmr_channel_selection_t channel:选择使用哪个通道,可选择的通道如下:

/*! @brief List of channel selection */
typedef enum _qtmr_channel_selection
{
    
    
    kQTMR_Channel_0 = 0U, /*!< TMR Channel 0 */
    kQTMR_Channel_1,      /*!< TMR Channel 1 */
    kQTMR_Channel_2,      /*!< TMR Channel 2 */
    kQTMR_Channel_3,      /*!< TMR Channel 3 */
} qtmr_channel_selection_t;

第三个参数uint32_t mask:要开启的中断类型,可选中断类型如下:
在fsl_qtmr.h中

/*! @brief List of Quad Timer interrupts */
// typedef enum _qtmr_interrupt_enable
typedef enum _qtmr_interrupt_enable
{
    
    
    kQTMR_CompareInterruptEnable  = (1U << 0), /*!< Compare interrupt.比较中断*/
    kQTMR_Compare1InterruptEnable = (1U << 1), /*!< Compare 1 interrupt.比较 1 中断*/
    kQTMR_Compare2InterruptEnable = (1U << 2), /*!< Compare 2 interrupt.比较 2 中断*/
    kQTMR_OverflowInterruptEnable = (1U << 3), /*!< Timer overflow interrupt.溢出中断*/
    kQTMR_EdgeInterruptEnable     = (1U << 4)  /*!< Input edge interrupt.输入边沿检测中断*/
} qtmr_interrupt_enable_t;

这里我们要使能的是匹配比较中断,因此选择 kQTMR_CompareInterruptEnable。

5.开启QTMR

在相关的配置完成以后,就可以使能 QTMR 了,使用函数 QTMR_StartTimer 来开启 QTMR定时器,此函数原型如下:

static inline void QTMR_StartTimer(TMR_Type *base, qtmr_channel_selection_t channel, qtmr_counting_mode_t clockSource)
{
    
    
    uint16_t reg = base->CHANNEL[channel].CTRL;

    reg &= (uint16_t)(~(TMR_CTRL_CM_MASK));
    reg |= TMR_CTRL_CM(clockSource);
    base->CHANNEL[channel].CTRL = reg;
}

第一个参数TMR_Type *base:指定初始化哪个 QTMR,可以选择:TMR1、TMR2、TMR3 和 TMR4
在MIMXRT1064.h中40148行

第二个参数qtmr_channel_selection_t channel:选择使用哪个通道

第三个参数qtmr_counting_mode_t clockSource:用来设置定时器的计数模式,可选模式如下

/*! @brief Quad Timer counting mode selection */
typedef enum _qtmr_counting_mode
{
    
    
    kQTMR_NoOperation = 0,          /*!< No operation */
    kQTMR_PriSrcRiseEdge,           /*!< Count rising edges or primary source */
    kQTMR_PriSrcRiseAndFallEdge,    /*!< Count rising and falling edges of primary source */
    kQTMR_PriSrcRiseEdgeSecInpHigh, /*!< Count rise edges of pri SRC while sec inp high active */
    kQTMR_QuadCountMode,            /*!< Quadrature count mode, uses pri and sec sources */
    kQTMR_PriSrcRiseEdgeSecDir,     /*!< Count rising edges of pri SRC; sec SRC specifies dir */
    kQTMR_SecSrcTrigPriCnt,         /*!< Edge of sec SRC trigger primary count until compare*/
    kQTMR_CascadeCount              /*!< Cascaded count mode (up/down) */
} qtmr_counting_mode_t;

6.使能QTMR1中断并设置优先级

在定时器配置完了之后,因为要产生中断,必不可少的要设置 NVIC 相关寄存器,以使能QTMR1 的相关中断,设置方法如下:

NVIC_SetPriority(TMR1_IRQHandler,1);

7.编写中断服务函数

在最后,还是要编写定时器中断服务函数,通过该函数来处理定时器产生的相关中断。在中断产生后,通过函数 QTMR_GetStatus 来获取中断状态,判断此次产生的中断来自哪个通道然后执行相关的操作,我们这里使用的是通道 0 的输出比较 1 中断。在处理完中断之后调用函数 QTMR_ClearStatusFlags 来清除相应的中断标志位。

中断状态获取函数 QTMR_GetStatus 原型如下:

/*!
 * @brief Gets the Quad Timer status flags
 *
 * @param base     Quad Timer peripheral base address
 * @param channel  Quad Timer channel number
 *
 * @return The status flags. This is the logical OR of members of the
 *         enumeration ::qtmr_status_flags_t
 */
uint32_t QTMR_GetStatus(TMR_Type *base, qtmr_channel_selection_t channel);

第一个参数TMR_Type *base:要获取的定时器
第二个参数qtmr_channel_selection_t channel:要获取的通道
此函数其实就是读取寄存器 SCTRL0 的值,然后做简单的处理并将处理后的值返回给调用者,通过这个返回值就可以知道中断发生的状态,也就是中断类型。

中断状态(标志位)清除函数 QTMR_ClearStatusFlags 原型如下:

/*!
 * @brief Clears the Quad Timer status flags.
 *
 * @param base     Quad Timer peripheral base address
 * @param channel  Quad Timer channel number
 * @param mask The status flags to clear. This is a logical OR of members of the
 *             enumeration ::qtmr_status_flags_t
 */
void QTMR_ClearStatusFlags(TMR_Type *base, qtmr_channel_selection_t channel, uint32_t mask);

前两个参数与其他的API函数一样,都是写入对应的定时器和对应的通道值
最后一个参数指定要清除的中断类型,可选参数如下:

/*! @brief List of Quad Timer flags */
typedef enum _qtmr_status_flags
{
    
    
    kQTMR_CompareFlag  = (1U << 0), /*!< Compare flag 比较标志位*/
    kQTMR_Compare1Flag = (1U << 1), /*!< Compare 1 flag 比较 1 标志位*/
    kQTMR_Compare2Flag = (1U << 2), /*!< Compare 2 flag 比较 2 标志位*/
    kQTMR_OverflowFlag = (1U << 3), /*!< Timer overflow flag 溢出标志位*/
    kQTMR_EdgeFlag     = (1U << 4)  /*!< Input edge flag 输入边沿检测标志位*/
} qtmr_status_flags_t;

示例

#include "qtmr.h"

void QTMR1_CH0_Int_Init(uint8_t prisrc,uint16_t cmp1)
{
    
    
    qtmr_config_t qtmr1_config;
    qtimer1_source = (qtmr_primary_count_source_t)prisrc;
    //配置为默认设置
    QTMR_GetDefaultConfig(&qtmr1_config);
    //设置第一时钟源
    qtmr1_config.primarySource = qtimer1_source;
    //初始化QTMR1
    QTMR_Init(TMR1, kQTMR_Channel_0,&qtmr1_config);
    //设置QTMR1的定时周期
    QTMR_SetTimerPeriod(TMR1, kQTMR_Channel_0, cmp1);
    //使能中断
    QTMR_EnableInterrupts(TMR1, kQTMR_Channel_0, kQTMR_CompareInterruptEnable);
    //设置中断优先级
    NVIC_SetPriority(TMR1_IRQHandler,1);
    //开启QTMR
    QTMR_StartTimer(TMR1, kQTMR_Channel_0, kQTMR_PriSrcRiseEdge);
}


//中断服务函数
void TMR1_IRQHandler(void)
{
    
    
    if((QTMR_GetStatus(TMR1, kQTMR_Channel_0)&kQTMR_CompareFlag) == kQTMR_CompareFlag)
    {
    
    
        //do something
        QTMR_ClearStatusFlags(TMR1, kQTMR_Channel_0,kQTMR_CompareFlag);
    }
    __DSB();
}

本文参照正点原子RT1052 开发指南修改编辑。
作者的软件基于逐飞部分库和fsl库开发

猜你喜欢

转载自blog.csdn.net/m0_56116736/article/details/123494688
今日推荐