尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32G030F6P6入门实战:从CubeMX配置到PCB打样全流程

STM32G030F6P6入门实战:从CubeMX配置到PCB打样全流程 简介这是一份基于STM32G030F6P6单片机的系列教程资源包面向嵌入式初学者、毕业设计或课程设计学生以及需要快速搭建单片机项目的开发者。资料内含完整源码、工程文件与配套说明并附带嘉立创PCB制版文件方便读者从硬件设计到程序烧录完整复现一个可运行的项目。对于不熟悉PCB绘制的小白也可参考引脚定义改用面包板加杜邦线连接外部模块以更低的门槛完成硬件搭建。资源包整体约81.36MB内容已经过实际测试可正常运行适合直接借鉴或在此基础上扩展更多功能适用于项目开发、课程设计、期末作业、工程实训与大创竞赛等多种场景。目前已有514人学习下载作者长期深耕嵌入式领域学习或复刻过程中遇到问题可通过CSDN私信获得及时解答与帮助。1. STM32G030F6P6这颗料凭什么单独做一套系列教程很多新手选单片机第一反应是STM32F103C8T6毕竟资料多、例程多、问谁都能答两句。但真到了自己画板子、打样、调低功耗、看BOM成本的时候F103的劣势就出来了封装偏大、价格偏高、时钟树繁琐、新上手还得处理一堆兼容性历史包袱。而STM32G030F6P6是意法半导体在入门级市场主推的Cortex-M0内核芯片主频48MHzFlash 64KBSRAM 16KBLQFP32封装价格只有F103的几分之一资源却足够跑一个带串口、PWM、I2C、SPI和Modbus协议的小型设备固件。这篇系列教程要解决的不是“单片机能干什么”这种空泛问题而是把从选型、建工程、写外设驱动到嘉立创打样这一整条链路跑通。你会看到我实际怎么配置CubeMX、怎么写串口接收的状态机、怎么在立创EDA里检查封装和布线以及打样回来的板子怎么一步步排除硬件故障。适合正在做课程设计、毕业设计或者准备把产品原型做成可量产小板的嵌入式开发者。2. STM32G030F6P6的资源盘点不只是便宜是够用且顺手2.1 Cortex-M0内核的性能边界在哪里G030系列使用的Cortex-M0内核主频最高48MHz没有硬件乘法器之外的DSP指令也没有FPU。很多人一看到M0就担心性能不够实际上对于大多数传感采集、电机控制、协议转换、小屏显示这类应用48MHz完全够用。以Modbus RTU从站为例一条典型的读保持寄存器报文大约8个字节波特率9600下每秒处理几十帧MCU占用率连10%都不到。M0相比M3/M4最大的优势在于功耗和代码密度。Cortex-M0支持单周期IO翻转GPIO速度虽然标称到50MHz但实际做PWM或时序模拟时瓶颈往往是HAL库的函数调用开销。所以这套教程里凡是涉及时间敏感的操作我会直接用寄存器操作或者LL库而不是全程HAL。Flash和SRAM的配比是64KB加16KB。有人觉得16KB SRAM小写一个带FreeRTOS、LCD缓冲、Modbus协议栈的工程确实紧张但如果只是裸机前后台架构16KB能存下不少数据。需要注意的是G030的Flash是单银行结构不像F103有系统存储器Bootloader的额外分区所以升级固件时要么用自举程序要么通过SWD直接烧录。2.1.1 引脚定义速查引脚号(LQFP32)引脚名默认功能复用功能常用场景1VBAT备份电源-接3.3V或悬空2PC13GPIO-按键、LED7PA0GPIOADC_IN0, TIM2_CH1按键、模拟采样8PA1GPIOADC_IN1, TIM2_CH2电位器、传感器12PA2GPIOUSART1_TX串口发送13PA3GPIOUSART1_RX串口接收20PA9GPIOUSART1_TX(部分封装)-21PA10GPIOUSART1_RX(部分封装)-24PB6GPIOI2C1_SCL, TIM1_CH3I2C屏幕25PB7GPIOI2C1_SDA, TIM1_CH4I2C屏幕31PA13SWDIO-调试32PA14SWCLK-调试这个表不是完整的datasheet而是我实际在G030F6P6上验证过、做小板最常用的几个引脚。注意LQFP32封装下PA9和PA10在部分型号上没有引出因此串口1最稳的选择是PA2和PA3这两个脚同时也是USART1的默认映射。2.2 为什么LQFP32封装更适合自做PCBLQFP32的引脚间距是0.8mm比TSSOP20的0.65mm和QFN32的0.5mm都好焊接。对于嘉立创打样这种常规工艺0.8mm间距的走线可以直接从焊盘中间穿出来不需要使用细线或盲埋孔。我做样板时一般用两层板、6mil线宽、0.3mm过孔完全能应付G030F6P6这类芯片成本也最低。引脚数量只有32个好处是原理图简单、布局清晰。电源引脚、地引脚、晶振引脚、SWD调试引脚各占几个位置之后真正可用的IO大约只剩20个左右。如果产品需要同时接屏幕、按键、传感器、串口、PWM输出引脚会略显紧张但这个容量也倒逼你精简设计把真正需要的外设放到总线上而不是每个设备占好几个独立IO。2.2.1 和GD32E230、N76E003怎么选对比项STM32G030F6P6GD32E230C8T6N76E003内核Cortex-M0Cortex-M23增强型8051主频48MHz72MHz16MHzFlash/SRAM64KB/16KB64KB/8KB18KB/1KB封装LQFP32LQFP48TSSOP20开发环境STM32CubeMXKEILKEIL厂商库KEIL价格区间(批量)较低略低较低从开发体验来看G030F6P6有一个很明显的优势CubeMX可以直接生成底层初始化代码不用看厂商自定义的寄存器库说明。GD32的固件库风格接近标准外设库也能上手但有些寄存器的位定义和STM32有细微差别。N76E003则完全是8051生态适合超低成本小家电不适合做需要复杂协议栈的产品。2.3 时钟树和复位电路的实际配置G030F6P6内部有一个48MHz的HSI48精度在-1%到1%之间对于串口通信来说够用。为什么很多时候仍然外接8MHz晶振呢因为USB通信需要精确的48MHz时钟另外RS485通信如果波特率要求严格外部晶振能提供更稳定的参考。我的习惯是只做Modbus或普通串口直接用HSI48省掉两个负载电容和一颗晶振如果要做USB设备或者对时序精度要求高就加8MHz晶振。复位电路方面G030内置上电复位和掉电复位外部只需要一个10k电阻把NRST上拉到3.3V再加一个100nF电容到地做滤波。不要用大电容否则会导致复位时间过长。BOOT0引脚直接通过10k电阻下拉不需要留跳线因为G030的BOOT0在系统存储器和主Flash之间选择正常运行固定接低电平即可。3. 用STM32CubeMX和Keil跑通最小工程从时钟到点灯3.1 新建工程的三个关键设置打开STM32CubeMX输入STM32G030F6P6创建工程。进入Pinout页面后第一件事是配置调试接口。很多人新板卡点不亮程序就是因为默认状态下PA13和PA14是GPIO模式SWD调试口没有使能第一次烧录完程序后调试器就连接不上了。把PA13设为SWDIO、PA14设为SWCLK同时芯片引脚图中的SYS选项保持Debug为Serial Wire。第二件事是配置时钟源。在System Core里的RCC选项中如果板子上有外部晶振HSE选择Crystal/Ceramic Resonator如果直接用内部时钟HSE保持Disable。这个决定会影响后面Clock Configuration页面的时钟树走向建议一开始就确定好不要等到代码生成后再改。第三件事是配置LED和按键引脚。以PA0接按键、PC13接LED为例把PA0设为GPIO_Input模式PC13设为GPIO_Output模式。PC13在G030上有输出电流限制直接驱动LED时串联一个1k电阻亮度不会太高但完全够用。3.1.1 Clock Configuration参数参考时钟源配置项值说明内部时钟PLL Source MuxHSI48选择内部48MHz作为PLL输入PLL配置PLLM2分频PLL配置PLLN16倍频PLL配置PLLP2分频系统时钟SYSCLK48MHz最终主频APB1Prescaler1APB1 48MHz外部晶振HSE8MHz如果使用则配置PLL如果使用外部8MHz晶振RCC配置里HSE选择为CrystalPLL源切换为HSEPLLM设为1、PLLN设为12、PLLP设为2得到48MHz主频。这两种方案我都在板子上验证过内部HSI48的好处是省两个电容和一颗晶振坏处是如果芯片个体差异大串口长时间传输大数据时偶尔会出现帧错误板子大货生产时建议还是上晶振。3.2 生成代码前的Project Manager配置Project Manager页面里Project Name不要带中文路径也不要放在带空格的目录下否则Keil编译时会有莫名其妙的中断报错。Toolchain选择MDK-ARMMinimized Heap和Minimized Stack分别设为0x200和0x400G030的SRAM一共16KB这两项不用太大。代码生成器选项里使能Generate peripheral initialization as a pair of .c/.h files per peripheral这样每个外设单独生成源文件和头文件不会堆在一个main.c里。还要勾选Call HAL init inside main()这样CubeMX生成的初始化代码会在main函数中按顺序执行不需要手工调用。3.3 点灯示例代码寄存器版和HAL版对比CubeMX生成代码后默认的main函数里已经有MX_GPIO_Init()的调用这时代码逻辑还很空。以PC13接LED、PA0接按键为例先在main.c的while(1)循环里写一个最简单的翻转逻辑#include main.h int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); while (1) { // 读取按键状态按下为低电平 if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) GPIO_PIN_RESET) { HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); HAL_Delay(200); // 消抖延时简单粗暴 } } }这段代码的关键在于HAL_GPIO_ReadPin和HAL_GPIO_TogglePin两个函数。前者读取PA0的电平状态后者将PC13的输出电平翻转。HAL_Delay(200)提供200毫秒的延时实现按键去抖和LED闪烁节奏控制。但HAL_Delay有个缺陷它是一个忙等待循环在延时期间MCU做不了其他任何事情如果后续要在协议处理中同时响应串口中断这个写法会拖慢接收速度。实际操作中我对点灯测试更倾向于直接用寄存器版因为可以确认GPIO的时钟和模式配置是否正确// 寄存器版点灯前提是RCC和GPIO模式已在CubeMX中配置 while (1) { if ((GPIOA-IDR GPIO_PIN_0) 0) // PA0被拉低 { GPIOC-ODR ^ GPIO_PIN_13; // 翻转PC13 for (volatile uint32_t i 0; i 200000; i); // 简单延时 } }GPIOA-IDR是输入数据寄存器读出来的值对应PA0的电平按下按键接地时为0。GPIOC-ODR是输出数据寄存器用异或的方式翻转第13位效果和HAL_GPIO_TogglePin一样。寄存器版的好处是不依赖HAL的延时函数也不需要额外的库函数调用开销。3.4 编译烧录和第一个坑Flasher连接失败在Keil里选择STM32G030F6P6点击Options for Target进入Debug页面选择ST-Link Debugger然后点击Settings。正常情况下能看到SW Device框里出现ARM CoreSight SW-DP的ID这说明ST-Link和芯片通信成功。如果这里显示No Target Connected先检查接线是否SWDIO、SWCLK、GND三根线都有很多自制板只接了这两根信号线而没共地导致调试器无法建立电平参考。还有一个容易被忽略的问题是目标板供电。ST-Link默认不对外供电如果板子没有独立的3.3V电源MCU压根没上电调试器自然扫描不到设备。我用ST-Link V2调自制板时习惯直接把ST-Link的3.3V引脚飞到板子的3.3V网络前提是板子当前没有接入其他电源否则两个电源会打架。板子带负载较少时这个方法很省事。烧录成功后如果LED不闪先用万用表量PC13引脚对地电压在按下按键的瞬间看是否有电平变化。如果有变化但LED不亮检查LED的极性是否接反以及串联电阻是否过大。PC13的驱动能力有限我用过2k电阻亮度低到几乎看不清换成1k才正常。4. 串口、定时器和Modbus帧接收的示例代码4.1 USART1串口配置的三个关键参数G030F6P6的USART1推荐使用PA2作为TX、PA3作为RX。在CubeMX的Pinout页面中点击PA2选择USART1_TX点击PA3选择USART1_RX然后转到Parameter Settings进行配置。参数推荐值说明Baud Rate9600或115200Modbus RTU通常9600Word Length8 Bits标准串口数据位ParityNoneModbus RTU使用无校验Stop Bits1Modbus RTU标准为1位ModeAsynchronous异步模式Over Sampling16默认即可InterruptUSART1 global interrupt开启全局中断波特率误差是这个芯片上需要特别关注的点。如果使用HSI48作为时钟源USART的波特率误差在48MHz整数倍分频后能达到0.1%以内完全满足Modbus对波特率容差的要求。但如果芯片工作电压低或HSI48校准值未写入误差会被放大。严格的方法是读取芯片出厂时的HSI48 calibration value写入ICSCR寄存器HAL库在HAL_RCC_ClockConfig中会自动做这一步一般不用手工处理。4.1.1 为什么串口中断要配合空闲中断只开启接收中断时每收到一个字节就会进一次中断。假设Modbus一帧8字节波特率9600一个字节大约1ms8个字节连续到达MCU会被打断8次。每次中断里做接收缓存写入和判断累计时间开销不小。更好的方案是开启串口的空闲中断即IDLE中断。当总线在一帧数据结束后出现一个字节时间的空闲硬件自动触发中断此时在中断里一次性处理整帧数据。G030的USART1支持IDLE检测和F103是一致的。CubeMX里UART中断勾选USART1 global interrupt后需要在代码里手动使能IDLE中断HAL库没有直接封装这一功能但可以操作寄存器来开启。4.2 串口接收完整示例中断加空闲判断首先定义接收缓冲区然后启用接收中断和空闲中断uint8_t rx_buffer[64]; // 接收缓冲区 volatile uint8_t rx_len 0; // 当前接收长度 volatile uint8_t rx_complete_flag 0; // 一帧接收完成标志 // 启动接收第一个参数是串口句柄第二个是接收缓冲区第三个是最大长度 HAL_UART_Receive_IT(huart1, rx_buffer, 64); // 使能空闲中断 __HAL_UART_ENABLE_IT(huart1, UART_IT_IDLE);这之后每次收到一个字节HAL库会自动把数据存入rx_buffer同时将接收长度计数减1。当接收长度减到0时会调用HAL_UART_RxCpltCallback但这只是缓冲区满了才触发。我们需要的是空闲中断在每帧结束后立刻触发所以在串口中断服务函数里处理IDLE标志void USART1_IRQHandler(void) { uint32_t isr huart1.Instance-ISR; // 读取中断标志 if ((isr USART_ISR_IDLE) ! 0) // 检查是否为空闲中断 { __HAL_UART_CLEAR_IDLEFLAG(huart1); // 清除空闲标志 HAL_UART_DMAStop(huart1); // 关闭正在进行的接收如果用DMA rx_complete_flag 1; // 置位完成标志主循环处理 // 重新启动接收准备下一帧 HAL_UART_Receive_IT(huart1, rx_buffer, 64); } HAL_UART_IRQHandler(huart1); // 继续处理普通串口中断 }这里有几个细节需要说明。读取ISR寄存器后必须清除IDLE标志清除方式是先读ISR再写ICR寄存器直接调用HAL中的__HAL_UART_CLEAR_IDLEFLAG即可。置位rx_complete_flag后主循环中对这个标志做轮询处理处理完成后将标志清零。如果启用了DMA流程基本一致只是停止DMA接收要调用HAL_UART_DMAStop并且重新启动DMA传输。4.3 Modbus RTU帧接收状态机比阻塞接收可靠得多Modbus RTU的报文格式是地址、功能码、数据和CRC16帧与帧之间必须有至少3.5个字符时间的静默间隔。用前面讲的空闲中断判断帧结束当然可以但项目中更规范的方案是使用一个有限状态机来解析每一帧。这样做的好处是无论数据是连续到达还是分片到达状态机都能保持同步。以Modbus 0x03功能码读保持寄存器为例从站接收一帧的标准状态机可以设计为typedef enum { STATE_IDLE 0, // 空闲等待 STATE_ADDR, // 接收地址 STATE_FUNC, // 接收功能码 STATE_DATA, // 接收数据区 STATE_CRC // 接收CRC } modbus_rx_state_t; modbus_rx_state_t modbus_state STATE_IDLE; uint8_t modbus_frame[64]; uint8_t modbus_frame_len 0; // 每收到一个字节调用一次本函数 void modbus_rx_process(uint8_t byte) { switch (modbus_state) { case STATE_IDLE: // 任何字节都可能是地址进入地址状态 modbus_frame[0] byte; modbus_frame_len 1; modbus_state STATE_ADDR; break; case STATE_ADDR: // 第二个字节是功能码 modbus_frame[1] byte; modbus_frame_len 2; if (byte 0x03) // 读保持寄存器功能码 { modbus_state STATE_DATA; } else { modbus_state STATE_IDLE; // 不支持的功能码丢弃 } break; case STATE_DATA: // 0x03报文结构固定数据长度取决于寄存器数量和起始地址 modbus_frame[modbus_frame_len] byte; if (modbus_frame_len 8) // 请求报文固定为8字节 { modbus_state STATE_CRC; } break; case STATE_CRC: modbus_frame[modbus_frame_len] byte; if (modbus_frame_len 8) // 加上2字节CRC长度为8 { // 校验CRC16通过则执行寄存器读写 modbus_state STATE_IDLE; } break; default: modbus_state STATE_IDLE; break; } }状态机里最关键的一点是CRC状态必须等到完整接收完8个字节后再做校验。0x03读保持寄存器请求帧固定是8字节包括地址1字节、功能码1字节、起始地址2字节、寄存器数量2字节、CRC2字节。如果功能码换成0x06写单个寄存器报文结构也是8字节。对于0x10写多个寄存器报文长度可变需要在DATA状态下根据字节计数寄存器数量动态计算剩余字节数。这套状态机的优势在于即使串口接收出现错位手动重置modbus_state到STATE_IDLE就能让从站重新同步。4.4 定时器PWM和输入捕获的实用配置G030的定时器资源有TIM1、TIM2、TIM3等其中TIM1是高级定时器可以输出互补PWM并带有刹车功能。做电机驱动或LED调光时我用TIM1_CH1作为PWM输出引脚映射到PA8。PWM频率和占空比的关键在PSC和ARR两个寄存器。参数计算方式示例值PSC预分频APB1时钟分频47ARR自动重装载决定周期999PWM频率48MHz / (PSC1) / (ARR1)1kHz占空比CCR / (ARR1)50%CubeMX里TIM1的Clock Source选择Internal ClockChannel1设置为PWM Generation CH1PSC设为47ARR设为999。生成代码后在主循环中调用__HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, 500)即可输出50%占空比。修改CCR的值能动态调光配合ADC读取电位器就能做一个简单的亮度调节器。输入捕获通常用于测频率或脉宽。TIM2_CH1输入捕获模式配置上升沿触发开启捕获中断。捕获中断中读取CCR1的值两次捕获值之差就是信号的周期。G030的TIM2是32位定时器最大值约89秒测低频信号不用担心溢出如果测高频信号需要开启预分频来降低计数精度。5. 嘉立创PCB制版文件从打开工程到下单打样的完整路径5.1 压缩包里通常会有什么文件一个标注了“含嘉立创PCB制版文件”的系列教程资源包打开后大致会有几类内容原理图源文件通常有SchDoc或Epub格式PCB多为PcbDoc或Epub格式还有BOM清单、坐标文件、Gerber文件以及基础的示例工程源码和烧录HEX文件。拿到后建议按类型建目录分开存放尤其是Gerber文件直接交给板厂下单最稳妥不用在EDA工具里二次转换。我一般会先打开BOM清单核对器件型号再打开原理图看电源拓扑和MCU引脚分配。这两个文件如果对得上说明作者至少实际做过一版对不上则说明只是示意。5.2 用嘉立创EDA打开工程并检查必要项嘉立创EDA目前有两套体系标准版和专业版。压缩包里的工程如果是标准版格式直接用标准版打开如果打不开用专业版导入。打开PCB后先检查下面几个关键项目检查项怎么查常见问题元件封装逐个点击元件看封装属性电解电容封装和丝印不匹配电源网络高亮3.3V和GND网络地网络没铺铜导致回路过长晶振负载电容检查容值是否匹配用了12pF但晶振规格要求20pF调试口丝印看SWD接口丝印是否清晰丝印被遮挡或方向反向修改工程后重新生成Gerber文件注意选择“嘉立创”的输出格式源文件是Epub格式时直接导出Gerber选择RS-274X格式单位为毫米精度为2.4即可。生成后要用嘉立创自带的CAM查看器打开检查一遍确认没有缺失的层和异常的开孔。5.2.1 打样时下单界面的参数怎么填嘉立创下单时板子参数按2层板、1.6mm板厚、FR4材质、绿色阻焊、喷锡工艺来选这是最标准的默认组合成本最低。数量5片以内面积只要不超过10x10cm都是特价。这里要注意的是最小线宽线距G030F6P6的LQFP32封装焊盘间距是0.8mm常规6mil线宽完全能走出来如果PCB文件里已经画好了4mil走线下单时工艺选择就要确认支持4mil。另一处容易出问题的是拼板。教程的PCB文件可能是单板不需要拼板时直接上传Gerber即可。如果需要拼板V割或邮票孔方式需要额外标注。自制样板时不建议拼板切板麻烦且容易崩边。5.3 打样回来的板子上电前先做三件事拿到PCB后不要急着焊MCU第一件事是目视检查。对着灯光看板面有无划伤、焊盘氧化、过孔有没有全堵。第二件事是万用表测量3.3V和GND之间有没有短路正常情况阻值应该是几十千欧以上如果接近0说明电源网络上有元件焊错或PCB布局有问题。第三件事是焊接电源部分只焊LDO和电容上电量输出电压是否正确。确认电源正常后再焊STM32G030F6P6。焊完检查引脚之间有无锡桥常规做法是涂助焊剂后用拖焊法清理。上电后用手摸芯片温度如果发烫立刻断电查电源短路。我用热风枪焊接LQFP32时温度设定在340度左右风速调低先焊固定对角两个引脚校准位置后再拉焊。5.4 从原理图到PCB的引脚检查清单这里提供一个自查表格方便做板前逐一确认引脚连接检查要点NRST10k上拉到3.3V 100nF电容到地复位时间是否足够BOOT010k下拉到地正常启动从FlashVDDA3.3V 100nF电容到地模拟电源必须滤波VDD3.3V 100nF去耦电容每个电源引脚都要有PA13/PA14SWD接口的SWDIO/SWCLK不要和LED等外设共用PA2/PA3串口TX/RX注意交叉连接6. 进阶调试技巧用SWD和逻辑分析仪定位外设问题6.1 程序跑飞了却不知道卡在哪硬件异常钩子在开发阶段程序跑飞或卡死是常事。G030的Cortex-M0内核遇到硬件错误时会跳转到HardFault_Handler默认实现是一个死循环。把死循环改成可定位的错误记录能极大缩短排查时间。在main.c里添加如下实现void HardFault_Handler(void) { volatile uint32_t pc 0; volatile uint32_t lr 0; // 读取栈帧中的PC和LR值 __asm volatile ( TST LR, #4\n ITE EQ\n MRSEQ R0, MSP\n MRSNE R0, PSP\n LDR R1, [R0, #24]\n MOV %0, R1\n LDR R1, [R0, #20]\n MOV %1, R1\n : r (pc), r (lr) : : r0, r1 ); while (1) { // 断点调试时查看 pc 和 lr 的值 } }这段代码利用Cortex-M0在异常入口自动压栈的寄存器布局从栈帧中提取出触发异常时的PC值。调试时在while处打断点查看pc变量对应到工程地图文件里的地址就能定位到具体函数。比盲猜要高效得多。6.2 串口数据不对时用逻辑分析仪看波形普通串口调试最常见的问题就是收发的数据全是乱码。这时候换一个思路不要直接用串口助手看结果而是用逻辑分析仪抓TX引脚的实际波形。8N1格式下一个字节在波形上是起始位低电平、8个数据位、1个停止位高电平。分析仪解码后直接显示波特率和数据值能立刻判断是波特率不匹配还是极性接反。便宜的USB逻辑分析仪配合开源软件采样率10MHz以上就够看9600和115200了。抓波形时探头接板子的TX引脚地线夹板子的GND不要只夹信号线不接地否则波形全是毛刺。如果解码出的数据每个字节都差1位多半是波特率配置和实际时钟有偏差回到时钟配置页面重新计算USART的分频值。6.3 接收缓冲区溢出的隐蔽故障串口接收中断和主循环处理之间是典型的“生产者-消费者”关系。如果主循环处理数据耗时太长下一帧数据来了缓冲区还没清空就会出现丢帧。我在Modbus从站里碰到过一次诡异故障每第5帧请求就无响应排查后发现是主循环里同时做了OLED刷新一次刷全屏要40毫秒而Modbus轮询周期只有20毫秒接收缓冲区在刷新期间被覆盖了。解决方案有两种。第一种简单粗暴把接收缓冲区加大到128字节利用串口硬件FIFO暂存数据第二种更规范主循环里用状态机分段处理耗时任务不让任何一段逻辑占住循环超过5毫秒。实际项目里两种方案同时用双保险。本文还有配套的精品资源点击获取
返回列表