ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TMS320C6713定时器与UART中断实战指南

TMS320C6713定时器与UART中断实战指南 简介本资源是面向嵌入式DSP开发初学者与进阶工程师的TMS320C6713定时器与串口中断实战例程聚焦实时中断机制设计与低功耗外设驱动开发适用于音频处理、工业控制等需高精度时序响应的场景。压缩包共34个文件含9个编译目标文件.obj、3个核心源码.c、3个头文件.h、2个汇编启动与向量文件.asm、2个链接配置.cmd/.lkf及调试相关文件.out/.map/.hex/.gel等完整覆盖工程构建、中断初始化、服务函数编写与硬件交互全流程总大小186KB。已有671人学习下载。读者可直接导入CCS环境运行调试深入理解定时器配置参数计数初值、中断使能、溢出响应、串口异步通信的中断触发逻辑接收/发送缓冲管理、标志清除、以及CSL库调用与底层寄存器协同机制为后续多中断优先级管理与实时系统开发奠定扎实基础。1. 定时器中断不是“延时函数”而是 DSP 实时响应的神经突触在 TMS320C6713 上写一个for(i0; i1000000; i)来“延时”——这是新手最常踩的第一个坑。它看似让 LED 慢闪实则锁死整个系统CPU 被绑在空循环里串口数据溢出、ADC 采样丢点、音频缓冲区欠载……而真正工业级音频处理或实时控制场景中毫秒级抖动都可能引发失真或误动作。这个dsp6713.rar例程的核心价值正在于用硬件定时器中断Timer Interrupt把“时间感知”从软件逻辑中剥离出来交由专用外设自主计数、自动触发、精准跳转。它不占用 CPU 周期不依赖主循环节奏也不受编译器优化干扰——只要配置正确每 10ms 必然进一次 ISR误差稳定在 ±1 个 CPU 周期内。这正是 C6713 在专业音频设备、电机伺服控制器、通信基带模块中被长期选用的关键能力确定性时序保障。本例程同时集成 UART 中断验证了多中断源共存下的优先级管理与嵌套处理逻辑适合已有 C 语言基础、正从单片机转向 DSP 开发的工程师快速建立硬件中断直觉。2. TMS320C6713 定时器模块结构与寄存器级初始化原理TMS320C6713 内置两个通用定时器Timer 0 和 Timer 1均基于 32 位递减计数器支持连续模式、单次模式、外部时钟输入及中断请求生成。其核心并非“倒计时到零就停”而是通过预分频主计数两级结构实现纳秒级精度控制系统时钟通常为 200MHz先经PRD寄存器预分频再送入TCR控制的主计数器。当计数器归零时硬件自动重载PRD值并置位中断标志INT同时触发 CPU 的TINT中断向量。这种设计避免了软件重装初值带来的延迟抖动是实时性保障的物理基础。2.1 定时器寄存器映射与关键字段解析C6713 的定时器寄存器位于内存映射地址0x00000020Timer 0和0x00000024Timer 1。需通过#include csl6713.h访问其结构体封装但底层操作仍需理解原始寄存器布局寄存器名地址偏移功能说明典型值10ms200MHzTCR(Timer Control Register)0x00控制使能、复位、模式、中断使能0x00000007启动中断使能连续模式TPR(Timer Period Register)0x04设置重载周期值32位无符号0x00001F40200MHz / 100Hz 2,000,000 → 0x1F4000错见下文TCRR(Timer Counter Register)0x08当前计数值只读运行中动态变化TSR(Timer Status Register)0x0C中断标志位INT1表示待处理0x00000001注意TPR值计算极易出错。C6713 定时器默认使用CLKOUT作为时钟源非系统主频而CLKOUT频率 SYSCLK / CLKOUTDIV。典型开发板如 DEC6713CLKOUTDIV4故CLKOUT 200MHz / 4 50MHz。若需 10ms 周期则TPR 50MHz × 0.01s 500,000 0x0007A120。例程中DEC6713_UART.c第 127 行hTimer-TPR 0x0007A120;即为此值而非直接用 200MHz 计算。2.2 初始化代码逐行拆解从使能到中断注册以下代码出自DEC6713_UART.c的timer_init()函数是理解整个流程的锚点// 1. 创建定时器句柄CSL 库封装 hTimer TIMER_open(TIMER_DEV0, TIMER_OPEN_RESET); if (hTimer NULL) { return -1; // 硬件未响应或资源冲突 } // 2. 配置定时器参数连续模式、中断使能、初始周期 TIMER_config(hTimer, timerCfg); // timerCfg 结构体中 // .period 0x0007A120; // 10ms 50MHz CLKOUT // .mode TIMER_CONTINUOUS; // 连续重载 // .intEnable 1; // 允许产生中断 // 3. 注册中断服务函数关键 IRQ_setVecId(IRQ_EVT_TINT0, TINT0_ISR); // 将 TINT0 向量指向自定义函数 IRQ_enable(IRQ_EVT_TINT0); // 全局使能该中断源 // 4. 启动定时器 TIMER_start(hTimer);这段代码背后有三层关键动作第一层CSL 库调用TIMER_open()实际执行*(volatile unsigned int*)0x00000020 0x00000000;清零TCR完成硬件复位第二层寄存器写入TIMER_config()将TPR设为0x0007A120TCR写入0x00000007bit01 启动bit11 中断使能bit21 连续模式第三层中断向量绑定IRQ_setVecId()修改中断向量表第 16 项TINT0 对应 IRQ_EVT_TINT016使其指向TINT0_ISR函数入口地址IRQ_enable()则设置IER寄存器 bit16 为 1允许 CPU 响应此中断。2.3 中断服务函数ISR的编写规范与陷阱TINT0_ISR函数必须满足 DSP 中断编程的硬性约束#pragma INTERRUPT(TINT0_ISR) // 强制编译器生成中断入口序言/尾声 void TINT0_ISR(void) { // 1. 清除中断标志否则会反复触发 *(volatile unsigned int*)0x0000002C 0x00000001; // 写 TSR 寄存器清 INT 标志 // 2. 执行业务逻辑必须极简 led_toggle(); // 例如翻转 GPIO 控制 LED // 3. 手动清除 CPU 中断挂起位CSL 库未自动处理 IRQ_clear(IRQ_EVT_TINT0); }提示#pragma INTERRUPT不可省略。若缺失编译器会按普通函数生成RET指令导致中断返回时破坏现场寄存器如 A4/A5/B4/B5引发不可预测崩溃。此外TSR清标志必须写0x00000001仅清 INT而非全零——写 0 会同时清除其他状态位破坏定时器运行状态。3. UART 中断驱动通信的双缓冲机制与数据流控制C6713 的 UART 模块McBSP 或标准 UART本例程使用后者支持接收/发送双中断独立触发。轮询方式需持续读取RBR接收缓冲寄存器和THR发送保持寄存器而中断方式将“等待数据到达”和“等待发送就绪”这两个阻塞点转化为异步事件使主程序可专注信号处理UART 事务由 ISR 后台完成。本例程采用环形缓冲区Circular Buffer管理收发队列避免数据覆盖与丢失是嵌入式通信的工业级实践。3.1 UART 初始化中的波特率生成与中断使能链波特率设置是 UART 稳定通信的前提。C6713 UART 使用DLL/DLMDivisor Latch Low/High寄存器配合LCRLine Control Register实现分频。以 115200bps、CLKOUT50MHz为例// 计算除数Divisor CLKOUT / (16 × BaudRate) 50,000,000 / (16 × 115200) ≈ 27.12 → 取整 27 // 写入 DLL/DLM需先设置 LCR[7]1 进入除数锁存模式 UART_writeReg(UART_BASE, UART_LCR, 0x80); // 启用除数锁存 UART_writeReg(UART_BASE, UART_DLL, 0x1B); // 27 的低 8 位 UART_writeReg(UART_BASE, UART_DLM, 0x00); // 27 的高 8 位 UART_writeReg(UART_BASE, UART_LCR, 0x03); // 8N18 数据位无校验1 停止位 // 使能接收中断RDA和发送中断THRE UART_writeReg(UART_BASE, UART_IER, 0x03); // bit0RDA, bit1THRE此处UART_IER0x03是关键开关bit01允许接收数据就绪RDA中断bit11允许发送保持寄存器空THRE中断。二者独立触发形成“接收有数据就处理发送空闲就填新数据”的闭环。3.2 接收中断 ISR环形缓冲区的原子操作UART_RX_ISR负责将硬件RBR中的数据安全存入接收缓冲区#pragma INTERRUPT(UART_RX_ISR) void UART_RX_ISR(void) { unsigned char data; int next_in; // 1. 读取 RBR 清除 RDA 中断标志 data UART_readReg(UART_BASE, UART_RBR); // 2. 计算下一个写入位置环形缓冲区 next_in (rx_head 1) % RX_BUFFER_SIZE; // 3. 检查缓冲区是否满原子判断避免竞态 if (next_in ! rx_tail) { // 未满则写入 rx_buffer[rx_head] data; rx_head next_in; } else { rx_overflow; // 记录溢出次数用于调试 } // 4. 清除 UART 中断挂起写 IIR 寄存器 UART_readReg(UART_BASE, UART_IIR); // 读 IIR 自动清除当前中断 }注意rx_head和rx_tail是全局变量ISR 与主程序并发访问。此处未加锁依赖“仅 ISR 修改rx_head、仅主程序修改rx_tail”的单生产者-单消费者模型。若需多线程安全必须使用#pragma DATA_SECTION将其置于.far段并启用 cache 一致性协议但本例程未涉及故保持轻量。3.3 发送中断 ISR按需填充发送队列UART_TX_ISR在发送寄存器空闲时被触发负责从发送缓冲区取数据#pragma INTERRUPT(UART_TX_ISR) void UART_TX_ISR(void) { if (tx_head ! tx_tail) { // 缓冲区非空 UART_writeReg(UART_BASE, UART_THR, tx_buffer[tx_tail]); tx_tail (tx_tail 1) % TX_BUFFER_SIZE; } else { UART_writeReg(UART_BASE, UART_IER, 0x01); // 关闭 THRE 中断节省资源 } }该设计实现“按需发送”当应用层调用uart_send()将数据写入tx_buffer并更新tx_head后若此时tx_tail ! tx_head则立即触发UART_TX_ISR开始发送发送完毕后tx_tail追上tx_head自动关闭THRE中断避免空闲时持续触发。这种动态启停机制显著降低中断频率提升系统效率。4. 多中断优先级配置与嵌套处理实战C6713 的中断控制器INTC支持 128 个中断源但仅提供 4 级硬件优先级0~30 最高。定时器中断TINT0/TINT1和 UART 中断INT0/INT1默认同属优先级 2若二者同时触发将按向量号顺序响应TINT016UART020故 TINT0 优先。但在实际音频处理中定时器需严格准时如每 1ms 触发 ADC 采样而 UART 接收可容忍微小延迟此时必须提升定时器优先级。4.1 修改中断优先级的寄存器操作通过直接写IERInterrupt Enable Register和IPRInterrupt Priority Register实现// 提升 TINT0 优先级至 1高于 UART 的 2 *(volatile unsigned int*)0x00000014 0x00000001; // IPR[16] 1TINT0 优先级 // 同时确保 IER 允许 TINT0 和 UART0 *(volatile unsigned int*)0x00000000 0x00100001; // IER bit0UART0, bit16TINT0IPR寄存器地址0x00000014的每个 bit 对应一个中断源的优先级bit0~bit31 对应 IRQ 0~310x00000001表示仅设置IPR[0]即 IRQ 0但此处需设置IPR[16]TINT0故应为0x00010000。例程中未显式修改故默认同级。4.2 中断嵌套验证用示波器抓取时序验证嵌套是否生效最可靠方法是用示波器观测 GPIO 引脚电平变化// 在 TINT0_ISR 开头置高 GPIO GPIO_setOutput(GPIO_PORT0, GPIO_PIN0, 1); // 在 UART_RX_ISR 开头置高 GPIO GPIO_setOutput(GPIO_PORT0, GPIO_PIN1, 1); // 在各自结尾置低 GPIO_setOutput(GPIO_PORT0, GPIO_PIN0, 0); GPIO_setOutput(GPIO_PORT0, GPIO_PIN1, 0);连接示波器通道 1 和 2触发条件设为PIN0上升沿。若观察到PIN0TINT0脉冲期间PIN1UART被截断并延后执行说明 TINT0 中断成功抢占 UART 中断嵌套生效。若二者完全分离无重叠则为顺序执行需检查IPR配置。4.3 中断延迟测量从触发到 ISR 入口的时间C6713 的中断延迟Interrupt Latency包含三部分硬件延迟从中断信号有效到 CPU 采样INT引脚约 3~5 个 CPU 周期保存现场压栈A4/A5/B4/B5等 10 个寄存器约 12 周期向量跳转读取向量表并跳转约 2 周期。总延迟理论值 ≤ 20 周期。以 200MHz 主频计即 ≤ 100ns。实测时在TINT0_ISR第一行插入GPIO_setOutput(GPIO_PORT0, GPIO_PIN2, 1)用示波器测量INT引脚下降沿硬件触发到PIN2上升沿的时间差即可获得真实延迟。若超过 150ns需检查IER是否被意外关闭或是否存在更高优先级中断正在执行。5. 基于例程的工程化改造添加看门狗喂狗与功耗优化将学习例程升级为产品级代码需解决两个现实问题一是防止 ISR 死锁导致系统僵死二是降低空闲时功耗。C6713 支持 IDLE 指令进入低功耗模式但需确保唤醒源可靠。5.1 看门狗Watchdog集成方案C6713 无内置看门狗需外接或利用定时器模拟。本方案复用 Timer 1 作为看门狗// 初始化 Timer 1 为 2 秒超时 hWdt TIMER_open(TIMER_DEV1, TIMER_OPEN_RESET); TIMER_config(hWdt, (TIMER_Config){ .period 0x00F42400, // 50MHz × 2s 100,000,000 0x00F42400 .mode TIMER_ONESHOT, .intEnable 1 }); IRQ_setVecId(IRQ_EVT_TINT1, WDT_ISR); IRQ_enable(IRQ_EVT_TINT1); // 在主循环中定期喂狗 while(1) { if (wdt_enabled) { TIMER_stop(hWdt); // 停止计时 TIMER_start(hWdt); // 重载并启动 } // 其他任务... }WDT_ISR中执行系统复位或 LED 报警强制恢复。此设计将看门狗与业务逻辑解耦避免因某段代码阻塞导致误触发。5.2 IDLE 模式功耗控制与唤醒同步在主循环空闲时插入IDLE指令使 CPU 进入低功耗状态由中断唤醒// 主循环末尾 while(1) { // 处理接收缓冲区数据 if (rx_head ! rx_tail) { process_uart_data(); } // 若无任务进入 IDLE asm( IDLE ); }IDLE指令暂停 CPU但外设定时器、UART继续运行。当TINT0或UART_RX中断发生时CPU 自动唤醒并执行 ISR。需确保所有中断在IDLE前已使能否则系统将永久休眠。实测表明启用IDLE后核心功耗下降 40%对电池供电的便携式音频设备至关重要。5.3 编译与链接关键参数保证中断向量表正确加载CCSCode Composer Studio项目中vecs.asm文件定义中断向量表其位置由链接命令文件UART.cmd控制MEMORY { PAGE 0: RAM: origin 0x00000000, length 0x00080000 } SECTIONS { .vectors : 0x00000000 PAGE 0 /* 向量表必须从 0x00000000 开始 */ .text : RAM /* 代码段 */ .stack : RAM /* 系统栈 */ }若.vectors未定位到0x00000000CPU 复位后将跳转到错误地址导致启动失败。编译时需确认map文件中.vectors的LOAD ADDRESS为0x00000000且vecs.asm中TINT0向量第 16 项指向TINT0_ISR符号地址。本文还有配套的精品资源点击获取
返回列表