行业资讯
CC13x2/CC26x2 UART驱动开发:从波特率计算到DMA配置的实战指南
1. 项目概述在嵌入式开发的世界里UART通用异步收发器就像一位沉默寡言但极其可靠的老朋友。无论你是调试代码、打印日志还是连接GPS模块、蓝牙透传芯片UART总是那个最基础、最直接的通信桥梁。它不需要复杂的握手协议仅凭两根线TX和RX就能建立起设备间的对话这种简洁和高效是其他总线难以比拟的。这次我们聚焦于德州仪器TI的CC13x2和CC26x2系列无线微控制器MCU。这两款芯片在物联网和低功耗无线领域名声显赫而它们内置的UART模块远不止是一个简单的串口。它支持高达3 Mbps的波特率拥有独立的收发FIFO集成了硬件流控制和DMA接口甚至可以通过分数波特率发生器实现精准的时钟分频。对于开发者而言这意味着你可以用更少的CPU干预实现更稳定、更高效的数据吞吐尤其是在电池供电的无线传感节点中这种效率直接关系到设备的续航能力。然而官方技术手册往往侧重于寄存器位的描述对于如何将这些功能模块组合成一个稳定、高效的驱动以及在实际项目中可能遇到的“坑”却着墨不多。我经历过不少因为波特率计算偏差导致的通信乱码也调试过因FIFO触发级别设置不当而丢失的数据包。这篇文章我将结合手册内容和多年的实战经验带你从原理到寄存器再到代码实践彻底吃透CC13x2/CC26x2的UART模块。无论你是刚接触嵌入式的新手还是想优化现有通信协议的老手相信都能从中找到有价值的细节。2. UART核心原理与CC13x2/CC26x2模块架构2.1 异步串行通信的本质要驾驭一个外设首先要理解它工作的基本逻辑。UART通信是异步的这意味着通信双方没有共享的时钟线来同步每一位数据。那么接收方如何知道一位数据的开始和结束呢答案就在于起始位和停止位以及一个预先约定好的速度——波特率。一个标准的UART数据帧以一位低电平的起始位开始这就像跑步比赛时的发令枪告诉接收方“数据要来了准备好采样”紧接着是5到8位的数据位通常为8位从最低有效位LSB开始发送。数据位之后是可选的奇偶校验位用于简单的错误检测。最后以一位或多位高电平的停止位结束标志着本次传输的终止并确保线路恢复到空闲高电平状态。这里的关键在于发送和接收双方必须使用完全相同的波特率。波特率定义为每秒传输的符号数对于UART一个符号就是一位。如果双方波特率有微小偏差在传输大量数据后采样点就会逐渐漂移最终导致帧错误。CC13x2/CC26x2的UART模块通过一个22位的分数波特率发生器16位整数 6位小数来精确匹配所需的波特率这是其高可靠性的基础。2.2 CC13x2/CC26x2 UART模块功能全景CC13x2/CC26x2的UART模块可以看作一个高度集成的通信引擎其功能远不止简单的串并转换。我们来看看它的核心组件双缓冲FIFO独立的32x8位发送FIFO和32x12位接收FIFO。接收FIFO多出的4位用于存储每个字节对应的状态信息帧错误、奇偶校验错误、溢出错误、线路中断。FIFO的引入极大地减轻了CPU负担。你可以配置FIFO的触发水位如1/2满只有当数据量达到这个阈值时才产生中断让CPU能批量处理数据而不是每收到一个字节就被打断一次。灵活的帧格式控制通过UART:LCRH寄存器你可以自由定义数据位长度5-8位、停止位数量1或2位、奇偶校验类型奇校验、偶校验、固定1、固定0或无校验。这种灵活性使其能够适配从老式设备到现代传感器的各种通信协议。硬件流控制RTS/CTS这是实现可靠高速通信的关键。当接收方FIFO快满时可以通过拉低RTS请求发送信号通知发送方暂停发送反之发送方在检测到CTS清除发送信号有效低电平后才开始发送。这避免了因接收端处理不及时而导致的数据溢出Overrun。模块的UART:CTL寄存器可以独立启用CTS和RTS流控制。强大的中断系统模块可以针对多种事件产生中断发送FIFO空、接收FIFO达到触发水平、接收超时、帧错误、奇偶校验错误、溢出错误、线路中断以及调制解调器状态变化。所有中断源在逻辑上“或”起来产生一个总的中断请求给CPU你需要在中断服务程序ISR中读取UART:MIS屏蔽后中断状态寄存器来判别具体是哪个事件触发了中断。μDMA微直接内存访问集成这是提升系统效率的利器。UART模块为发送和接收分别提供了独立的DMA请求信号单次请求和突发请求。启用DMA后数据可以在UART FIFO和系统内存之间直接搬运完全无需CPU参与。例如在接收大量数据时你可以配置DMA在接收FIFO达到半满时自动将32字节的数据块搬移到指定的内存缓冲区仅在DMA传输完成时产生一个中断通知CPU处理。这显著降低了CPU负载和中断延迟。理解这个整体架构后我们再深入各个功能模块的配置细节就会清晰很多。这个模块的设计思想很明确把能硬件化的任务都交给硬件让CPU专注于应用逻辑。3. 关键功能深度解析与配置要点3.1 波特率计算精度与误差控制波特率配置是UART通信的第一步也是最容易出错的一步。CC13x2/CC26x2的波特率发生器公式如下BRD UART_Ref_Clk / (16 * Desired_Baud_Rate)其中BRD是一个22位的数值高16位是整数部分IBRD.DIVINT低6位是小数部分FBRD.DIVFRAC。UART_Ref_Clk通常是外设时钟PERDMACLK例如24 MHz或48 MHz。计算示例假设PERDMACLK 24 MHz目标波特率为115200。计算理论BRD24,000,000 / (16 * 115,200) ≈ 13.02083333整数部分IBRD 13(0xD)小数部分计算FBRD integer(0.02083333 * 64 0.5) integer(1.83333) 1这里有一个非常重要的细节对UART:FBRD寄存器的写入必须遵循特定的顺序。手册指出UART:IBRD、UART:FBRD和UART:LCRH寄存器共同构成一个内部的30位锁存器。这个锁存器只有在写入UART:LCRH时才会更新。因此正确的配置顺序必须是先写UART:IBRD再写UART:FBRD最后写UART:LCRH即使LCRH的值没有改变也必须执行一次写操作来锁存新的波特率除数如果顺序错误新的波特率将不会生效。这是一个常见的陷阱。实操心得在实际项目中我习惯使用一个计算函数来生成配置值并封装一个波特率设置函数确保顺序正确。同时对于非常见波特率如非整数的自定义速率要计算实际产生的波特率与目标值的误差。误差计算公式为误差 (实际波特率 - 目标波特率) / 目标波特率。通常要求误差小于2%很多串口芯片的容限在3%以内但为了稳定性最好控制在1%以下。对于24MHz时钟和115200波特率实际波特率为24e6 / (16 * (13 1/64)) ≈ 115207误差仅为0.006%完全可以忽略。3.2 FIFO与中断协同工作策略FIFO和中断的配置直接决定了UART驱动的效率和实时性。UART:IFLS中断FIFO级别选择寄存器是这里的控制核心。接收中断触发点(RXSEL)决定接收FIFO中积累了多少数据后才触发接收中断。选项有1/8, 1/4, 1/2, 3/4, 7/8满。默认是1/2满。发送中断触发点(TXSEL)决定发送FIFO空到什么程度时触发发送中断请求更多数据。选项有7/8, 3/4, 1/2, 1/4, 1/8空。默认是1/2空。配置策略分析低延迟、小数据量场景如交互式命令可以将接收触发点设为1/8或1/4。这样一两个字节到达就能立刻触发中断处理响应迅速。但频繁的中断会增加CPU开销。高吞吐、大数据块场景如固件升级、文件传输应将接收触发点设为1/2或更高发送触发点设为1/2或更低。这样让FIFO积累更多数据再处理减少中断次数提高批量传输效率。配合DMA使用效果更佳。流控场景当启用硬件流控RTS/CTS时RTS信号指示本机接收状态会在接收FIFO达到“满”阈值非中断触发阈值时自动置位低电平有效表示“不要发”在FIFO空出一定空间后自动清除。这个阈值是硬件固定的与IFLS设置无关。你需要确保发送方能正确响应这个流控信号。中断服务程序ISR编写要点 进入UART ISR后第一件事是读取UART:MIS寄存器确定中断源。常见处理流程如下void UART0_IRQHandler(void) { uint32_t mis HWREG(UART0_BASE UART_O_MIS); // 读取屏蔽后中断状态 if (mis UART_MIS_RXMIS) { // 接收中断 // 循环读取UART_DR直到接收FIFO为空或低于触发水平 while (!(HWREG(UART0_BASE UART_O_FR) UART_FR_RXFE)) { uint32_t data HWREG(UART0_BASE UART_O_DR); uint8_t rxByte data 0xFF; // 提取数据 uint8_t errors (data 8) 0xF; // 提取错误位(FE, PE, BE, OE) // 处理接收到的字节和错误 processRxByte(rxByte, errors); } // 清除接收中断可选读DR也可清除 HWREG(UART0_BASE UART_O_ICR) UART_ICR_RXIC; } if (mis UART_MIS_TXMIS) { // 发送中断 // 检查发送FIFO是否还有空间TXFF位为0 while (!(HWREG(UART0_BASE UART_O_FR) UART_FR_TXFF)) { if (txBufferHasData()) { uint8_t byteToSend getNextTxByte(); HWREG(UART0_BASE UART_O_DR) byteToSend; } else { // 没有更多数据要发送可以禁用发送中断以避免空循环 HWREG(UART0_BASE UART_O_IMSC) ~UART_IMSC_TXIM; break; } } HWREG(UART0_BASE UART_O_ICR) UART_ICR_TXIC; } // 处理错误中断OEIM, BEIM, PEIM, FEIM if (mis (UART_MIS_OEMIS | UART_MIS_BEMIS | UART_MIS_PEMIS | UART_MIS_FEMIS)) { // 读取RSR或DR的高位获取具体错误并写入ECR清除错误标志 uint32_t rsr HWREG(UART0_BASE UART_O_RSR); handleUartErrors(rsr); HWREG(UART0_BASE UART_O_ECR) 0xFF; // 写任何值到ECR即可清除错误标志 } }注意事项务必在ISR中清除相应的中断标志通过写UART:ICR寄存器否则会立即再次触发中断导致系统锁死。对于接收中断通过读取UART:DR寄存器将数据从FIFO中取出当FIFO数据量低于触发水平时中断标志会自动清除但显式清除是个好习惯。3.3 硬件流控制RTS/CTS实战配置硬件流控制是保证高速、大数据量通信不丢包的“保险丝”。CC13x2/CC26x2的UART0支持完整的RTS/CTS流控。连接方式 你的设备DTE如MCU的UART0_RTS输出应连接到对方设备DCE如模组的CTS输入。你的UART0_CTS输入应连接到对方的RTS输出。注意RTS和CTS都是低电平有效。寄存器配置步骤引脚复用通过IOC模块IOCFGn寄存器将对应的GPIO引脚配置为UART0_RTS和UART0_CTS功能。务必在使能UART模块之前完成此配置否则初始的引脚状态抖动可能会被UART误认为是有效的流控信号。使能流控在UART:CTL寄存器中设置CTSEN位和RTSEN位为1同时使能RTS和CTS流控。理解自动行为CTS流控当你的UART0_CTS输入引脚为低电平时对方允许你发送你的发送器才能工作。如果CTS变高发送器会暂停直到CTS再次变低。这一切由硬件自动完成无需软件干预。RTS流控当你的接收FIFO达到“满”阈值硬件固定时UART0_RTS输出引脚会自动变为低电平通知对方“我快满了请暂停发送”。当FIFO中的数据被读取空出足够空间后RTS会自动恢复高电平“可以继续发送”。这个阈值与FIFO深度有关通常是接近满的位置。调试技巧 在调试流控时最容易出现的问题是“通信卡死”。一个常见的错误是只连接了TX/RX而RTS/CTS引脚悬空或未正确配置。悬空的输入引脚状态不确定可能导致CTS始终为高禁止发送或RTS始终为低一直请求对方暂停。我的建议是在初始化阶段先用万用表或逻辑分析仪确认这四个引脚的电平状态。如果暂时不需要流控确保在软件中禁用CTSEN和RTSEN并将对应的GPIO配置为通用输入带上拉以避免干扰。3.4 DMA配置与高效数据传输对于需要连续、高速传输数据的应用如通过串口传输音频采样数据、图像数据块使用DMA可以解放CPU。CC13x2/CC26x2的UART与μDMA控制器紧密集成。DMA请求类型单次请求(SREQ)当FIFO中有数据接收或有空间发送时立即触发。适合零散、非连续的数据传输。突发请求(BREQ)当FIFO中的数据量达到你在UART:IFLS中设置的触发水平时触发。适合批量数据传输能最大化总线利用率。配置流程使能UART的DMA功能设置UART:DMACTL寄存器中的RXDMAE接收DMA使能和/或TXDMAE发送DMA使能位。配置μDMA控制器这通常通过TI的DriverLib库函数或直接配置DMA控制表来完成。你需要为UART的发送和接收分别设置一个DMA通道。源地址/目标地址对于发送DMA源地址是内存中的数据缓冲区目标地址是UART:DR寄存器。对于接收DMA源地址是UART:DR寄存器目标地址是内存中的接收缓冲区。传输大小设置一次DMA传输需要搬运的数据量字节数。仲裁大小定义每次DMA突发传输的数据量。为了匹配UART FIFO的触发水平通常将其设置为与FIFO触发水平对应的字节数例如接收触发水平为1/2则仲裁大小可设为16字节。请求类型选择单次请求或突发请求。连接DMA完成中断当DMA完成一次设定的传输总量后会产生完成中断。你可以在中断中重新填充发送缓冲区或处理接收到的数据块然后重新启动DMA传输。一个典型的DMA接收示例 假设你需要连续接收不定长数据包。一个高效的做法是设置接收FIFO触发水平为1/2满16字节使能接收DMA突发请求。配置DMA通道为“Ping-Pong”模式或使用两个交替的缓冲区。当FIFO中数据达到16字节时触发DMA突发传输将16字节数据搬移到缓冲区A。同时CPU可以处理之前已满的缓冲区B的数据。这样实现了数据搬运和处理的并行几乎没有数据丢失的风险。避坑指南启用DMA时务必注意DMAONERR位位于UART:DMACTL寄存器。如果此位置1当发生任何UART错误奇偶、帧错误等时DMA请求会被自动禁用。这可以防止错误数据被DMA盲目地搬移到内存。在错误处理ISR中你需要检查错误状态清除错误标志并可能需要重新使能DMA。4. 从零开始UART模块初始化与驱动实现4.1 引脚复用与时钟使能在操作任何外设之前时钟和引脚是基础。CC13x2/CC26x2的UART信号需要通过IOC模块映射到具体的GPIO引脚上。步骤详解使能外设时钟UART模块位于外设电源域需要先使能其运行时钟。使用TI的DriverLib库函数最为便捷#include ti/drivers/pin/PINCC26XX.h #include ti/drivers/power/PowerCC26XX.h #include driverlib/prcm.h #include driverlib/ioc.h // 1. 使能UART0的外设时钟运行、睡眠、深度睡眠模式 PRCMPeripheralRunEnable(PRCM_PERIPH_UART0); PRCMPeripheralSleepEnable(PRCM_PERIPH_UART0); PRCMPeripheralDeepSleepEnable(PRCM_PERIPH_UART0); // 加载时钟设置 PRCMLoadSet();这一步确保了UART模块在任何功耗模式下都有时钟供给。配置GPIO引脚假设我们使用DIO2作为UART0_TXDIO3作为UART0_RX。// 配置DIO2为UART0 TX功能输出 IOCPinTypeUart(UART0_BASE, IOID_2, PIN_MODE_7); // PIN_MODE_7 通常是UART TX // 配置DIO3为UART0 RX功能输入 IOCPinTypeUart(UART0_BASE, IOID_3, PIN_MODE_6); // PIN_MODE_6 通常是UART RX // 如果需要硬件流控还需配置RTS和CTS引脚 // IOCPinTypeUart(UART0_BASE, IOID_4, PIN_MODE_?); // RTS // IOCPinTypeUart(UART0_BASE, IOID_5, PIN_MODE_?); // CTSIOCPinTypeUart函数内部会设置IOCFGn寄存器的IOMODE字段将引脚功能切换到UART模式并配置正确的上下拉和驱动强度。务必查阅具体芯片的数据手册确认每个引脚支持的模式编号。4.2 寄存器级初始化流程在时钟和引脚就绪后我们开始配置UART模块本身。遵循手册推荐的步骤至关重要。标准初始化序列// 假设 PERDMACLK 24MHz 目标波特率 115200 #define UART0_BASE 0x40001000 #define SYSTEM_CLOCK_FREQ 24000000 #define BAUDRATE 115200 void UART0_Init(void) { // 步骤1: 禁用UART (UARTEN 0) HWREG(UART0_BASE UART_O_CTL) ~(UART_CTL_UARTEN); // 步骤2: 计算并设置波特率除数BRD // BRD SysClk / (16 * BaudRate) 24e6 / (16 * 115200) 13.02083333 uint32_t brd (SYSTEM_CLOCK_FREQ * 64) / (BAUDRATE * 16); // 先乘以64保留小数精度 uint32_t ibrd brd / 64; // 整数部分 uint32_t fbrd ((brd % 64) * 64 32) / 64; // 小数部分四舍五入 HWREG(UART0_BASE UART_O_IBRD) ibrd; // 写入整数部分 HWREG(UART0_BASE UART_O_FBRD) fbrd; // 写入小数部分 // 步骤3: 配置线路控制寄存器 (数据位、停止位、奇偶校验、FIFO) uint32_t lcrh 0; lcrh | UART_LCRH_WLEN_8; // 8位数据位 lcrh | UART_LCRH_FEN; // 使能FIFO // lcrh | UART_LCRH_STP2; // 2位停止位 (默认1位) // lcrh | UART_LCRH_PEN; // 使能奇偶校验 // lcrh | UART_LCRH_EPS; // 偶校验 (与PEN一起使用) HWREG(UART0_BASE UART_O_LCRH) lcrh; // 此写入操作同时锁存了步骤2的波特率设置 // 步骤4: 配置中断FIFO触发水平 (可选) HWREG(UART0_BASE UART_O_IFLS) ~(UART_IFLS_RX_MASK | UART_IFLS_TX_MASK); HWREG(UART0_BASE UART_O_IFLS) | UART_IFLS_RX1_2 | UART_IFLS_TX1_2; // RX/TX均为1/2 // 步骤5: 使能所需的中断 (可选) // 先清除所有挂起的中断 HWREG(UART0_BASE UART_O_ICR) 0x7FF; // 写入1清除所有中断标志 // 使能接收中断和接收超时中断 HWREG(UART0_BASE UART_O_IMSC) | UART_IMSC_RXIM | UART_IMSC_RTIM; // 注意发送中断通常在需要发送数据时再临时使能 // 步骤6: 配置DMA (可选) // HWREG(UART0_BASE UART_O_DMACTL) | UART_DMACTL_RXDMAE; // 使能接收DMA // 步骤7: 配置硬件流控制 (可选) // HWREG(UART0_BASE UART_O_CTL) | UART_CTL_CTSEN | UART_CTL_RTSEN; // 步骤8: 最后使能UART模块 HWREG(UART0_BASE UART_O_CTL) | UART_CTL_UARTEN | UART_CTL_RXE | UART_CTL_TXE; }这段代码清晰地展示了从禁用模块、配置波特率、设置帧格式到最后使能模块的完整流程。步骤3中写入LCRH是激活波特率设置的关键。4.3 发送与接收的基础操作初始化完成后就可以进行数据收发了。阻塞式发送轮询 最简单的方式是查询UART_FR_TXFF标志发送FIFO满。这种方式会占用CPU只适合在初始化或极低频率的调试输出中使用。void UART0_SendBytePolling(uint8_t data) { // 等待发送FIFO有空间 while (HWREG(UART0_BASE UART_O_FR) UART_FR_TXFF) { // 可在此处加入超时机制 } HWREG(UART0_BASE UART_O_DR) data; } void UART0_SendStringPolling(const char *str) { while (*str) { UART0_SendBytePolling(*str); } }中断驱动发送 高效的方式是使用中断。通常维护一个发送环形缓冲区。用户调用发送函数将数据填入缓冲区。如果发送器空闲TXFF为0且发送中断未使能则直接启动第一次发送写DR并使能发送中断UART_IMSC_TXIM。在发送中断服务程序ISR中检查发送FIFO是否还有空间TXFF为0如果有则从环形缓冲区取出下一个字节写入UART:DR。当环形缓冲区为空时在ISR中禁用发送中断避免无意义的中断。中断驱动接收 接收通常完全由中断驱动。在接收ISR中循环读取UART:DR直到FIFO为空RXFE为1。每次读取会得到一个32位值低8位是数据高8位中的某些位是错误状态FE, PE, BE, OE。必须检查这些错误位并进行相应处理如丢弃错误数据、记录错误计数等。DMA驱动收发 配置好DMA后发送和接收几乎不需要CPU参与。对于发送你只需要将数据放入DMA源缓冲区启动DMA传输然后等待DMA完成中断。对于接收你设置好DMA目标缓冲区启动接收DMA数据就会自动填充缓冲区并在达到指定传输量或接收超时如果使能了超时中断时产生中断通知CPU。5. 高级应用与调试技巧5.1 回环测试与自检在硬件连接前或怀疑硬件有问题时回环测试Loopback是极佳的诊断工具。CC13x2/CC26x2的UART支持内部数字回环。操作方法在初始化序列的最后一步使能UARTEN之前将UART:CTL寄存器的LBE回环使能位置1。正常使能UART。此时芯片内部将UART_TXD输出直接连接到UART_RXD输入。你发送的任何数据都会被自己接收回来。编写测试代码发送一串已知的数据如0x00, 0x55, 0xAA, 0xFF等然后在接收端验证收到的数据是否完全一致。这个测试可以验证UART模块本身的软硬件配置波特率、数据格式、FIFO、中断逻辑是否正确排除了外部线路和对方设备的影响。5.2 低功耗模式下的UART操作CC13x2/CC26x2是低功耗MCUUART模块在睡眠模式下如何工作是需要考虑的。睡眠模式如果UART时钟在睡眠模式下保持使能通过PRCMPeripheralSleepEnable并且UART模块本身使能它可以在接收到数据时产生中断将CPU从睡眠中唤醒。这常用于由外部设备触发唤醒的场景。深度睡眠模式大多数高速时钟在深度睡眠下会被关闭UART无法工作。如果需要在深度睡眠下维持极低速率的串口监听可能需要依赖GPIO边沿中断结合软件模拟的“唤醒”机制唤醒后再初始化UART进行通信。这不是UART模块的标准功能需要精心设计。最佳实践在进入低功耗模式前确保所有UART数据传输已经完成并根据需求决定是否保持UART时钟和模块使能。如果不需要在睡眠时接收最好禁用UART以节省功耗。5.3 常见问题排查速查表在实际开发中UART通信失败是家常便饭。下面这个表格总结了我遇到过的典型问题及排查思路现象可能原因排查步骤与解决方案完全无通信TX引脚无波形1. UART模块时钟未使能。2. GPIO引脚未正确复用为UART功能。3. UART模块未使能UARTEN0。4. 发送器被禁用TXE0。1. 检查PRCM相关寄存器确认UART外设时钟已开启并加载。2. 用逻辑分析仪或示波器检查TX引脚确认其已不是高阻态并有正确的UART输出波形如果没有检查IOC配置。3. 确认UART:CTL寄存器的UARTEN和TXE位为1。有波形但数据错误乱码1.波特率不匹配最常见。2. 数据格式不匹配数据位、停止位、奇偶校验。3. 双方电平标准不匹配如3.3V vs 5V。1.双查波特率计算确认系统时钟频率、BRD整数和小数部分计算无误且写入顺序正确IBRD-FBRD-LCRH。用逻辑分析仪测量实际位宽反推波特率。2. 核对双方LCRH设置WLEN, STP2, PEN, EPS。3. 检查硬件电平转换电路。通信不稳定偶尔丢数据1. 未使用流控且接收端处理速度跟不上发送端。2. 中断服务程序处理时间过长导致FIFO溢出。3. 系统中断被长时间关闭。4. 电源噪声或地线问题。1. 考虑启用硬件RTS/CTS流控。2. 优化ISR只做最必要的操作如将数据移入缓冲区将处理任务交给主循环。增大接收FIFO触发水平减少中断频率。3. 检查代码中是否有长时间关中断的操作。4. 检查PCB布局确保UART线路远离噪声源并做好滤波。只能发送不能接收1. RX引脚配置错误应为输入。2. 接收器被禁用RXE0。3. 接收中断未使能且采用轮询方式时未检查RXFE标志。4. 对方设备TX未正确连接或损坏。1. 确认RX引脚的IOC配置为UART输入模式。2. 确认UART:CTL的RXE位为1。3. 如果使用中断检查UART:IMSC的RXIM位如果轮询检查UART:FR的RXFE位。4. 交换TX/RX线进行交叉测试。使能DMA后数据混乱1. DMA源/目标地址或传输大小配置错误。2. DMA和CPU同时访问同一缓冲区未做同步。3.DMAONERR导致错误后DMA被禁用。1. 仔细检查DMA控制表的配置特别是地址递增模式和数据大小。2. 使用双缓冲区或标志位进行CPU和DMA的同步。3. 在UART错误中断中检查并重新使能DMA。硬件流控无效1. RTS/CTS引脚未正确配置或连接。2.CTSEN/RTSEN位未使能。3. 对方设备不支持或不响应流控。1. 用示波器同时测量TX和CTS信号。当CTS为高时TX应暂停发送。2. 测量RTS信号当本机FIFO快满时RTS应变低。3. 确认对方设备手册了解其流控行为。5.4 性能优化与资源权衡在资源受限的嵌入式系统中需要在性能、功耗和代码复杂度之间取得平衡。中断 vs 轮询对于低速、间歇性通信如配置命令轮询足够简单高效。对于连续数据流中断是必须的。但过于频繁的中断如FIFO触发水平设得太低会成为系统负担。中断 vs DMA如果数据包较大例如大于32字节或传输非常频繁DMA的优势巨大。它几乎零CPU开销且能保证确定的延迟。缺点是设置稍复杂且会占用DMA通道资源。FIFO深度利用32字节的FIFO是宝贵资源。对于发送可以在启动传输前尽可能多地将数据写入FIFO直到TXFF置位以减少中断次数。对于接收设置合理的触发水平让一次中断能处理更多数据。功耗考虑如果通信是间歇性的可以在空闲时关闭UART模块和时钟以省电。需要通信时再重新初始化。TI的驱动库通常提供了相应的低功耗接口。最后再分享一个调试“笨”办法但极其有效使用一个USB转UART调试器连接到你的MCU。用PC端的串口助手如Putty、Tera Term发送数据同时用逻辑分析仪抓取MCU的TX/RX引脚波形。对比PC发送的数据、逻辑分析仪捕获的波形、以及MCU程序实际接收到的数据任何不一致都能帮你迅速定位问题是在软件配置、硬件连接还是时序方面。嵌入式开发很多时候就是一场与细节的较量而UART正是这场较量中最基础的战场之一。把它摸透了其他更复杂的通信协议也就有了扎实的根基。
郑州网站建设
网页设计
企业官网