低成本MCU软件串口实现与MISC协议栈集成实战

发布时间:2026/6/8 19:39:26

低成本MCU软件串口实现与MISC协议栈集成实战 1. 项目概述低成本MCU的软件串口与协议栈实战在嵌入式开发里串口通信UART/SCI几乎是每个项目都绕不开的基础功能。硬件UART模块用起来确实方便但很多超低成本的微控制器MCU比如一些8位机或者为了极致性价比而阉割了外设的型号压根就没有这个硬件模块。这时候项目还得做通信还得有怎么办答案就是“软件模拟”也就是我们常说的“软件串口”或“位敲打Bit-Banging”。我最近在翻一些老项目的资料正好重新梳理了一遍在Freescale现在的NXPHC05系列这类低成本MCU上用软件完整实现SCISerial Communication Interface功能并成功驱动MISCMotorola Interconnect Serial Communication协议栈的经验。这不仅仅是简单地用两个GPIO口模拟出TX和RX那么简单而是要构建一个包含完整错误检测、中断管理甚至能支持虚拟全双工通信的可靠数据链路层。这对于那些成本敏感但又需要稳定主从通信的领域比如工业传感节点、老式家电控制板、简单的分布式IO模块非常有参考价值。如果你正在为一块“裸奔”的MCU寻找可靠的异步通信方案或者对软件模拟外设的底层细节感兴趣这篇分享或许能给你一些直接的思路和可复用的代码逻辑。2. 核心思路从硬件依赖到软件模拟的完整迁移2.1 硬件SCI与软件模拟的本质差异硬件SCI模块之所以省心是因为它把通信的“脏活累活”都包了自动检测起始位、按波特率时钟采样数据、校验停止位、设置各种错误标志帧错误FE、噪声NF、超限OR并且收发都有独立的数据寄存器甚至FIFOCPU只需要读写数据寄存器即可通信过程与程序执行是真正并行的。当我们决定用软件模拟时所有这些功能都需要我们自己用代码来实现。这就带来了几个核心挑战时序精度如何在没有硬件波特率发生器的情况下精确地控制每个比特位的持续时间位周期tp 1 / BaudRate中断响应如何可靠地检测到起始位的下降沿并同步整个接收过程错误处理如何在没有硬件比较器的情况下实现噪声、帧错误和接收超限的检测CPU占用如何在保证通信可靠性的前提下尽量减少软件轮询对CPU时间的占用原文中提到的方案其高明之处在于它并非一个简单的“演示代码”而是一个以寄存器接口为目标的完整模拟层。它定义了SW_SCCR1,SW_SCCR2,SW_SCSR,SW_SCDR这四个内存映射的“虚拟寄存器”其功能、标志位都尽量与硬件SCI寄存器保持一致。这意味着上层协议栈如MISC的代码几乎无需修改只需将底层硬件访问的宏或函数指向这些软件模拟的寄存器即可极大地提高了代码的复用性和可移植性。2.2 虚拟全双工与MUX总线适配另一个关键点是通信模式。标准的UART是全双工有独立的TX和RX线。但在一些特定的总线拓扑中比如MUX多路复用单线总线物理层会将MCU发送的TX信号“回环”到RX引脚上。这就形成了一种“虚拟全双工”设备在发送的同时也能“听到”自己发送的内容更重要的是能听到总线上其他设备的信号。软件模拟需要适应这种拓扑。在发送每一位数据输出到TX引脚后软件不能傻等必须利用位周期内电平稳定的时间窗口立刻去采样RX引脚此时是总线状态的镜像完成接收位的采样和拼装。这样在软件层面虽然物理上收发共用时序但逻辑上仍然维持了“同时”收发的能力为基于碰撞检测或主从应答的协议如MISC奠定了基础。注意这种“虚拟全双工”对时序的要求极为苛刻。发送子程序PUT_byte必须在输出位电平后严格在位的中间点进行采样且采样过程本身不能干扰输出时序。这通常需要精心计算指令周期并可能需要在关键路径禁用中断。3. 软件SCI核心实现细节拆解3.1 位定时与采样策略精度是生命线软件串口的首要问题是“时钟从哪里来”。通常有两种选择一是使用MCU内置的定时器/计数器模块产生精确的波特率中断二是使用指令延时循环。对于HC05J1A2MHz总线频率和9600波特率这种经典组合原文方案采用了核心定时器溢出中断TOF作为时间基准结合循环延时来产生位周期。每个位周期tp对于9600波特率大约是104us。在2MHz下一个指令周期是0.5us这意味着一个位周期大约对应208个指令周期。软件需要在这个时间框架内完成输出电平、采样输入、检查错误、移位数据等一系列操作。三次采样抗噪声是提升鲁棒性的关键。如图3-2所示接收时在每个位周期的中点附近进行三次快速采样采样间隔ts由CPU频率决定如7个时钟周期。三次采样值取“多数表决”即三中取二以此作为该比特位的最终值。如果三次采样结果不一致则置起噪声标志NF。这个策略能有效滤除短时间的毛刺干扰。3.2 关键子程序PUT_byte 与 GET_byte整个软件SCI的核心是两个状态机发送状态机PUT_byte和接收状态机GET_byte。PUT_byte发送流程初始化从TX_SHIFT_REG加载待发送数据设置位计数器。发送起始位拉低TX线并维持一个位周期tp。在此期间调用GET_BIT子程序采样RX线进行噪声检测。发送数据位根据M标志决定8位或9位数据循环移位发送TX_SHIFT_REG的每一位LSB先发。每发送一位同样采样RX并检测噪声。发送停止位拉高TX线发送1或2个停止位。采样RX并检查停止位是否为高否则置起帧错误FE。结束与标志管理发送完成置位发送完成TC和发送数据寄存器空TDRE标志。如果接收缓冲器已满则置位超限OR标志。GET_byte接收流程起始位检测由IRQ中断服务程序调用。IRQ引脚与RX引脚连接的下降沿触发中断。在中断中延时到起始位中点进行三次采样。如果采样结果不是稳定的低电平即至少两次为高则判定为干扰退出中断。接收数据位成功检测到起始位后程序进入循环在每个后续位的中点进行三次采样将结果拼装到RX_SHIFT_REG。停止位与错误检查采样停止位若非高电平则置FE。检查接收缓冲器SW_SCDR是否已被读取若未读则置OR新数据会覆盖旧数据旧数据丢失。数据转移与标志设置将RX_SHIFT_REG的数据转移到SW_SCDR置位接收数据寄存器满RDRF标志。同时设置“最后访问LA”标志表示RX线最近忙过为后续IDLE检测提供条件。实操心得GET_byte是一个阻塞式例程一旦进入CPU会一直停留直到收完一整个字符。这意味着在此期间所有其他中断包括更高优先级的都会被屏蔽。因此必须确保GET_byte的执行时间10-11个位周期短于系统中任何关键事件的响应时限否则可能造成系统性问题。在低波特率如1200下这个问题尤为突出。3.3 错误检测机制的实现软件模拟需要复现硬件SCI的三种主要错误检测噪声错误NF如前所述通过每个位周期内的三次采样不一致来判定。帧错误FE在停止位采样点如果检测到逻辑0应为逻辑1则判定为帧错误。这通常意味着波特率不匹配或严重的线路干扰。超限错误OR软件实现了双缓冲机制。SW_SCDR是CPU可读的数据寄存器RX_SHIFT_REG是正在接收的移位寄存器。当RX_SHIFT_REG接收完一个字符准备向SW_SCDR转移时如果发现SW_SCDR的RDRF标志仍为1表示CPU还未读取上一个数据则发生超限新字符被丢弃OR标志置位。3.4 IDLE线状态检测IDLE状态定义为RX线上持续至少一个完整字符时间10或11个位周期的高电平。检测IDLE对于协议解析如判断一帧消息结束至关重要。由于GET_byte是阻塞的无法在接收字符的同时检测IDLE。原文的方案很巧妙利用核心定时器溢出中断TOF。将TOF中断周期设置为一个固定值如512us。在TOF中断服务程序中调用S_IDLE子程序。S_IDLE的工作逻辑检查LA标志。如果LA为0表示RX线最近空闲则直接退出。如果LA为1则采样RX线。若为高则递减一个计数器COUNT_IDLE。若为低则清零COUNT_IDLE和IDLE标志并清除LA标志因为检测到活动。当COUNT_IDLE递减到0时置位IDLE标志。COUNT_IDLE的初始值根据波特率计算使得从最后一次RX活动到判定为IDLE刚好超过一个字符时间。这个方案虽然不是实时检测但基于“主从轮询”通信模型从机只在被主机寻址后才回复在大部分情况下是足够且高效的。4. 与MISC协议栈的集成与适配4.1 MISC协议栈简介MISC是一种用于主从式网络的数据链路层协议。一条完整的MISC消息由主设备发送的Push字段和从设备回复的Pull字段组成中间由帧间间隔IFS分隔。每个字段都包含帧头、数据帧和校验和。这种结构要求从设备能够在接收完Push字段后迅速切换为发送模式回复Pull字段。4.2 软件SCI层LMAC的改造协议栈的低级介质访问控制层LMAC是设备相关的。要将基于硬件SCI的LMAC移植到软件SCI上主要修改点在于中断和状态的管理。关键点在于区分“接收字段”和“发送字段”两种状态因为这两种状态下对中断的需求是矛盾的接收状态等待/接收Push字段必须使能IRQ中断以便及时检测起始位。必须禁用TOF中断因为GET_byte是阻塞的如果在接收位流中间发生TOF中断会破坏精确定时导致接收错位。发送状态发送Pull字段必须禁用IRQ中断因为此时RX引脚的状态由PUT_byte例程主动采样控制不应被外部中断打断。必须使能TOF中断以便在发送间隙IFS期间能够运行S_IDLE例程检测总线是否恢复IDLE从而判断发送结束或总线异常。为此引入了LMAC_SEND状态标志并编写了相应的控制例程SEND_FIELD_ENA(): 准备发送Pull字段。禁用IRQ中断使能TOF中断设置LMAC_SEND1。REC_FIELD_ENA(): 准备接收Push字段。使能IRQ中断禁用TOF中断清除LMAC_SEND0。LMAC_TOF_OFF(): 当从设备检测到IDLE表示主机Push字段发送完毕后在等待切换为发送模式的短暂间隙仅保持IRQ使能禁用TOF中断。4.3 中断服务例程的协同整个系统的中断协作构成了一个精密的时序链条如图7-1所示IRQ中断专用于帧接收。触发后调用GET_byte接收完成后根据SCI状态标志RDRF, NF等决定是否向上层UMAC传递数据。RTI实时中断用于协议栈的定时服务如超时管理。在RTI中断中可以进行数据链路层DL的服务以及用户自定义的定时任务。TOF定时器溢出中断专用于IDLE检测。仅在LMAC_SEND1从机发送态或总线空闲期被使能在其中调用S_IDLE例程。这种分工确保了每个中断源职责单一避免了复杂的嵌套和优先级冲突在资源有限的MCU上是一种稳健的设计。5. 实际移植与调试中的关键问题5.1 波特率容错与时钟校准软件SCI的波特率精度完全依赖于CPU的时钟精度和延时循环的准确性。即使使用定时器如果系统时钟源如内部RC振荡器偏差较大也会导致通信失败。计算与校准步骤确定指令周期例如HC05J1A在2MHz总线频率下一个机器周期为0.5us。计算位周期指令数9600波特率的位周期tp 1/9600 ≈ 104.17us。对应指令数N 104.17us / 0.5us ≈ 208.33。取整为208个周期会产生约0.16%的误差在允许范围内。编写精确延时循环用汇编或内联汇编编写一个消耗确定周期数的循环。需要考虑PUT_byte和GET_byte中除了延时外其他指令如采样、移位、判断的执行时间并从总周期数中扣除。实际测试与微调使用逻辑分析仪或示波器观察实际输出的波形测量位宽。如果偏差超过2-3%可能需要调整延时循环的计数值或者考虑使用更精确的外部晶振。5.2 中断延迟与临界区保护中断延迟是软件模拟串口的“隐形杀手”。GET_byte例程从IRQ触发到开始采样起始位中点这之间的时间中断响应时间现场保护时间判断时间必须稳定且小于半个位周期。否则采样点就会偏离位中心导致容错性下降甚至误码。应对策略优化中断服务程序ISR使用汇编编写最关键的入口部分尽可能减少现场保存的寄存器数量。避免在关键通信期间处理高耗时中断如果系统中有其他中断如ADC需要评估其最坏执行时间确保不会在GET_byte执行期间发生。临界区操作PUT_byte和GET_byte在操作共享的软件SCI寄存器如SW_SCDR和状态标志时需要短暂关闭中断以防止数据被异步访问破坏。5.3 资源消耗评估软件SCI会消耗两项关键资源CPU时间和内存。CPU占用率在9600波特率下接收或发送一个字节10位大约需要1.04ms。在这1ms内CPU被GET_byte或PUT_byte完全占用。这意味着系统的最大中断响应时间会被拉长约1ms对于有实时性要求的任务需要仔细评估。内存占用除了代码空间还需要为软件SCI寄存器、双缓冲移位寄存器、状态变量和计数器分配RAM。对于只有几十字节RAM的MCU这部分开销需要精打细算。下表对比了硬件SCI与软件模拟的主要区别特性硬件SCI软件模拟SCICPU占用极低仅数据搬运高通信期间CPU被独占时序精度由硬件时钟保证高依赖软件延时和时钟精度较低错误检测硬件自动完成需软件实现增加代码复杂度全双工原生支持需特定拓扑如回环实现“虚拟全双工”开发难度低配置寄存器即可高需深入理解时序和中断成本需要MCU具备该硬件模块仅需两个GPIOMCU成本最低灵活性固定受硬件限制高可灵活调整波特率、数据位等5.4 常见问题与排查技巧在实际调试中你可能会遇到以下问题完全收不到数据检查电平首先用万用表或示波器确认TX引脚是否有波形输出电压是否符合逻辑电平如0V和3.3V/5V。检查起始位用示波器单次触发模式抓取起始位的下降沿。看下降沿是否清晰以及MCU的IRQ引脚是否成功检测到并触发了中断。检查中断向量确认IRQ中断服务程序的入口地址是否正确写入MCU的中断向量表。检查全局中断使能确认主程序中是否开启了全局中断。收到乱码或数据错位测量波特率用示波器测量发送的一个完整字节如0x55即01010101的周期计算实际波特率是否与预设值匹配。误差应控制在2%以内。检查采样点在GET_byte的采样点处设置一个GPIO翻转用示波器另一通道观察看这个脉冲是否稳定出现在每个比特位的正中央。检查噪声如果NF标志频繁置位检查硬件线路是否有过长、未屏蔽的走线电源是否干净考虑在RX引脚增加一个小电容如10-100pF到地滤波。通信不稳定时好时坏检查电源在MCU电源引脚处用示波器观察是否有大的毛刺或跌落。软件模拟非常消耗电流GPIO频繁翻转可能引起电源噪声。检查中断冲突确认在GET_byte执行期间是否有其他中断发生。可以通过暂时禁用其他所有中断来测试。检查状态机逻辑特别是LMAC_SEND标志的切换逻辑是否在正确的时间点调用了SEND_FIELD_ENA和REC_FIELD_ENA。逻辑错误会导致中断配置混乱。无法进入IDLE状态检查LA标志IDLE检测依赖于LA标志。确保在成功接收一字节后LA标志被正确置位在S_IDLE检测到RX活动后LA被正确清除。检查TOF中断确认TOF中断是否在需要的时候发送态被正确使能中断服务程序是否调用了S_IDLE。检查COUNT_IDLE计算根据系统时钟和波特率重新核算COUNT_IDLE的初始值确保其对应的等待时间略大于一个字符时间。6. 总结与扩展思考将这份二十年前的方案移植到现代的ARM Cortex-M0甚至RISC-V这类低成本MCU上核心思想依然完全适用甚至更容易实现。现代MCU的时钟更精准中断响应更快性能也更强可以轻松支持更高的波特率如115200。你可以用其中一个通用定时器如TIM来产生精确的位周期中断在中断服务程序里进行位的输出和采样这样能极大地解放CPU实现更接近硬件的“后台”通信。软件模拟外设的本质是在时间、资源和功能之间做权衡。它用CPU的计算时间换取了硬件成本用代码的复杂性换取了设计的灵活性。这个“低成本MCU软件SCI实现与MISC协议栈应用”的方案不仅仅是一套可运行的代码更展示了一种在资源极端受限环境下进行系统设计的思维方式如何通过精准的时序控制、巧妙的中断管理和状态机设计在软件的层面上“铸造”出一个可靠的硬件功能。当你下次面对一颗“要啥没啥”的MCU时希望这份来自旧时光的工程智慧能给你带来一些解决问题的底气和新思路。

相关新闻