C6713定时器中断实战:从时钟配置到ISR可靠响应
2026/9/12 12:20:04 网站建设 项目流程

简介:本资源是面向嵌入式DSP开发初学者与进阶工程师的TMS320C6713定时器与串口中断实战例程,聚焦实时中断机制理解与底层驱动开发能力提升。压缩包共34个文件,涵盖9个目标文件(.obj)、3个C源码(.c)、3个头文件(.h)、2个汇编启动文件(.asm)及调试相关文件(.pjt、.map、.out、.hex等),完整呈现工程构建、中断初始化、服务函数编写与硬件交互全流程;186KB体积精炼,便于快速导入CCS环境编译运行。已有671人学习下载,适合在音频处理、工业控制等实时场景中实践DSP中断响应与串口通信优化。读者可直接复用定时器配置逻辑、中断向量表设置方法及中断驱动串口收发框架,并通过多版本obj与log文件对比理解编译过程与调试要点,显著降低TI C6000系列DSP中断开发入门门槛。

1. 为什么在 TMS320C6713 上写定时器中断,不能只抄例程就完事?

很多刚接触 C6713 的工程师拿到dsp6713.rar解压后直接烧进仿真器,发现 LED 不闪、串口没输出、甚至 CCS 报错“Target not responding”,第一反应是“例程坏了”或“板子虚焊”。其实问题常出在三个被忽略的底层耦合点:CPU 频率配置与定时器时钟源不匹配中断向量表未重定向到片内 RAM(L2)导致跳转失败IEP(Internal Event Pulse)使能顺序错误引发中断屏蔽。C6713 的定时器(TINT0/TINT1)不是简单写个寄存器就能触发——它依赖于 PLL 设置后的 SYSCLKOUT 分频链、EDMA 通道状态机就绪信号、以及 PIE(Peripheral Interrupt Expansion)模块的三级使能结构。本文面向已能点亮 LED 但卡在中断响应环节的开发者,不讲汇编启动流程,不堆寄存器定义表,只聚焦从 CCS v3.3 环境下跑通一个可验证的 1ms 定时器中断例程所需的最小闭环路径:配置 → 使能 → 触发 → 验证。所有代码均可在合众达 SEED-XDS560v2 仿真器 + SEED-DTK6713 开发板上实测通过。

2. 用 C6713 的 TIMER0 实现 1ms 中断:从时钟树推导到寄存器写入

2.1 先确认你的硬件时钟链路是否真实生效

C6713 的定时器时钟源并非直接来自晶振,而是经过 PLL 倍频后的 SYSCLKOUT 再分频。若你使用默认 100MHz 晶振且未修改 PLLCR 寄存器,实际 SYSCLKOUT = 200MHz(PLL=2x)。此时 TIMER0 的输入时钟为 SYSCLKOUT/4 = 50MHz(由 TIMCTL[CLKSRC] 和 TIMCTL[CLKDIV] 控制)。这个值必须和你在代码中设置的周期值严格对应,否则中断间隔会偏差 2~5 倍。验证方法是在 CCS 的 Memory Browser 中查看地址0x01800000(PLLSTAT),确认 bit15(PLLEN)为 1;再读0x01800004(PLLCR),确认 bit7:0(DIV)为 0x01(即倍频系数为 2)。若为 0x00,则 PLL 未启用,SYSCLKOUT 仍为 100MHz,此时定时器频率需重新计算。

提示:不要依赖 CCS Project → Build Properties 中的 “Target Clock” 设置——该值仅影响仿真器时序估算,不修改实际硬件寄存器。真实时钟必须由代码初始化 PLL 后生效。

2.2 TIMER0 寄存器组的最小化配置序列

C6713 的 TIMER0 有 5 个关键寄存器,但只需操作其中 3 个即可启动中断:

