简介这份资源面向正在学习STM32单片机、需要打通双板串口通信的嵌入式初学者与进阶开发者基于正点原子mini板与精英板搭建了一套可实际运行的USART通信方案mini板经USART3PB10、PB11发送数据精英板经USART3接收并通过USART1CH340下载口打印到串口调试助手同时支持LCD屏实时显示接收内容接收成功时LED10翻转、发送成功时LED1翻转便于直观验证通信状态。资源包共321个文件、约7.35MB以69个h头文件、66个c源文件为核心配合Keil工程文件uvprojx、uvoptx、编译产物o、axf、hex、crf及少量说明文档工程结构完整、代码调试可用。目前已有4418人学习下载。读者可据此掌握双板串口收发配置、中断与轮询处理、串口打印调试及LCD显示联动等关键环节并借助LED翻转快速定位收发是否成功对缺少mini板或LCD屏的情况也预留了替代与裁剪思路适合作为串口通信实验的参考工程。1. 两块板子怎么“说话”STM32 串口通信到底是怎么回事刚摸到单片机的新手最容易卡在一个特别具体的场景里手里有两块 STM32 开发板想让它们互相传个数据结果线接好了、程序也烧进去了串口助手却一个字都不显示。这时候网上一搜“stm32 串口通信”出来的全是寄存器、波特率、中断、DMA越看越觉得自己不适合干这行。其实串口通信没那么玄乎。你可以把它想成两个人隔着一条走廊喊话双方得先说好语速波特率得约定一句话说几个字数据位还得知道什么时候算一句话说完停止位。STM32 的串口外设干的就是把你要发的字节按这套约定一位一位推出去再把对方推过来的位拼回字节。它解决的是“两块芯片之间怎么低成本、少接线地交换数据”这个问题适合所有刚接触嵌入式、要做板间通信、要接传感器或调试输出的新手。2. 为什么新手第一课就该学串口它是最便宜的“对话通道”2.1 串口是嵌入式的“普通话”不会它寸步难行学 STM32 的人常有个误区觉得串口太基础不如直接上 SPI、I2C、CAN 显得高级。但真实情况是串口几乎是所有调试手段的底座。你程序跑飞了第一反应是打印一行日志出来看看这行日志走的就是串口你买了个温湿度模块说明书上写的通信方式大概率还是串口你要把板子上的数据传到电脑上存起来最省事的还是串口。我第一次学的时候导师只说了一句话先把串口调通你才有资格说自己在“调试”否则你只是在“猜”。因为串口是唯一一个你能亲眼看到字节流动的通道发送什么、收到什么串口助手上一目了然。其他总线要么速度太快抓不到要么需要逻辑分析仪对零基础的人来说门槛高得多。所以入门路径上串口不是可选项是必经的第一站。从资源角度看串口通信的硬件成本也最低。两根线TX 接对方 RXRX 接对方 TX再共地就能通信。不需要额外的时钟线不需要上拉电阻不需要复杂的地址分配。对新手来说接线越少翻车点越少你才能把注意力放在“数据怎么发、怎么收”这件事本身而不是先跟硬件打架。2.2 先搞懂三个参数比背寄存器有用得多新手看串口代码最容易被一堆初始化结构体吓住。其实你只需要先记住三个参数剩下的都是围绕它们转的。第一个是波特率单位是 bps意思是每秒传多少位。常见的有 9600、115200。它就像两个人说话的语速你说太快对方听不清说太慢又浪费时间。双方必须设成一样的值差一点都会变成乱码。第二个是数据位通常是 8 位也就是一个字节。第三个是停止位通常是 1 位用来告诉对方“这个字节说完了”。还有一个校验位新手阶段可以先设成无校验。这四个东西合起来叫“帧格式”常见写法是 115200-8-N-1翻译过来就是每秒 115200 位、8 个数据位、无校验、1 个停止位。你在串口助手里看到的那些下拉框选的就是这套东西。只要两边帧格式一致通信就成功了一半。提示新手最常犯的错不是代码写错而是串口助手那边波特率选错了。程序里写 115200助手里选 9600屏幕上就是一堆问号和方块别急着怀疑人生。2.3 第一个能跑的例子把一句话从板子发到电脑下面这段是轮询方式发送字符串的最小例子用的是 STM32 标准外设库的写法思路重点看每一步在干什么不用纠结具体型号的寄存器名。// 串口初始化以 USART1 为例PA9 为 TXPA10 为 RX void UART1_Init(void) { // 1. 开时钟串口和对应 GPIO 端口的时钟都要开 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); // 2. 配置 TX 引脚为复用推挽输出数据才能从芯片推出去 GPIO_InitTypeDef gpio; gpio.GPIO_Pin GPIO_Pin_9; gpio.GPIO_Mode GPIO_Mode_AF_PP; gpio.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, gpio); // 3. 配置 RX 引脚为浮空输入用来接收对方发来的电平 gpio.GPIO_Pin GPIO_Pin_10; gpio.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, gpio); // 4. 串口核心参数115200-8-N-1收发模式都打开 USART_InitTypeDef uart; uart.USART_BaudRate 115200; uart.USART_WordLength USART_WordLength_8b; uart.USART_StopBits USART_StopBits_1; uart.USART_Parity USART_Parity_No; uart.USART_Mode USART_Mode_Rx | USART_Mode_Tx; uart.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_Init(USART1, uart); // 5. 最后使能串口前面配的参数才真正生效 USART_Cmd(USART1, ENABLE); } // 发送一个字节等发送寄存器空再写数据 void UART1_SendByte(uint8_t data) { while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); USART_SendData(USART1, data); } // 发送字符串逐个字节发出去 void UART1_SendString(char *str) { while (*str ! \0) { UART1_SendByte((uint8_t)*str); str; } }这段代码的逻辑很直白先给串口和引脚“通电”开时钟再告诉引脚该干什么活TX 输出、RX 输入然后约定好说话规则115200-8-N-1最后打开开关。发送部分是一个字节一个字节地等、一个字节一个字节地写TXE这个标志位的意思是“发送寄存器空了可以塞下一个了”不等到它置位就写数据会丢。参数上你只需要改两处就能适配自己的板子一是波特率电脑端串口助手要跟它保持一致二是引脚号不同型号的 STM32 串口对应的引脚不一样查一下自己板子的原理图别照着抄。烧进去之后打开串口助手选对 COM 口和 115200就能看到板子发来的字符串了。第一次看到屏幕上跳出自己写的那行字那种感觉比点亮 LED 还实在。3. 两块板子互发数据从“能发”到“能收”3.1 接线别接反TX 对 RX 是血泪经验单板发数据到电脑你只需要接 TX、RX 和 GND 三根线到 USB 转串口模块。但一旦变成两块 STM32 互连接线就成了第一个翻车点。记住一句话一块板子的 TX 接另一块板子的 RXRX 接对方的 TXGND 必须共地。为什么不能 TX 接 TX因为 TX 是“说”的引脚两个都在说没人听数据自然对不上。GND 不共地更隐蔽两块板子各自的地电位不一样电平判断就会出错表现是时好时坏、偶尔收到乱码这种玄学问题最折磨人。我第一次做双板通信就是忘了共地调了一下午最后拿万用表一量才发现两块板子的地根本没连上。接线顺序建议是先断电接线接完检查一遍再上电。TX 和 RX 交叉、GND 相连这两条确认了硬件层面就八九不离十。剩下的问题基本都在软件配置上排查范围一下就缩小了。3.2 接收端怎么写轮询收一个字节发送会了接收其实是对称的。下面这段是轮询方式接收一个字节配合上面的初始化直接用。// 接收一个字节返回 1 表示收到0 表示超时没收到 uint8_t UART1_ReceiveByte(uint8_t *data, uint32_t timeout) { // 等接收寄存器非空说明有数据到了 while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) RESET) { if (timeout-- 0) { return 0; // 等太久认为对方没发直接返回 } } // 把收到的字节读出来读操作会自动清标志位 *data USART_ReceiveData(USART1); return 1; }RXNE这个标志位的意思是“接收寄存器里有新数据了”。轮询就是死等它置位等到了就把数据读走。timeout参数是给死等加个保险不然对方一直不发程序就卡死在这里别的活都干不了。这个写法适合入门理解流程但真实项目里一般不会这么干因为主循环会被拖住。两块板子联调时建议先让 A 板只发、B 板只收B 板收到后通过它自己的串口转发到电脑上打印出来。这样你能同时看到“发了什么”和“收到了什么”对不上就说明中间某一环有问题。等单向通了再改成双向互发难度会低很多。3.3 中断方式别让等数据把主程序卡死轮询的毛病很明显等数据的时候CPU 什么也干不了。中断就是来解决这个的——数据到了硬件自动打断主程序跳去执行接收函数处理完再回来。对新手来说中断的概念比轮询绕一点但用起来其实更省心。// 串口1中断服务函数名字要和启动文件里的一致 void USART1_IRQHandler(void) { // 判断是不是“收到数据”触发的中断 if (USART_GetITStatus(USART1, USART_IT_RXNE) ! RESET) { uint8_t data USART_ReceiveData(USART1); // 读走数据 // 这里可以做简单处理比如回显或者存进缓冲区 UART1_SendByte(data); // 收到什么原样发回去方便验证 USART_ClearITPendingBit(USART1, USART_IT_RXNE); // 清中断标志 } }用中断之前初始化里要多做两步一是打开串口的接收中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)二是在 NVIC 里给串口中断分配优先级并使能。中断函数里不要写耗时操作读走数据、做个标记就够了复杂的处理丢回主循环去做。这是新手容易忽略的规矩中断里拖太久整个系统的实时性都会受影响。4. 新手最容易踩的五个坑现象、原因、解决4.1 串口助手一片乱码现象板子确实在发但电脑上显示的是问号、方块或者一堆看不懂的符号。原因九成是波特率不一致程序里设的 115200助手里选的 9600或者反过来。解决先核对两边波特率再核对数据位、停止位、校验位是否都是 8-N-1。如果参数全对还是乱码检查一下晶振配置时钟不对会导致实际波特率偏移。4.2 一个字节都收不到现象发送端程序在跑接收端毫无反应。原因通常是接线问题TX/RX 没交叉、GND 没共地或者引脚选错了。解决拿万用表量一下 TX 引脚在发送时有没有电平变化没有就说明引脚配置错了有变化但对方收不到重点查交叉和共地。还有一种情况是接收端没使能串口USART_Cmd忘了调用。4.3 收到第一个字节后就卡死现象能收到数据但只收到一个就再也不动了。原因多半是中断标志没清硬件一直认为“还有中断没处理”反复进中断或者干脆卡住。解决确认中断服务函数里读了数据寄存器并且清了对应的中断标志位。读USART_ReceiveData本身会清RXNE但如果你用的是别的标志就得手动清。4.4 数据偶尔丢一两个字节现象大部分数据对但隔一段时间少一个字节。原因常见于轮询方式下主循环太忙数据到了没及时读走被下一个字节覆盖了。解决要么改用中断接收要么提高读取频率要么加一个接收缓冲区先把数据存起来。串口速度不快的时候问题不明显一旦波特率上到 115200 以上轮询丢字节的概率会明显上升。4.5 两块板子单独都正常连起来就不行现象各自跟电脑通信都没问题一互连就出问题。原因往往是电平或共地问题也可能是两边帧格式没统一。解决先确认两块板子的 GND 确实连在一起再确认两边波特率、数据位完全一致。如果还不行把其中一块的 TX 断开只做单向通信排除双向同时发导致的冲突。5. 下一步学什么从会发字节到会传数据串口调通之后你其实已经站在嵌入式通信的门口了。接下来最该补的不是更多外设而是“怎么把一串字节变成有意义的数据”。因为真实场景里你发的不会是一个字符而是一帧一帧的数据包里面可能有帧头、长度、内容、校验。这就引出了两个方向一是协议设计二是接收缓冲。协议设计入门可以先从最简单的做起约定一个帧头比如 0xAA后面跟数据长度再跟数据本身最后加一个校验字节。接收端先找帧头再按长度收最后校验对上了才算一帧有效数据。这套思路不复杂但能让你从“收到什么打印什么”升级到“收到一帧完整数据再处理”。我第一次写这种解析的时候没加帧头结果数据里恰好出现和长度一样的字节整个包就错位了从那以后我每次设计帧格式都强制先定帧头和校验这个习惯救了我很多次。接收缓冲则建议了解一下环形缓冲区。中断里只管往缓冲区塞数据主循环慢慢从里面取两边互不干扰。这是串口接收的标准做法代码不长但能彻底解决丢字节的问题。学完这两个你再去看 DMA 串口收发会发现它只是把“搬运数据”这件事交给了硬件思路是一脉相承的。学习路径上我的建议是先把轮询收发跑通再换中断然后加环形缓冲最后碰 DMA。每一步都只改一个变量出了问题才知道是哪一步引入的。别一上来就抄一份带 DMA 和协议解析的完整工程那样跑通了也不知道为什么通跑不通更不知道从哪查。如果你手里还没有一份能直接对照的串口通信代码和接线说明这份资源就是按“先单板发、再双板收、最后加中断”的顺序组织的拿来跟着敲一遍比只看文章快得多。入门阶段最怕的不是学得慢是一直不敢动手。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?