基于 STM32F103C8T6 的循迹避障小车 Proteus 拟真 + CubeMX 全流程开发

张开发
2026/5/3 7:19:44 15 分钟阅读
基于 STM32F103C8T6 的循迹避障小车 Proteus 拟真 + CubeMX 全流程开发
一、项目概述本项目基于 STM32F103C8T6 主控在 Proteus 中搭建了完整的循迹避障小车拟真环境同时配套 STM32CubeMX 的完整配置方案实现了按键模式切换、轨道循迹、障碍物检测、超声波测距、电机驱动、LCD 状态显示等功能二、硬件电路设计Proteus 拟真环境2.1 整体电路架构整个拟真系统分为 5 大核心模块电路结构清晰完全匹配实物开发的接线逻辑主控模块STM32F103C8T6 最小系统人机交互模块I2C 接口 LCD1602 显示屏、6 路按键输入模式 / 方向 / 轨道 / 障碍物模拟传感器模块HC-SR04 超声波测距模块电机驱动模块L298N 直流电机驱动 4 路直流电机状态指示模块运行状态 LED、串口调试接口2.2 核心模块电路详解1主控与电源电路采用 STM32F103C8T6 LQFP48 封装芯片预留 SWD 下载接口PA13/PA14支持程序在线调试电源部分统一 5V 供电VDDA/VSSA 单独供电保证模拟电路稳定性NRST 复位引脚外接复位电路BOOT0 引脚接地默认从 Flash 启动。2交互LCD1602 I2C 显示屏采用 I2C 总线驱动SDA 接 PB7、SCL 接 PB6仅需 2 根引脚即可实现显示节省 IO 资源用于实时展示小车运行模式、传感器状态、电机转速等信息。按键输入电路控制显示区PC13总开关、PC14前进 / 后退切换、PC15模式切换采用上拉输入按键按下触发低电平轨道 / 障碍物模拟区PB4轨道右转弯、PB5轨道左转弯、PB6右侧障碍物、PB7左侧障碍物模拟小车循迹过程中的轨道变化和障碍物场景3传感器电路HC-SR04 超声波模块TRIG 触发端接 PB8GPIO 输出ECHO 回波端接 PB9GPIO 输入通过发送触发信号、测量回波高电平时间计算障碍物距离实现避障功能。4电机驱动电路采用 L298N 电机驱动芯片驱动 4 路直流电机两轮差速结构方向控制引脚IN1 (PA15)、IN2 (PB3) 控制左电机IN3 (PB10)、IN4 (PB11) 控制右电机PWM 调速引脚ENA (PB13)、ENB (PB12) 分别控制左右电机转速通过 TIM2 定时器输出 PWM 波实现调速电路中预留示波器监测接口PA15/PB3/PB10/PB11可实时查看 PWM 波形和 IO 状态方便调试5状态指示电路PB2 引脚外接绿色 LED 限流电阻用于指示小车运行状态正常运行时 LED 闪烁故障时常亮 / 熄灭直观反馈系统状态。三、STM32CubeMX 完整配置方案3.1 工程创建与芯片选择打开 STM32CubeMX搜索STM32F103C8T6生成项目3.2 系统与调试配置SYS 配置Debug选择Serial WireSWD 模式仅用 PA13/PA14节省引脚Timebase Source选择TIM1避免占用 TIM2 定时器RCC 配置High Speed Clock (HSE)选择Crystal/Ceramic Resonator外部 8MHz 晶振匹配 Proteus 仿真LSE选择Disable3.3 引脚配置和外设3.4主函数代码采用模块化编程并加了注释/* USER CODE BEGIN Header */ /** ****************************************************************************** * file : main.c * brief : Main program body ****************************************************************************** * attention * * Copyright (c) 2024 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include main.h #include tim.h #include usart.h #include gpio.h /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include stdio.h #include oled.h #include sr04.h #include SYSTEM/sys/sys.h #include SYSTEM/delay/delay.h #include SYSTEM/usart/usart.h #include USMART/usmart.h /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ //低速运行时PWM的脉宽时间 uint16_t lowPlus 200 - 1; //高速运行时PWM的脉宽时间 uint16_t highPlus 1000 - 1; //当前车速的PWM的脉宽时间 uint16_t plus 1000 - 1; //小车启停标识, 0停止, 1运行 uint8_t running 0; //OLED清屏标识, 0不清清屏, 1清屏 uint8_t clean 0; //运行方向标识, 1前进, -1后退 int forward 1; //小车运行模式, 1循迹, 2避障, 3自由运行(循迹避障) uint8_t mode 1; //轨道转弯标识, 0直行, 1左转, 2右转 uint8_t turn 0; //路障标识, 0无, 1左侧有路障, 2右侧有路障 uint8_t block 0; //障碍物示警距离到达该距离后降低车速 uint8_t alertDistance 30; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ //声明改变小车前进方向的函数 void Change_Forward(void); //声明小车转弯函数 void Car_Turn(uint8_t t); //声明小车避障函数 void Car_Block(void); //声明GPIO回调函数供main源文件内调用 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin); /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * brief The application entry point. * retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM2_Init(); MX_TIM3_Init(); MX_TIM4_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ //OLED屏幕初始化 OLED_Init(); //启动驱动四个电机的PWM HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_2); HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_3); HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_4); //初始化USMART delay_init(64); usart_init(9600); usmart_dev.init(64); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { //检测是否清屏 if(clean) { clean 0; OLED_Clear(); } //小车停止 if(!running) { OLED_ShowString(2, 2, Car Stop); } else//小车运行 { //小车后退 if(forward 0)//, 1前进, -1后退 { OLED_ShowString(2, 2, Car Backward); } else //小车前进 { OLED_ShowString(1, 1, Car Forward); //循迹模式 if(mode 1)//1循迹, 2避障, 3自由运行 { OLED_ShowString(2, 1, Mode:Tracking); } else//避障或自由模式 { OLED_ShowString(2, 1, Mode:); OLED_ShowString(2, 6, mode 2 ? Blocking : Free); char msg[10]; char *dir (block 1 ? Left: : (block 2 ? Right: : None:)); sprintf(msg, %s%.2fcm, dir, distance); OLED_ShowString(3, 1, msg); } } //如果在避障或自由模式下开启雷达测距并检测是否需要避障 if(mode ! 1)//1循迹, 2避障, 3自由运行 { Trigger_Signal();//超声波模块工作一次 HAL_Delay(50); Car_Block();//小车躲避障碍物 } } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * brief System Clock Configuration * retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue RCC_HSE_PREDIV_DIV2; RCC_OscInitStruct.HSIState RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL RCC_PLL_MUL2; if (HAL_RCC_OscConfig(RCC_OscInitStruct) ! HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(RCC_ClkInitStruct, FLASH_LATENCY_0) ! HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /** * brief GPIO中断回调 * param GPIO_Pin: 触发中断的引脚 */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin GPIO_PIN_13)//按下启停按钮 { //OLED清屏 clean 1; if(running)//如果小车正在运行则停止 { running 0;//运行标识, 0停止, 1运行 //L298芯片使能引脚低电平截止高电平导通 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);//停止右侧两个车轮 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET);//停止左侧两个车轮 //熄灭运行指示灯 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET); } else//如果小车没有运行则启动 { running 1;//运行标识, 0停止, 1运行 //启动小车时默认为前进方向 if(forward 0)//0前进0后退 { Change_Forward();//切换小车运行方向 } //启动时默认为循迹模式 mode 1;//1循迹, 2避障, 3自由运行 //L298芯片使能引脚低电平截止高电平导通 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);//启动右侧两个车轮 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);//启动左侧两个车轮 //点亮运行指示灯 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET); } } else if(GPIO_Pin GPIO_PIN_14)//按下运行方向切换按钮 { Change_Forward();//转换方向 } else if(GPIO_Pin GPIO_PIN_15)//按下运行模式切换按钮 { if(running) { clean 1;//OLED清屏 //mode取值1循迹2避障3自由 if(mode 1) mode 2; else if(mode 2) mode 3; else if(mode 3) mode 1; } } else if(GPIO_Pin GPIO_PIN_4 mode ! 2)//模拟轨道右转小车右转条件系统运行且在循迹或自由模式下 { //读取模拟右转弯引脚电平 GPIO_PinState state HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4); //检测是否模拟了右转弯 if(state GPIO_PIN_SET) Car_Turn(2);//右转弯 else Car_Turn(0);//直行 } else if(GPIO_Pin GPIO_PIN_5 mode ! 2)//模拟轨道左转小车左转条件系统运行且在循迹或自由模式下 { //读取模拟右转弯引脚电平 GPIO_PinState state HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5); //检测是否模拟了左转弯 if(state GPIO_PIN_SET) Car_Turn(1); else Car_Turn(0); } else if(GPIO_Pin GPIO_PIN_8)//超声波测距 { //读取两个障碍物模拟引脚的电平 GPIO_PinState state_6 HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6);//右侧障碍物模拟 GPIO_PinState state_7 HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7);//左侧障碍物模拟 //只有在避障模式或自由运行模式下并且开启了障碍物模拟时才进行测距 if(mode ! 1 (state_6 GPIO_PIN_SET || state_7 GPIO_PIN_SET)) Calc_Distance(); else//不测距时距离显示0 distance .0; } } /** * brief 改变小车前进方向 * note 直流电机转换运行方向交换两个驱动电机PWM的脉宽时间 */ void Change_Forward(void) { if(running) { clean 1;//OLED清屏 forward * -1;//0前进0后退 //读取右侧车轮的PWM脉宽参数 uint16_t plus_1 __HAL_TIM_GetCompare(htim2, TIM_CHANNEL_1); uint16_t plus_2 __HAL_TIM_GetCompare(htim2, TIM_CHANNEL_2); //两个PWM交换脉宽 __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_1, plus_2); __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_2, plus_1); //读取左侧车轮的PWM脉宽参数 uint16_t plus_3 __HAL_TIM_GetCompare(htim2, TIM_CHANNEL_3); uint16_t plus_4 __HAL_TIM_GetCompare(htim2, TIM_CHANNEL_4); //两个PWM交换脉宽 __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_3, plus_4); __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_4, plus_3); } } /** * brief 小车转弯 * note 左转弯条件: 向前运行并且在避障模式或在自由模式 * 左转弯方法: 转弯方向那侧的轮速小于另一侧的轮速 * param t: 转弯方向, 0直行, 1左转, 2右转 */ void Car_Turn(uint8_t t) { //小车转弯条件向前运行并且在避障模式或自由模式 if(running forward 0 mode ! 2) { if(t 1)//0直行1左转2右转 { if(__HAL_TIM_GetCompare(htim2, TIM_CHANNEL_3) highPlus) { __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_3, lowPlus);//PWM通道3负责左轮速左转时左轮速 右轮速 } } else if(t 2)//0直行1左转2右转 { if(__HAL_TIM_GetCompare(htim2, TIM_CHANNEL_1) highPlus) { __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_1, lowPlus);//PWM通道1负责右轮速右转时左轮速 右轮速 } } else//0直行1左转2右转 { //不转弯时恢复两侧轮速 if(__HAL_TIM_GetCompare(htim2, TIM_CHANNEL_1) lowPlus) __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_1, highPlus); if(__HAL_TIM_GetCompare(htim2, TIM_CHANNEL_3) lowPlus) __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_3, highPlus); } } } /** * brief 小车避障 * note 避障条件向前运行并且在避障模式或在自由模式 * 避障方法到达警戒距离先降车速到达避障距离障碍物一侧车轮向前转对侧车轮向后转 */ void Car_Block(void) { //小车避障条件向前运行并且在避障模式或自由模式 if(running forward 0 mode ! 1) { //如果小车没降速则降低小车速度开启躲避功能时小车到障碍物的距离比警告距离少5cm if(distance alertDistance distance (alertDistance - 5)) { if(plus ! lowPlus) { plus lowPlus; __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_1, lowPlus);//设置右轮电机PWM的脉宽 __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_2, 0); __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_3, lowPlus);//设置左轮电机PWM的脉宽 __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_4, 0); } } else if(distance (alertDistance - 5) distance 0) { plus 0; //先停车L298芯片使能引脚低电平截止高电平导通 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET); //HAL_Delay(1000); //右侧有障碍向左避障左轮向后转右轮向前转 if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6) GPIO_PIN_SET) { //设置左轮向后转 __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_3, 0); __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_4, lowPlus); } //左侧有障碍向右避障右轮向后转左轮向迁转 else if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) GPIO_PIN_SET) { //设置右轮向后转 __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_1, 0); __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_2, lowPlus); } //再启动小车L298芯片使能引脚低电平截止高电平导通 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET); } else//恢复高速运行 { if(plus ! highPlus) { plus highPlus; __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_1, highPlus);//设置右轮电机PWM的脉宽 __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_2, 0); __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_3, highPlus);//设置左轮电机PWM的脉宽 __HAL_TIM_SetCompare(htim2, TIM_CHANNEL_4, 0); } } } } /** * brief 远程开关小车 */ void Remote_Switch(void) { HAL_GPIO_EXTI_Callback(GPIO_PIN_13); } /** * brief 远程切换小车运行方向 */ void Remote_Forward(void) { HAL_GPIO_EXTI_Callback(GPIO_PIN_14); } /** * Brief 远程小车转弯 * param dir 转弯方向0直行, 1左转, 2右转 */ void Remote_Turn(uint8_t dir) { //转弯之前先把小车修正成直行 if(dir 1 || dir 2) { Car_Turn(0); } //转弯 Car_Turn(dir); } /* USER CODE END 4 */ /** * brief This function is executed in case of error occurrence. * retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * param file: pointer to the source file name * param line: assert_param error line source number * retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf(Wrong parameters value: file %s on line %d\r\n, file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */四、遇到的问题与踩坑总结CubeMX 配置类错误1. 调试引脚与普通 GPIO 冲突2. I2C 配置模式错误(没设置开漏输出Proteus 拟真环境仿真问题1.电机不转占空比设置错了2.仿真中电源未共地出现一部分工作一部分不工作

更多文章