文章目录
-
- 一、项目概述
- 二、硬件清单
- 三、硬件电路设计与接线
-
- [3.1 核心电路原理](#3.1 核心电路原理)
- [3.2 详细接线表](#3.2 详细接线表)
- 四、软件开发环境搭建
-
- [4.1 环境准备](#4.1 环境准备)
- [4.2 工程文件结构](#4.2 工程文件结构)
- 五、代码编写(详细版)
-
- [5.1 电机驱动模块](#5.1 电机驱动模块)
- [5.2 红外循迹模块](#5.2 红外循迹模块)
- [5.3 超声波避障模块](#5.3 超声波避障模块)
- [5.4 主函数(核心逻辑)](#5.4 主函数(核心逻辑))
- 六、硬件组装与调试
-
- [6.1 硬件组装步骤](#6.1 硬件组装步骤)
- [6.2 代码下载与调试](#6.2 代码下载与调试)
- [6.3 常见问题排查](#6.3 常见问题排查)
- 七、功能扩展建议
一、项目概述
本项目基于STM32F103C8T6最小系统板,实现一款具备循迹和避障功能的智能小车。核心功能包括:
- 循迹:通过红外循迹模块识别地面黑色轨迹线,自动沿轨迹行驶;
- 避障:通过超声波模块检测前方障碍物,当距离小于设定阈值时自动停车/绕行;
- 驱动控制:使用L298N电机驱动模块控制直流减速电机,实现小车前进、后退、转向。
本教程面向零基础小白,从硬件选型、电路接线、代码编写到实物调试,全程详细讲解,确保能落地实现。
二、硬件清单
| 器件名称 | 数量 | 作用 |
|---|---|---|
| STM32F103C8T6最小系统板 | 1 | 主控核心 |
| L298N电机驱动模块 | 1 | 驱动直流电机 |
| 直流减速电机 | 4 | 小车动力来源 |
| 智能小车底盘 | 1 | 承载所有硬件 |
| 红外循迹模块 | 3 | 识别循迹线(左/中/右) |
| HC-SR04超声波模块 | 1 | 检测前方障碍物距离 |
| 12V锂电池 | 1 | 供电(电机) |
| 5V稳压模块(LM1117) | 1 | 给STM32和传感器供电 |
| 杜邦线(公对公/公对母) | 若干 | 电路连接 |
| 面包板 | 1 | 临时接线 |
三、硬件电路设计与接线
3.1 核心电路原理
以下是系统整体硬件连接逻辑流程图,清晰展示各模块与STM32的交互关系:
供电
降压至5V
5V供电
5V供电
5V供电
GPIO控制
GPIO输入
Trig/Echo GPIO
驱动
12V锂电池
L298N电机驱动模块
LM1117稳压模块
STM32F103C8T6
红外循迹模块
HC-SR04超声波模块
直流减速电机
3.2 详细接线表
| STM32F103引脚 | 外接模块 | 模块引脚 | 备注 |
|---|---|---|---|
| PA0 | 红外循迹模块(左) | OUT | 低电平表示检测到黑线 |
| PA1 | 红外循迹模块(中) | OUT | 低电平表示检测到黑线 |
| PA2 | 红外循迹模块(右) | OUT | 低电平表示检测到黑线 |
| PA3 | HC-SR04 | Trig | 超声波触发引脚 |
| PA4 | HC-SR04 | Echo | 超声波回响引脚 |
| PB0 | L298N | IN1 | 左电机正转控制 |
| PB1 | L298N | IN2 | 左电机反转控制 |
| PB2 | L298N | IN3 | 右电机正转控制 |
| PB3 | L298N | IN4 | 右电机反转控制 |
| 5V | 红外循迹模块 | VCC | 传感器供电 |
| 5V | HC-SR04 | VCC | 传感器供电 |
| GND | 所有模块 | GND | 共地,必须接在一起 |
| 12V | L298N | 12V | 电机驱动供电 |
| L298N输出端 | 直流电机 | 电机接线 | 左电机接OUT1/OUT2,右电机接OUT3/OUT4 |
四、软件开发环境搭建
4.1 环境准备
- 下载并安装Keil MDK-ARM V5(推荐版本:5.38),注册激活(用于STM32代码编译);
- 下载STM32F103C8T6的器件库(Keil中安装Device Pack:STM32F1xx_DFP);
- 准备ST-Link V2下载器,安装对应的驱动程序;
- 配置Keil工程,选择STM32F103C8T6芯片,启用微库(MicroLIB)。
4.2 工程文件结构
STM32_Car_Project/
├── Core/
│ ├── Inc/
│ │ ├── main.h
│ │ ├── stm32f1xx_hal_conf.h
│ │ ├── motor.h // 电机驱动头文件
│ │ ├── track.h // 循迹模块头文件
│ │ ├── ultrasonic.h // 超声波模块头文件
│ ├── Src/
│ │ ├── main.c
│ │ ├── stm32f1xx_hal_msp.c
│ │ ├── stm32f1xx_it.c
│ │ ├── motor.c // 电机驱动源码
│ │ ├── track.c // 循迹模块源码
│ │ ├── ultrasonic.c // 超声波模块源码
├── Drivers/ // STM32官方驱动库
└── MDK-ARM/ // Keil工程文件
五、代码编写(详细版)
5.1 电机驱动模块
文件名:motor.h
c
#ifndef __MOTOR_H
#define __MOTOR_H
#include "stm32f1xx_hal.h"
// 电机控制引脚定义
#define IN1_PIN GPIO_PIN_0
#define IN1_PORT GPIOB
#define IN2_PIN GPIO_PIN_1
#define IN2_PORT GPIOB
#define IN3_PIN GPIO_PIN_2
#define IN3_PORT GPIOB
#define IN4_PIN GPIO_PIN_3
#define IN4_PORT GPIOB
// 电机动作枚举
typedef enum {
MOTOR_STOP = 0, // 停止
MOTOR_FORWARD, // 前进
MOTOR_BACKWARD, // 后退
MOTOR_LEFT, // 左转
MOTOR_RIGHT // 右转
} Motor_State;
// 函数声明
void Motor_GPIO_Init(void); // 电机引脚初始化
void Motor_Control(Motor_State state); // 电机动作控制
void Motor_Set_Speed(uint8_t left_speed, uint8_t right_speed); // 速度控制(PWM版,可选)
#endif
文件名:motor.c
c
#include "motor.h"
/**
* @brief 电机控制GPIO初始化
* @note PB0-PB3设置为推挽输出,速度50MHz
* @param 无
* @retval 无
*/
void Motor_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIOB时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
// IN1 (PB0) 配置
GPIO_InitStruct.Pin = IN1_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(IN1_PORT, &GPIO_InitStruct);
// IN2 (PB1) 配置
GPIO_InitStruct.Pin = IN2_PIN;
HAL_GPIO_Init(IN2_PORT, &GPIO_InitStruct);
// IN3 (PB2) 配置
GPIO_InitStruct.Pin = IN3_PIN;
HAL_GPIO_Init(IN3_PORT, &GPIO_InitStruct);
// IN4 (PB3) 配置
GPIO_InitStruct.Pin = IN4_PIN;
HAL_GPIO_Init(IN4_PORT, &GPIO_InitStruct);
// 初始状态:电机停止
HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET);
}
/**
* @brief 电机动作控制
* @note 控制小车前进、后退、左转、右转、停止
* @param state: 电机动作状态(MOTOR_STOP/MOTOR_FORWARD等)
* @retval 无
*/
void Motor_Control(Motor_State state)
{
switch(state)
{
case MOTOR_STOP: // 停止
HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET);
break;
case MOTOR_FORWARD: // 前进
// 左电机正转:IN1=1, IN2=0
HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
// 右电机正转:IN3=1, IN4=0
HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET);
break;
case MOTOR_BACKWARD: // 后退
HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_SET);
break;
case MOTOR_LEFT: // 左转(左电机停,右电机转)
HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET);
break;
case MOTOR_RIGHT: // 右转(右电机停,左电机转)
HAL_GPIO_WritePin(IN1_PORT, IN1_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(IN2_PORT, IN2_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(IN3_PORT, IN3_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(IN4_PORT, IN4_PIN, GPIO_PIN_RESET);
break;
default: // 默认停止
Motor_Control(MOTOR_STOP);
break;
}
}
/**
* @brief 电机速度控制(PWM版,可选)
* @note 如需精准调速,可将PB0-PB3配置为TIM3 PWM输出,此处为预留接口
* @param left_speed: 左电机速度(0-100)
* @param right_speed: 右电机速度(0-100)
* @retval 无
*/
void Motor_Set_Speed(uint8_t left_speed, uint8_t right_speed)
{
// 基础版教程暂不实现PWM调速,如需实现可配置TIM3_CH3-CH6输出PWM
// 此处仅做占位,实际使用时需补充TIM初始化和PWM输出代码
}
5.2 红外循迹模块
文件名:track.h
c
#ifndef __TRACK_H
#define __TRACK_H
#include "stm32f1xx_hal.h"
// 循迹模块引脚定义
#define TRACK_LEFT_PIN GPIO_PIN_0
#define TRACK_LEFT_PORT GPIOA
#define TRACK_MID_PIN GPIO_PIN_1
#define TRACK_MID_PORT GPIOA
#define TRACK_RIGHT_PIN GPIO_PIN_2
#define TRACK_RIGHT_PORT GPIOA
// 循迹状态枚举
typedef enum {
TRACK_LEFT = 0, // 左传感器检测到黑线
TRACK_MID, // 中传感器检测到黑线
TRACK_RIGHT, // 右传感器检测到黑线
TRACK_NONE, // 无传感器检测到黑线
TRACK_ALL // 所有传感器检测到黑线
} Track_State;
// 函数声明
void Track_GPIO_Init(void); // 循迹引脚初始化
Track_State Track_Get_State(void); // 获取循迹状态
#endif
文件名:track.c
c
#include "track.h"
/**
* @brief 循迹模块GPIO初始化
* @note PA0-PA2设置为上拉输入(红外模块OUT为低电平有效)
* @param 无
* @retval 无
*/
void Track_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 左循迹传感器(PA0)
GPIO_InitStruct.Pin = TRACK_LEFT_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉输入,无黑线时为高电平
HAL_GPIO_Init(TRACK_LEFT_PORT, &GPIO_InitStruct);
// 中循迹传感器(PA1)
GPIO_InitStruct.Pin = TRACK_MID_PIN;
HAL_GPIO_Init(TRACK_MID_PORT, &GPIO_InitStruct);
// 右循迹传感器(PA2)
GPIO_InitStruct.Pin = TRACK_RIGHT_PIN;
HAL_GPIO_Init(TRACK_RIGHT_PORT, &GPIO_InitStruct);
}
/**
* @brief 获取循迹状态
* @note 检测左/中/右传感器是否检测到黑线(低电平=检测到)
* @param 无
* @retval Track_State: 循迹状态
*/
Track_State Track_Get_State(void)
{
uint8_t left_state = HAL_GPIO_ReadPin(TRACK_LEFT_PORT, TRACK_LEFT_PIN);
uint8_t mid_state = HAL_GPIO_ReadPin(TRACK_MID_PORT, TRACK_MID_PIN);
uint8_t right_state = HAL_GPIO_ReadPin(TRACK_RIGHT_PORT, TRACK_RIGHT_PIN);
// 左传感器检测到黑线
if(left_state == GPIO_PIN_RESET && mid_state == GPIO_PIN_SET && right_state == GPIO_PIN_SET)
{
return TRACK_LEFT;
}
// 中传感器检测到黑线
else if(left_state == GPIO_PIN_SET && mid_state == GPIO_PIN_RESET && right_state == GPIO_PIN_SET)
{
return TRACK_MID;
}
// 右传感器检测到黑线
else if(left_state == GPIO_PIN_SET && mid_state == GPIO_PIN_SET && right_state == GPIO_PIN_RESET)
{
return TRACK_RIGHT;
}
// 无传感器检测到黑线
else if(left_state == GPIO_PIN_SET && mid_state == GPIO_PIN_SET && right_state == GPIO_PIN_SET)
{
return TRACK_NONE;
}
// 所有传感器检测到黑线
else if(left_state == GPIO_PIN_RESET && mid_state == GPIO_PIN_RESET && right_state == GPIO_PIN_RESET)
{
return TRACK_ALL;
}
// 默认状态
else
{
return TRACK_NONE;
}
}
5.3 超声波避障模块
文件名:ultrasonic.h
c
#ifndef __ULTRASONIC_H
#define __ULTRASONIC_H
#include "stm32f1xx_hal.h"
// 超声波模块引脚定义
#define TRIG_PIN GPIO_PIN_3
#define TRIG_PORT GPIOA
#define ECHO_PIN GPIO_PIN_4
#define ECHO_PORT GPIOA
// 避障阈值(单位:cm)
#define OBSTACLE_THRESHOLD 20
// 函数声明
void Ultrasonic_GPIO_Init(void); // 超声波引脚初始化
float Ultrasonic_Get_Distance(void); // 获取检测距离
uint8_t Ultrasonic_Check_Obstacle(void); // 检测是否有障碍物
#endif
文件名:ultrasonic.c
c
#include "ultrasonic.h"
#include "delay.h" // 需自行实现微秒级延时(HAL库默认只有毫秒级)
/**
* @brief 超声波模块GPIO初始化
* @note Trig(PA3)输出,Echo(PA4)输入
* @param 无
* @retval 无
*/
void Ultrasonic_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// Trig引脚(PA3)- 输出
GPIO_InitStruct.Pin = TRIG_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(TRIG_PORT, &GPIO_InitStruct);
// Echo引脚(PA4)- 输入
GPIO_InitStruct.Pin = ECHO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(ECHO_PORT, &GPIO_InitStruct);
// 初始状态:Trig置低
HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET);
}
/**
* @brief 微秒级延时函数(HAL库无原生us延时,需自行实现)
* @note 基于系统时钟,适用于72MHz主频
* @param us: 延时微秒数
* @retval 无
*/
void Delay_us(uint32_t us)
{
uint32_t delay = (HAL_RCC_GetHCLKFreq() / 1000000) * us / 5;
while(delay--);
}
/**
* @brief 获取超声波检测距离
* @note 原理:Trig发送10us高电平,检测Echo高电平持续时间,计算距离
* 距离 = 时间(us) * 声速(340m/s) / 2 / 10000
* @param 无
* @retval float: 检测距离(单位:cm)
*/
float Ultrasonic_Get_Distance(void)
{
uint32_t time = 0;
float distance = 0.0f;
// 发送触发信号:10us高电平
HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_SET);
Delay_us(10);
HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET);
// 等待Echo引脚变为高电平
while(HAL_GPIO_ReadPin(ECHO_PORT, ECHO_PIN) == GPIO_PIN_RESET);
// 记录Echo高电平持续时间
while(HAL_GPIO_ReadPin(ECHO_PORT, ECHO_PIN) == GPIO_PIN_SET)
{
time++;
Delay_us(1);
// 超时保护:避免死循环(最大检测距离约400cm)
if(time > 40000)
{
break;
}
}
// 计算距离
distance = (float)time * 0.017f; // 0.017 = 340/2/10000
return distance;
}
/**
* @brief 检测是否有障碍物
* @note 距离小于阈值则判定为有障碍物
* @param 无
* @retval uint8_t: 1=有障碍物,0=无障碍物
*/
uint8_t Ultrasonic_Check_Obstacle(void)
{
float distance = Ultrasonic_Get_Distance();
if(distance < OBSTACLE_THRESHOLD && distance > 0)
{
return 1;
}
else
{
return 0;
}
}
5.4 主函数(核心逻辑)
文件名:main.c
c
#include "main.h"
#include "motor.h"
#include "track.h"
#include "ultrasonic.h"
#include "stm32f1xx_hal.h"
/* 函数声明 */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
/**
* @brief 主函数:智能小车核心逻辑
* @note 流程:初始化 → 循环检测避障 → 循迹控制
* @param 无
* @retval int
*/
int main(void)
{
// 1. HAL库初始化
HAL_Init();
// 2. 系统时钟配置(72MHz)
SystemClock_Config();
// 3. GPIO初始化
MX_GPIO_Init();
// 4. 模块初始化
Motor_GPIO_Init(); // 电机初始化
Track_GPIO_Init(); // 循迹模块初始化
Ultrasonic_GPIO_Init(); // 超声波模块初始化
// 5. 初始状态:电机停止
Motor_Control(MOTOR_STOP);
HAL_Delay(1000); // 上电延时1秒
// 6. 主循环
while (1)
{
// 第一步:检测障碍物
if(Ultrasonic_Check_Obstacle() == 1)
{
// 有障碍物:停止 → 后退 → 左转(绕行)
Motor_Control(MOTOR_STOP);
HAL_Delay(500);
Motor_Control(MOTOR_BACKWARD);
HAL_Delay(1000);
Motor_Control(MOTOR_LEFT);
HAL_Delay(800);
}
else
{
// 无障碍物:循迹控制
Track_State track_state = Track_Get_State();
switch(track_state)
{
case TRACK_MID: // 中间检测到黑线:前进
Motor_Control(MOTOR_FORWARD);
break;
case TRACK_LEFT: // 左边检测到黑线:左转
Motor_Control(MOTOR_LEFT);
HAL_Delay(200); // 左转200ms后回到前进
Motor_Control(MOTOR_FORWARD);
break;
case TRACK_RIGHT: // 右边检测到黑线:右转
Motor_Control(MOTOR_RIGHT);
HAL_Delay(200); // 右转200ms后回到前进
Motor_Control(MOTOR_FORWARD);
break;
case TRACK_NONE: // 无黑线:停止
Motor_Control(MOTOR_STOP);
break;
case TRACK_ALL: // 全黑线:停止
Motor_Control(MOTOR_STOP);
break;
default:
Motor_Control(MOTOR_STOP);
break;
}
}
// 小延时,避免频繁检测
HAL_Delay(50);
}
}
/**
* @brief 系统时钟配置(72MHz)
* @note STM32F103C8T6默认外部8MHz晶振,倍频至72MHz
* @param 无
* @retval 无
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 配置外部高速晶振(HSE)
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; // 8MHz *9 =72MHz
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
// 配置系统时钟总线
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_DIV2; // APB1最大36MHz
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief GPIO初始化(全局)
* @note 此处为HAL库默认GPIO初始化入口,模块专属GPIO在各自文件中初始化
* @param 无
* @retval 无
*/
static void MX_GPIO_Init(void)
{
// 如需全局GPIO初始化,可在此添加
}
/**
* @brief 错误处理函数
* @note 死循环,方便调试
* @param 无
* @retval 无
*/
void Error_Handler(void)
{
__disable_irq();
while (1)
{
// 可添加LED闪烁等错误提示
}
}
#ifdef USE_FULL_ASSERT
/**
* @brief 断言失败处理函数
* @param file: 文件名
* @param line: 行号
* @retval 无
*/
void assert_failed(uint8_t *file, uint32_t line)
{
// 可添加调试信息输出
}
#endif
六、硬件组装与调试
6.1 硬件组装步骤
- 将4个直流减速电机安装到小车底盘,连接L298N驱动模块的OUT1-OUT4;
- 固定STM32最小系统板、L298N模块、锂电池到底盘,注意布局紧凑且不影响车轮转动;
- 将红外循迹模块安装在小车前部下方(距离地面1-2cm),左/中/右排列,对准地面轨迹线;
- 将HC-SR04超声波模块安装在小车前部居中位置,高度适中(避免地面反射);
- 按照接线表连接所有杜邦线,重点检查:
- 所有GND是否共地;
- 电机供电是否为12V,传感器/STM32供电是否为5V;
- 引脚是否接错(尤其是IN1-IN4,接错会导致电机转向异常)。
6.2 代码下载与调试
- 将ST-Link V2连接到STM32最小系统板(SWD接口:SWCLK/SWDIO/GND/3.3V);
- 在Keil中打开工程,编译无错误后,点击"Download"下载代码到STM32;
- 上电测试:
- 首先测试电机:短接红外传感器,观察小车是否前进/转向正常;
- 测试循迹:在地面贴黑色轨迹线,小车应沿轨迹行驶;
- 测试避障:在小车前方20cm内放置障碍物,小车应停止→后退→左转绕行。
6.3 常见问题排查
- 电机不转:检查L298N供电、IN1-IN4引脚是否接错、代码中电机控制逻辑;
- 循迹不灵敏:调整红外模块高度、检查上拉电阻是否启用、传感器引脚是否接反;
- 超声波检测不准:检查Trig/Echo引脚、微秒延时函数是否准确、模块安装角度;
- 小车跑偏:调整左右电机速度(可通过PWM实现)、检查车轮是否卡滞。
七、功能扩展建议
- 添加PWM调速:将电机控制引脚改为TIM3 PWM输出,精准控制左右电机速度,解决跑偏问题;
- 添加蓝牙遥控:增加HC-05蓝牙模块,通过手机APP控制小车;
- 添加OLED显示:实时显示检测距离、循迹状态;
- 优化避障逻辑:增加多个超声波模块,实现360°避障。
总结
- 本项目核心是STM32F103通过GPIO控制L298N驱动电机,结合红外循迹模块和超声波模块实现循迹避障功能,硬件接线需严格遵循引脚定义,避免接错导致器件损坏;
- 代码层面分为电机驱动、循迹检测、超声波避障三大模块,主函数通过循环检测障碍物和循迹状态,控制小车动作,零基础小白可按文件逐一编写代码,确保每个模块独立调试通过后再整合;
- 调试阶段需重点检查供电、引脚连接、传感器安装高度,这些是影响小车功能落地的关键因素,遇到问题可逐一模块排查,确保每个功能点单独验证通过。