// 地址映射(C6713 datasheet Table 6-3) #define TIMER0_BASE 0x01C20000 #define TIM0PRD *(volatile unsigned int*)(TIMER0_BASE + 0x00) // 周期寄存器 #define TIM0CNT *(volatile unsigned int*)(TIMER0_BASE + 0x04) // 计数寄存器 #define TIM0CTL *(volatile unsigned int*)(TIMER0_BASE + 0x08) // 控制寄存器 // 步骤1:停止计数并清零 TIM0CTL = 0x0000; // 关闭使能位(bit0=0),清除所有控制位 TIM0CNT = 0x0000; // 清空当前计数值 // 步骤2:设置 1ms 周期(假设 SYSCLKOUT=200MHz, 分频后=50MHz → 50000 个时钟周期) TIM0PRD = 50000; // 注意:这是 32 位无符号整数,非十六进制 // 步骤3:配置控制寄存器(关键!) TIM0CTL = 0x0007; // bit0=1(使能 TIMER)、bit1=1(自动重载)、bit2=1(使能中断)

参数说明:

  • TIM0PRD = 50000:当 TIMER 输入时钟为 50MHz 时,50000 个周期 = 1ms。若实测偏差,优先检查 PLL 配置而非修改此值。
  • TIM0CTL = 0x0007:bit0(TSS)为 1 表示运行;bit1(TRB)为 1 表示重载 PRD 到 CNT;bit2(TIE)为 1 表示使能中断请求。缺一不可,常见错误是只写0x0001(仅使能)导致无中断。

2.3 PIE(外设中断扩展)模块的三级使能必须完整

C6713 的中断不是 CPU 直接响应外设,而是经 PIE 模块路由。TIMER0 中断属于 PIE Group 1,子中断号 INT1.0(对应 PIECTRL[1])。必须按顺序执行三步:

  1. 使能 PIE 组中断PIECTRL[1] = 0x0001(Group 1 使能)
  2. 使能 TIMER0 子中断PIEIER1.bit.INTx0 = 1(x=0,即 TIMER0)
  3. 全局使能 CPU 中断ST1_55[INTM] = 0(清除中断屏蔽位)
// PIE 寄存器基址(C6713 TRM Section 11.3.1) #define PIE_BASE 0x000CE000 #define PIECTRL *(volatile unsigned int*)(PIE_BASE + 0x00) #define PIEIER1 *(volatile unsigned int*)(PIE_BASE + 0x10) // 使能 PIE Group 1 PIECTRL = 0x0001; // 使能 TIMER0(INT1.0)——注意:不是 INT1.7 或其他 PIEIER1 = 0x0001; // 清除 CPU 全局中断屏蔽 asm(" CLR INTM");

注意:PIEIER1 = 0x0001是关键。很多例程误写为PIEIER1 = 0x0080(对应 INT1.7),导致 TIMER0 中断永远无法到达 CPU。C6713 的 TIMER0 固定映射到 INT1.0,不可更改。

3. 在 CCS v3.3 中构建可调试的中断服务程序(ISR)

3.1 中断向量表必须重定向到 L2 RAM 才能可靠跳转

C6713 复位后中断向量表默认位于 Flash(0x00000000),但 Flash 执行速度慢且不支持动态修改。若 ISR 函数编译后放在 Flash,中断响应延迟可能超过 20 个周期,导致丢失后续中断。必须将向量表复制到片内 L2 RAM(0x00800000)并更新中断向量指针(IVPD/IVPDH)。CCS v3.3 提供标准宏MEMCPY实现:

// 在 main() 开头添加(必须在 TIMER 初始化之前) extern unsigned int _vectors_start; extern unsigned int _vectors_end; unsigned int *vectors_src = &_vectors_start; unsigned int *vectors_dst = (unsigned int*)0x00800000; int vectors_size = (int)&_vectors_end - (int)&_vectors_start; MEMCPY(vectors_dst, vectors_src, vectors_size); // 更新中断向量指针寄存器(IVPD = 0x00800000) asm(" MVKL 0x00800000, B0"); asm(" MVKH 0x00800000, B0"); asm(" STW B0, *0x00000000"); // IVPD 地址为 0x00000000 asm(" STW B0, *0x00000004"); // IVPDH 地址为 0x00000004

