基于STM32智能小车问题分析
2026/9/8 18:28:18 网站建设 项目流程

文章目录

    • 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.c
      • Avo.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_ta=Test_Distance();Serial_SendNumber(a,3);if(a<15){Car_Stop();Servo_SetAngle(0);Delay_ms(1000);uint16_tb=Test_Distance();Serial_SendNumber(b,3);if(b>15){Servo_SetAngle(90);Delay_ms(1000);Self_Right();Delay_ms(1000);Go_Ahead();}else{Servo_SetAngle(180);Delay_ms(1000);uint16_tc=Test_Distance();Serial_SendNumber(c,3);if(c>15){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(timeout>50000){// 超时,无回波,返回一个大的距离TIM4->CNT=0;TIM_Cmd(TIM4,DISABLE);return999.0;// 表示无障碍或超出量程}}// 启动定时器计数TIM_Cmd(TIM4,ENABLE);// 等待Echo变为低,超时检测timeout=0;while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)==SET){timeout++;if(timeout>50000){// 超时,回波时间过长,可能异常TIM_Cmd(TIM4,DISABLE);TIM4->CNT=0;return999.0;}}
2. 解决大量Delay_ms()阻塞主循环:

直接设置一个状态标志。用来切换自动和手动,初始化为1,默认自动进行自动避障

uint8_tAutoMode=1;

给蓝牙一个新的数据用来切换

case'A':AutoMode=!AutoMode;if(AutoMode)//自动模式回正{Servo_SetAngle(90);}else//手动模式停车{Car_Stop();}break;case'T':if(!AutoMode){AutoMode=1;// 手动模式下,切换为自动模式}break;

手动模式下想进行自动避障直接强行切换回自动模式,避免中断嵌套出问题。

3. 解决代码封装后循环粒度改变:

避障任务代码把Go_Ahead();放到else后面,不放在开头强制执行,需要进读取数值后进行代码执行。

voidAvoidanceTask(void){floatdist=Test_Distance();uint16_ta=(uint16_t)dist;if(a<15){......}}else{Go_Ahead();}Delay_ms(100);}

3.修订后的完整代码示例

main.c

#include"stm32f10x.h"// Device header#include"Delay.h"#include"OLED.h"#include"PWM.h"#include"Car.h"#include"Serial.h"#include"Servo.h"#include"HCSR04.h"#include"Avo.h"uint8_tAutoMode=1;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_tcurrentDist=0;if(AutoMode){// 自动模式:执行避障任务,内部会测距并前进/避障AvoidanceTask();// 注意:AvoidanceTask 内部会测距,但为了显示,我们可以再次测距,但为了效率,// 可在 AvoidanceTask 中返回距离,或使用全局变量。这里简单起见,在自动模式也调用一次测距(但 AvoidanceTask 已经测过了)// 更好的方式:让 AvoidanceTask 返回距离,或使用全局变量保存最新距离。// 为了简化,我们直接调用一次 Test_Distance() 用于显示(会多测一次,但不影响功能)currentDist=(uint16_t)Test_Distance();}else{// 手动模式:不执行避障,仅更新显示(距离显示 999 表示未知,或显示上次值)// 这里我们显示 999 表示手动模式不测距currentDist=999;// 或保持上一次的值Delay_ms(10);}// 每次循环都更新 OLED 显示OLED_DisplayInfo(currentDist,AutoMode);}}voidUSART1_IRQHandler(void){if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET){Data1=USART_ReceiveData(USART1);switch(Data1){case'0':Car_Stop();break;case'1':Go_Ahead();break;case'2':Go_Back();break;case'3':Turn_Left();break;case'4':Turn_Right();break;case'5':Self_Left();break;case'6':Self_Right();break;case'7':Servo_SetAngle(0);break;case'8':Servo_SetAngle(90);break;case'9':Servo_SetAngle(180);break;case'A':AutoMode=!AutoMode;if(AutoMode)//自动模式回正{Servo_SetAngle(90);}else//手动模式停车{Car_Stop();}break;case'T':if(!AutoMode){AutoMode=1;// 手动模式下,切换为自动模式}break;default:break;}USART_ClearITPendingBit(USART1,USART_IT_RXNE);}}

Avo.c

#include"stm32f10x.h"#include"CAR.h"#include"HCSR04.h"#include"Servo.h"#include"Delay.h"#include"Serial.h"#include"OLED.h"externuint8_tAutoMode;externuint8_tManualTrigger;// 显示函数:显示距离和模式voidOLED_DisplayInfo(uint16_tdist,uint8_tmode){OLED_ShowString(1,1,"Dist:");// 第一行:距离if(dist==999){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){floatdist=Test_Distance();uint16_ta=(uint16_t)dist;if(a<15){Car_Stop();Servo_SetAngle(0);Delay_ms(1000);uint16_tb=(uint16_t)Test_Distance();if(b>15){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(c>15){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_ttimeout=0;while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)==RESET){timeout++;if(timeout>50000){// 超时,无回波,返回一个大的距离TIM4->CNT=0;TIM_Cmd(TIM4,DISABLE);return999.0;// 表示无障碍或超出量程}}// 启动定时器计数TIM_Cmd(TIM4,ENABLE);// 等待Echo变为低,超时检测timeout=0;while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)==SET){timeout++;if(timeout>50000){// 超时,回波时间过长,可能异常TIM_Cmd(TIM4,DISABLE);TIM4->CNT=0;return999.0;}}TIM_Cmd(TIM4,DISABLE);uint32_tCnt=TIM_GetCounter(TIM4);floatdistance=(Cnt*1.0/10*0.34)/2;// 单位cmTIM4->CNT=0;Delay_ms(100);returndistance;}

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询