TMS320F2812 中断管理与CPU定时器实验:LED周期闪烁 + 作弊秒表
前言
继GPIO和SPI实验之后,本次实验深入DSP的核心机制——中断管理和CPU定时器。通过配置PIE(外设中断扩展)和CPU定时器0,我们可以实现精确的时间控制。实验分为两个任务:任务1实现LED以1:5的比例周期性闪烁;任务2则是一个“作弊秒表”,能够通过按键调节计时速度、暂停/继续、清零,并在四位数码管上显示时间。
本文将详细讲解TMS320F2812的中断架构、CPU定时器工作原理,并给出完整的代码解析和实验现象。
一、实验概述
1.1 实验目的
-
理解TMS320F2812的PIE扩展中断结构(PIEIER、PIEIFR、PIEACK等)
-
掌握CPU定时器0的配置方法(周期寄存器、预定标寄存器、控制寄存器)
-
学习在中断服务函数中实现精确的定时任务
-
结合GPIO按键输入、SPI数码管显示,完成一个完整的嵌入式小项目
1.2 实验环境
-
硬件:SEED-DPS2812M开发板、4位数码管模块、按键SW3/SW6/SW9、XDS510仿真器、PC
-
软件:CCS(Code Composer Studio)
1.3 实验任务
| 任务 | 功能描述 |
|---|---|
| 任务1 | 利用CPU定时器实现LED点亮1个时间周期、熄灭5个时间周期(一个周期=定时器中断周期) |
| 任务2 | 四位作弊秒表:数码管显示0~9999,每秒递增1(正常速度);SW3加速(周期减少0.2s),SW9减速(周期增加0.2s),SW6暂停/继续;同时按下SW3+SW6清零 |
二、实验原理
2.1 TMS320F2812的中断系统
F2812的中断分为三个级别:外设级 → PIE级 → CPU级。
-
外设级:每个外设(如CPU定时器、SPI、ADC)都有自己的中断标志和使能位。
-
PIE级:将96个外设中断复用为12个CPU中断(INT1~INT12)。每个INTx下有8个中断源(INTx.1~INTx.8)。PIE模块通过
PIEIERx(使能)、PIEIFRx(标志)、PIEACK(应答)来管理。 -
CPU级:CPU有16个可屏蔽中断(INT1~INT14,以及RTOSINT、DLOGINT),通过
IER(中断使能寄存器)和全局中断屏蔽INTM控制。
中断响应流程(以CPU定时器0为例):
-
定时器0中断事件发生 → 外设标志位(TIF)置1。
-
PIE模块识别INT1.7(定时器0挂在INT1.7),设置
PIEIFR1.7 = 1。 -
若
PIEIER1.7 = 1且PIEACK1 = 0,则向CPU发出INT1请求。 -
CPU设置
IFR1 = 1,若IER1 = 1且INTM = 0,则响应该中断。 -
CPU从PIE向量表中取出预置的中断服务函数地址并执行。
-
中断服务函数中必须:
-
清除外设中断标志(
CpuTimer0Regs.TCR.bit.TIF = 1) -
清除PIEACK(
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1)
-
2.2 CPU定时器结构
F2812有三个32位定时器:Timer0、Timer1、Timer2。Timer0可供用户自由使用,Timer1/Timer2通常预留给DSP/BIOS。
核心寄存器:
-
TIMH:TIM:32位计数寄存器(递减) -
PRDH:PRD:32位周期寄存器(重装载值) -
TDDRH:TDDR:16位分频寄存器 -
PSCH:PSC:16位预定标计数器 -
TCR:控制寄存器(包含TSS启动/停止位、TIE中断使能位、TIF中断标志位等)
定时器中断频率公式:
fTINT=SYSCLKOUT(PRDH:PRD+1)×(TDDRH:TDDR+1)fTINT=(PRDH:PRD+1)×(TDDRH:TDDR+1)SYSCLKOUT
通常我们配置TDDRH:TDDR = 0,则中断周期 = (PRDH:PRD + 1) / SYSCLKOUT。
例如:SYSCLKOUT = 150 MHz(时钟周期 ≈ 6.67 ns),若需要10ms中断,则 PRD = 150e6 × 0.01 - 1 = 1,499,999。
2.3 硬件连接回顾
-
数码管:SPI(GPIOF0~F3)驱动74HC164,段码发送;位选由GPIOB8~B13控制(四位数码管)
-
按键:SW3(GPIOB5)、SW6(GPIOB4)、SW9(GPIOB3),公共端GPIOB0输出低电平(线性键盘接法)
-
LED(任务1):GPIOA0
三、任务1:LED周期性闪烁(1:5)
3.1 设计思路
-
配置CPU定时器0产生1秒中断(周期可调,本实验用1秒作为“一个时间周期”)。
-
在中断服务函数中,使用状态机:
-
状态0:LED点亮,计数器加1,当计数器达到1时切换到状态1。
-
状态1:LED熄灭,计数器加1,当计数器达到5时切换回状态0。
-
-
这样LED点亮1秒、熄灭5秒,循环。
3.2 关键代码解析
// 全局变量
Uint32 TimerState = 0; // 0:点亮阶段, 1:熄灭阶段
Uint32 BlinkCounter = 0; // 记录当前阶段经过的中断次数
interrupt void cpu_timer0_isr(void)
{
if(TimerState == 0) // 点亮阶段
{
GpioDataRegs.GPADAT.bit.GPIOA0 = 1; // LED亮
BlinkCounter++;
if(BlinkCounter >= 1) // 点亮1个周期
{
TimerState = 1;
BlinkCounter = 0;
}
}
else // 熄灭阶段
{
GpioDataRegs.GPADAT.bit.GPIOA0 = 0; // LED灭
BlinkCounter++;
if(BlinkCounter >= 5) // 熄灭5个周期
{
TimerState = 0;
BlinkCounter = 0;
}
}
// 清除中断标志和PIE应答
CpuTimer0Regs.TCR.bit.TIF = 1;
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}
3.3 定时器初始化
InitCpuTimers(); // 必须调用,关联地址
ConfigCpuTimer(&CpuTimer0, 150, 1000000); // 150MHz, 1秒周期(单位微秒)
CpuTimer0Regs.TCR.bit.TIE = 1; // 使能定时器中断
IER |= M_INT1; // 使能CPU INT1
PieCtrlRegs.PIEIER1.bit.INTx7 = 1; // 使能PIE INT1.7(定时器0)
StartCpuTimer0(); // TSS=0启动
EINT; // 开全局中断
3.4 实验现象
-
下载程序后,LED(GPIOA0)以点亮1秒、熄灭5秒的规律循环闪烁。
-
变化分明,符合预期。
四、任务2:作弊秒表(四位数码管 + 按键调速)
4.1 系统功能
-
正常计时:每秒递增1,数码管显示0000~9999,循环。
-
SW3(加速):每按一次,计时周期减少0.2秒(即每秒递增更快),最小周期0.2秒。
-
SW9(减速):每按一次,计时周期增加0.2秒,最大周期3秒。
-
SW6(暂停/继续):单按暂停计时,再按恢复。
-
SW3 + SW6同时按下:秒表清零(且自动暂停)。
4.2 设计要点
(1)定时器中断周期选择
由于需要支持0.2秒、0.4秒、…、3.0秒的可变周期,如果直接修改定时器的PRD值会比较麻烦(需要重新配置定时器)。更简单的做法是:定时器固定产生10ms中断,在中断服务函数中使用一个计数器Timer10msCount累加,当累加值达到 Interval / 10(Interval单位毫秒)时,才更新秒表数值。这样只需修改Interval变量即可调速,无需重新配置硬件定时器。
(2)按键扫描与消抖
-
按键采用线性连接(GPIOB0输出低,GPIOB3~B5为输入)。
-
在
KeyScan()函数中实现:-
独立按键去抖(计数3次,约30ms)。
-
组合键检测(SW3和SW6同时按下为清零)。
-
防止重复触发(按下标志
keyPressFlag)。
-
(3)数码管动态扫描
-
主循环中持续扫描四位,每位点亮约500μs,利用人眼视觉暂留显示完整数字。
-
SPI发送段码时,注意
spi_xmit函数将数据放到高8位(因为代码中SPI配置为16位模式,但只用了高8位)。
(4)中断服务函数中的注意点
interrupt void cpu_timer0_isr(void)
{
Timer10msCount++;
KeyScanCounter++; // 通知主循环进行按键扫描(每10ms一次)
if(!PauseFlag)
{
if(Timer10msCount >= (Interval / 10))
{
Timer10msCount = 0;
DisData++;
if(DisData > 9999) DisData = 0;
}
}
// 清除标志...
}
4.3 关键代码片段
调速逻辑(按键处理)
// SW3加速
if(sw3_state == 0 && combPressFlag == 0) {
// 消抖后
if(Interval < 3000) Interval += 200;
if(Interval > 3000) Interval = 3000;
}
// SW9减速
if(sw9_state == 0) {
if(Interval > 200) Interval -= 200;
if(Interval < 200) Interval = 200;
}
组合键清零
if(sw3_state == 0 && sw6_state == 0) { // 同时按下
if(comboDebounce >= 2 && comboPressed == 0) {
comboPressed = 1;
DisData = 0;
Timer10msCount = 0;
PauseFlag = 1; // 清零后自动暂停,防止立即增加
}
}
SPI发送时注意数据位置
void spi_xmit(unsigned char a)
{
SpiaRegs.SPITXBUF = (Uint16)a << 8; // 高8位有效
}
这是因为SPICCR配置为16位字符长度,但实际段码只有8位,放在高字节,低字节补0。如果希望直接使用8位,可将SPICCR的CHAR位域配置为7(8位字符模式)。
4.4 实验现象
-
正常情况:数码管从0000开始,每1秒加1。
-
按SW3:数字跳动速度加快,每按一次,速度提升一档(周期减少0.2s),最快约每秒跳5次(0.2s周期)。
-
按SW9:速度减慢,最慢约每3秒加1。
-
按SW6:数字停止变化;再按SW6,继续变化。
-
同时按SW3+SW6:数字立即归零并暂停。
-
数码管显示稳定,无闪烁,位选切换正确。
五、实验总结与心得体会
5.1 中断配置要点
-
EALLOW保护:修改PIE向量表时需要
EALLOW。 -
清除标志顺序:先清除外设中断标志,再清除PIEACK,否则可能丢失中断。
-
PIEACK机制:同一组的某个中断被响应后,
PIEACKx会被硬件置位,该组其他中断暂时无法向CPU请求。在ISR结束时必须写1清除PIEACKx。 -
全局中断:
DINT关闭,EINT开启,注意ERTM(实时仿真模式下的中断使能)一般也调用。
5.2 定时器配置经验
-
ConfigCpuTimer()的第三个参数单位是微秒,最大值受32位限制(约4295秒)。 -
如果定时器中断周期较短(如10ms),中断服务函数中应避免复杂的延时或大量计算,以免影响系统响应。
-
调速方案采用软件计数器(
Timer10msCount比较Interval)比动态修改硬件周期寄存器更简单可靠。
5.3 按键处理心得
-
机械按键必须消抖,本实验采用30ms延时去抖(中断10ms一次,计数3次)。
-
组合键检测需要独立的状态变量,避免和单键功能冲突。
-
清零后自动暂停,符合用户预期(否则清零后计数器立即又递增,看不到清零效果)。
5.4 系统集成体会
本实验综合了:
-
CPU定时器(精确计时)
-
PIE中断管理
-
GPIO按键输入
-
SPI驱动数码管
-
动态扫描显示
通过这个“作弊秒表”项目,真正理解了嵌入式系统中如何将多个外设协同工作,以及中断驱动模型的重要性。
六、完整代码与工程
任务1和任务2的完整代码如附件所示(或参见文中代码块)。在CCS中新建工程,将代码放入main.c,编译下载即可运行。
注意事项:
-
确保开发板上的数码管位选GPIOB8~B13与代码中的宏定义一致(可能与具体外设板有关,必要时调整)。
-
按键连接方式:SW3→GPIOB5,SW6→GPIOB4,SW9→GPIOB3,公共端GPIOB0接地。
-
任务1的LED连接GPIOA0,也可以改成其他引脚。
七、常见问题排查
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 定时器不中断 | 未使能PIE或CPU中断,或未清除PIEACK | 检查IER、PIEIER1.7、PIEACK清除代码 |
| LED一直亮或一直灭 | 状态机逻辑错误或中断未进入 | 在ISR中设置断点,观察TimerState变化 |
| 按键无响应 | GPIO方向配置错误,或未设置公共输出低电平 | 检查Init_Key_Gpio是否正确将GPIOB0置0并保持 |
| 数码管显示乱码 | SPI数据位宽或相位不匹配 | 确认SPICCR的CHAR位域,尝试改为0x47(8位)或调整时钟极性 |
| 调速后时间不准确 | Interval单位理解错误 | 确保定时器中断周期为10ms,Interval单位是毫秒,比较值为Interval/10 |
八、拓展思考
-
如果要求秒表精确到0.01秒,应该如何修改?(提高定时器中断频率,例如1ms中断)
-
能否将变速改为连续可调(如按住SW3持续加速)?
-
如何增加一个复位按键,使间隔恢复默认1秒?
这些问题留给读者自行尝试。
本文为TMS320F2812中断与定时器实验的完整记录,希望对学习DSP的小伙伴有帮助!
如果觉得写得好,欢迎点赞、收藏、转发!如有疑问,评论区交流讨论。
更多推荐
所有评论(0)