前言
本文主要讲一下DSP CPU Timer的学习笔记,主要内容是用CPU Timer实现软分频功能。本文主要的作用是对于一个知识点的备份,另外呢,也是作为文章基于DSP的三相开关霍尔永磁同步电机控制_江湖上都叫我秋博的博客-CSDN博客知识框架中的一部分。
正文
定时器的原理好像没什么可讲的,所以下面将直接贴代码,然后对代码进行解释。
main.h的代码如下:
#ifndef PROGRAM_MAIN_H_
#define PROGRAM_MAIN_H_
#include <F28x_Project.h>
#include <F2837xD_Ipc_drivers.h>
void CPUTimerConfig(void);
interrupt void CpuTimer0ISR(void);
void DivideFreq(void);
#define SYS_BASIC_FREQUENCY 20000
extern int16 iFreqCnt;
extern int16 iFreq1Hz;
extern int16 iFreq2Hz;
extern int16 iFreq20Hz;
extern int16 iFreq40Hz;
extern int16 iFreq50Hz;
extern int16 iFreq100Hz;
extern int16 iFreq200Hz;
extern int16 iFreq250Hz;
extern int16 iFreq500Hz;
extern int16 iFreq1000Hz;
extern int16 iFreq2500Hz;
extern int16 iFreq5000Hz;
extern int16 iFreq10000Hz;
extern int16 iFreq20000Hz;
#define CNT1HZ 20000
#define CNT2HZ 10000
#define CNT20HZ 1000
#define CNT40HZ 400
#define CNT50HZ 400
#define CNT100HZ 200
#define CNT200HZ 100
#define CNT250HZ 80
#define CNT500HZ 40
#define CNT1000HZ 20
#define CNT2500HZ 8
#define CNT5000HZ 4
#define CNT10000HZ 2
#define CNT20000HZ 1
#endif /* PROGRAM_MAIN_H_ */
main.c的代码如下
#include <main.h>
int16 iFreqCnt = 0;
int16 iFreq1Hz = 0;
int16 iFreq2Hz = 0;
int16 iFreq20Hz = 0;
int16 iFreq40Hz = 0;
int16 iFreq50Hz = 0;
int16 iFreq100Hz = 0;
int16 iFreq200Hz = 0;
int16 iFreq250Hz = 0;
int16 iFreq500Hz = 0;
int16 iFreq1000Hz = 0;
int16 iFreq2000Hz = 0;
int16 iFreq2500Hz = 0;
int16 iFreq5000Hz = 0;
int16 iFreq10000Hz = 0;
int16 iFreq20000Hz = 0;
void main(void)
{
InitSysCtrl();
DINT;
InitPieCtrl();
IER = 0x0000;
IFR = 0x0000;
InitPieVectTable();
InitCpuTimers(); // Initialize CPU Timers
CPUTimerConfig(); // ▲
PieCtrlRegs.PIECTRL.bit.ENPIE = 1; // 使能PIE
EINT; // 使能全局中断
ERTM; // 使能实时仿真中断
CpuTimer0Regs.TCR.bit.TSS = 0; // 使能CPUTimer0
while(1){
if(iFreq5000Hz==1){
// do something
iFreq5000Hz = 0;
}
}
}
void CPUTimerConfig (void){
// CPU Timer0
ConfigCpuTimer(&CpuTimer0, 200, 1000000/SYS_BASIC_FREQUENCY);
CpuTimer0Regs.TCR.bit.TIE = 1; // 1: The CPU-Timer interrupt is enabled
// TIMER0 INT1.7 [Page102: Table 3-2.PIE Channel Mapping]
EALLOW;
PieVectTable.TIMER0_INT = &CpuTimer0ISR; // Specify the interrupt service routine
EDIS;
IER |= M_INT1; // Enable CPU Level interrupt
PieCtrlRegs.PIEIER1.bit.INTx7 = 1; // Enable PIE Level interrupt
}
// Generate frequency division signal
interrupt void CpuTimer0ISR(void){
DivideFreq();
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}
void DivideFreq(void){
if(iFreqCnt >= CNT1HZ){
iFreq1Hz = 1;
iFreqCnt = 0;
}else{
iFreq1Hz = 0;
}
if((iFreqCnt%CNT2HZ)==0){
iFreq2Hz = 1;
}else{
iFreq2Hz = 0;
}
if((iFreqCnt%CNT20HZ)==0){
iFreq20Hz = 1;
}else{
iFreq20Hz = 0;
}
if((iFreqCnt%CNT40HZ)==0){
iFreq40Hz = 1;
}else{
iFreq40Hz = 0;
}
if((iFreqCnt%CNT50HZ)==0){
iFreq50Hz = 1;
}else{
iFreq50Hz = 0;
}
if((iFreqCnt%CNT100HZ)==0){
iFreq100Hz = 1;
}else{
iFreq100Hz = 0;
}
if((iFreqCnt%CNT200HZ)==0){
iFreq200Hz = 1;
}else{
iFreq200Hz = 0;
}
if((iFreqCnt%CNT250HZ)==0){
iFreq250Hz = 1;
}else{
iFreq250Hz = 0;
}
if((iFreqCnt%CNT500HZ)==0){
iFreq500Hz = 1;
}else{
iFreq500Hz = 0;
}
if((iFreqCnt%CNT1000HZ)==0){
iFreq1000Hz = 1;
}else{
iFreq1000Hz = 0;
}
/*
if((iFreqCnt%CNT2000HZ)==0){
iFreq2000Hz = 1;
}else{
iFreq2000Hz = 0;
}
*/
if((iFreqCnt%CNT2500HZ)==0){
iFreq2500Hz = 1;
}else{
iFreq2500Hz = 0;
}
if((iFreqCnt%CNT5000HZ)==0){
iFreq5000Hz = 1;
}else{
iFreq5000Hz = 0;
}
if((iFreqCnt%CNT10000HZ)==0){
iFreq10000Hz = 1;
}else{
iFreq10000Hz = 0;
}
if((iFreqCnt%CNT20000HZ)==0){
iFreq20000Hz = 1;
}else{
iFreq20000Hz = 0;
}
iFreqCnt++;
}
main函数重点关注下面这几行代码
CPUTimerConfig(); // ▲
PieCtrlRegs.PIECTRL.bit.ENPIE = 1; // 使能PIE
EINT; // 使能全局中断
ERTM; // 使能实时仿真中断
CpuTimer0Regs.TCR.bit.TSS = 0; // 使能CPUTimer0
while(1){
if(iFreq5000Hz==1){
// do something
iFreq5000Hz = 0;
}
}
CPUTimerConfig();的定义如下。
void CPUTimerConfig (void){
// CPU Timer0
ConfigCpuTimer(&CpuTimer0, 200, 1000000/SYS_BASIC_FREQUENCY);
CpuTimer0Regs.TCR.bit.TIE = 1; // 1: The CPU-Timer interrupt is enabled
// TIMER0 INT1.7 [Page102: Table 3-2.PIE Channel Mapping]
EALLOW;
PieVectTable.TIMER0_INT = &CpuTimer0ISR; // Specify the interrupt service routine
EDIS;
IER |= M_INT1; // Enable CPU Level interrupt
PieCtrlRegs.PIEIER1.bit.INTx7 = 1; // Enable PIE Level interrupt
}
重点关注系统自带的函数:ConfigCpuTimer(&CpuTimer0, 200, 1000000/SYS_BASIC_FREQUENCY);
ConfigCpuTimer函数的作用是配置CPU Timer的一个计数周期,说白了是你CPU Timer计多少个数,我产生一次中断。
第一个参数是指定我们要配置的CPU Timer
第二个参数是指定我们系统的主频是多少MHz,TMS320F28377D的主频是200MHz,因此第二个参数我们设置的是200
第三个参数是配合第二个参数一同作用配置计数周期的,第二个参数如果设置成CPU主频200MHz,那么现在第三个参数,则是在假设1MHz的主频下,你要计多少个数,才产生中断了。假设我们想要分频得到基本频率 为20kHz,那么你就需要计 1MHz/20kHz个数,而我代码的第三个参数也是用这样的写法, 1000000/SYS_BASIC_FREQUENCY,你翻看main.h的SYS_BASIC_FREQUENCY定义,你会发现SYS_BASIC_FREQUENCY定义的就是20000.
CpuTimer0Regs.TCR.bit.TIE = 1; 这行代码则是打开CPU Timer0的计数溢出中断。也就是说CPU Timer0模块每20kHz就会产生一个中断信号。
EALLOW;
PieVectTable.TIMER0_INT = &CpuTimer0ISR; // Specify the interrupt
EDIS;
这个三行代码则是指定CPU Timer0的计数溢出中断的中断处理函数是CpuTimer0ISR。
IER |= M_INT1; // Enable CPU Level interrupt
PieCtrlRegs.PIEIER1.bit.INTx7 = 1; // Enable PIE Level interrupt
这两行的作用则是如它们的注释所说,打开CPU Timer0的计数溢出的PIE级中断和打开CPU级中断。关于中断部分,我会在另一篇文章里面详细讲,这里不赘述。CPUTimerConfig();函数的定义讲完了,下面接着往后讲。
PieCtrlRegs.PIECTRL.bit.ENPIE = 1; // 使能PIE
EINT; // 使能全局中断
ERTM; // 使能实时仿真中断
这3行代码也如注释所言,咱们外设(CPU Timer0也视为外设)的中断信号 是连接到 PIE模块的 然后PIE模块的信号会连接到 全局/CPU级中断。 因此要让CPU Timer0的计数溢出中断最终生效,要层层打开PIE模块和使能全局中断。
CpuTimer0Regs.TCR.bit.TSS = 0;则是让CPU Timer0开始不停的计数,溢出产生中断,再计数,再溢出产生中断…。 说溢出也有可能不恰当哈,我搞混了DSP和STM32了,反正它们有一个是从0开始向上递增计数,这个可以说是溢出,另一个是从你设置的计数值向下递减计数,当计数值=0时产生中断,下次计数则是重新装载你之前写到计数周期寄存器的计数值,重新向下递减计数。
while(1){
if(iFreq5000Hz==1){
// do something
iFreq5000Hz = 0;
}
} // 这些代码则是举例如何去使用分频好的信号,比如实现一个5k的离散控制器。
另外,关于中断处理函数interrupt void CpuTimer0ISR(void)和软分频函数void DivideFreq(void)是很容易理解,应该一看就懂了,也就不赘述了。
愿我们共同进步! 感谢您的阅读,欢迎留言讨论、收藏、点赞、分享。