逻辑说明:_vectors_start/_vectors_end是链接脚本生成的符号,指向.vecs段起止地址;MEMCPY将整个向量表(含 RESET、NMI、INT1.0 等 128 个入口)拷贝到 L2 RAM;最后两行用汇编指令将 IVPD/IVPDH 寄存器指向新地址,确保 CPU 取中断向量时访问 RAM 而非 Flash。

3.2 编写可验证的 TIMER0 ISR 并绑定到 INT1.0

C6713 的 ISR 必须用interrupt关键字声明,并显式清除中断标志。TIMER0 的中断标志位在 PIEACK 寄存器中(Group 1 ACK),不清除会导致中断只触发一次

// 全局变量用于验证(避免优化掉) volatile unsigned int timer_count = 0; interrupt void timer0_isr(void) { timer_count++; // 每次中断加 1 // 清除 PIE Group 1 的中断应答(关键!) *(volatile unsigned int*)0x000CE040 = 0x0001; // PIEACK1 地址为 0x000CE040 // 返回前必须清除 TIMER0 自身的中断标志(写 1 清零) *(volatile unsigned int*)(TIMER0_BASE + 0x0C) = 0x0001; // TIM0INTFLG }

参数说明:

  • *(volatile unsigned int*)0x000CE040 = 0x0001:向 PIEACK1 写 1,表示 Group 1 中断已处理,允许下次触发。若遗漏,中断将被锁死。
  • *(volatile unsigned int*)(TIMER0_BASE + 0x0C) = 0x0001:TIMER0 的中断标志寄存器(TIM0INTFLG)是写 1 清零,非读取清零。这是 C6713 与其他 DSP 的重要区别。

3.3 在 CCS 中设置断点并观测中断行为

timer0_isr函数首行设置断点,全速运行后观察:

  • 若断点从未命中:检查PIEIER1是否为0x0001TIM0CTLbit2 是否为 1、ST1_55[INTM]是否为 0;
  • 若断点命中但timer_count不递增:检查TIM0INTFLG是否被正确清零(用 Memory Browser 查看地址0x01C2000C,值应为 0);
  • timer_count每次加 2:说明中断响应过慢,部分中断被合并,需检查 PLL 配置或降低中断频率。

提示:在 CCS 的 Graph → Time/Frequency Analysis 中,将timer_count添加为实时曲线,可直观看到 1ms 方波——这是比万用表测 GPIO 更可靠的验证方式。

4. 排查三大高频故障:时钟漂移、中断丢失、仿真器失联

4.1 时钟漂移:用示波器测量 GPIO 翻转验证真实周期

理论计算的 1ms 可能因晶振精度、PCB 走线电容而偏差 ±5%。最可靠的方法是让 ISR 翻转一个 GPIO(如 EMIF CE1),用示波器实测高电平宽度:

