第 8 篇:把一切串起来——整车联调、OLED 显示与调试避坑
第 8 篇把一切串起来——整车联调、OLED 显示与调试避坑回顾全套小车工厂模型第1篇看懂main入口、头文件工具箱、开机初始化、while(1)主线流水线第2篇掌握变量储物盒、嵌入式专用定宽数据类型第3篇学会分支循环控制流、函数模块化、枚举状态机第4篇搞懂中断定时闹钟、volatile关键字、中断标准三步写法第5篇吃透PWM油门、编码器测速、PID速度闭环、陀螺仪角度纠偏小车稳直准行驶第6篇打通无线通信NRF24L01、SPI、自定义通信帧、异或校验、断线保护第7篇给小车装眼睛OpenMV 色块/二维码识别、USART3 串口帧、环形缓冲状态机解析本篇是收官篇把前 7 篇的身体、肌肉、耳朵、眼睛真正拧成一台能跑的车主循环如何统筹遥控视觉屏幕、OLED 怎么显示实时状态、中断优先级怎么排、新手联调四大坑以及一套先动哪、再看哪的排查套路。0. 引子零件都会了怎么拼成一台车前 7 篇每个模块单独看都讲透了。但真实项目里新手最常卡在每个零件单独测都行凑一起就乱。原因无非几类主循环顺序没排好、中断互相打架、共享变量忘加 volatile、PID 参数瞎调、一上来就全开结果不知错在哪。本篇就解决整合与调试给你一套能直接照做的落地方法。1. 主循环小车的总指挥流水线回到第1篇的while(1)骨架。整车跑起来后主循环就是一条固定顺序的流水线每轮都按这个节奏走intmain(void){// ... 全部 _Init第1篇时钟、定时、OLED、MPU、编码器、PWM、NRF、USART3Delay_ms(2000);// 上电稳定 2 秒第1篇讲过防刚上电乱出状况while(1){HandleNRFCommands();// 1. 先看遥控有没有新指令第6篇Cam_Parse();// 2. 再拼视觉帧第7篇if(cam_frame_ready){cam_frame_ready0;HandleCamFrame();}Motion_Update();// 3. 按当前模式/目标算四轮该转多快第5篇 PIDif((Timer7_5ms_Count-disp_ms)20)// 4. 每 100ms 刷一次屏本节{disp_msTimer7_5ms_Count;OLED_Refresh();}}}几个排布要点遥控和视觉谁优先本项目用模式标志仲裁手动遥控时视觉追踪暂停切到自动模式才听摄像头的。别让两套指令同时改target否则车会左右互搏PID 在哪跑真正每 5ms 算一次 PWM的活放在 TIM7 中断里第4、5篇主循环只负责定目标中断负责追目标分工清晰屏幕别每轮刷OLED 走 I2C 很慢每轮都刷会拖垮主循环所以用Timer7_5ms_Count做个显示节拍呼应第4篇时间基准。2. OLED 显示让车看得见自己的状态调试时最痛苦的就是车不动了却不知道卡在哪。OLED 就是小车的状态面板把关键信息显示出来一眼定位问题。2.1 显示什么屏幕内容作用对应知识点当前运动模式确认指令有没有被正确解析第3、6、7篇模式枚举四轮实时速度看 PID 有没有在调、极性对不对第5篇 Speed1~4、第2篇 int16_t 带符号航向角 yaw直行跑偏时看角度环在不在工作第5篇陀螺仪遥控/视觉连接状态断链保护有没有误触发第6篇 500ms 断线2.2 刷新代码带符号显示呼应第2篇voidOLED_Refresh(void){OLED_ShowString(1,1,MODE:);OLED_ShowNum(1,6,(uint16_t)target,2);OLED_ShowString(2,1,SPD:);OLED_ShowSignedNum(2,6,Speed1,4);// 带正负验证编码器极性第5篇思考2OLED_ShowSignedNum(2,11,Speed2,4);OLED_ShowString(3,1,YAW:);OLED_ShowSignedNum(3,6,(int16_t)yaw,5);OLED_ShowString(4,1,nrf_ok?NRF:OK :NRF:LOST);}Speed1用int16_t带符号显示呼应第2篇车轮速度必须能表达正负否则倒退显示不出来。屏幕变量同样是中断写、主循环读背后那套volatile纪律一个都不能少。3. 中断优先级多中断别打架第1篇NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2)给中断排了座次。整车有 3 类中断同时跑优先级怎么排有讲究// TIM75ms 定时负责编码器采样 陀螺仪 PID 输出最高时间最敏感NVIC_InitStructure.NVIC_IRQChannelTIM7_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority0;// EXTI9_5NRF 收到指令中偶尔来一包处理快NVIC_InitStructure.NVIC_IRQChannelEXTI9_5_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority1;// USART3视觉串口字节流最低慢慢收缓冲兜底NVIC_InitStructure.NVIC_IRQChannelUSART3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority2;排优先级的原则呼应第4篇中断要短平快越频繁、越时间敏感的优先级越高编码器 5ms 采样绝不能丢排最高中断里只做最少的事TIM7 里只采样算 PID置标志绝不调printf、不刷屏、不Delay长活丢给主循环视觉拼帧、遥控解析都在主循环慢慢干中断只负责收字节 / 置标志。4. 新手联调四大坑4.1 坑一上电顺序 / 初始化遗漏忘了调某个_Init()那个设备就一直睡着后面怎么写都不动第1篇强调过_Init结尾 开机自检上电后不加那 2 秒延时部分传感器没稳定就开始读数据全是乱的电机相关初始化顺序先MOTOR_GPIO_Init再TIM1_PWM_Init别让 PWM 在电机引脚还没配置好时就输出。4.2 坑二共享变量忘加 volatile全项目清单这是整套车最容易集体翻车的地方。把所有中断写、主循环读的变量列一张表缺一个就埋一个隐性 bug变量写在哪读在哪来源篇Timer7_5ms_CountTIM7 中断主循环第4篇Speed1~4TIM7 中断主循环/PID第5篇yawTIM7 中断主循环/角度环第5篇new_frame_flag、rx_bufNRF 外部中断主循环第6篇ring_buf、ring_w、cam_frame_readyUSART3 中断主循环第7篇纪律一条只要一个变量同时出现在中断服务函数和主循环里立刻加volatile。忘了它表现就是屏幕卡死、速度不更新、遥控偶尔失灵——查半天查不到因为代码逻辑看着完全没错。4.3 坑三PID 参数瞎调本项目是 PI 控制第5篇说过不加 D 防噪声放大。调参有固定套路别乱拧// 第一步只开 PKi0。慢慢加大 Kp直到车反应灵敏但开始轻微抖Kp0.5;Ki0;// 第二步加一点 Ki消除永远差一点的稳态误差第5篇思考1Ki0.02;// 第三步限幅防积分饱和/炸机第5篇 PWM_MAX/MINif(pid_outPWM_MAX)pid_outPWM_MAX;if(pid_outPWM_MIN)pid_outPWM_MIN;常见症状对照车越调越偏 / 反向狂奔先查编码器 A、B 相接反第5篇思考2、PID 输出符号接反直行缓慢跑偏I 项太小或角度环没工作查yaw显示起步窜动切换模式没PID_All_Reset()清积分第5、6篇都强调过。4.4 坑四中断里干重活 / 漏清标志呼应第4篇铁律中断里调Delay_ms、刷 OLED、printf重活 → 主循环被饿死车卡顿失控忘了ClearITPendingBit→ 硬件反复触发同一中断程序卡死不判断GetITStatus就清/就处理 → 多中断源时误触发数据错乱。5. 排查套路“先动哪、再看哪”不要一上来就把遥控视觉PID 全开。按从底层到顶层、分层点亮的顺序来哪层出问题就锁定哪层上电看 OLED 亮不亮→ 验证供电、I2C、初始化第1、2篇开环 PWM 让轮转→ 验证电机、驱动、PWM第5篇 1.3看 Speed 有没有数、正负对不对→ 验证编码器第5篇思考2 极性调 PID 直行稳→ 验证速度/角度闭环第5篇单独测遥控→ 验证 NRF 帧、断线保护第6篇单独测视觉→ 验证 OpenMV 组帧、串口状态机第7篇最后联调优先级与仲裁→ 查中断打架、模式冲突本节第3章。每一步都用 OLED 或串口打印把关键变量亮出来。定位问题的速度取决于你愿不愿意一次只验证一件事。6. 本篇小结知识点通俗理解主循环流水线固定顺序遥控→视觉→算运动→按节拍刷屏遥控/视觉仲裁用模式标志二选一别让两套指令互搏OLED 状态面板把模式/速度/角度/连接亮出来调试全靠它显示节拍别每轮刷屏用 Timer7 计时节流中断优先级频繁敏感的排最高长活丢主循环volatile 全清单所有中断↔主循环共享变量必须加漏一个埋一个坑PID 调参套路先 P 后 I限幅防炸机符号不对先查极性分层点亮从电机到视觉逐层验证一次只查一件事7. 第7篇趣味思考标准答案思考1OpenMV 只发结论、不发整图给 STM32如果改成传整张 160x120 彩图会怎样现实三大问题带宽根本不够160×120 的 RGB565 图约 38400 字节115200 波特率串口每秒有效才一万多字节传一帧图要好几秒彻底丧失实时性STM32 没算力做图像复杂图像算法找色块、解码二维码本来就是 OpenMV 的强项塞给 STM32 既慢又浪费RAM 装不下38KB 帧缓冲对 STM32 是很重的负担。所以OpenMV 看图出结论、STM32 只收小结果是正确的分工——重活在会做的芯片上做。思考2色块偏左 err 为负按 turn err*6 小车该往哪转色块偏左 → 中心 x 80 →err 0→turn 0。约定负值左转车就向左转、朝色块方向追把色块重新拉回画面中央。这是一个负反馈闭环偏差方向决定转向方向使偏差趋零。如果实测发现越追越偏、越跑越歪说明turn的转向符号接反了取反即可——本质和编码器 A、B 相接反是同一类符号/极性问题呼应第5篇思考2。8. 交流 收官我目前也是嵌入式自学新手这 8 篇文章是我结合自己麦轮小车项目、从零啃下来的实操总结。部分知识点理解可能有偏差、讲解也有不够严谨的地方。技术大佬如果发现文中存在错误、逻辑漏洞欢迎评论区指正我会及时修正零基础小白有看不懂的知识点、实操踩坑问题都可以留言提问我看到会逐条回复大家也可以分享自己做智能小车的经验、代码优化思路一起交流学习共同进步系列收官从看代码到做出来回头看这 8 篇我们其实只做了一件事把一台麦轮小车拆成能看懂的零件再原样装回去。第1篇的mainwhile(1)是整台车的骨架第2、3篇的变量、函数、枚举是你写每行业务的字和词第4篇的 volatile 与中断是嵌入式和纯软件最不一样的地方也是新手 90% 玄学 bug 的根源第5篇的 PWM/编码器/PID/陀螺仪是让车稳直准的动力核心第6、7篇的 NRF 与 OpenMV是让车听得懂人话、看得见路的耳朵和眼睛第8篇的联调与避坑是把零件拧成一台真车的最后一公里。你不需要一次全记住。真正吃透的办法是照着分层点亮的顺序在自己车上把每一篇跑通一遍——哪篇卡住就回到哪篇。等八篇全部跑顺你手里就不只是一台小车而是一整套嵌入式开发的底层直觉。祝愿各位大佬项目开发一路顺畅调试零bug思路源源不断祝愿所有入门小白少踩弯路快速吃透STM32早日做出属于自己的智能小车
