
1. 初识DSP28069 SCI模块串口通信的硬件基石第一次接触DSP28069的SCI模块时我盯着开发板上那两个不起眼的GPIO引脚发愣——这就是实现串口通信的全部硬件后来才明白正是这两个看似简单的引脚SCITXD和SCIRXD承载了嵌入式系统与外界对话的重要使命。SCISerial Communication Interface本质上是一种异步串行通信接口和我们常说的UART属于同类技术。DSP28069芯片内置了两个独立的SCI模块SCI-A和SCI-B每个模块都支持全双工通信这意味着数据可以同时收发而互不干扰。实际项目中我特别喜欢用示波器观察这两个引脚上的波形。当发送Hello World时SCITXD引脚上会出现整齐的方波脉冲每个字节都被拆分成起始位、数据位和停止位。这种直观的物理层信号让我对串口通信的理解从抽象概念变成了具体现象。需要注意的是虽然SCI和UART在功能上相似但TI的SCI模块在寄存器配置上有些独特之处比如双缓冲设计能有效防止数据丢失而16位波特率寄存器则提供了更灵活的速率选择。2. 硬件连接与引脚配置从电路板到寄存器记得第一次调试SCI通信时我犯了个低级错误——把SCITXD和SCIRXD直接短接结果自然无法通信。后来才明白这两个引脚需要交叉连接DSP的发送端SCITXD要接对方设备的接收端RX而接收端SCIRXD接对方设备的发送端TX。除了正确连线引脚的功能复用配置更是关键。DSP28069的GPIO28和GPIO29默认是普通IO口需要通过以下代码激活SCI功能EALLOW; // 启用GPIO28(SCIRXDA)和GPIO29(SCITXDA)的上拉电阻 GpioCtrlRegs.GPAPUD.bit.GPIO28 0; GpioCtrlRegs.GPAPUD.bit.GPIO29 0; // 设置GPIO28为异步输入消除信号毛刺 GpioCtrlRegs.GPAQSEL2.bit.GPIO28 3; // 将GPIO28/29配置为SCI功能引脚 GpioCtrlRegs.GPAMUX2.bit.GPIO28 1; GpioCtrlRegs.GPAMUX2.bit.GPIO29 1; EDIS;这段代码有几个易错点首先必须用EALLOW/EDIS宏包裹对保护寄存器的操作其次GPIO28需要特别设置输入滤波为异步模式否则默认的同步采样可能导致通信异常。我在早期项目中就曾因为漏掉GPAQSEL2配置导致接收数据出现随机错误。如果使用TI提供的库函数可以直接调用InitSciaGpio()完成这些配置但了解底层实现对排查问题大有裨益。3. 波特率计算的艺术精度与误差的平衡波特率配置是串口通信的第一个拦路虎。某次项目中使用9600波特率与传感器通信时发现每20个字节就会丢一个字节最终发现是波特率误差累积导致的。DSP28069的波特率计算公式为波特率 LSPCLK / (BRD 1) / 8其中LSPCLK是系统时钟SYSCLK的4分频BRD是16位波特率分频值。假设SYSCLK为90MHz要配置9600波特率时LSPCLK 90MHz / 4 22.5MHz BRD 22.5MHz / (9600 * 8) - 1 ≈ 292.96取整后BRD2930x0125此时实际波特率为9589bps误差约0.11%在允许范围内。但若SYSCLK为80MHz同样配置会导致误差超过3%就可能出现通信问题。这时就需要调整系统时钟或接受较低的通信速率。以下是常用波特率的配置示例波特率SYSCLK90MHzSYSCLK60MHz96000x01250x00C21152000x00180x00104608000x0006不支持实际编程中波特率配置通过SCIBAUD寄存器实现SciaRegs.SCIHBAUD 0x01; // BRD高字节 SciaRegs.SCILBAUD 0x25; // BRD低字节4. SCI模块初始化从寄存器到数据流完成硬件配置后SCI模块本身的初始化才是重头戏。有次我调试三天都没能发出数据最后发现是漏写了SCICTL1的复位释放操作。完整的初始化包括通信参数设置和FIFO配置两部分// 通信参数配置 SciaRegs.SCICCR.all 0x0007; // 8位数据位无校验1停止位 SciaRegs.SCICTL1.all 0x0003; // 使能发送和接收 SciaRegs.SCICTL2.bit.TXINTENA 1; // 使能发送中断 SciaRegs.SCICTL2.bit.RXBKINTENA 1; // 使能接收中断 SciaRegs.SCIHBAUD 0x01; // 波特率设置 SciaRegs.SCILBAUD 0x25; SciaRegs.SCICTL1.all 0x0023; // 释放SCI复位 // FIFO配置 SciaRegs.SCIFFTX.all 0xE040; // 使能TX FIFO SciaRegs.SCIFFRX.all 0x2044; // 使能RX FIFO SciaRegs.SCIFFCT.all 0x00; // 禁止自动波特率检测这段代码有几个关键点SCICCR配置帧格式时0x0007表示8位数据模式SCICTL1需要两次配置第一次启用基本功能第二次释放复位FIFO配置中SCIFFTX的0xE040表示使能FIFO、清除发送标志位并设置触发级别。我曾遇到FIFO使能后无法接收数据的问题最后发现是SCIFFRX配置错误正确的触发级别设置对中断处理至关重要。5. 数据收发实战从字节到字符串真正让串口活起来的是数据的收发函数实现。发送单个字节的函数看似简单却暗藏玄机void scia_xmit(int a) { while (SciaRegs.SCIFFTX.bit.TXFFST ! 0) {} // 等待发送缓冲区空 SciaRegs.SCITXBUF a; // 写入发送缓冲区 }这个函数中最容易忽略的是发送缓冲区的状态检查。如果不检查TXFFST就直接写入SCITXBUF可能导致数据覆盖。基于这个基础函数我们可以实现字符串发送void scia_msg(char *msg) { while(*msg ! \0) { scia_xmit(*msg); // 逐字节发送 } }在接收端最简单的轮询方式如下Uint16 ReceivedChar; while(SciaRegs.SCIFFRX.bit.RXFFST ! 1) {} // 等待接收数据 ReceivedChar SciaRegs.SCIRXBUF.all; // 读取数据但在实际项目中我强烈建议使用中断方式处理接收数据否则CPU会长期阻塞在等待状态。一个常见的应用场景是实现回声功能——将接收到的字符原样发回ReceivedChar SciaRegs.SCIRXBUF.all; scia_xmit(ReceivedChar); // 回显接收到的字符6. 完整示例Hello World与交互式回显将所有模块组合起来就能实现经典的串口Hello World示例。这个完整案例包含系统初始化、SCI配置和主循环#include DSP28x_Project.h void main(void) { InitSysCtrl(); // 系统时钟初始化 InitSciaGpio(); // SCI引脚配置 DINT; // 禁用全局中断 scia_fifo_init(); // FIFO初始化 scia_echoback_init();// SCI通信参数配置 scia_msg(\r\nHello World!\r\n); // 发送欢迎信息 while(1) { Uint16 ch; while(SciaRegs.SCIFFRX.bit.RXFFST !1) {} // 等待输入 ch SciaRegs.SCIRXBUF.all; // 获取字符 scia_msg(Echo: ); scia_xmit(ch); // 回显字符 scia_msg(\r\n); } }调试这个程序时建议先用串口调试助手发送单个字符测试回显功能。我曾遇到回显字符乱码的情况最终发现是波特率误差过大导致的。另一个常见问题是发送字符串时丢失结尾字符这通常是因为没有给字符串留出足够的发送时间。7. 故障排查那些年我踩过的坑在SCI调试过程中有些问题会反复出现。最典型的就是能发不能收或者能收不能发这时候就需要系统性地排查硬件检查确认SCITXD和SCIRXD交叉连接检查地线是否共接测量信号线电压正常应为3.3V电平配置验证确认GPIO复用功能已正确配置检查波特率计算是否正确验证SCICCR中的帧格式设置信号测量用示波器观察发送引脚是否有信号检查信号频率是否符合波特率预期查看信号质量是否有毛刺有个特别隐蔽的bug我花了整整一周才解决当系统中有其他高优先级中断时SCI接收可能会丢失数据。这是因为默认的FIFO触发级别是1个字节如果在中断服务程序执行期间又收到数据就会溢出。解决方案是提高FIFO触发级别或优化中断优先级SciaRegs.SCIFFRX.bit.RXFFIL 4; // 设置FIFO触发级别为4字节另一个实用技巧是在初始化完成后发送一组测试字符如0x55的方波用示波器测量实际波特率这比软件调试更直接有效。