FEATURED · 精选文章

基于STM32智能小车问题分析

发布时间 / 2026/9/8 18:28:25
来源 / 创域科博编辑部
栏目 / 资讯中心
基于STM32智能小车问题分析 文章目录1.无法驱动舵机和电机1.1.代码问题2.1模块和设备排查2.1.1电机直连测试2.1.2更换电源测试2.1.3舵机测试2.封装自动避障模块后调用无法正常实现功能主函数自动避障代码——能跑想法结果问题分析1. 超声波处理异常2. 大量Delay_ms()阻塞主循环3. 代码封装后循环粒度改变解决问题1.解决超声波异常2. 解决大量Delay_ms()阻塞主循环3. 解决代码封装后循环粒度改变3.修订后的完整代码示例main.cAvo.c测量函数Test_Distance()1.无法驱动舵机和电机注尽量使用独立电源供电避免烧坏电脑解决过程分析可能出现的问题驱动模块坏了、设备坏了、代码有问题以及供电原因1.1.代码问题检查代码初始化引脚主函数驱动函数一一对应编译烧录没问题现象OLED有显示无法驱动电机舵机响动可能问题模块或者设备坏了以及电源供电不足2.1模块和设备排查2.1.1电机直连测试用电源直接连接电机正负极现象电机转动注舵机没办法直连测试可能问题驱动模块有问题、电源供电不足2.1.2更换电源测试3.3v-5.v进行升压处理进行完整的流程处理现象可以正常驱动解析3.3v电压不足以驱动整个面包板的设备导致供电不足电机无法驱动2.1.3舵机测试注独立电源无法测试直接进行升压处理用5v供电现象舵机正常转动。但是90°之后速度变慢可能问题电源供电还是太小。需要更大的电压。待解决2.封装自动避障模块后调用无法正常实现功能主函数自动避障代码——能跑while(1){Go_Ahead();uint16_taTest_Distance();Serial_SendNumber(a,3);if(a15){Car_Stop();Servo_SetAngle(0);Delay_ms(1000);uint16_tbTest_Distance();Serial_SendNumber(b,3);if(b15){Servo_SetAngle(90);Delay_ms(1000);Self_Right();Delay_ms(1000);Go_Ahead();}else{Servo_SetAngle(180);Delay_ms(1000);uint16_tcTest_Distance();Serial_SendNumber(c,3);if(c15){Servo_SetAngle(90);Delay_ms(1000);Self_Left();Delay_ms(1000);Go_Ahead();}else{Servo_SetAngle(90);while(1){};}}}Delay_ms(100);}封装自动避障的第一版代码示例想法想用蓝牙控制开关自动避障功能。结果执行给定的数据没反应蓝牙手动控制没问题只有控制开启自动避障有问题问题分析1. 超声波处理异常Last_distance为局部栈变量测距输出随机异常值避障条件误触发小车直接停车表现如同避障未启动2. 大量Delay_ms()阻塞主循环开启避障后程序会长时间阻塞在 Avo 函数内部主循环无法快速迭代即使蓝牙中断已经修改 Mode 标志也需要等待当前一轮完整避障动作执行完毕才会生效主观感受为指令无响应3. 代码封装后循环粒度改变将避障逻辑从 main while 循环提取到 Avo 函数后Go_Ahead()调用频次发生变化小车运动行为与直接写在 main 中不一致解决问题1.解决超声波异常增加超时检测超时直接返回 999不再依靠局部变量保存历史距离。while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)RESET){timeout;if(timeout50000){// 超时无回波返回一个大的距离TIM4-CNT0;TIM_Cmd(TIM4,DISABLE);return999.0;// 表示无障碍或超出量程}}// 启动定时器计数TIM_Cmd(TIM4,ENABLE);// 等待Echo变为低超时检测timeout0;while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)SET){timeout;if(timeout50000){// 超时回波时间过长可能异常TIM_Cmd(TIM4,DISABLE);TIM4-CNT0;return999.0;}}2. 解决大量Delay_ms()阻塞主循环直接设置一个状态标志。用来切换自动和手动初始化为1默认自动进行自动避障uint8_tAutoMode1;给蓝牙一个新的数据用来切换caseA:AutoMode!AutoMode;if(AutoMode)//自动模式回正{Servo_SetAngle(90);}else//手动模式停车{Car_Stop();}break;caseT:if(!AutoMode){AutoMode1;// 手动模式下切换为自动模式}break;手动模式下想进行自动避障直接强行切换回自动模式避免中断嵌套出问题。3. 解决代码封装后循环粒度改变避障任务代码把Go_Ahead();放到else后面不放在开头强制执行需要进读取数值后进行代码执行。voidAvoidanceTask(void){floatdistTest_Distance();uint16_ta(uint16_t)dist;if(a15){......}}else{Go_Ahead();}Delay_ms(100);}3.修订后的完整代码示例main.c#includestm32f10x.h// Device header#includeDelay.h#includeOLED.h#includePWM.h#includeCar.h#includeSerial.h#includeServo.h#includeHCSR04.h#includeAvo.huint8_tAutoMode1;uint8_tData1;// 显示函数声明具体实现在 Avo.c 中voidOLED_DisplayInfo(uint16_tdist,uint8_tmode);intmain(void){Car_Init();Serial_Init();PWM_Init2();//ServoHCSR04_Init();OLED_Init();// OLED 初始化移到这里只一次while(1){uint16_tcurrentDist0;if(AutoMode){// 自动模式执行避障任务内部会测距并前进/避障AvoidanceTask();// 注意AvoidanceTask 内部会测距但为了显示我们可以再次测距但为了效率// 可在 AvoidanceTask 中返回距离或使用全局变量。这里简单起见在自动模式也调用一次测距但 AvoidanceTask 已经测过了// 更好的方式让 AvoidanceTask 返回距离或使用全局变量保存最新距离。// 为了简化我们直接调用一次 Test_Distance() 用于显示会多测一次但不影响功能currentDist(uint16_t)Test_Distance();}else{// 手动模式不执行避障仅更新显示距离显示 999 表示未知或显示上次值// 这里我们显示 999 表示手动模式不测距currentDist999;// 或保持上一次的值Delay_ms(10);}// 每次循环都更新 OLED 显示OLED_DisplayInfo(currentDist,AutoMode);}}voidUSART1_IRQHandler(void){if(USART_GetITStatus(USART1,USART_IT_RXNE)SET){Data1USART_ReceiveData(USART1);switch(Data1){case0:Car_Stop();break;case1:Go_Ahead();break;case2:Go_Back();break;case3:Turn_Left();break;case4:Turn_Right();break;case5:Self_Left();break;case6:Self_Right();break;case7:Servo_SetAngle(0);break;case8:Servo_SetAngle(90);break;case9:Servo_SetAngle(180);break;caseA:AutoMode!AutoMode;if(AutoMode)//自动模式回正{Servo_SetAngle(90);}else//手动模式停车{Car_Stop();}break;caseT:if(!AutoMode){AutoMode1;// 手动模式下切换为自动模式}break;default:break;}USART_ClearITPendingBit(USART1,USART_IT_RXNE);}}Avo.c#includestm32f10x.h#includeCAR.h#includeHCSR04.h#includeServo.h#includeDelay.h#includeSerial.h#includeOLED.hexternuint8_tAutoMode;externuint8_tManualTrigger;// 显示函数显示距离和模式voidOLED_DisplayInfo(uint16_tdist,uint8_tmode){OLED_ShowString(1,1,Dist:);// 第一行距离if(dist999){OLED_ShowString(1,7,---);// 手动模式显示 ---}else{OLED_ShowNum(1,7,dist,3);}OLED_ShowString(2,1,Mode:);// 第二行模式if(mode){OLED_ShowString(2,7,Auto);}else{OLED_ShowString(2,7,Manu);}}// 避障任务原样但删除了 OLED_Init 和显示部分voidAvoidanceTask(void){floatdistTest_Distance();uint16_ta(uint16_t)dist;if(a15){Car_Stop();Servo_SetAngle(0);Delay_ms(1000);uint16_tb(uint16_t)Test_Distance();if(b15){Servo_SetAngle(90);Delay_ms(1000);Self_Right();Delay_ms(1000);Go_Ahead();}else{Servo_SetAngle(180);Delay_ms(1000);uint16_tc(uint16_t)Test_Distance();if(c15){Servo_SetAngle(90);Delay_ms(1000);Self_Left();Delay_ms(1000);Go_Ahead();}else{Servo_SetAngle(90);Car_Stop();}}}else{Go_Ahead();}// 注意这里移除了 Delay_ms(100) 或保留根据你的需求Delay_ms(100);}测量函数Test_Distance()floatTest_Distance(){// 触发超声波GPIO_SetBits(GPIOB,GPIO_Pin_12);Delay_us(20);GPIO_ResetBits(GPIOB,GPIO_Pin_12);// 等待Echo变为高超时检测uint32_ttimeout0;while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)RESET){timeout;if(timeout50000){// 超时无回波返回一个大的距离TIM4-CNT0;TIM_Cmd(TIM4,DISABLE);return999.0;// 表示无障碍或超出量程}}// 启动定时器计数TIM_Cmd(TIM4,ENABLE);// 等待Echo变为低超时检测timeout0;while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)SET){timeout;if(timeout50000){// 超时回波时间过长可能异常TIM_Cmd(TIM4,DISABLE);TIM4-CNT0;return999.0;}}TIM_Cmd(TIM4,DISABLE);uint32_tCntTIM_GetCounter(TIM4);floatdistance(Cnt*1.0/10*0.34)/2;// 单位cmTIM4-CNT0;Delay_ms(100);returndistance;}
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