新闻详情

新闻详情

首页 / 资讯中心 / 详情

UART串口通信详解:从异步原理到STM32实战与故障排查

发布时间:2026/10/2 12:19:36来源:尧图网络
UART串口通信详解:从异步原理到STM32实战与故障排查
如果你接触过单片机或者嵌入式开发不管做的是哪一块几乎都绕不开UART。十个开发板里有九个都留了串口而串口背后最常见的通信协议就是UART全称Universal Asynchronous Receiver/Transmitter通用异步收发传输器。它可能是你人生中第一个真正打交道的通信协议也是后面理解I2C、SPI、CAN、RS485这些串行协议的重要地基。这篇作为通信协议系列的第一篇我们就把UART从原理到时序、从驱动到实战一次理清适合刚入门的朋友照着走也适合有经验的人回来看细节。1. UART到底是什么先弄懂“异步”和“收发器”1.1 为什么叫异步没有时钟线也能同步UART这个名字拆开看关键就在“Asynchronous”和“Transceiver”。很多新手第一次听到“异步通信”会懵没有时钟线收发双方怎么知道每个bit从哪开始、在哪结束难道靠猜吗事实是UART确实不用单独的时钟线它约定的是速率和帧格式。发送方按照预先设定好的波特率把每个bit依次放到数据线上接收方同样按照这个波特率不停地采样数据线。为了让双方在某一帧上对齐每一帧开头都有一个起始位这个起始位是一个由高到低的跳变相当于喊了一声“注意我要开始发了”。接收方检测到这个下降沿后就按波特率推算出的bit间隔在每个bit的中间位置去采样。这样一来只要两边波特率误差不太大整个帧的数据就能正确解出来。“异步”指的不是随便乱发而是“没有外部共享时钟靠帧结构自行同步”。与此相对的是SPI、I2C这类带SCLK/SCL时钟线的同步通信。这一点是理解UART的根基很多人把UART调不通其实都是没有真正理解这个同步机制。1.2 一对线就能通信TX/RX交叉相连UART最基础的模式是点对点、全双工通信物理上至少需要两根数据线发送端TX和接收端RX。设备A的TX要接设备B的RX设备A的RX要接设备B的TX也就是交叉连接。名字也很直白一根线是人家A.TX发给你B.RX另一根线是B.TX发给你A.RX。想反了或者看到模块上的TXD就一头怼上去是新手最常见的问题之一。除了TX和RX还要把两边的GND连在一起。这个问题很多人会忽略尤其是用USB转串口模块调试时就插了一根USB线以为供电和地都有了但实际上模块的GND和MCU板的GND不是同一个电位。如果两边地的电位差太大接收端看到的信号电平就会乱轻则乱码重则烧引脚。我在调试时吃过一次大亏两块板子都各自用USB供电结果TTL电平判断始终不对最后把GND飞线连上一切恢复正常。地线不是可有可无的附件它是信号回流的参考路径。如果只是单方向传数据理论上可以只接一根TX到RX再加GND但实际工程里几乎都会把两根线都接上方便双向交互。1.3 帧格式起始位、数据位、校验位、停止位UART一帧数据由四部分组成起始位、数据位、校验位可选、停止位。最常见的是“8-N-1”也就是8个数据位、无校验、1个停止位。这个配置在串口助手、CubeMX、各种芯片手册里都是默认值大多数场景我们照用就行但你要知道它是什么意思。线路空闲时保持高电平。发送方要发一帧时先拉低电平并维持一个bit周期这就是起始位。接收方看到这个从高到低的边沿就知道帧开始了。接着是数据位默认低位在前也就是LSB first。比如你发0x55这个字节二进制是01010101先从最低位D0开始发依次是1、0、1、0、1、0、1、0。这个细节在抓波形时尤其重要我见过有人拿示波器测串口数据按MSB first去解结果怎么都对不上。校验位可以选无、奇校验或偶校验。奇校验要求数据位加校验位里“1”的个数是奇数偶校验则要求是偶数。校验位只能检测奇数位翻转不能纠错更不能防错现在很多协议已经改用CRC或者累加和做校验。停止位表示一帧结束常见有1位、1.5位、2位。停止位保持高电平一方面给接收方一点处理时间另一方面也作为下一帧起始位的“空闲”参考。配置串口时波特率、数据位、校验位、停止位这四个参数必须收发双方完全一致否则就是乱码或不收。这里的“一致”不是软件里看着一致就行你可以用自己的逻辑分析仪去抓一帧数据数一数实际的bit位宽和理论值对比这才是最可靠的证据。2. 核心细节拆解时序、波特率误差与缓冲2.1 一帧数据在示波器上长什么样理解UART时序最好的方式就是用逻辑分析仪或示波器抓一帧数据。下面是一个简单的波形文本示意空闲(高) 起始(低) D0 D1 D2 D3 D4 D5 D6 D7 停止(高) 1 0 0/1 0/1 0/1 0/1 0/1 0/1 0/1 0/1 1空闲是高电平起始位拉低然后8个数据位依次输出最后恢复高电平。如果你发送0x55数据位依次是1、0、1、0、1、0、1、0波形就是方方正正的占空比50%的交替脉冲非常漂亮所以0x55经常被用来做串口自测0xAA则是完全相反的一组交替电平。两者都是调试时的经典测试字节。接收端不是简单地在每个bit周期内随便采一次。为了保证抗干扰能力UART接收器通常会用更高频率的时钟去过采样一般是16倍波特率。什么意思呢比如波特率是9600bps一个bit的时间约104.17us接收端会在每个bit周期内采样16次然后取中间三次的值做多数判决或者直接取中间位置的电平。这样做的好处是即使起始位检测有一点边沿抖动也不至于采错数据。STM32的USART外设支持过采样16倍或8倍由寄存器OVERSAMPLE位控制。8倍过采样可以达到更高波特率但采样窗口稍窄16倍更稳。日常115200及以下建议保持默认16倍。2.2 波特率误差怎么算为什么115200那么流行波特率就是每秒传输的bit数量单位bps。115200bps意味着每个bit约8.68us9600bps则是104.17us。不要小看这个计算很多问题都出在波特率不匹配上。UART的波特率来自芯片时钟的分频不能随便生成任意值。以STM32为例波特率计算公式大致是BRR分频值 PCLK / (16 * 目标波特率)。如果PCLK是72MHz目标是115200算出来是39.0625寄存器只能是整数写入39后实际波特率大约是72000000 / (16 * 39) ≈ 115384bps误差约0.16%完全没问题。但如果PCLK是36MHz写入39实际波特率就只剩57692误差高达0.16%其实不对这里只是举例说明时钟频率、预分频、目标波特率组合不同误差也不同。误差容限通常建议控制在2%以内。UART接收端有16倍过采样冗余理论上容忍范围大概是±3%到±4%但这是理想情况。更怕的是两端误差方向相反发送方时钟偏快1.5%接收方时钟偏慢1.5%相对误差就接近3%可能刚好采到bit边界出现随机乱码。115200这个速率之所以流行是因为它基于1.8432MHz晶振除以16整数次得到PC和USB转串口芯片都很容易精确做到。而很多MCU内部时钟是8MHz、72MHz这种非整数关系生成115200时会有少量误差。实测中只要误差在1%以内通信就是稳的。你可以在同一块板子上把波特率设成115200和57600对比看波形数一下每个bit位宽心里就有数了。2.3 16550标准与FIFOUART的家底PC时代的COM口就基于8250/16550 UART芯片其中16550是行业标准一直影响到现在。16550相比早期8250最大的改进是内置了16字节的发送和接收FIFO。为什么需要FIFO因为早期UART没有缓冲CPU必须在一个字符到达后立刻读走否则下一个字符到来就会覆盖导致丢数据。波特率一高中断频率也高CPU根本伺候不过来。有了FIFO之后接收到的数据会先暂存在FIFO里等攒到一定数量再触发中断比如设置成触发阈值14字节CPU一次中断就能读走14个字符大大降低了中断频率。很多USB转串口芯片比如FT232R、FT231X在PC端都被识别成标准COM口内部模拟的寄存器行为就是16550这一套。所以你在设备管理器里看到的COM口属性、FIFO缓冲区设置其历史渊源就在这儿。理解FIFO对单片机的UART调试也有帮助。现代MCU的USART外设虽然寄存器不叫16550但思路类似有的有硬件FIFO有的没有没有FIFO时就必须靠DMA或高频中断来保证不丢字节。STM32F103这类芯片的UART没有独立大FIFO数据寄存器一次只能装一个字节但配合DMA就能实现类似FIFO的批量搬运效果。这也是为什么很多老工程师一提到串口就强调“用DMA不要傻等”。2.4 USB转串口芯片与驱动FT232R/FT231X避坑现在嵌入式调试基本离不开USB转串口。最流行的芯片无非就是FT232R、FT231X、CH340、CP2102这几类。FT232R是老牌经典FT231X是它的升级版支持更高的传输速率、更低的功耗内部还带了可编程的EEPROM可以配置VID、PID、序列号等信息。对普通使用者来说两者都很好用Windows通常会把它识别成“USB Serial Port”分配一个COM口号。但驱动安装还是有一点坑。Windows 10/11多数情况下会自动安装驱动可有时候插入后设备管理器显示黄色感叹号或者显示“未知USB设备”。如果遇到这种情况建议直接去芯片原厂官网下载对应驱动而不是随便用第三方驱动精灵之类的工具。安装完驱动后设备管理器里的端口列表会出现比如“COM7”这样的编号这个编号就是后面串口工具要选的口。拿到USB转串口模块后第一步建议做回环测试把模块的TX和RX用一根杜邦线直接短接然后打开任意串口调试助手选择对应的COM口发送一串字符。如果正常你会在接收区看到同样的字符说明USB转串口芯片、驱动以及串口助手软件整条链路都是好的。这个测试排除了模块本身的问题再接到单片机板上时如果失败问题就在接线或MCU侧配置排查范围一下子缩小了一半。有一点要注意USB转串口芯片提供的是TTL电平不是RS232电平。老式PC的DB9串口是RS232电平正负12V左右绝不能直接连STM32的UART引脚。市面上有些模块上面直接写着RS232那就需要电平转换电路。另外USB转串口芯片有缓冲和延迟在高波特率下可能出现连续大数据发送时PC端延迟不一致的现象这属于正常现象调试用可以做严格实时通信要另想办法。3. STM32 HAL库实战从初始化到收发3.1 CubeMX配置先配时钟再配串口如果你用STM32最稳妥的方式是先用STM32CubeMX生成工程。新建工程时在RCC里选好外部晶振HSE在SYS里选好Debug方式然后在左侧找到USART1勾选Asynchronous异步模式。接着在Configuration面板里设置参数波特率115200Word Length 8位Parity NoneStop Bits 1。这些参数对应8-N-1。生成代码后你会看到初始化函数里有一堆引脚复用配置比如把PA9设成USART1_TX的复用推挽输出PA10设成USART1_RX的复用输入。GPIO为什么要设成复用模式因为引脚不只是简单的高/低电平而是要交给内部USART外设控制。如果这里配错了串口大概率发不出数据。还要注意时钟树。USART1在F1系列里挂在APB2总线上最高72MHzUSART2、USART3挂在APB1上最高36MHz。CubeMX的时钟树会帮你算但前提是你先把系统时钟配置对。很多人习惯直接生成默认工程没选外部晶振结果单片机跑在内部8MHz HSIC上然后USART1的波特率计算基准也变了115200实际变成了其他值表现出来就是乱码。所以顺序一定是先配好主时钟再配串口不要反过来。3.2 阻塞、中断与DMA三种收发方式怎么选HAL库提供了三种串口收发方式阻塞、中断、DMA。新手最容易上手的是阻塞轮询模式。发送用HAL_UART_Transmit接收用HAL_UART_Receive都带一个超时时间参数。比如uint8_t msg[] UART test\r\n; HAL_UART_Transmit(huart1, msg, strlen((char*)msg), 100);这里100表示最多等100ms超时返回错误。接收同理但你要特别注意如果在主循环里用HAL_MAX_DELAY调用阻塞接收程序会一直卡在那等数据导致其他任务无法执行。所以阻塞接收更适合用在简单的、只需要等待一帧数据的场景或者用于上电自检。真正常用的是中断方式。典型写法uint8_t rx_data; HAL_UART_Receive_IT(huart1, rx_data, 1);这行代码的意思是启动一次中断接收只接收1个字节收完后进入回调函数void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart-Instance USART1) { rx_buffer[rx_index] rx_data; HAL_UART_Receive_IT(huart1, rx_data, 1); // 必须再次使能 } }很多人踩过一个坑开启一次中断接收收完第一个字节之后进了一次回调然后没有再次调用HAL_UART_Receive_IT导致后面所有数据都进不了中断表现就是“只能收到一个字节后面全没了”。所以中断接收的常见模式是在回调里重新使能下一次接收。DMA方式适合大批量数据。HAL_UART_Receive_DMA可以配合串口空闲中断实现不定长接收这是目前比较推荐的工程做法数据先经过DMA自动搬进缓冲区CPU只在每次空闲中断时去处理一段完整数据负荷很低。但DMA配置涉及BufferSize、循环模式、空闲中断选择对新手来说门槛高一些。我的建议是先把阻塞和中断玩熟再上DMA。3.3 自己组一个协议帧从“能跑”到“好用”裸的UART只是字节流传输它不知道什么是“一包数据”。如果你的程序只是收发一个个字符串还能凑合但到了实际项目比如传感器上报、上位机下发指令一定要定义自己的应用层协议帧。协议帧解决三个问题边界、校验、语义。一个简单但够用的帧格式可以这样帧头0xA5 0x5A接着是长度、命令字、数据区、校验和。比如| 帧头(2B) | 长度(1B) | 命令(1B) | 数据(N) | 校验和(1B) | | A5 5A | N2 | CMD | ... | SUM |解析时不要依赖串口“一包一次完整到达”因为UART可能拆包也可能粘包。稳妥做法是用状态机逐字节处理uint8_t rx_state 0; uint8_t pkt[64]; uint8_t pkt_len 0; uint8_t pkt_index 0; void process_rx_byte(uint8_t d) { switch (rx_state) { case 0: if (d 0xA5) rx_state 1; else rx_state 0; break; case 1: if (d 0x5A) rx_state 2; else rx_state 0; break; case 2: pkt_len d; pkt_index 0; rx_state 3; break; case 3: pkt[pkt_index] d; if (pkt_index pkt_len - 1) { // 这里做校验和校验通过后处理一帧 rx_state 0; } break; default: rx_state 0; break; } }在中断回调或DMA空闲中断里每收到一个字节就把这个字节丢给状态机。状态机的好处是无论数据分成多少段到达都能正确处理。另外强烈建议用环形缓冲区。中断把收到的字节按顺序放进去主循环或者解析任务从缓冲区另一头取字节解析两边互不阻塞。如果不用环形缓冲区只是一段固定数组加索引很容易因为处理速度跟不上而丢数据。4. 串口通信常见问题与排查实录4.1 乱码先怀疑波特率再怀疑接地乱码是最最常见的现象原因有一大半出在波特率不匹配上。你设备端设置115200串口助手却选了9600打印出来的必然是乱码。先花十秒钟把两边参数核对一遍波特率、数据位、校验位、停止位必须完全一致。如果参数都一致还是乱码那就是实际波特率和理论值有偏差或者地线没连好。我调试时会先发一个0x55然后用逻辑分析仪抓波形数一下每个bit宽度。如果实际宽度和目标波特率不符合说明MCU的时钟或寄存器配置有问题如果波形是正确的但PC串口助手显示乱码那问题多半在USB转串口侧换个驱动版本或换一个USB口试试。还有一部分乱码来自电平不对。比如你把一个5V的单片机出来的UART电平直接接到了3.3V的USB转串口模块上若不共地或电平不兼容接收端看到的很可能不是干净的高/低电平。轻则乱码重则损坏引脚。4.2 发不出、收不到排查接线和引脚复用如果完全没数据第一步先做回环测试。把USB转串口的TX、RX短接串口助手自发自收。回环正常说明模块和驱动没问题。然后接上单片机用万用表确认单片机引脚上确实有波形输出再接上逻辑分析仪看接收端有没有波形。接线方面最常见的就是TX、RX没交叉。模块的TXD要接单片机的RX模块的RXD要接单片机的TX。另外GND必须共地。如果用的是老式RS232板子还要确认电平标准是否和MCU匹配。STM32这边还要检查GPIO复用模式是否已经配置成AF_PP。很多人在CubeMX里开了USART但后来又手动改过引脚模式导致串口外设不工作。另外别忘了使能串口的时钟和GPIO时钟。这些看起来基础但排错时一定要按顺序查一遍。4.3 偶发丢帧、卡死、数据错位偶发问题最折磨人。如果是高波特率下长时间跑着跑着丢一个字节优先怀疑接收缓冲不够。没有FIFO的MCU如果CPU没有及时读走数据寄存器下一个字节一到就会触发溢出错误数据就丢了。解决办法是开DMA或者把串口中断优先级调高一点。如果数据错位比如上位机收到的内容偶尔多几个字节或少几个字节很可能是没有做帧同步。直接把收到的原始字节当成完整帧处理遇到起始位不完整就会错。用状态机加帧头能解决绝大部分错位问题。还有一种情况是卡死。中断接收线程里如果不停地往缓冲区写而主循环来不及读缓冲区满了之后继续写就可能覆盖。我的习惯是把缓冲区开大一些并实现读索引和写索引互相追赶时不覆盖的逻辑。另外在HAL_UART_ErrorCallback里加上恢复处理比如重新初始化串口这样遇到溢出错误时不会死在那。4.4 长线传输、现场干扰和电平标准TTL电平的UART适合短距离一般1米以内比较稳妥超过几米就容易受线间电容和干扰影响。项目里如果设备距离超过5米就别再想着直接用TTL电平硬拉了换RS232或者RS485。RS232用正负电压传输抗干扰能力比TTL好一点但距离也就十几米。RS485用差分信号A、B两根线传输相反的电压抗共模干扰强传输距离可达几百米甚至上千米而且支持多节点挂在同一总线上。我看到很多工厂现场传感器和PLC之间用的就是RS485里面跑的底层数据仍然是UART格式RS485只是改变了电气层。如果现场地电位差较大比如两个设备分别在不同配电箱里供电直接共地容易形成地环路产生干扰甚至烧毁设备。这种情况下建议用带隔离的RS485收发器比如隔离电源加ADM2483这样的隔离芯片。不要为了省钱省麻烦把两个不共地的设备直接连TTL。4.5 常见问题速查表现象可能原因建议排查动作乱码波特率不一致、晶振时钟不准、地线没连核对配置用0x55抓波形共地完全无数据TX/RX接反、引脚复用错误、驱动未装好做回环测试检查引脚和USB转串口只能收不能发只接了RX没接TX、TX引脚损坏、发送代码没执行检查接线用示波器看MCU TX脚是否有波形偶发丢字节FIFO/缓冲区溢出、中断优先级低、DMA未配好开中断/DMA用环形缓冲区增大缓冲高速率下异常线材过长、电平标准不匹配、地环路降速测试改RS485加隔离5. UART和它的小伙伴们I2C、SPI、CAN、RS-485怎么选5.1 都是串行通信凭什么UART最简单只要是嵌入式开发就一定会碰到I2C和SPI还会有CAN、RS485等。很多人会问这些协议到底有什么区别其实它们从不同维度解决问题。UART最核心的特点是简单、全双工、点对点不需要时钟线也不需要地址仲裁两根线加一个GND就能跑起来。I2C是半双工两根线分别是SDA和SCLSCL提供时钟SDA传输数据靠设备地址区分谁在通信支持一主多从甚至多主。SPI是四根线MOSI、MISO、SCLK、CS全双工高速但本质是主从结构从机不能主动发起通信。CAN和RS485则更偏工业现场。CAN是差分两线多主通信带仲裁和错误检测车载和工业总线用得非常多RS485也是差分两线但协议层还是依靠UART的帧格式只是把TTL电平变成差分信号。这里顺便解开一个常见误区“485协议”并不是一种新的帧协议它只是UART的电气变换数据依然是起始位、数据位、停止位那一套。5.2 选型思路先看距离、速率、节点数做板内或者短距离调试UART是最省事的。接个USB转串口就能看日志这也是为什么串口被称为嵌入式开发的“生命线”。如果需要挂好几个低速外设比如温度传感器、EEPROM、RTC板内距离很短I2C很合适因为两根线就能挂多个地址不同的器件。SPI更适合高频读写场景比如Flash、TFT屏幕速率高、不需要地址但是占用引脚多、只能一主多从。如果设备之间距离远、现场干扰大、节点多那就得上RS485或CAN。RS485适合点对多点、半双工轮询式通信Modbus就是跑在RS485上的经典例子。CAN则适合多主并发、实时性要求高的场景比如车载ECU之间。UART本身不擅长多节点最多只能点对点虽然可以通过软件方式模拟多点但不稳定不建议。5.3 从TTL到RS485的一次实战改造有一年我在现场调试一个离主机二十米的传感器刚开始直接拉TTL线调试期间发现数据偶尔乱码线长了以后信号边沿变得很钝根本没法定时采样。后来把传感器板和主机板各加了一个MAX485转换电路A接A、B接B总线两端各并联一个120欧姆终端电阻通信一下子就稳定了。代码上其实没改多少底层还是UART只是需要在发送数据前把方向引脚拉高发完再拉低因为RS485默认是半双工不能同时收发。那次之后我就记住了距离和现场环境决定电气层而不是由单片机里跑的逻辑协议决定。UART作为数据链路层/协议层的一个小模块配合不同的收发器可以用来做RS232、RS485甚至光模块通信。这也是为什么把UART学透很重要它不是一个孤立的外设而是很多工业总线的基础承载方式。说了这么多最后再分享一个我自己的习惯新板子到手永远先写一个“print测试”把波特率、引脚、共地全部验证通过再接业务代码。串口调不通的时候先看波形再看代码逻辑分析仪比你的眼睛可靠得多。UART入门不难但每一个细节都可能成为项目里的隐性坑把这些基础踩实后面学I2C、SPI、CAN都会顺畅很多。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

英飞凌AURIX TC264开发入门:ADS环境搭建到点灯全流程解析 2026/10/2 13:07:56

英飞凌AURIX TC264开发入门:ADS环境搭建到点灯全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
MES生产产品追溯:6个硬性节点与数据贯通实战指南 2026/10/2 13:07:56

MES生产产品追溯:6个硬性节点与数据贯通实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
STM32用C++点灯入门:从启动文件到中断回调的实战指南 2026/10/2 13:07:56

STM32用C++点灯入门:从启动文件到中断回调的实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
海康威视HCNetSDK实战:C++实现局域网设备搜索 2026/10/2 13:07:56

海康威视HCNetSDK实战:C++实现局域网设备搜索

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Python实现B站充电视频下载器:接口解析与ffmpeg合并实战 2026/10/2 13:07:49

Python实现B站充电视频下载器:接口解析与ffmpeg合并实战

写这个B站充电视频下载器,起因其实挺直白的:我给几位UP主充过电,有几期充电专属视频确实质量高,想存一份离线看,但搜索一圈下来,不是让你注册来路不明的解析网站,就是让你装一堆看不懂的软件。作…

阅读更多 →
嵌入式裸机实战:从寄存器操作到工业级故障调试 2026/10/2 13:07:49

嵌入式裸机实战:从寄存器操作到工业级故障调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