文章目录
前言
TMS320F28377D有3个32位的定时器,分别为定时器0(timer0)、定时器1(timer1)和定时器2(timer2).这次利用28377的三个定时器,分别实现中断,在各自的中断程序中翻转GPIO的输出电平,实现板上LED1、2、3以不同周期闪烁。
一、TMS320F28377D的定时器介绍
1.1 28377定时器工作原理
TMS320F28377D有3个32位的定时器,分别为定时器0(timer0)、定时器1(timer1)和定时器2(timer2)。由2837xD的数据手册可以看到,定时器的工作原理为下图:
定时器的工作原理为
- 32位的计数器寄存器TIMH:TIM装载了周期寄存器PRDH:PRD的值
- 当TDDRH:TDDR作为定时分频器,计数器TIMH:TIM每SYSCLKOUT周期减1,SYSCLKOUT周期为TPR[TDDRH:TDDR]+1
- 当计数器TIMH:TIM为0时,输出中断信号。
也就是说,每来个时钟信号,PSCH:PSC都会减1,当PSCH:PSC减到0时,就会使减TIMH:TIM减1,并重载TDDRH:TDDR 的值。当TIMH:TIM减到0时,输出中断信号,并重新装载。
1.2 定时器中断原理
三个定时器的中断信号分别为下图
其中可以看到定时器0是经过PIE,将中断信号传入CPU中;而定时器1和定时器2是直接将中断信号传入CPU中。查看PIE通道图,可以看到定时器0为第1组第7个通道中断:
所以在启用定时器0中断时需要对PIE第1组第7个小中断进行使能;定时器1和定时器2则不需要。使能程序如下
PieCtrlRegs.PIEIER1.bit.INTx7 = 1; //使能PIE第1组中断的第7个通道中断,即定时器0中断
另外,DSP的中断管理分为3个层次:外设级,PIE级,CPU级。其中,外设级中断管理负责具体外设中断源的允许与禁止,PIE级中断管理负责对外设级中断分组并按照优先级管理,CPU内核级中断管理则负责处理直接向CPU申请的中断请求。
二、程序介绍
2.1 GPIO引脚宏定义
这次程序是在上次day2程序的基础上进行修改得到,沿用了上次程序的GPIO口设置。由于要在中断中翻转GPIO引脚输出电平,为了方便编写,采用宏定义简化。宏定义如下
#define LED1_TOGGLE GpioDataRegs.GPCTOGGLE.bit.GPIO89 = 1 //GPIO89切换电平
#define LED2_TOGGLE GpioDataRegs.GPCTOGGLE.bit.GPIO91 = 1
#define LED3_TOGGLE GpioDataRegs.GPCTOGGLE.bit.GPIO93 = 1
寄存器GPCTOGGLE用于翻转引脚的输出电平,从而起到改变LED亮灭的作用。
2.2 中断函数地址存放
这里将中断函数地址赋值给PieVectTable中,PieVectTable就是中断向量表,它是一个存放ISR(中断服务程序,interrupt service routine)地址的结构体。因为它是受保护的,所以需要加上EALLOW,EDIS语句。EALLOW,EDIS 一般是成对使用的,有些寄存器是受到保护的,不能任意写,EALLOW 相当于去掉保护,对写保护的寄存器进行操作后,EDIS 是重新把这个寄存器保护起来的意思。
EALLOW; //写保护
PieVectTable.TIMER0_INT = &cpu_timer0_isr; //将cpu_timer0_isr的地址赋给TIMER0_INT
PieVectTable.TIMER1_INT = &cpu_timer1_isr;
PieVectTable.TIMER2_INT = &cpu_timer2_isr;
EDIS; //关闭保护
2.3 初始化定时器和设置周期、频率
InitCpuTimers为定时器初始化程序。ConfigCpuTimer用于设置定时器的周期和频率,其中频率单位为MHz,时间单位为us(1e-6s)。因为28377D的主频为200Mhz,所以设置为频率设置为200.
InitCpuTimers(); //初始化CPU定时器
ConfigCpuTimer(&CpuTimer0, 200, 500000); //设置定时器的频率和周期,这里设置为CPU0、200MHz、500000us(0.5s)
ConfigCpuTimer(&CpuTimer1, 200, 1000000); //1s
ConfigCpuTimer(&CpuTimer2, 200, 2000000); //2s
点击ConfigCpuTimer函数,可以看到参数Freq和Period,对于中断时间,可以采用如下的公式计算:
T=Freq*Period/(200e6)
其中200e6为28377的CPU频率200MHz
void ConfigCpuTimer(struct CPUTIMER_VARS *Timer, float Freq, float Period)
{
...
}
2.3 对中断标志进行设置
CpuTimer0Regs.TCR.all = 0x4000; //对定时器0的寄存器进行设置,使能定时器0中断
CpuTimer1Regs.TCR.all = 0x4000;
CpuTimer2Regs.TCR.all = 0x4000;
IER |= M_INT1; //使能第1组中断
IER |= M_INT13;
IER |= M_INT14;
如下图TCR为定时器控制寄存器(Timer Control Register),其中第14位为控制中断使能功能,使该位为1,使能定时器中断。
2.3 定时器中断函数
//定时器0中断程序
__interrupt void cpu_timer0_isr(void)
{
CpuTimer0.InterruptCount++; //记录中断次数
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; //中断响应标志位
LED1_TOGGLE; //LED1切换
}
其中PIEACK为中断应答寄存器,用来说明该中断组已经被锁存。
三、附录-程序
#include "F28x_Project.h"
#include "F2837xD_Ipc_drivers.h"
void GPIO_Setup(void);
__interrupt void cpu_timer0_isr(void);
__interrupt void cpu_timer1_isr(void);
__interrupt void cpu_timer2_isr(void);
#define LED1_TOGGLE GpioDataRegs.GPCTOGGLE.bit.GPIO89 = 1 //GPIO89切换电平
#define LED2_TOGGLE GpioDataRegs.GPCTOGGLE.bit.GPIO91 = 1
#define LED3_TOGGLE GpioDataRegs.GPCTOGGLE.bit.GPIO93 = 1
void main(void)
{
InitSysCtrl();
#ifdef _STANDALONE
#ifdef _FLASH
// CPU2运行程序
IPCBootCPU2(C1C2_BROM_BOOTMODE_BOOT_FROM_FLASH);
#else
// CPU2运行程序
IPCBootCPU2(C1C2_BROM_BOOTMODE_BOOT_FROM_RAM);
#endif
#endif
// FALSH烧写配置
#ifdef _FLASH
InitFlash();
#endif
InitGpio();
GPIO_Setup();
DINT;
InitPieCtrl();
IER = 0x0000;
IFR = 0x0000;
InitPieVectTable();
//EALLOW,EDIS 一般是成对使用的,有些寄存器是受到保护的,不能任意写
//EALLOW 相当于去掉保护,对写保护的寄存器进行操作后
//EDIS 是重新把这个寄存器保护起来的意思
EALLOW; //写保护
PieVectTable.TIMER0_INT = &cpu_timer0_isr; //将cpu_timer0_isr的地址赋给TIMER0_INT
PieVectTable.TIMER1_INT = &cpu_timer1_isr;
PieVectTable.TIMER2_INT = &cpu_timer2_isr;
EDIS; //关闭保护
InitCpuTimers(); //初始化CPU定时器
ConfigCpuTimer(&CpuTimer0, 200, 500000); //设置定时器的频率和周期,这里设置为CPU0、200MHz、500000us(0.5s)
ConfigCpuTimer(&CpuTimer1, 200, 1000000); //1s
ConfigCpuTimer(&CpuTimer2, 200, 2000000); //2s
CpuTimer0Regs.TCR.all = 0x4000; //对定时器0的寄存器进行设置,使能定时器0中断
CpuTimer1Regs.TCR.all = 0x4000;
CpuTimer2Regs.TCR.all = 0x4000;
IER |= M_INT1; //使能第1组中断
IER |= M_INT13;
IER |= M_INT14;
PieCtrlRegs.PIEIER1.bit.INTx7 = 1; //使能第1组中断的第7个通道中断,即定时器0中断
EINT; //使能全局中断
ERTM; // 使能全局中断实时调试
while(1)
{
}
}
void GPIO_Setup() //配置GPIO的功能
{
GPIO_SetupPinMux(89, GPIO_MUX_CPU1, 0);//对引脚89设置;选择CPU1为内核,选择外设功能0
GPIO_SetupPinOptions(89, GPIO_OUTPUT, GPIO_PUSHPULL);//GPIO89,作为输出,上拉
GPIO_SetupPinMux(91, GPIO_MUX_CPU1, 0);
GPIO_SetupPinOptions(91, GPIO_OUTPUT, GPIO_PUSHPULL);
GPIO_SetupPinMux(93, GPIO_MUX_CPU1, 0);
GPIO_SetupPinOptions(93, GPIO_OUTPUT, GPIO_PUSHPULL);
}
//定时器0中断程序
__interrupt void cpu_timer0_isr(void)
{
CpuTimer0.InterruptCount++; //记录中断次数
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; //中断响应标志位
LED1_TOGGLE; //LED1切换
}
//定时器1中断程序
__interrupt void cpu_timer1_isr(void)
{
CpuTimer1.InterruptCount++;
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
LED2_TOGGLE;
}
__interrupt void cpu_timer2_isr(void)
{
CpuTimer2.InterruptCount++;
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
LED3_TOGGLE;
}