1、通用定时间器简介
可编程定时器可用来计数或者定时驱动定时器输入引脚的外部事件。TM4C123GH6PM 通用定时器模块(GPTM)包含了 6 个 16/32 位 GPTM 块和 6 个 32/64 位宽 GPTM 块。每个 16/32位 GPTM 块提供了 2 路 16 位的定时器/计数器(称为定时器 A 和定时器 B),它们能级联起来作为32位定时器或者32位的实时时钟使用。每个 32/64 位宽 GPTM 块提供 32 位定时器(定时器 A 和定时器 B),它们能级联作为 64 位定时器使用。定时器也可以用于触发 uDMA 传输。另外,当超时以单次模式周期出现时,定时器能够触发数模转换(ADC)。在到达 ADC模块之前所有的通用定时器触发的 ADC 信号都进行或操作,所以最后结果只有一个定时器会触发 ADC 事件。
通用定时器模块(GPTM)包含 6 个 16/32 位 GPTM 块和 6 个 32/64 位宽 GPTM 块,分别具有以下特点:
16/32 位操作模式:
— 16/32 位一次性可编程定时器
— 16/32 位可编程周期定时器
— 16 位通用定时器,含 8 位预分频器
— 使用 32.768KHz 时钟输入时可用作 32 位实时时钟
— 16 位输入沿计数或者定时捕获模式,含一个 8 位预分频器
— 16 位 PWM 模式含一个 8 位预分频器,和软件可编程 PWM 信号反转输出
32/64 操作模式:
— 32/64 位一次性可编程定时器
— 32/64 位可编程周期定时器
— 32 位通用定时器,含 16 位预分频器
— 使用 32.768KHz 时钟输入时可用作 64 位实时时钟
— 32 位输入沿计数或者定时捕获模式,含一个 16 位预分频器
— 32 位 PWM 模式含一个 16 位预分频器,和软件可编程 PWM 信号反转输出
向上或向下计数
12 个 16/32 位 PWM 输入捕获引脚(CCP)
12 个 32/64 位 PWM 输入捕获引脚(CCP)
定时器菊花链式连接,允许一个定时器开始多个定时事件
定时器同步允许选定的定时器以相同的时钟频率开始计数
ADC 事件触发
在调试过程中,用户可以暂停调试只要让微控制器置位 CPU 暂停标志位(RTC 模式除外)
可以计算从定时器中断产生到进入中断服务程序需要的时间
采用微型直接内存访问控制器进行高效传输(uDMA)
— 每个定时器拥有独立通道
— 定时器中断产生突发请求
2、功能描述
每个 GPTM 模块的主要组成部分是两个自由运行的向上/向下计数器(称为定时器 A 和定时器 B),两个预分频寄存器,两个匹配寄存器,两个预分频匹配寄存器,两个影子寄存器,和两个装载/初始化寄存器以及相关的控制功能。每个 GPTM 的具体功能由软件控制,并通过寄存器接口进行配置。定时器 A 和定时器 B 可以分别单独使用拥有 16 位的计数范围和拥有 32 位计数范围。定时器 A 和定时器 B 能够级联拥有 32 位计数范围和拥有的 64 位计数范围。
注意:
4、在输入边沿计数,输入边沿定时和 PWM 模式,预分频器始终作为定时器扩展使用,与向上还是向下计数无关。
3、通用定时器的各类模式:
3.1、定时器模式
当使用定时器 A 和定时器 B 级联模式时,只有定时器 A 的控制位和状态位必须使用;定时器的控制位和状态位没必要使用。通过写 0x4 到GPTM 配置寄存器(GPTMCFG)可以配置 GPTM 模块为独立/分割模式。
3.1.1、单次/周期定时器模式:
选择单次模式还是周期模式由写入 GPTM 定时器 n 模式寄存器(GPTMTnMR)中 TnMR 位域的值决定。
定时器向上计数还是向下计数由 GPTMTnMR 寄存器中 TnCDIR 位决定。
3.2、实时时钟定时器模式
在实时时钟模式,定时器 A 和定时器 B 级联的话会作为向上计数器。当复位之后第一次选择 RTC 模式时候,计数器会先加载值 0x1。随后的装载值必须写入 GPTMTnILR 寄存器。如果 GPTMTnILP 寄存器加载新值,计数器就会从这个值开始计数,并在值 0xFFFFFFFF 处重新开始计数。
RTC 模式下 CCP0 的输入时钟要求是 32.768KHz。时钟信号会被分频成 1Hz,然后送到计数器的输入端。
3.3、 输入边计数模式
注:对于上升沿检测,输入信号必须在上升沿之后保持高电平至少两个系统时钟周期。
对于下降沿检测,输入信号必须在下降沿之后保持低电平至少两个系统时钟周期。
根据这个标准,边沿检测的最大输入频率是系统频率的 1/4.
3.3.1在边沿计数模式,定时器配置成 24 位或 48 位的向上或向上/向下计数器,同时包含一个可选预分频器,为了将定时器设置
在边沿计数模式,GPTMTnMR 寄存器的 TnCMR 位必须清除。
定时器能够捕获三种类型的事件:上升沿,下降沿,上升下降沿。定时器计数的事件类型由 GPTMCTL中 TnEVENT 决定。
3.3.2、输入边沿计数模式工作原理。这个例子中,定时器开始值设置为 0x000A而匹配值设置为 0x0006,那么就会计数四个边沿事件。此时的计数器是信号上升下降沿检测。注意最后两个边沿没有被计数,因为在当前计数值和 GPTMTnMATCHR 中值匹配后,定时器会自动清除 TnEN 位,这样计数器会忽略之后的边沿事件。
3.4 、输入边沿定时模式
注:对于上升沿检测,输入信号必须在上升沿之后保持高电平至少两个系统时钟周期。
对于下降沿检测,输入信号必须在下降沿之后保持低电平至少两个系统时钟周期。
根据这个标准,边沿检测的最大输入频率是系统频率的 1/4.
3.4.1在边沿计数模式,定时器配置成 24 位或 48 位的向上或向上/向下计数器,同时包含一个可选预分频器,为了将定时器设置
在边沿计数模式,GPTMTnMR 寄存器的 TnCMR 位必须清除。
定时器能够捕获三种类型的事件:上升沿,下降沿,上升下降沿。定时器计数的事件类型由 GPTMCTL中 TnEVENT 决定。
每当检测到一个上升边沿事件时,当前计数值就会被加载到 GPTMTnR 和 GPTMTnPS 中,这些数值会一直持续到下一个上升边沿出现,此时新的计数值就会被加载到 GPTMTnR 和GPTMTnPS 中。
当出于边沿定时模式下,如果启用预分频器计数器会采用模 2的24次方,如果预分频器禁用那么计数器会采用模 2的16次方 。如果有可能出现边沿花费的时间比计数事件的时间长,那么就可以利用一个配置成周期性计数模式的定时器来确保边沿检测不会丢失。周期性定时器必须按照以下步骤配置:
周期性定时器的循环周期速率要和边沿定时器一样
周期性定时器的中断比边沿定时器的超时中断拥有更高的优先级
如果进入周期性定时器中断服务程序,软件必须检查是否有边沿定时中断挂起,如果有中断挂起那么计数器的值必须在被用来计算事件快照时间之前减 1
3.5、 PWM 模式
3.5.1、GPTM 支持简单的 PWM 生成模式。在 PWM 模式下,定时器会配置成一个 24 位或者 48 位的向下计数器,开始值GPTMTnILR 和 GPTMTnPR 寄存器决定。这种模式中 PWM 频率和周期是同步事件,因此可以保证 PWM 不会有障碍。通过写 GPTMTnMR 中的 TnAMS 为 0x1,TnCMR为 0x0,TnMR 为 0x2 来启用 PWM 模式。
当软件写 GPTMCTL 中的 TnEN 位,那么计数器就开始向下计数直到 0x0 状态。另外,如果设置了 GPTMTnMR 寄存器的 TnWOT 位,那么一旦设置了 TnEN,定时器就会等待触发才开始计数。周期性模式的下一个计数循环,计数器会从GPTMTnILR和 GPTMTnPR 重装载开始值,然后继续计数直到软件清除 GPTMCTL 中 TnEN 位,它才会停止计数。
当计数器的值等于 GPTMTnILR 和 GPTMTnPR 中的值时,PWM 输出信号有效。
当它的值和 GPTMTnMATCHR 和 GPTMTnPMR 寄存器相同时,PWM 输出信号无效。
软件可以设置 GPTMCTL中 TnPWML 位来反转输出的 PWM 信号。
注意:如果启用了 PWM 输出信号反转,那么边沿检测中断也会反转。也就是说,如果设置了正边沿中断触发,而且 PWM 输出信号反转之后产生了一个正边沿,这时候不会有事件触发。相反,中断会在 PWM 信号的负边沿产生。
3.5.2、下图显示了如何产生一个 1ms 周期且占空比为 66%的 PWM 输出,假定是在 50MHz 输入时钟并且 TnPWML=0(如果 TnPWML=1 的时候占空比需要为 33%)的情况下。这个例子中,GPTMTnILR 中的开始值是 0xC350,GPTMTnMATCHR 中的匹配值是 0x411A。
4、各类模式初始化配置
4.1、 单次/周期定时器模式
GPTM 通过以下顺序配置成单次或者周期模式:
1. 确保定时器在做任何更改之前处于禁用状态(GPTMCTL 中 TnEN 位清零)
2. 写 0x00000000 到 GPTM 配置寄存器 GPTMCFG
3. 配置 GPTM 定时器 n 模式寄存器 GPTMTnMR 的 TnMR 位:
a. 写 0x1 启用单次模式
b. 写 0x2 启用周期模式
4. 可选配置 GPTMTnMR 中的 TnSNAPS,TnWOT,TnMTE 和 TnCDIR 位,这些位确定是否捕获独立定时器超时时候的值,是否使用外部触发来开始计数,是否要配置额外的触发器或者中断,向上还是向下计数。
5. 将开始值加载到 GPTM 定时器 n 时间间隔加载寄存器 GPTMTnILR
6. 如果需要使用中断,还需要设置 GPTM 中断屏蔽寄存器 GPTMIMR 的相应位
7. 设置 GPTMCTL 寄存器中 TnEN 位来启用定时器并开始计数
8. 轮询 GPTMRIS 寄存器或者等待中断的产生。这两种情况下,写 1 到 GPTMICR 寄存器的相应位置都会将状态标志清除。
如果 GPTMTnMR 中 TnMIE 位设置了,而且 GPTMRIS 中 RTCRIS 位也设置了,那么定时器就会持续计数。在单次模式中,定时器会在发生超时事件时停止计数。要重新启用定时器的话,就需要重复上述过程。配置成周期模式的定时器在超时事件发生时会重新装载值然后继续计数。
4.2 、RTC 模式
要使用 RTC 模式,定时器必须在一个偶数 CCP 输入上有 32.768KHz 输入信号。按照以下步骤启用 RTC 功能:
1. 确保在任何更改前定时器是禁用的
2. 如果定时器在此之前是工作在其他不同的模式,在重新配置前需要清除 GPTMTnMR寄存器中任何残留位。
3. 写 0x00000001 值到 GPTM 配置寄存器 GPTMCFG
4. 写匹配值到 GPTM 匹配寄存器 GPTMTnMATCHR
5. 按照需要设置/清除 GPTMCTL 中的 RTCEN 和 TnSTALL 位
6. 如果需要使用中断,设置 GPTMIMR 中的 RTCIM 位
7. 设置 GPTMCTL 的 TAEN 位启用定时器并开始计数当定时器的计数值和 GPTMTnMATCHR 寄存器中的值匹配时,GPTM 会置位 GPTMRIS 中RTCRIS 位使它有效,,然后继续计数直到定时器 A 禁用或者接收到一个硬件复位。写 GPTMICR寄存器的 RTCCINT 位会清除中断。注意如果 GPTMTnILR 寄存器装载一个新的值,定时器会从新的值开始计数,一直持续到数值为 0xFFFFFFFF,这时候它会翻转重新开始计数。
4.3 、输入边沿计数模式
定时器通过以下步骤配置成输入边沿计数模式:
1. 确保定时器在做任何更改是禁用的
2. 写 0x00000004 值到 GPTMCFG 寄存器
3. 在 GPTMTnMR 寄存器中,写 0x0 到 TnCMR,写 0x3 到 TnMR
4. 写 GPTMCTL 的 TnEVENT 可以设置定时器需要捕获的事件类型
5. 如果使用预分频器,将预分频值写入 GPTMTnPR 寄存器中
6. 将定时器的开始值装载入 GPTMTnILR 寄存器
7. 将预分频器的匹配值装载入 GPTMTnPMR 寄存器
8. 将事件计数值装载入 GPTMTnMATCHR 寄存器。注意当执行向上计数时,GPTMTnPR 和GPTMTnILR 中的数值必须大于 GPTMTnPMR 和 GPTMTnMATCHR 中的数值
9. 如果使用中断,需要设置 GPTMIMR 的 CnMIM 位
10. 设置 GPTMCTL 的 TnEN 位启用定时器并开始等待边沿事件
11. 轮询 GPTMRIS 的 CnMRIS 位或者等待中断。这两种情况下,写 1 到 GPTMICR 的 CnMCINT位都会将状态标志清除当在边沿计数模式向下计数时,定时器在检测到编程写入的边沿事件数时就会停止。要重新启用定时器 TnEN 位清零并且重复 4-9 步骤
4.4 、输入边沿定时模式
定时器按照以下步骤配置成输入边沿定时模式:
1. 确保定时器在做任何更改之前是禁用的
2. 写 0x00000004 到 GPTMCFG 寄存器
3. 在 GPTMTnMR 寄存器中,写 0x1 到 TnCMR,写 0x3 到 TnMR
4. 写 GPTMCTL 的 TnEVENT 可以设置定时器需要捕获的事件类型
5. 如果使用预分频器,将预分频值写入 GPTMTnPR 中
6. 定时器的开始值写入 GPTMTnILR 中
7. 如果需要使用中断,那么需要设置 GPTMIMR 的 CnEIM 位
8. 设置 GPTMCTL 的 TnEN 位启用定时器并开始计数
9. 轮询 GPTMRIS 寄存器的 CnERIS 位或者等待中断。这两种情况下,写 1 到 GPTMICR的 CnECINT 位都会将状态标志清除。事件发生的时间可以通过读取 GPTMTnR 寄存器得到在输入边沿定时模式中,定时器在检测到边沿事件后会继续运行,但是定时器的间隔值可以通过写 GPTMTnILR 寄存器在任何时候更改。这一操作会在下一个周期生效。
4.5、 PWM 模式
定时器按照以下步骤配置成 PWM 模式:
1. 确保定时器在做任何更改之前是禁用的
2. 写 0x00000004 值到 GPTMCFG 寄存器
3. 在 GPTMTnMR 寄存器中,写 0x1 到 TnAMS,写 0x0 到 TnCMR,写 0x2 到 TnMR
4. 通过设置 GPTMCTL 寄存器的 TnPWML 可以配置 PWM 信号的输出状态
5. 如果使用预分频器,将预分频值写入 GPTMTnPR 中
6. 如果使用 PWM 中断,在 GPTMCTL 的 TnEVENT 配置中断条件并且设置 GPTMTnMR 的 PWMIE位来使能中断。注意当 PWM 输出反转情况下,边沿检测产生中断的条件也会反转
7. 将开始值写入 GPTMTnILR 中
8. 将匹配值写入 GPTMTnMATCHR 中
9. 设置 GPTMCTL 的 TnEN 位启用定时器并开始产生 PWM 输出信号
在 PWM 定时模式,在 PWM 信号产生之后定时器会持续运行。PWM 周期可以通过写 GPTMTnILR寄存器在任何时间更改,这一改变会在定时器的下一个周期生效。
5、源代码
/*****************************************************************************
*功能:Timer 产生周期性中断,一个每秒触发两次,另一个每秒触发一次,
* 每个中断都有相应的中断处理程序进行处理
*作者:MountXing
*时间:2019-7-4
*
****************************************************************************/
#include
#include
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
//*****************************************************************************
// 配置 Timer0B 为周期性定时器,每隔 2s 触发一次中断。当 20 次中断之后,暂停触发//中断
//
//*****************************************************************************
#define NUMBER_OF_INTS 20
//定义变量计数中断的触发次数
static volatile uint32_t g_ui32Counter = 0;
/**************************************************************************
*函数名 :InitConsole
*描述 :配置 UART0 用于显示程序信息
*参数 :无
**************************************************************************/
void InitConsole(void)
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
UARTStdioConfig(0, 115200, 16000000);
}
/*****************************************************
*函数名 :Timer0BIntHandler
*描述 :定时器中断处理程序
*参数 :无
*****************************************************/
void Timer0BIntHandler(void)
{
//清除定时器中断标志
TimerIntClear(TIMER0_BASE, TIMER_TIMB_TIMEOUT);
//更新中断计数值
g_ui32Counter++;
//判断中断数值
if(g_ui32Counter == NUMBER_OF_INTS)
{
//禁用TIMER0B中断
IntDisable(INT_TIMER0B);
//关闭TIMER0B中断
TimerIntDisable(TIMER0_BASE, TIMER_TIMB_TIMEOUT);
//清除挂起的中断
TimerIntClear(TIMER0_BASE, TIMER_TIMB_TIMEOUT);
}
}
//*****************************************************************************
//配置Timer0B 定时器中断的 16—bit外设计数每一秒中断一次
//*****************************************************************************
int main(void)
{
uint32_t ui32PrevCount = 0;
SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
SYSCTL_XTAL_16MHZ);
//使能TIME0外设
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
InitConsole();
UARTprintf("16-Bit计数定时器中断实验 ->");
UARTprintf("\n 定时器 = Timer0B");
UARTprintf("\n 模式=周期定时器模式");
UARTprintf("\n 中断数量 = %d", NUMBER_OF_INTS);
UARTprintf("\n 比率 = 1ms\n\n");
//将Timer0B配置为16位定期计时器。
TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_PERIODIC);
//配置定时器装载值
TimerLoadSet(TIMER0_BASE, TIMER_B, SysCtlClockGet() / 50);
//使能总中断
IntMasterEnable();
//配置 TIMER0B 中断事件为定时器超时
TimerIntEnable(TIMER0_BASE, TIMER_TIMB_TIMEOUT);
//使能 NVIC 中的 TIMER0B 中为定时器中断指定中断处理函数
IntEnable(INT_TIMER0B);
//为定时器中断指定中断处理函数
g_ui32Counter = 0;
//使能Timer0B.
TimerEnable(TIMER0_BASE, TIMER_B);
while(1)
{
//当两值不相等时,说明有新中断产生,调用了中断处理函数
if(ui32PrevCount != g_ui32Counter)
{
UARTprintf("中断数量: %d\r", g_ui32Counter);
ui32PrevCount = g_ui32Counter;
}
}
}