interrupt void timer0_isr(void) { *(volatile unsigned int*)0x01800010 ^= 0x0002; // 翻转 GPIO0(EMIF CE1 对应 bit1) *(volatile unsigned int*)0x000CE040 = 0x0001; *(volatile unsigned int*)(TIMER0_BASE + 0x0C) = 0x0001; }

若示波器显示高电平为 1.05ms,则需将TIM0PRD从 50000 改为50000 * 1000 / 1050 ≈ 47619不要调整 PLL 倍频系数来凑周期——这会影响整个系统时序

4.2 中断丢失:检查 PIEACK 和 CPU 状态寄存器

timer_count停止增长,但TIM0CNT仍在递减,说明中断请求发出但未被 CPU 响应。此时需检查:

  • PIEACK1(0x000CE040):值应为0x0001(表示 Group 1 已应答),若为0x0000,说明 ISR 未执行到 ACK 行;
  • ST1_55(0x00000004):bit12(INTM)必须为 0,bit11(OVM)为 0(溢出模式关闭);
  • IFR(Interrupt Flag Register,0x00000000):bit0 应为 1(INT1 标志置位),若为 0 则中断根本未到达 PIE。
# 在 CCS Script Window 中快速检查(无需暂停) mem read -form hex -len 1 0x000CE040 # PIEACK1 mem read -form hex -len 1 0x00000004 # ST1_55 mem read -form hex -len 1 0x00000000 # IFR

4.3 仿真器失联:禁用 TIMER0 的调试冲突模式

C6713 的 TIMER0 在仿真器连接时可能与 JTAG 时序冲突,尤其当TIM0CTLbit3(FREE)为 1(自由运行模式)。解决方案是强制进入“停止模式”:

// 在 TIMER 初始化末尾添加 TIM0CTL |= 0x0008; // bit3 = 1,启用 FREE 模式(调试时保持计数) // 但更稳妥的做法是:在 CCS Debug → Realtime Mode 中勾选 "Enable RTDX", // 并在 Project → Build Options → C6000 Compiler → Optimization 中设置 // "Optimization level" = 0(关闭优化,避免 ISR 被内联)

提示:若 CCS 报错 “Target not responding” 且复位无效,立即断电重启开发板——C6713 的 PLL 锁相环在异常状态下可能进入不可恢复的亚稳态,仅软件复位无法解决。

5. 将定时器中断用于实际场景:PWM 波形生成与 ADC 触发同步

5.1 用 TIMER0 中断驱动 10kHz PWM 输出(占空比可调)

C6713 无硬件 PWM 模块,需在 ISR 中翻转 GPIO 模拟。关键是要保证 ISR 执行时间 < 50μs(10kHz 周期的一半),否则占空比失真:

volatile unsigned int pwm_duty = 3000; // 0~5000 对应 0%~100% volatile unsigned int pwm_counter = 0; interrupt void timer0_isr(void) { pwm_counter++; if (pwm_counter < pwm_duty) { *(volatile unsigned int*)0x01800010 |= 0x0002; // GPIO0 = 1 } else if (pwm_counter < 5000) { *(volatile unsigned int*)0x01800010 &= ~0x0002; // GPIO0 = 0 } else { pwm_counter = 0; // 重置周期 } *(volatile unsigned int*)0x000CE040 = 0x0001; *(volatile unsigned int*)(TIMER0_BASE + 0x0C) = 0x0001; }

参数说明:pwm_duty = 3000表示高电平持续 3000 个 1ms 中断周期 → 即 3ms,占空比 = 3ms/5ms = 60%。注意:此处利用了 TIMER0 的 1ms 基础节拍,而非直接配置 PWM 频率——这是嵌入式 DSP 中最常用的“软 PWM”实现法。

5.2 同步触发 ADC 采样:用 TIMER0 的输出信号作为 ADC 启动源

C6713 的 ADC 模块(如外挂 TLC549)可通过 GPIO 引脚触发。将 TIMER0 的输出引脚(如 TOUT0)连接到 ADC 的 CONVST,即可实现精确时序控制:

TIMER0 寄存器功能
TIM0PRD10000200MHz/4=50MHz → 200μs 周期
TIM0CTL0x000Fbit3=1(FREE)+ bit2=1(TIE)

此时TOUT0引脚输出 5kHz 方波(200μs 周期),每下降沿触发一次 ADC 转换。在 ISR 中无需额外操作,只需确保TOUT0引脚功能已使能(通过GPIOCR寄存器配置)。

提示:若需更高采样率(如 100kHz),应改用 TIMER0 的内部事件脉冲(IEP)直接触发 EDMA 通道搬运 ADC 数据,避免 CPU 干预——这已是进阶应用,需另行展开 DMA 配置。

5.3 一个实用技巧:用 TIMER0 中断实现毫秒级软件延时函数

在无操作系统环境下,常需delay_ms(10)这类函数。基于已验证的 TIMER0 中断,可安全实现:

volatile unsigned int delay_flag = 0; void delay_ms(unsigned int ms) { timer_count = 0; delay_flag = 0; while (timer_count < ms) { // 空循环,等待中断更新 timer_count } delay_flag = 1; } // 在 timer0_isr 中添加: if (delay_flag && (timer_count >= ms)) { delay_flag = 0; // 防止重复触发 }

此函数比for()循环延时更精准,且不阻塞中断——因为while循环期间 TIMER0 中断仍可正常触发并更新timer_count

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询