欢迎光临
我们一直在努力

C2000 用 CLB 实现 SCI 串口空闲中断(总线空闲检测)

C2000 的 SCI 没有像 STM32 那样的硬件空闲(IDLE)中断。做 Modbus RTU 这类变长帧协议时,需要靠“总线空闲一段时间”来判断一帧结束,而 SCI 只有收发和 FIFO 中断,没有现成的接收超时。常见做法是用 CPU 定时器,或者在接收中断里软件计时,但这两种都要占 CPU,字符间隔一抖动还容易误判。

这篇记录另一种思路:用 CLB(Configurable Logic Block,可配置逻辑模块)在硬件里搭一个 RX 空闲超时检测器。RX 线持续为高超过设定时间,CLB 直接产生中断,整个过程不占 CPU,阈值也能精确控制。下面把从 SCI 到 CLB tile 的完整配置过一遍。

平台 TMS320F28P550,SysConfig 配置:SCI 负责收字节,CLB 负责检测空闲并产生帧结束中断。

一、实现原理

整体框架

整体信号通路很短:SCI 的 RX 引脚(本例是 GPIO28)→ Input XBAR → CLB1 的 Input 0。

CLB 内部做的事情:

  • UART 空闲时 RX 为高,一旦开始收数据,起始位把 RX 拉低,数据位之间也会出现低电平。
  • 用一个计数器测量 RX 连续为高的时间:只要 RX 被拉低就清零重计,持续为高就一直累加。
  • 计数器累加到设定阈值(即空闲超时)时,说明线路已经空了足够久,这一帧结束了。
  • 此时用一个状态机产生一个一次性脉冲(只触发一次,不会因为线路一直空着而连续触发),再由 HLC 把脉冲变成一个 CPU 中断。

这样 CPU 平时只在 SCI 接收中断里收字节,帧结束的判断完全交给 CLB,空闲中断一来就去处理整帧数据。一个 tile 里用到的资源:LUT0、LUT1、Counter0、FSM0、HLC。

二、SCI 基本配置

SCI 这边按普通串口配:

  • 实例 mySCI0,波特率 115200,数据位 8,停止位 1,无校验(8N1)。
  • 勾选 Use Interrupt 和 Use FIFO,接收用 FIFO 中断收字节。
  • RX 中断生成 INT_mySCI0_RX_ISR,在 PIE 里使能。

空闲检测不依赖 SCI 本身的任何超时功能,SCI 只负责把字节收进来,帧结束由后面的 CLB 中断告诉你。

三、RX 引脚经 Input XBAR 接入 CLB

CLB 看不到 SCI 外设内部的信号,只能看引脚电平,所以要把 RX 引脚通过 Input XBAR 引到 CLB:

  • INPUT 选 XBAR_INPUT1。
  • INPUT Source 选 GPIO28,也就是 SCI 的 RX 脚。
  • 勾上 INPUT Lock。

注意: 这里的 INPUT Source 填的是 GPIO 编号,不是芯片封装上的管脚号。两套编号完全不同——如果把封装管脚号当成 GPIO 号填进去,配置语法全对、编译也过,但 CLB 监视的其实是另一根引脚,空闲中断永远不来,而且很难往这个方向怀疑。务必对着数据手册的 GPIO 复用表确认 RX 实际是哪个 GPIO。

四、CLB 实例配置

  • 实例 myCLB0,CLB Instance 选 CLB1,勾选 Enable CLB。
  • Inputs Used 选 Input 0(就是上一步从 Input XBAR 引进来的 RX)。
  • CLB Input 0 下勾选 Register Interrupt Handler、Attach a TILE Config、Initialize TILE。
  • CLB Interrupt 生成 INT_myCLB0_ISR,在 PIE 里使能。这个就是后面的“帧结束/空闲”中断。

五、Tile Design(核心逻辑)

tile 里的逻辑是整个方案的核心。按 LUT → 计数器 → 状态机 → HLC 的顺序拆开看,各模块的分工是:

  • LUT0:把 RX 取反,得到“RX 当前是否为低”。
  • Counter0:空闲计时器,RX 一低就清零,持续为高就累加。
  • LUT1:控制计数器什么时候允许计数。
  • FSM0:计满阈值时产生一次性脉冲。
  • HLC:把脉冲变成 CPU 中断。

5.1 LUT0:RX 取反

  • 描述 rx_low = !RX,逻辑式 !i0,Input 0 接 Boundary input 0(也就是引进来的 RX)。
  • 输出含义:RX 为低(有数据)时输出 1,RX 为高(空闲)时输出 0。

LUT0 的输出有两个用途:给计数器做复位(Reset),给状态机做输入 e0。

5.2 Counter0:空闲计时器

  • 描述 RX idle timer。
  • Reset 接 LUT0 Output:RX 一被拉低,计数器立刻清零。也就是每个起始位、每个低电平都会让计时重新开始。
  • Counter Enable 接 LUT1 Output:由 LUT1 决定什么时候计数(见 5.3)。
  • Direction 选 One(向上计数)。
  • Match Reference 1 = 13020:计数到这个值时 Counter 0 Equals Match 1 为真,表示空闲已经够久。

所以这个计数器测的是 RX 连续保持高电平的时间,被打断(拉低)就归零重来,只有真正空闲到 13020 个时钟才会触发 match。

阈值怎么算:

Match = 期望空闲时间 × CLB 模块时钟频率
期望空闲时间 = (每字符位数 / 波特率)× 空闲字符数
8N1 → 每字符 10 位;115200 下一个字符 ≈ 86.8 µs

本例 13020,若 CLB 模块时钟为 100 MHz,对应约 130 µs,差不多是 1.5 个字符时间——正是 Modbus RTU 里常用的字符间隔判据。具体数值要按你自己的 SYSCLK / CLB 时钟和想要的空闲字符数重新换算。

