ARTICLE DETAIL

资讯详情

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

C2000 SCI串口通信从原理到实战:波特率误差、GPIO滤波与FIFO排坑指南

C2000 SCI串口通信从原理到实战:波特率误差、GPIO滤波与FIFO排坑指南 上次在产线上调试一台基于TMS320F28034的小型变频器上位机通过RS485转TTL模块和板子通信现象很典型数据偶发乱码手一靠近排线就错帧。排查了一下午最后发现不是硬件抗干扰的问题而是SCI模块的波特率取整误差叠加了GPIO输入滤波的延迟两件事凑在一起把接收端逼到了采样判决的边缘。从那之后我养成了一个习惯——拿到任何C2000的板子先把SCI的时钟链路、寄存器配置和GPIO复用从头到尾捋一遍。这篇东西就是把这个过程记录下来。F28034作为TI的Piccolo系列实时控制MCU在工控、电源、电机驱动里用得非常多SCISerial Communication Interface虽然是个老掉牙的异步串口协议但它在实际项目里承担的活一点不比CAN少参数下发、状态上报、屏显通信、固件升级全都要靠它。内容会从通信原理讲到寄存器操作再给出可以直接抄的初始化和收发代码最后把我在现场踩过的坑一个个摆出来。无论你是刚转到C2000平台的单片机工程师还是已经写了几年DSP代码但SCI这块一直没吃透这篇都值得看完。1. F28034的SCI接口到底擅长干什么活1.1 工控系统里的“万金油”通信口先明确一个概念SCI在C2000平台上的本质就是通用异步收发器也就是我们常说的UART。TTL电平信号收一根线、发一根线加上公共地就能通信。它不像ePWM那样用来输出控制波形也不像ADC那样用来采集模拟量SCI的定位非常纯粹——把控制器和外部世界之间的二进制数据搬运好。F28034这颗芯片在变频器、伺服驱动器、UPS电源、BMS电池管理、小型PLC里出现频率很高。这些设备几乎都有一个共同的需求需要跟人机界面HMI、上位机组态软件、外部仪表或者另一个控制器交换数据。HMI要读写运行频率、故障代码上位机要下发PID参数、启停指令产线还要通过串口做批量校准和固件升级。这些场景里SCI就是最通用、最省成本的接口。有人可能会问现在CAN总线这么普及为什么还用SCI答案很简单不是所有设备都有CAN控制器但几乎所有设备都能轻易引出串口。一台几十块钱的串口屏、一个RS232转USB调试线甚至老旧的工控机串口都能和F28034对上。开发调试阶段SCI更是刚需——程序跑飞了、中断没触发、外设没工作第一件事往往就是往串口打印几个调试信息。1.2 和SPI、I2C、CAN放一起怎么选把F28034上的几个常用通信接口摆在一起对比你会更清楚SCI的位置接口信号线通信方式典型速率适用场景SCI/UARTTX、RXRS485时加DE异步、点对点或多机9600~115200bps居多屏显、调试、参数下发、BootloaderSPISCLK、MOSI、MISO、CS同步、主从片选可达Mbps级Flash读写、高速ADC、外部DACI2CSCL、SDA需上拉同步、多主多从100k~400kHz传感器、EEPROM、周边慢速芯片CANCANH、CANL异步、多主、带仲裁125k~1Mbps工业总线、设备互联、强电磁环境从这个表能看出SPI和I2C更适合板内短距离通信SCLK和CS都是专门引线做芯片之间的互连很高效但跨板子、跨机柜就力不从心了。CAN适合多节点组网抗干扰能力强可一旦你只需要一对一通信或者对方就是一个串口屏CAN反而显得杀鸡用牛刀成本和协议复杂度都上去了。SCI恰好卡在中间速度够用硬件简单协议自由度极高。哪怕你要做多机通信F28034的SCI也内置了地址位唤醒和空闲线唤醒模式可以支持一条总线上挂多个从机。所以我的选型经验是板级高速大吞吐选SPI多节点总线选CAN点对点、人机交互、远程维护选SCI基本不会错。2. SCI数据帧与波特率把异步通信的底账算明白2.1 一帧数据到底长什么样SCI的数据帧是NRZ编码也就是用高低电平代表0和1中间不会强制跳变。空闲状态下TX线保持高电平要发数据了先拉低一个位时间作为起始位接收方看到这个下降沿就知道“要开始收数据了”。接下来按顺序传输数据位默认是LSB在前也就是最低有效位先发。数据位长度可配置为1到8位我们最常用的8位数据不带校验。如果使能了校验位会在数据位之后发送一位奇偶校验。最后是1到2个停止位停止位必须保持高电平用来给接收方一个“这一帧结束”的边界。从接收方视角看它做的事情很简单检测到起始位后按波特率每个位周期在位的中心点附近采样一次。这里关键来了——双方必须对“一位的时间长度”有一致的约定这个约定就是波特率。如果一个按9600发另一个按9600收问题不大但如果时钟偏差大接收方采样点会一点点偏离位的中心最后滑到位的边沿采到错误电平整帧就乱了。2.2 波特率计算公式与误差这笔账F28034的SCI波特率不是随便就能配出来的它由低速外设时钟LSPCLK分频得到。公式是BRR LSPCLK / (目标波特率 × 8) - 1当BRR算出来是0时模块会用16倍过采样模式。BRR是16位值拆成高8位写入SCIHBAUD寄存器低8位写入SCILBAUD寄存器。实际波特率是实际波特率 LSPCLK / (8 × (BRR 1))问题在于BRR只能取整数所以实际波特率和目标波特率之间一定有误差。举个例子假设系统时钟90MHzLSPCLK默认分频到15MHz我们常用波特率算出来是这样目标波特率BRR理论值BRR取整实际波特率误差9600194.311949615.380.16%1920096.669719132.65-0.35%3840047.834838265.31-0.35%11520015.2815117187.501.72%9600几乎没有误差但115200误差到了1.72%。这个数值单独看不算大很多资料会说串口通信误差在3%以内都可以接受但要注意这是理想情况。实际链路上还有GPIO采样抖动、外部晶振本身的精度、RS485收发器的上升下降时间这些误差会叠加。帧里的数据位越多、连续传输的长帧越多点位累积越明显。我见过不少设备在9600下运行几年都没问题一改成115200就偶尔吐乱码根因就是这里。所以配置波特率时一定要算一遍不要直接抄例程。如果你的目标波特率算出来误差超过3%优先考虑换一个更友好的波特率或者调整PLL让LSPCLK重新分配。比如目标波特率是250000时15MHz LSPCLK算出来的误差非常大我就会把LSPCLK改成20MHz或者22.5MHz再算直到误差压到1%以内。3. 从寄存器视角看F28034的SCI外设结构3.1 配置链路协议格式、开关、速度、信箱接触C2000的SCI时第一眼看到十几个寄存器可能有点晕但捋清楚之后就发现它的配置链路非常清晰。打个比方你用串口和人说话SCICCR相当于约定“你说中文还是英文”SCICTL1相当于“张嘴说话还是闭嘴听”SCIHBAUD/SCILBAUD相当于“说话的速度”SCIRXBUF/SCITXBUF是“信箱”SCIRXST是“告诉你对方说了什么、有没有听错”。具体下来SCICCR配置数据位长度、奇偶校验使能和类型、停止位个数、多机通信模式、环回模式。SCICTL1控制模块的收发使能、软件复位SWRESET、错误中断使能、休眠模式。SCICTL2FIFO增强控制、发送就绪标志、发送缓冲空标志。SCIHBAUD/SCILBAUD波特率分频值。SCIRXST接收状态寄存器包含RXRDY、BRKDT检测到间断帧、FE帧错误、OE溢出错误、PE奇偶错误等状态位。SCITXBUF/SCIRXBUF发送和接收数据缓冲区。实际开发中最容易忽略的是SWRESET软件复位位。SCI模块上电后要先把SWRESET置0完成所有寄存器配置后再把它置1模块才会正常工作。如果配置顺序错了比如在复位状态下写完波特率然后就直接发送数据数据可能会丢。这个细节在数据手册里写得很清楚但现场出问题的时候很少有人第一时间想到。3.2 FIFO不是摆设16级缓冲能省很多事F28034的SCI带FIFO增强功能发送和接收各有一个16级深度的FIFO。没有FIFO的时候收一个字节触发一次中断CPU每一字节都要进一次ISR有了FIFO你可以设置一个触发级别比如收到4个字节才触发一次中断CPU一次把4个字节都读走。发送方向也一样。你可以把多个字节一次性写入发送FIFO然后等TXFFST标志显示FIFO已经空了再继续写下一批。在实际的协议发送场景里我一般把TXFFIL设为1也就是FIFO里空出1个位置就触发发送中断CPU可以持续往FIFO里灌数据不用一字节一停顿。FIFO相关的寄存器是SCIFFTX、SCIFFRX、SCIFFCT。需要特别注意SCIFFTX里的SCIRST位和TXFIFORESET位SCIRST复位整个SCI模块TXFIFORESET只复位发送FIFO。配置时先复位再释放复位等到状态寄存器里的复位标志清除后再操作FIFO。很多莫名其妙的问题排查到最后都是FIFO复位时序不对。3.3 看懂SCIRXST的错误位排查乱码才有方向SCIRXST接收状态寄存器是整个SCI调试过程中最该盯着的寄存器。它里面有RXRDY接收就绪、BRKDT检测到间断、FE帧错误、OE溢出错误、PE校验错误、RXERROR错误汇总这些位。我排查通信问题时有个固定习惯先在调试器里把SCIRXST的值挂在Watch窗口然后让设备运行一段时间。如果FE频繁变1说明波特率对不上或者电平翻转不完整如果OE频繁变1说明CPU读数据的速度跟不上接收速度FIFO溢出如果PE变1而波特率设置看起来没问题那就要怀疑线路干扰或者对端设备发出的数据本身带了错误信号。另外注意RXERROR像是这几个错误位的“或”标志中断里可以通过判断它快速决定要不要做错误处理。如果使能了RXERRINTENA任何一个错误出现都会进错误中断这个机制很适合用来做通信异常监控比如连续错误超过一定次数就重启通信或者上报故障。4. 走一遍实际配置和收发代码4.1 硬件连接与电平转换F28034的SCI引脚输出的是TTL电平也就是0~3.3V。如果直接和电脑串口连接必须经过电平转换。RS232用MAX232这类芯片RS485用MAX3485或ISO3082这类芯片还可以选带隔离的模块。RS485场景里有一个额外问题485总线是半双工的需要一根GPIO控制收发器芯片的DE/RE引脚发送时把方向切到发送发送完再切回接收。方向切换的时机非常关键切早了末尾字节发不完切晚了会丢掉对端马上返回的数据。我在代码里通常用发送FIFO的空标志作为切换依据确认TX FIFO全部排空后再置低DE这样末尾字节能完整上总线。4.2 GPIO复用、SCI初始化和一个可以直接抄的例子C2000的引脚是复用型的GPIO28/29、GPIO7/8等引脚都可能挂SCI功能具体要看你的板子原理图和F28034数据手册的GPIO MUX表。举例来说很多TI评估板上SCI-A的RX配在GPIO28、TX配在GPIO29但换一块板子就可能不一样所以第一步永远是查手册。GPIO配好之后就是SCI模块初始化下面这段是我常用的配置模板8数据位、无校验、1停止位、9600波特率void SCI_Init(void) { // 1. GPIO复用为SCI功能 // 下面以GPIO28SCIRXDA、GPIO29SCITXDA为例按实际原理图调整 GpioCtrlRegs.GPAMUX1.bit.GPIO28 1; // GPIO28 - SCIRXDA GpioCtrlRegs.GPAMUX1.bit.GPIO29 1; // GPIO29 - SCITXDA // 2. 接收引脚一定要避免输入限定滤波影响 GpioCtrlRegs.GPAQSEL1.bit.GPIO28 0; // 异步输入直接采样 // 3. 复位SCI模块 SciaRegs.SCICTL1.bit.SWRESET 0; // 4. 配置数据格式8位数据无校验1位停止位 SciaRegs.SCICCR.all 0x0007; // 5. 使能接收和发送 SciaRegs.SCICTL1.all 0x0003; // 6. 设置波特率假设LSPCLK15MHz目标9600 SciaRegs.SCIHBAUD 0x00; SciaRegs.SCILBAUD 0xC2; // BRR 194 // 7. 使能FIFO增强收发FIFO中断触发级别设为4 SciaRegs.SCIFFTX.bit.SCIRST 1; SciaRegs.SCIFFTX.bit.SCIFFENA 1; SciaRegs.SCIFFTX.bit.TXFIFORESET 1; SciaRegs.SCIFFTX.bit.TXFFIENA 1; SciaRegs.SCIFFTX.bit.TXFFIL 4; SciaRegs.SCIFFRX.bit.RXFFIENA 1; SciaRegs.SCIFFRX.bit.RXFFIL 4; // 8. 释放软件复位 SciaRegs.SCICTL1.bit.SWRESET 1; }这里有个很容易踩的细节SCICTL1的0x0003同时使能了接收和发送但SWRESET位还没置1。要等所有寄存器都配置完最后再把SWRESET置1。我见过有人把SWRESET也写进一次赋值里结果数据格式还没生效模块就跑了收发都不正常。4.3 轮询和中断两种收发方式最简单的收发就是轮询。发送前等TXRDY变1说明发送缓冲已经空了然后写入SCITXBUFvoid SCI_SendByte(Uint16 data) { while (SciaRegs.SCICTL2.bit.TXRDY 0) {} SciaRegs.SCITXBUF data; }接收同理等RXRDY变1然后读SCIRXBUFUint16 SCI_ReceiveByte(void) { while (SciaRegs.SCIRXST.bit.RXRDY 0) {} return SciaRegs.SCIRXBUF.all; }轮询的缺点很明显接收数据时CPU一直被占住别的实时任务没法执行。所以在F28034上跑实际项目我更推荐用中断。开启FIFO后设置RXFFIL为某个阈值当接收FIFO里的字节数达到阈值时触发一次中断CPU在中断里把一批数据搬走。中断ISR最后要记得清标志并写PIEACK否则同级中断不会再次触发。PIE这块要特别提醒F28034的SCI收发中断挂在PIE模块的某个组里具体是第几组、第几个子通道务必要查F2803x的中断向量表不要凭印象猜。不同系列芯片的PIE表格有差异同一个F28034在不同头文件版本里的宏定义也可能不大一样。写代码时直接使用头文件里的中断向量宏比手写通道号安全得多。5. 实测排坑乱码、误码和中断疑难5.1 波特率误差造成的“偶发错帧”之前提到过115200在15MHz LSPCLK下有1.72%的波特率误差这个误差平时不显眼但在两种情况下很容易暴露一是对端设备的晶振精度一般两边误差反向叠加二是通信数据帧很长比如一包超过100字节每个bit的采样位置都在累积偏移到帧尾部就接近位的边沿。我调过一台设备上位机发的每帧数据包80字节偶尔最后几个字节会变成0xFF或者0x00。用逻辑分析仪抓波形发现每个字节本身还能对齐但帧末尾已经明显能看出位宽差异。最后就是把波特率从115200改成了57600误差从1.72%降到1.36%问题消失。如果必须用115200就重新调整PLL配置让LSPCLK变成22.1184MHz这类更合适的频率。实战建议协议里在尾部加一个0xA5之类的固定结束字节接收端以结束字节判断帧边界和同步状态。一旦连续多次没收到的主动重新同步。通信偶尔会错不可怕可怕的是错了还不知道把错数据当真数据处理。5.2 GPIO输入限定把起始位滤没了这是C2000平台独有的坑很多人从STM32转过来时根本不了解。F28034的GPIO输入默认带输入限定功能也就是为了滤除毛刺会连续采样N次都一样才认为电平真正变化。这本是抗干扰的好设计但SCI的RX引脚如果也配了这个功能问题就来了——SCI起始位是一个下降沿如果输入限定要求连续采样6次都确认是低电平这个判决时间会占用一部分位周期波特率越高影响越大接收端会错过起始位或者采样点严重偏离。解决方案是配置GPIO的AQSEL寄存器把SCI接收引脚设为异步输入也就是不做限定电平变化立即被外设感知。具体代码GpioCtrlRegs.GPAQSEL1.bit.GPIO28 0; // 异步输入如果出于抗干扰考虑非要做输入限定那就把采样窗口压到最小。但以我的经验SCI这种低速异步通信只要线路设计正常不需要也不应该做输入限定。这个配置不查明白波特率高的时候会在诡异位置丢字节而且时好时坏非常难查。5.3 RX悬空和RS485方向切换导致的丢字节硬件上有一个很常见的隐患开发阶段板子没有外接串口设备SCI的RX引脚悬空。悬空的引脚会像天线一样拾取环境噪声这些噪声电平一旦满足起始位条件就会被识别成数据触发接收中断、错误中断甚至唤醒事件。不定时进一次错误中断谁看到都一头雾水。解决办法有三个方向PCB上给RX引脚加一个10kΩ上拉电阻让空闲状态稳定在高电平代码里使能RXERRINTENA对错误中断做计数处理超过阈值才报警避免频繁打断主流程调试时实在没设备就通过软件把SCITXBUF和SCIRXBUF接成环回模式自测收发通路。RS485方向切换的问题我在前面提到过这里再展开一点。如果用GPIO控制DE发送函数返回后不能立刻把DE拉低因为SCITXBUF写入只是把数据放进了FIFO数据还在移位寄存器里没发完。正确做法是查TXSHF状态或者发送完标志确认最后一个字节的电平真正从TX引脚出去之后再切换方向。如果程序里用了一个延时来等待这个延时必须有明确的上限别在高波特率下白白拖慢通信周期。5.4 中断“无缘无故”不触发或者疯狂进中断中断进不去或者进个不停多数不是SCI本身的问题而是PIE嵌套和标志位的问题。我在调试中最常碰到三类情况。第一类外设中断已经使能PIE里也打开了对应位但IER寄存器没有使能对应组或者总中断EINT没开。C2000的中断使能是三级结构——PIE级、IER级、全局级少了任何一级都白搭。第二类中断ISR里处理完数据后没有清接收标志。比如RXFFINT标志没有写1清除甚至有的人把SCIRXBUF里的值读走了但没有清PIEACK导致同一优先级的中断被堵住系统表现为“进一次中断之后再也不进来了”。第三类接收FIFO的中断触发级别低于实际收到的字节数时每来一个字节都可能触发一次。如果你只想在收到完整一帧后再处理把RXFFIL设成跟帧长度一致或者在ISR里根据RXFFST判断没凑够就返回标志照清。6. 把SCI用得更顺手多机通信、唤醒和轻量协议6.1 一条总线上挂多个从机F28034的SCI支持两种多机通信模式空闲线多机模式和地址位多机模式。简单说就是让总线上多个从机都监听数据但只有被点名的那一个才处理后续数据。空闲线模式靠“总线空闲时间”来区分帧和帧。主机先发一个地址字节从机在总线空闲后收到数据判断是发给自己的就继续收后续数据不是发给自己的就进入休眠继续监听但不进接收中断。地址位模式则是在每个字节后多发一个地址标志位SCICCR里的ADDRIDLE配置硬件根据这个标志位识别地址字节和数据字节判断更加精确。实际项目里我更喜欢空闲线模式因为它不需要牺牲一个数据位普通8位数据就能传。但要注意总线空闲时间要设定得足够长避免正常数据的字节间隔超过空闲阈值导致帧被拆成两段。6.2 低功耗场景下的SCI唤醒如果产品是电池供电的比如无线传感器节点、便携式监测仪主控大部分时间都在休眠但需要能随时被上位机唤醒。F28034的SCI可以配置成休眠和唤醒模式接收信号满足条件时产生唤醒事件把CPU从低功耗状态拉起来。这个功能的关键在于唤醒源最好放在“收到地址字节”这个级别而不是只靠任意一个下降沿否则总线噪声就会反复把系统吵醒。配合前面说的RX引入上拉电阻能让误唤醒概率低很多。唤醒之后先清标志再进行初始化确认别上来就处理数据因为唤醒瞬间前几个字节可能已经丢了一部分。6.3 一个够用的通信协议设计思路SCI只是搬运字节的通道数据格式完全由你定义。我见过的很多通信问题本质上不是SCI不好用而是协议没设计好。一个最低成本的轻量协议至少要有这么几部分帧头固定字节比如0xAA 0x55接收端用它来找到帧起点。长度字段一字节表示后续数据长度有了它接收端才知道这帧到哪里结束。命令字标识这条报文是读参数、写参数还是状态上报。数据区实际载荷。校验我习惯用CRC16差一些的场景用累加和也能接受但别不校验。工控环境里偶尔一个电磁干扰就能让几个bit翻转没有校验的分帧等于裸奔。帧尾固定字节作为第二重帧边界确认。接收端用状态机处理先找帧头再收长度字段接着按长度收数据收完校验校验通过才把整包数据交给应用层。数据不完整时用CPU定时器做超时判断比如50ms内没收到完整帧就清空缓存重新同步。这种协议思路一点也不复杂但能挡住大部分偶发干扰。很多同事上来就写裸数据流数据错位之后连关键的解码逻辑都跟着跑飞最后只能靠反复复位设备解决。花半天时间定一个带帧头、长度、CRC的协议后面能省一个月的现场排查时间。这些年用F28034做通信项目我最深的体会是SCI这个外设说简单真的简单说坑多也真的坑多。简单在于它协议古老、资料丰富、人人都懂坑多在于它太依赖时钟链路、GPIO配置和寄存器时序这些底层细节任何一个环节脱节表现都是悬浮的偶发乱码。如果你现在正被某个串口问题折磨建议按这个顺序排查先算波特率误差再看GPIO的AQSEL输入限定然后查PIE中断链路最后拿逻辑分析仪看波形。绝大多数问题都逃不出这四个环节。
返回列表