提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
一、定时器工作原理
TMS320F28335 的 CPU Time 有三个,分别为 Timer0,Timer1,Timer2,其中Timer2 是为操作系统 DSP/BIOS 保留的,当未移植操作系统时,可用来做普通的定时器。这三个定时器的中断信号分别为 TINT0, TINT1, TINT2,分别对应于中断向量 INT1,INT13,INT14。
定时器的功能如下图所示:
从上图可知,定时器有一个预分频模块和一个定时/计数模块,其中预分频模块包括一个 16 位的定时器分频寄存器(TDDRH:TDDR)和一个 16 位的预定标计数器(PSCH:PSC);定时/计数模块包括一个 32 位的周期寄存器(PRDH:PRD)和一个 32 位的计数寄存器(TIMH:TIM)。当系统时钟(SYSCLKOUT)来一个脉冲,PSCH:PSC 预定标计数器减 1,当 PSCH:PSC 预定标计数器减到 0 的时候,预定标计数器产生下溢后向定时器的 32 位计数器 TIMH:TIM 借位,即 TIMH:TIM 计数器减 1,同时 PSCH:PSC 可以重载定时器分频寄存器(TDDRH:TDDR)的值;当计数寄存器 TIMH:TIM 减到 0 产生下溢的时候,计数寄存器会重载周期寄存器(PRDH:PRD)的值,同时定时器会产生一个中断信号给 CPU。定时器的中断结构如下图所示:
定时器0中断属于 PIE 中断,中断信号经过 PIE 后,再进入处理器,定时器 0的中断属于 PIE 第一组中断中的第 7 个小中断。*定时器1与定时器2直接进入CPU中断,故在这两个定时器操作时无需使能定时器对应的PIE中断,以及在中断服务函数中无需对PIE中断标志进行配置。
二、定时器配置步骤
接下来我们介绍下如何使用库函数对通用定时器进行配置。这个也是在编写程序中必须要了解的。
(1)使能定时器时钟
SysCtrlRegs.PCLKCR3.bit.CPUTIMER0ENCLK = 1; // CPU Timer 0
SysCtrlRegs.PCLKCR3.bit.CPUTIMER1ENCLK = 1; // CPU Timer 1
SysCtrlRegs.PCLKCR3.bit.CPUTIMER2ENCLK = 1; // CPU Timer 2
(2)初始化定时器参数,包括制定定时器寄存器地址、周期寄存器值、预
定标计数器值等:
//初始化定时器参数-> 可用InitCpuTimers();直接替代
//指向Timer0的寄存器地址
CpuTimer0.RegsAddr=&CpuTimer0Regs;
//周期寄存器值得配置
CpuTimer0Regs.PRD.all=0xFFFFFFFF;
//设置Timer0的预标计数器值为0
CpuTimer0Regs.TPR.all =0;
CpuTimer0Regs.TPRH.all=0;
//设置Timer0的状态为停止状态
CpuTimer0Regs.TCR.bit.TSS =1;
//重装载使能
CpuTimer0Regs.TCR.bit.TRB =1;
//重置中断计数器
CpuTimer0.InterruptCount =0;
以上参数配置可直接调用"DSP2833x_CpuTimers.C"的 InitCpuTimers();函数。
(3)定时器的设置
要使用定时器还需要设置它的定时周期以及设置定时器的控制寄存器等。这些在 TI 提供的库函数“DSP2833x_CpuTimers.c”内的函数可实现:
void ConfigCpuTimer(struct CPUTIMER_VARS *Timer, float Freq, float Period);
(4)开启定时器中断功能,并使能定时器
//开启定时器中断功能并使能定时器
//设置Timer0的中断入口地址为中断向量表的INT0
PieVectTable.TINT0=&TIM0_IRQn;
//开启定时器功能
CpuTimer0Regs.TCR.bit.TSS=0;
//开启CPU第一组中断并使能第一组中断的第7个小中断,即为Timer0->INT1.7
IER |=M_INT1; //使能CPU中断1(INT1)
PieCtrlRegs.PIEIER1.bit.INTx7=1;
//使能总中断
EINT;
ERTM; //当使用仿真器调试时可开启 DEBUG 中断,即使用 ERTM 语句
EDIS; //开启写保护
(5)编写定时器中断服务函数
编写中断服务函数时需要在函数名前面加上一个关键字“interrupt”
interrupt void TIM0_IRQn(void)
{
EALLOW;
LED1_TOGGLE;
PieCtrlRegs.PIEACK.bit.ACK1=1; //清除中断标志位
EDIS;
}
三、定时器TIMER0的函数配置
1.定时器TIMER0初始化函数的配置
代码如下(示例):
//Timer0 初始化函数
//Freq: CPU时钟频率
//Period:定时器周期值,单位us
//T=Freq*Period/150M (s)
void TIM0_Init(float Freq, float Period)
{
EALLOW; //关闭写保护
SysCtrlRegs.PCLKCR3.bit.CPUTIMER0ENCLK = 1; // CPU Timer 0
//初始化定时器参数-> 可用InitCpuTimers();直接替代
//指向Timer0的寄存器地址
CpuTimer0.RegsAddr=&CpuTimer0Regs;
//周期寄存器值得配置
CpuTimer0Regs.PRD.all=0xFFFFFFFF;
//设置Timer0的预标计数器值为0
CpuTimer0Regs.TPR.all =0;
CpuTimer0Regs.TPRH.all=0;
//设置Timer0的状态为停止状态
CpuTimer0Regs.TCR.bit.TSS =1;
//重装载使能
CpuTimer0Regs.TCR.bit.TRB =1;
//重置中断计数器
CpuTimer0.InterruptCount =0;
//定时器配置
ConfigCpuTimer(&CpuTimer0,Freq,Period);
//开启定时器中断功能并使能定时器
//设置Timer0的中断入口地址为中断向量表的INT0
PieVectTable.TINT0=&TIM0_IRQn;
//开启定时器功能
CpuTimer0Regs.TCR.bit.TSS=0;
//开启CPU第一组中断并使能第一组中断的第7个小中断,即为Timer0->INT1.7
IER |=M_INT1; //使能CPU中断1(INT1)
PieCtrlRegs.PIEIER1.bit.INTx7=1;
//使能总中断
EINT;
ERTM; //当使用仿真器调试时可开启 DEBUG 中断,即使用 ERTM 语句
EDIS; //开启写保护
}
2.中断服务函数
代码如下(示例):注意:中断函数也需要在.h文件中声明。
interrupt void TIM0_IRQn(void)
{
EALLOW;
LED1_TOGGLE;
PieCtrlRegs.PIEACK.bit.ACK1=1; //清除中断标志位
EDIS;
}
四、定时器TIMER1的函数配置
1.定时器TIMER1初始化函数的配置
void TIM1_Init(float Freq, float Period)
{
EALLOW; //关闭写保护
SysCtrlRegs.PCLKCR3.bit.CPUTIMER1ENCLK = 1; // CPU Timer 1
//初始化定时器参数
InitCpuTimers();
//定时器的配置
ConfigCpuTimer(&CpuTimer1,Freq,Period);
//设置定时器的中断入口地址
PieVectTable.XINT13=&TIM1_IRQn;
//开启定时器
CpuTimer1Regs.TCR.bit.TSS =0;
//使能CPU中断(INT13)
IER |=M_INT13;
//使能总中断
EINT;
ERTM; //当使用仿真器调试时可开启 DEBUG 中断,即使用 ERTM 语句
EDIS; //开启写保护
}
2.中断服务函数
interrupt void TIM1_IRQn(void)
{
EALLOW;
LED2_TOGGLE;
EDIS;
}
五、定时器TIMER2的函数配置
1.定时器TIMER2初始化函数的配置
void TIM2_Init(float Freq, float Period)
{
EALLOW; //关闭写保护
SysCtrlRegs.PCLKCR3.bit.CPUTIMER2ENCLK = 1; // CPU Timer 2
//初始化定时器参数
InitCpuTimers();
//定时器的配置
ConfigCpuTimer(&CpuTimer2,Freq,Period);
//设置定时器的中断入口地址
PieVectTable.TINT2=&TIM2_IRQn;
//开启定时器
CpuTimer2Regs.TCR.bit.TSS =0;
//使能CPU中断(INT13)
IER |=M_INT14;
//使能总中断
EINT;
ERTM; //当使用仿真器调试时可开启 DEBUG 中断,即使用 ERTM 语句
EDIS; //开启写保护
}
2.中断服务函数
interrupt void TIM2_IRQn(void)
{
EALLOW;
LED3_TOGGLE;
EDIS;
}
六、主函数
#include "key.h"
#include "leds.h"
#include "beep.h"
#include "exti.h"
#include "timer.h"
void main(void)
{
MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);
InitFlash();
InitSysCtrl(); //系统时钟初始化
Uint32 i=0;
//失能CPU级中断,并初始化 PIE 控制器寄存器和 PIE 中断向量表
InitPieCtrl();
IER =0x0000;
IFR =0x0000;
InitPieVectTable();
LED_Init();
BEEP_Init();
KEY_Init();
EXTI1_Init();
TIM2_Init(150,700000); //T=Freq*Period/150M (s)=0.7s
TIM1_Init(150,500000); //T=Freq*Period/150M (s)=0.5s
TIM0_Init(150,800000); //T=Freq*Period/150M (s)=0.8s
while(1)
{
}
}
问题:按照上诉程序配置,当单独调用任意一个中断定时器时,程序都可以按照中断服务函数运行,可实现定时器中断,但当在主程序中同时调用3个定时器的初始化函数时,单片机只能正常运行1个或2个定时器中断,且会出现当3个定时器调用先后顺序不同时,其运行的定时器中断也不同,但也不能3个中断函数同时实现,这是为什么?
七、总结
DSP28335中的三个定时器Timer0、Timer1、Timer2的中断配置的区别主要体现在:
(1)开启定时器中欧那个端功能并使能定时器中开启CPU第一组中断并使能定时器对应的PIE中断配置中,对于Timer0,需要两者都需要使能,而对于Timer1、Timer2,由于直接到CPU,故只需使能CPU中断。
(2)中断服务函数的配置,Timer0,需要对PIE中断标志位清零。而对于Timer1、Timer2,由于直接到CPU,不需要做任何标志位清零。