5.3 LUT1:计数使能

  • 描述 ctr0_enable = COUNT && RX_high,逻辑式 i0 & !i1 & !i2(配置见图4 下半部分)。
  • Input 0 = FSM 0 State 0 Value(s0),Input 1 = FSM 0 State 1 Value(s1),Input 2 = LUT 0 Output(rx_low)。
  • 展开:计数使能 = s0 & !s1 & !rx_low,即(状态机处于计数态)且(RX 当前为高)时才计数。

配合 5.2 的 Reset:RX 高时累加;RX 一低,既被 Reset 清零、计数使能也撤掉;状态机一旦完成触发(离开计数态),计数也停下来。

5.4 FSM0:一次性触发状态机

这是保证“一帧只触发一次”的关键。两个状态位 s0、s1 编码三个状态:

状态(s1, s0)含义
IDLE 0, 0 等待数据,RX 一直空闲
COUNTING 0, 1 帧已开始,正在给空闲计时
DONE 1, 0 空闲超时已到,已触发过一次

外部输入:

  • e0 = LUT0 Output(rx_low,RX 为低)
  • e1 = Counter 0 Equals Match 1(空闲计满)

转移逻辑(对应 SysConfig 里的状态式):

  • IDLE:RX 一被拉低(e0=1,帧开始)→ 进入 COUNTING,开始计时。
  • COUNTING:还没计满(e1=0)就留在 COUNTING;计满(e1=1)→ 进入 DONE。期间 RX 每被拉低一次计数器就清零重计,所以只有连续空闲到阈值才会真的计满。
  • DONE:已经触发过,停在这里;直到 RX 再次被拉低(新一帧)才回到起点重新武装。

FSM 输出式 !s1 & s0 & e1:只有在 COUNTING 态、且这一刻 e1 变为真时输出才为 1。下一拍状态就跳到 DONE,s0 变 0,输出立刻回 0。所以输出是一个单拍脉冲——线路后面一直空着也不会再触发,直到下一帧重新武装。

为什么要做 one-shot: 空闲时 RX 一直是高,计数器计满后如果不锁住,会反复满足条件、中断连发。用 DONE 态把它停住,等新帧来再放开,就能保证一帧只产生一次空闲中断。

5.5 HLC:产生中断

  • High Level Controller 的 Event 0(e0)接 FSM 0 Output。
  • HLC Program 0 的 Instruction 0 写 INTR 1。

含义:FSM 给出那个一次性脉冲时,触发 HLC 的 e0,执行 INTR 1,向 CPU 发出 CLB 中断,进入 INT_myCLB0_ISR。这就是我们要的“串口空闲中断”。

六、整体时序

把一帧从头到尾串起来看:

阶段RXLUT0计数器FSM 状态CLB 输出
帧前空闲 0 停在 0 IDLE 0
收到起始位 1 清零 IDLE→COUNTING 0
帧内数据 翻转 翻转 随低电平反复清零 COUNTING 0
帧结束转空闲 0 从 0 开始累加 COUNTING 0
空闲达 13020 0 = Match1 COUNTING→DONE 1(单拍)
继续空闲 0 已停 DONE 0
下一帧起始位 1 清零 DONE→… 0

中断只在“空闲达阈值”那一拍产生一次。

七、软件侧处理(ISR)

CPU 这边的分工:

  • INT_mySCI0_RX_ISR:把收到的字节存进接收缓冲区(环形或乒乓)。
  • INT_myCLB0_ISR:清中断标志,置“帧结束”标志或给接收任务发信号,由上层去解析整帧。

ISR 骨架(函数名以 SysConfig 生成的为准,具体 API 按你用的 driverlib 对应,这里只示意流程):

// SCI 接收中断:收字节
interrupt void INT_mySCI0_RX_ISR(void)
{
// FIFO 里有数据就一直读
while (SCI_getRxFIFOStatus(mySCI0_BASE) != SCI_FIFO_RX0)
rxBuf[rxLen++] = SCI_readCharNonBlocking(mySCI0_BASE);

SCI_clearInterruptStatus(mySCI0_BASE, SCI_INT_RXFF);
SCI_clearOverflowStatus(mySCI0_BASE);
Interrupt_clearACKGroup(/* 对应 PIE 组 */);
}

// CLB 空闲中断:帧结束
interrupt void INT_myCLB0_ISR(void)
{
// 关键:清掉 CLB / HLC 中断标志,否则不会再次进入
CLB_clearInterruptTag(myCLB0_BASE);

frameLen = rxLen; // 记录本帧长度
rxLen = 0; // 复位,准备下一帧
frameReady = 1; // 通知主循环 / 任务:一帧收完

Interrupt_clearACKGroup(/* 对应 PIE 组 */);
}

重点是 INT_myCLB0_ISR 里一定要清掉 CLB 的中断标志,否则中断不会再次进入;剩下的 frameReady / frameLen 交给上层去解析。

八、小结

用 CLB 做空闲检测的好处:帧结束判断在硬件里完成,CPU 零开销,阈值精确可调,比软件计时稳。适合 Modbus RTU 等没有固定帧长、要靠总线空闲分帧的场景。

几个要点:

  • RX 引脚要经 Input XBAR 进 CLB,INPUT Source 填的是 GPIO 编号,别填成封装管脚号。
  • 计数器阈值 = 空闲时间 × CLB 时钟,按实际时钟和想要的空闲字符数算。
  • FSM 的 one-shot 是关键,保证一帧只触发一次中断。
  • ISR 里务必清 CLB 中断标志。
赞(0)
未经允许不得转载:171主机测评 » C2000 用 CLB 实现 SCI 串口空闲中断(总线空闲检测)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址