SPI转USART接口扩展方案:基于LPC802的嵌入式通信桥接实战
1. 项目概述在嵌入式物联网项目的开发过程中我们常常会遇到一个经典难题作为系统核心的中央控制器比如一颗高性能的MCU其片上外设接口资源是有限的尤其是像UART通用异步收发器这种最常用、最基础的串行通信接口。然而随着系统功能的扩展需要接入的传感器、执行器、显示模块等外围设备却越来越多它们大多依赖UART进行通信。这种接口需求与资源供给之间的矛盾在星型拓扑的物联网系统中尤为突出。直接增加中央控制器的UART接口数量既不经济也受限于芯片选型。这时一种高效、低成本的解决方案就是进行接口扩展。今天要和大家深入探讨的正是这样一个在工程实践中极具价值的方案基于NXP LPC802微控制器实现的SPI转USART接口扩展桥。这个方案的核心思想非常巧妙——利用中央控制器上相对富余的SPI串行外设接口总线通过一颗廉价的、作为“桥梁”的MCU这里选用LPC802扩展出额外的、完全独立的USART通道。简单来说就是让SPI“变身”为USART从而让主控能够与更多的串口设备对话。这个方案不仅解决了接口数量瓶颈还因为SPI本身是高速同步接口在吞吐量上也有保障。接下来我将结合官方应用笔记AN13066并融入我个人在类似项目中的实战经验为你拆解这个方案从硬件设计、寄存器驱动到数据流控制的每一个细节手把手带你实现一个稳定可靠的通信桥梁。2. 方案核心思路与硬件选型解析2.1 为什么选择SPI转USART在深入代码之前我们必须先理解这个方案背后的设计逻辑。选择SPI作为扩展总线而非I2C或其他接口是经过多方面权衡的。首先SPI是全双工、高速同步串行总线。其主从架构和时钟同步机制使得数据传输速率高通常可达几十Mbps、时序简单可控非常适合作为桥接芯片与主控之间大数据量、低延迟的“数据高速公路”。相比之下I2C虽然节省引脚但速率较低且在多主模式下协议更复杂而单纯用GPIO模拟时序则对主控的CPU占用率高软件复杂度也陡增。其次USART通用同步/异步收发器是嵌入式领域最通用、最成熟的异步串行通信接口。几乎所有的传感器如GPS、蓝牙模块、执行器如某些电机驱动器、人机交互设备如串口屏、调试终端都支持USART。因此扩展USART接口具有最广泛的设备兼容性。那么这个“桥”需要做什么它的核心任务可以概括为两点协议转换与数据缓冲。协议转换即把主控通过SPI发送过来的命令和数据按照USART的帧格式起始位、数据位、校验位、停止位发送出去反之将USART接收到的数据打包通过SPI上报给主控。数据缓冲则是为了解决SPI与USART之间可能存在的速率不匹配问题以及应对突发数据流确保数据不丢失。LPC802在这其中扮演了一个“智能协议转换器”和“数据中转站”的角色。2.2 硬件平台搭建与接口定义根据应用笔记演示系统采用了三块NXP的开发板中央控制器 (SPI Master): LPC804 (LPCXpresso804开发板)。它作为系统大脑通过SPI总线管理整个数据交换。桥接芯片 (SPI to USART Bridge): LPC802 (LPCXpresso802开发板)。这是本方案的核心负责执行协议转换和数据缓冲。USART外设: 两个LPC51U68 (LPCXpresso51U68开发板)。它们模拟需要接入系统的串口设备例如两个不同的传感器。注意在实际产品设计中你完全可以根据成本、性能需求选择其他主控和外围设备。LPC802在这里的核心价值在于其极低的成本、丰富的USART外设和灵活的GPIO功能非常适合作为专用桥接芯片。你也可以用STM32G0系列、GD32E230系列等Cortex-M0内核的芯片实现类似功能。硬件连接图是理解整个系统的钥匙。如下图所示连接关系非常清晰SPI接口连接LPC804主与LPC802从。包含四根线SCK时钟、MOSI主出从入、MISO主入从出、SSEL片选。这是主控与桥之间通信的“主干道”。中断信号 (IRQ)一根GPIO线从LPC802连接到LPC804的某个外部中断引脚。这是桥向主控“主动汇报”的通道用于通知“有数据待取”或“发送完成”等事件避免了主控不断轮询的效率低下问题。USART接口两个独立的USART通道从LPC802连接到两个LPC51U68。每个通道包含TX发送、RX接收两根线如果需要硬件流控则还需RTS和CTS。[LPC804 - Central Controller] | (SPI Master) | SCK, MOSI, MISO, SSEL | IRQ (Interrupt) | [LPC802 - Bridge Chip] | (SPI Slave, USART Master) / \ / (USART Channel 0) \ (USART Channel 1) / \ [LPC51U68-0] [LPC51U68-1] (USART Peripheral) (USART Peripheral)这种星型连接确保了每个USART外设与主控之间的通信在逻辑上是独立的桥接芯片LPC802负责管理这些通道的复用与调度。3. 寄存器驱动模型软件设计的基石整个桥接功能的核心是一个精心设计的寄存器驱动模型。这意味着主控LPC804对桥LPC802的所有操作无论是读取USART数据还是配置桥的参数都抽象为对LPC802内部一系列虚拟寄存器的读写操作。这极大地简化了主控端的软件设计使其像操作一个普通的串口扩展芯片如SC16IS752一样简单。3.1 关键寄存器功能详解LPC802桥接固件模拟了以下几类关键寄存器它们的地址和功能是主控与桥之间通信的“协议字典”表1数据缓冲寄存器 (THR/RHR)寄存器名称地址读写属性功能描述发送保持寄存器 (THR)0x00只写这是一个64字节深的发送FIFO缓冲区。主控向此地址写入的数据会被桥缓存并最终通过指定的USART通道发送出去。接收保持寄存器 (RHR)0x00只读这是一个64字节深的接收FIFO缓冲区。桥从USART通道接收到的数据会暂存于此主控通过读取此地址来获取数据。这里有一个关键点THR和RHR共享同一个地址0x00。如何区分是读还是写呢这依赖于SPI传输帧中的命令字节后面会详细讲的读写位。当命令指示“写”操作且地址为0x00时数据流向THR当命令指示“读”操作且地址为0x00时数据来自RHR。这种设计节省了寄存器地址空间。表2控制与状态寄存器 (BCR/IIR)寄存器名称地址读写属性功能描述缓冲控制寄存器 (BCR)0x02只写用于配置发送(TX)和接收(RX)缓冲区的触发阈值以及清空缓冲区。中断标识寄存器 (IIR)0x02只读用于向主控报告当前中断产生的原因。主控在收到IRQ中断后首先应读取此寄存器来判断发生了什么事件。BCR寄存器的位定义需要特别关注位[7:6] (RX Trigger): 设置接收缓冲区(RHR)的触发水平。例如设置为11表示当RHR中累积的数据达到64字节时才产生RHR中断通知主控来读取。这可以减少中断频率提高效率。位[5:4] (TX Trigger): 设置发送缓冲区(THR)的触发水平。例如设置为11表示当THR中空闲空间达到64字节即缓冲区空时才产生THR中断通知主控可以发送下一批数据。或者当主控写入的数据量达到此阈值时桥立即开始发送。位[2] (Clear TX Buffer): 写1清空整个发送缓冲区。位[1] (Clear RX Buffer): 写1清空整个接收缓冲区。位[0] (Buffer Enable): 写1使能缓冲区功能。IIR寄存器的低6位编码了中断源主控通过查表即可知道是“接收数据就绪”、“发送缓冲区空”还是“接收超时”。表3缓冲水平寄存器 (TXLVL/RXLVL)寄存器名称地址读写属性功能描述发送缓冲水平寄存器 (TXLVL)0x08只读返回发送缓冲区(THR)中当前可用的空闲空间字节数0-64。主控在发送数据前应读取此值确保不会溢出。接收缓冲水平寄存器 (RXLVL)0x09只读返回接收缓冲区(RHR)中当前已存储的数据字节数0-64。主控在读取数据前应读取此值知道有多少数据可读。实操心得在实际编程中TXLVL和RXLVL这两个寄存器非常实用。它们使得主控可以实现“按需传输”而非盲目发送或读取。例如主控有一个100字节的数据包要发送它可以先读TXLVL如果空闲空间大于等于100则一次性发送如果小于100则先发送TXLVL指示的字节数等待THR中断缓冲区空后再发送剩余部分。这种流控机制是保证数据不丢失的关键。3.2 命令字节通信的“指令集”所有的寄存器读写和数据传输都始于一个8位的命令字节。这个字节由主控在每次SPI传输开始时首先发出它告诉桥接芯片“我要做什么”。表4命令字节位域定义位符号描述7R/~W传输方向。0: 下游传输主控 - 桥 - USART外设。1: 上游传输USART外设 - 桥 - 主控。6:3A[3:0]寄存器地址。指定要读写的寄存器地址0x00, 0x02, 0x08, 0x09等。2:1CH[1:0]USART通道选择。00: 通道001: 通道111: 广播模式同时操作通道0和1用于特定配置。0X保留位通常设置为0。一个典型的命令字节可能是0x80。我们来解析一下二进制为1000 0000。位7 (R/~W) 1: 表示这是一个读操作上游传输。位[6:3] (A[3:0]) 0000: 表示寄存器地址为0x00。位[2:1] (CH[1:0]) 00: 表示选择USART通道0。位0 0: 保留位。所以0x80这个命令的含义是从通道0的接收保持寄存器(RHR)中读取数据。理解了寄存器模型和命令字节我们就掌握了与桥接芯片对话的“语言”。接下来我们看看如何运用这套语言来完成实际的数据收发。4. 数据传输流程深度剖析与实操数据传输分为两个方向下游传输主控到外设和上游传输外设到主控。每个方向又根据数据量是否达到缓冲区触发阈值细分为不同的处理流程。理解这些流程是编写稳定驱动代码的关键。4.1 下游传输主控 - 外设下游传输的目标是将主控的数据通过桥发送到指定的USART外设。假设我们已将TX触发阈值设置为64字节。场景一发送恰好64字节数据达到触发阈值这是最标准、效率最高的一种情况流程如下检查缓冲区空间主控首先读取TXLVL寄存器。如果返回值大于等于64说明发送缓冲区有足够空间。发送数据主控构造命令字节写操作地址0x00选择通道然后通过SPI连续发送64个数据字节。桥接芯片将这些字节存入其内部的THR发送保持寄存器。桥触发发送并产生中断由于数据量64字节达到了TX触发阈值桥接芯片会立即或在极短延时后开始通过USART向外设发送数据。同时它会产生一个THR中断发送缓冲区空/达到阈值并通过IRQ线拉低通知主控。主控响应中断主控检测到IRQ引脚低电平进入中断服务程序。查询中断源主控读取IIR寄存器得知是THR中断。这通常意味着“上一批数据已发送完毕缓冲区有空闲可以准备发送下一批数据”。对于单次发送64字节的场景主控可以简单地清除中断标志本次传输完成。场景二发送少于64字节数据例如19字节这种情况更常见因为数据包长度往往是不固定的。检查缓冲区空间主控读取TXLVL确认空间足够肯定足够因为19 64。发送数据主控发送命令字节和19个数据字节。桥的等待与超时发送桥接芯片将19字节存入THR。但由于数据量未达到TX触发阈值64字节它不会立即启动USART发送。此时桥内部的一个发送超时定时器开始工作。超时触发发送如果在定时器超时例如典型值设为4个字符传输时间之前主控没有发送新的数据桥就认为“当前数据包已经结束”于是将THR中现有的19字节数据通过USART发送出去。无中断产生因为未达到触发阈值桥不会产生THR中断。主控无需查询IIR。注意事项这里的“发送超时”机制至关重要。它确保了小数据包也能被及时发送而不是一直等待缓冲区填满。这个超时时间需要根据你的实际通信波特率和应用场景合理设置。设置太短可能将本属于一个逻辑包的数据拆分成多个物理包发送设置太长则会导致通信延迟增加。4.2 上游传输外设 - 主控上游传输是桥接芯片从USART外设接收数据并通知主控来读取的过程。假设RX触发阈值也设置为64字节。场景一接收恰好64字节数据达到触发阈值桥接收数据并产生中断外设通过USART向桥发送64字节数据。桥将其存入RHR接收保持寄存器。当存满64字节达到RX触发阈值时桥产生RHR中断并通过IRQ线通知主控。主控响应中断主控进入中断服务程序。查询中断源主控读取IIR寄存器确认是RHR中断数据就绪。获取数据量主控读取RXLVL寄存器得知有64字节数据待读取。读取数据主控发送“读数据”命令字节地址0x00R/~W1然后连续进行65次SPI读操作发出65个时钟从桥那里读回64字节数据以及一个额外的通道状态字节。场景二接收少于64字节数据例如63字节桥接收数据并等待外设发送63字节数据桥将其存入RHR。接收超时中断由于数据量未达到64字节的触发阈值RHR中断不会产生。但是桥在开始接收一串数据后会启动一个接收超时定时器。如果在一段时间内例如超过4个字符传输时间的间隔没有收到新数据它就认为“这一帧数据接收完毕”。产生超时中断此时桥会产生一个接收超时中断并通过IRQ通知主控。主控响应并读取主控查询IIR得知是接收超时中断然后读取RXLVL值为63最后将这63字节数据读出。4.3 上游传输的关键细节SPI的“时钟交换”本质这是很多初学者容易困惑的地方也是本方案的一个关键实现要点。SPI协议是一种全双工同步协议这意味着主机在发送数据的同时也在接收数据时钟SCK是由主机产生的。当主控想要从桥SPI从设备读取数据时它必须先提供时钟。而提供时钟的方式就是主控同时要发送一些数据出去即使这些数据内容没有意义。这些为了产生时钟而发送的数据被称为“哑元数据”。在“读取数据”的SPI事务中对应命令字节R/~W1主控先发送命令字节。这个字节本身是主控要“写”给桥的用于告诉桥“我要读数据了”。紧接着主控为了读回N个字节的有效数据它必须再发出N1个SPI时钟周期。对应的它需要发送N1个哑元字节任意值如0x00或0xFF。第一个哑元字节被发送后桥会通过MISO线返回一个通道状态字节指示来自哪个通道。随后的N个哑元字节被发送时桥会依次返回RHR缓冲区中的N个数据字节。所以读取N字节实际需要的SPI事务长度是 1 (命令) (N1) (哑元) N2 个字节的传输。在编写底层SPI驱动时必须确保这个长度否则无法读全数据。5. 软件实现与代码流程详解有了前面的理论铺垫我们现在来看具体的程序流程图和代码实现思路。这里我会用伪代码和流程描述来阐述你可以将其转化为具体的C语言代码。5.1 数据读取上游流程这是主控响应中断从桥读取USART数据的核心流程。主控端伪代码流程 (中断服务程序或主循环中检测到IRQ) 1. 读取IIR寄存器判断中断源 - 若为RHR中断数据就绪 a. 读取RXLVL寄存器获取待读取数据字节数 data_count。 b. 构造读命令字节 cmd 0x80 | (channel 1)。 // 假设R/~W1, 地址0x00 c. 发起一次SPI传输先发送cmd然后连续发送(data_count 1)个哑元字节如0xFF。 d. 从SPI接收缓冲区中丢弃第一个返回的字节通道状态后续的data_count个字节即为有效数据。 e. 处理这些数据。 - 若为接收超时中断 a. 同样读取RXLVL获取data_count此时通常小于触发阈值。 b. 执行上述b-d步骤读取剩余数据。 - 若为THR中断下游流程用到或其他中断 执行相应处理。 2. 清除中断标志如果需要具体取决于LPC802的中断清除方式。桥接芯片端LPC802的对应逻辑其USART中断服务程序中收到数据就存入RHR FIFO。当RHR数据量达到触发阈值或接收超时定时器到期则设置相应的中断标志并将IRQ引脚拉低。在SPI从机中断中解析主控发来的命令字节。如果是读命令地址0x00则从RHR FIFO中取出数据在后续的SPI时钟周期中通过MISO线发送出去。5.2 数据写入下游流程这是主控通过桥向USART外设发送数据的流程。主控端伪代码流程 1. 准备要发送的数据 tx_buffer[] 和长度 len。 2. 循环发送直到所有数据发送完毕 a. 读取TXLVL寄存器获取发送缓冲区当前空闲空间 free_space。 b. 本次可发送量 send_len min(len, free_space)。 c. 如果 send_len 0 i. 构造写命令字节 cmd 0x00 | (channel 1)。 // R/~W0, 地址0x00 ii. 发起SPI传输先发送cmd然后连续发送 send_len 个数据字节从tx_buffer中取出。 iii. 更新 tx_buffer指针和剩余长度 len - send_len。 d. 如果 len 0 且 free_space 期望值例如本次send_len很小 // 说明缓冲区快满了需要等待 可以进入阻塞等待或退出函数等待THR中断缓冲区空后再继续执行步骤2。 3. 如果发送的数据包长度很小例如小于触发阈值且希望立即发送 可以在发送完数据后延时一小段时间大于桥的发送超时时间或者通过写某个控制寄存器如果桥支持来手动触发发送。桥接芯片端LPC802的对应逻辑在SPI从机中断中收到写数据命令地址0x00和数据后将数据存入THR FIFO。如果THR中数据量达到触发阈值或距离上次收到数据已超过发送超时时间则启动USART发送流程将THR中的数据通过TX引脚发出。当THR变空时产生THR中断通知主控可以发送更多数据。5.3 寄存器读写流程寄存器读写是配置和查询桥状态的基础。流程比数据读写更简单因为只涉及一个寄存器值。寄存器写流程主控发送命令字节R/~W0地址A[3:0]非0选择通道。主控紧接着发送一个字节的数据这个数据就是要写入指定寄存器的值。桥接芯片解析命令后将接收到的数据字节写入其内部对应的控制变量或寄存器映射中。寄存器读流程主控发送命令字节R/~W1地址A[3:0]非0选择通道。主控发送两个哑元字节。桥接芯片在第一个哑元字节时钟周期返回一个无意义字节或通道状态在第二个哑元字节时钟周期返回所请求寄存器的值。主控从SPI接收缓冲区中取第二个字节即为寄存器值。6. 常见问题排查与实战经验分享在实际调试基于LPC802的SPI转USART桥时你可能会遇到以下典型问题。这里我结合自己的踩坑经历给出排查思路和解决方案。6.1 数据丢失或错乱症状主控发送的数据外设只收到一部分或收到乱码。排查步骤检查波特率这是最常见的原因。确保主控SPI时钟、桥接芯片的USART波特率、外设的USART波特率三者设置一致且精确。特别是USART波特率计算出的分频值可能因时钟源误差而产生累积误差建议使用示波器测量实际波特率。检查缓冲区溢出在主控发送端每次发送前务必读取TXLVL确保不发送超过空闲空间的数据。在接收端及时响应RHR中断或超时中断快速读取RXLVL指示的数据避免桥的RHR缓冲区被新数据覆盖。检查SPI相位和极性(CPOL/CPHA)确保主控和LPC802作为SPI从机的SPI模式设置完全一致。模式错误会导致数据位移错位一位。检查中断处理延迟如果系统中断被长时间关闭可能导致桥的IRQ信号未被及时响应造成缓冲区溢出。优化中断服务程序使其尽量短小精悍。逻辑分析仪是神器用逻辑分析仪同时抓取SPI总线SCK, MOSI, MISO, SSEL和USART总线TX, RX的波形。对比主控SPI发出的数据、桥MISO返回的数据、以及桥USART-TX发出的数据可以精准定位问题发生在协议转换的哪个环节。6.2 通信完全无反应症状IRQ引脚无变化发送数据后外设无任何接收。排查步骤检查硬件连接使用万用表检查SPI四根线、IRQ线、USART线是否连通有无接错如MOSI与MISO反接。检查电源和地确保所有芯片供电正常共地良好。检查片选(SSEL)确保主控在通信时正确拉低了连接LPC802的片选引脚。SPI从机只有在片选有效时才会响应。检查LPC802程序是否运行给LPC802的某个GPIO加一个闪烁LED的程序或者通过调试器连接确认其固件已正确烧录并运行。检查寄存器初始化确认主控在上电后是否正确初始化了桥接芯片例如是否通过写BCR寄存器使能了缓冲区并设置了触发阈值6.3 只能发送一次数据后续失败症状第一次通信成功第二次发送时卡住或数据错误。排查步骤中断标志未清除LPC802在产生中断如THR、RHR后可能需要主控通过某种方式如读取IIR寄存器来清除中断标志。如果标志未清除IRQ线可能一直保持有效导致主控中断逻辑混乱。仔细查阅桥接固件的中断清除机制。SPI事务未正确结束确保每次SPI传输后片选(SSEL)信号被正确拉高表示一次事务结束。有些SPI从机需要这个边沿来复位内部状态机。缓冲区未正确复位在开始新一轮大量数据传输前可以考虑通过写BCR寄存器的位1或位2来清空RX/TX缓冲区避免旧数据干扰。6.4 性能优化建议调整触发阈值默认的64字节触发阈值适用于大数据块传输。如果你的数据包都很小如十几个字节可以将RX/TX触发阈值调小例如8或16字节以降低通信延迟。但注意这会增加中断频率对CPU有一定负担。使用DMA如果主控如LPC804支持SPI DMA强烈建议使用DMA来搬运数据。这可以极大解放CPU尤其是在高速、连续传输的场景下。将SPI的发送和接收配置为DMA模式在中断中只需处理DMA传输完成回调即可。合理设置超时时间发送超时和接收超时时间需要根据你的应用场景权衡。对于交互式、实时性要求高的应用超时时间应设短如1-2个字符时间。对于文件传输等场景可以设长一些以减少数据包碎片。7. 演示与验证让方案跑起来理论最终要付诸实践。按照应用笔记的步骤搭建演示环境是验证和理解整个方案的最佳方式。准备三份代码工程你需要分别编写或获取三份代码。SPI Master工程运行在LPC804上实现上述的主控端驱动逻辑包括SPI初始化、中断处理、寄存器读写、数据收发API。Bridge工程运行在LPC802上这是方案的核心固件。它需要实现SPI从机中断服务程序解析命令、读写数据、USART中断服务程序收发数据、双缓冲区THR/RHR管理、中断触发与IRQ引脚控制、超时定时器管理等。这部分代码最为复杂。USART Peripheral工程运行在两个LPC51U68上功能很简单一个作为发送方循环向上游发送数据另一个作为接收方打印接收到的下游数据。硬件连接参照前面的框图用杜邦线仔细连接好SPI、IRQ和USART线路。务必再三检查线序。上电与观察首先给作为桥的LPC802上电确保其程序开始运行。然后给LPC804和LPC51U68上电。打开连接LPC804和LPC51U68的串口调试助手如Tera Term、SecureCRT。复位LPC804主控你应该在作为接收方的LPC51U68的串口终端上看到它发送出来的测试数据例如0x00到0x3F。复位作为发送方的LPC51U68你应该在LPC804的串口终端上看到它发送出来的测试数据。当你在两个终端上都看到正确、完整的数据流时恭喜你这个SPI转USART的桥梁已经成功搭建并稳定运行了。这个过程不仅验证了硬件连接和底层驱动更让你深刻理解了从SPI命令到USART数据流的完整转换链条。这个方案的价值在于其通用性你可以将LPC802替换成其他MCU将USART外设替换成任何串口设备其核心的“寄存器驱动双缓冲中断通知”的设计思想是相通的。掌握了它你就拥有了在资源受限的嵌入式系统中灵活扩展通信接口的能力。