在此篇文章中,我会介绍PIT定时器的一种实现方法与原理,首先我会介绍什么是PIT,PIT的运用,以及介绍一些关键的寄存器,最后结合代码编程介绍一种常用的实现方法。
1、什么是PIT?
2、PIT的运用?
3、PIT模块关键的寄存器介绍
4、实现方法
什么是PIT?
PIT的全称是programmable interval timer (可编程间隔定时器),顾名思义即是可编程的定时器,用于实现计数,定时的效果。
PIT的运用?
定时器的运用在嵌入式当中是离不开的,通常用于任务调度的分配,定时执行某些任务功能,比如每10ms采集一次AD值,每100ms发送一次CAN信号,每1S读取一次RTC时钟的信息等等。合理的分配任务的执行周期,会更加契合CPU的限度。
此文章讲述如何实现1s的计时,每1S实现LED灯闪烁。
PIT寄存器介绍
此处介绍了一些比较重要的寄存器的使用,介绍的不是很详细,如有不对的地方,欢迎指出,仅作参考。
PARAM寄存器
EXT_TRIG: 0X04 (默认值)外部触发器的数量为4
CHANNEL : 0X04 (默认值)定时器通道数为4,即可以实现4个不同的定时器函数
提供了定时器配置的信息,作为了解,对驱动配置不影响。
MCR寄存器
DBG_EN :官方手册说的是在调试模式下,设0停止定时器通道,设1允许在调试模式下继续允许。我0 和 1都试过,在中断函数里面打断点,仍然能够进入中断。所以不理解这个到底是什么意思,欢迎补充。
DOZE_EN : 0:在休眠模式停止运行,1:在休眠模式允许允许。这个还没有试过。
SW_RST: 0:定时器通道和寄存器不复位 1:定时器通通道和寄存器复位
设1的话,定时器中断不会再进入,已尝试
M_CEN : 外围时钟使能位。在正常允许时,该位为1,置0则停止
MSR寄存器:
Bit0-3:分别对应0,1,2,3定时器通道的中断响应标志,单片机检测对应位为1时,进入中断函数后,需要往对应位写1,才能重新计时,写0无效,这样下一个中断才会响应。否则会一直进定时器中断
MIER寄存器:
Bit0-3:分别对应0,1,2,3定时器通道的中断使能标志,如果为0,则代表禁能定时器中断,但是计时功能会有,只是不会进入中断函数。
SETTEN 寄存器:
Bit0-3:分别对应0,1,2,3定时器通道,置1对应通道中断使能,置0无作用
CLRTEN寄存器:
Bit0-3:分别对应0,1,2,3定时器通道,置1启动对应通道中断不使能,置0无作用
TVAL寄存器:分别四个寄存器TVAL[0] - TVAL[3]
Bit0-31:比较模式:寄存器里面的值表示超时的值,即计时到多少,会触发中断
在捕获模式下:每当触发器断言时,计时器值寄存器存储计数器值的逆(不做介绍,没研究过怎么用,原谅我太菜)。
TCVAL分为四个寄存器TCVAL[0] - TCVAL[3],分别对应四个定时器的通道的实时的计数值。
TCTRL寄存器十分重要,同样分为TCTRL[0] - TCTRL[3],用于配置对应定时器的配置信息。
Bit23: 0:选择外部触发源 1:选择内部触发源
Bit17:0:超时(触发中断后)不停止中断定时器1:超时后停止中断定时器
Bit2-3: 00b:32位周期计数器
01b:双16位周期计数器
10b:32位触发累加器
11b-:32位触发输入捕获通道
Bit0: 0:不使能定时器 1:使能定时器
实现方法:
外设配置:
下面Name的lpit1_ChnConfig0在代码中会生成一样名字的结构体,包含了两个信息:是否允许调试模式下运行,是否允许休眠情况下允许,我这里Run In Debug 和 Run In Doze两个都不勾选。
read only不勾选,
Timer Mode选择32位计数器,
Period Unit选择 Count Unit
敲黑板!!!重中之重的重点来了!!!
同时下面会有提示信息,包含定时器时钟的频率8MHZ,和每一个周期的运行时间125ns,表示定时器中断的计数器每加1一次,需要125ns,一秒钟可以加8000000次,如果我们要实现1S的定时器中断,首先1S = 1 000 000 000 ns ,则Timer Period里面填入的数为1000000000/125 = 8000000,表示计数器从0加到8000000就触发中断,加8000000次的时间就是1s 这样就实现了 1S的中断。
同理,如果要实现1ms的中断,首先转换一下1ms = 1000000ns ,用1000000/125 = 8000,即填入8000就可以实现1ms的计时,这里根据实际需求选择。
Trigger Source表示中断源,这里外部触发源,可以节省一定内部资源。
最后一定要勾选 Interrupt Enable ,使能中断。
代码部分相对比较固定,用的基本都是库函数和官方Demo的部分函数
1、首先是时钟初始化和GPIO的初始化。
2、然后对PIT进行初始化,使用的函数和结构体名,都可以在库函数中找到,LPIT_DRV_Init就是配置是否调试模式下运行(这个我也没搞懂实际作用),和休眠模式下是否运行(未测试)。
3、PIT内部有四个定时器通道,LPIT_DRV_InitChannel函数则是对配置的其中一个通道进行初始化,配置计数的模式和定时的时长。
4、LPIT_DRV_EnableTimerChannelInterrupt则是运行定时器产生中断
5、LPIT_DRV_StartTimerChannels开启计数的功能
6、INT_SYS_SetPriority启动中断,和设定中断的优先级
7、最后要声明LPIT0_Ch0_IRQHandler函数,在里面写入需要执行的任务,我实现的是LED的反转,这个函数名是固定的,系统会自动进入该中断函数。在进入中断函数之后,需要清除中断标志位,LPIT0->MSR = 1对MSR寄存器的BIT0置1即可,这里表示的是0通道的定时器,如果是另外的通道的,就要对应通道置1,才能清除中断,也可以使用函数LPIT_DRV_ClearInterruptFlagTimerChannels,如果不清0,系统检测到中断标志为1,会一直进入中断,影响程序的正常运行。
关于函数里面的传参也很简单,用的结构体都是系统生成好的。还有其他的,第一个参数0表示PIT的通道,第二个参数需要做位移运算,表示内部的定时器通道,基本都是bit0-bit3,对应一个内部通道
代码实现:
#include "sdk_project_config.h"
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include"CLOCK_S32K1xx.h"
#include "interrupt_manager.h"
#include"lpit_driver.h"
extern void LPIT0_Ch0_IRQHandler(void);
int main(void)
{
CLOCK_SYS_Init(g_clockManConfigsArr, CLOCK_MANAGER_CONFIG_CNT, g_clockManCallbacksArr, CLOCK_MANAGER_CALLBACK_CNT);
CLOCK_SYS_UpdateConfiguration(0U, 1); // 时钟初始化
PINS_DRV_Init(NUM_OF_CONFIGURED_PINS0, g_pin_mux_InitConfigArr0); //GPIO初始化
LPIT_DRV_Init(0,&lpit1_InitConfig); //LPIT初始化
LPIT_DRV_InitChannel(0,0,&lpit1_ChnConfig0); //配置计数模式,定时时间
LPIT_DRV_EnableTimerChannelInterrupt(0,1<<0); //允许定时器生成中断
LPIT_DRV_StartTimerChannels(0, (1 << 0)); //启动计数
INT_SYS_SetPriority(LPIT0_Ch0_IRQn,9); //设置中断优先级
while(1)
{
}
}
void LPIT0_Ch0_IRQHandler(void)
{
LPIT0->MSR = 1 ; /* 也可以用 LPIT_DRV_ClearInterruptFlagTimerChannels(0, (1 << 0)); */ /* 清除中断响应标志位 */
PTD->PTOR = 1 << 16; /* LED灯反转 */
}