STM32项目实战:基于STM32F103的智能循迹避障小车

文章目录

一、项目概述

本项目基于STM32F103C8T6最小系统板,实现一款具备循迹和避障功能的智能小车。核心功能包括:

  1. 循迹:通过红外循迹模块识别地面黑色轨迹线,自动沿轨迹行驶;
  2. 避障:通过超声波模块检测前方障碍物,当距离小于设定阈值时自动停车/绕行;
  3. 驱动控制:使用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 环境准备

  1. 下载并安装Keil MDK-ARM V5(推荐版本:5.38),注册激活(用于STM32代码编译);
  2. 下载STM32F103C8T6的器件库(Keil中安装Device Pack:STM32F1xx_DFP);
  3. 准备ST-Link V2下载器,安装对应的驱动程序;
  4. 配置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 硬件组装步骤

  1. 将4个直流减速电机安装到小车底盘,连接L298N驱动模块的OUT1-OUT4;
  2. 固定STM32最小系统板、L298N模块、锂电池到底盘,注意布局紧凑且不影响车轮转动;
  3. 将红外循迹模块安装在小车前部下方(距离地面1-2cm),左/中/右排列,对准地面轨迹线;
  4. 将HC-SR04超声波模块安装在小车前部居中位置,高度适中(避免地面反射);
  5. 按照接线表连接所有杜邦线,重点检查:
    • 所有GND是否共地;
    • 电机供电是否为12V,传感器/STM32供电是否为5V;
    • 引脚是否接错(尤其是IN1-IN4,接错会导致电机转向异常)。

6.2 代码下载与调试

  1. 将ST-Link V2连接到STM32最小系统板(SWD接口:SWCLK/SWDIO/GND/3.3V);
  2. 在Keil中打开工程,编译无错误后,点击"Download"下载代码到STM32;
  3. 上电测试:
    • 首先测试电机:短接红外传感器,观察小车是否前进/转向正常;
    • 测试循迹:在地面贴黑色轨迹线,小车应沿轨迹行驶;
    • 测试避障:在小车前方20cm内放置障碍物,小车应停止→后退→左转绕行。

6.3 常见问题排查

  1. 电机不转:检查L298N供电、IN1-IN4引脚是否接错、代码中电机控制逻辑;
  2. 循迹不灵敏:调整红外模块高度、检查上拉电阻是否启用、传感器引脚是否接反;
  3. 超声波检测不准:检查Trig/Echo引脚、微秒延时函数是否准确、模块安装角度;
  4. 小车跑偏:调整左右电机速度(可通过PWM实现)、检查车轮是否卡滞。

七、功能扩展建议

  1. 添加PWM调速:将电机控制引脚改为TIM3 PWM输出,精准控制左右电机速度,解决跑偏问题;
  2. 添加蓝牙遥控:增加HC-05蓝牙模块,通过手机APP控制小车;
  3. 添加OLED显示:实时显示检测距离、循迹状态;
  4. 优化避障逻辑:增加多个超声波模块,实现360°避障。

总结

  1. 本项目核心是STM32F103通过GPIO控制L298N驱动电机,结合红外循迹模块和超声波模块实现循迹避障功能,硬件接线需严格遵循引脚定义,避免接错导致器件损坏;
  2. 代码层面分为电机驱动、循迹检测、超声波避障三大模块,主函数通过循环检测障碍物和循迹状态,控制小车动作,零基础小白可按文件逐一编写代码,确保每个模块独立调试通过后再整合;
  3. 调试阶段需重点检查供电、引脚连接、传感器安装高度,这些是影响小车功能落地的关键因素,遇到问题可逐一模块排查,确保每个功能点单独验证通过。
相关推荐
v先v关v住v获v取2 小时前
NEXUS卡丁车前悬挂控制臂的结构建模与多工况受力分析5张cad+三维图+设计说明书
科技·单片机·51单片机
luoshanxuli20102 小时前
ESP-IDF 简介
嵌入式硬件·物联网·系统架构
GodKK老神灭2 小时前
SWD读取AP寄存器完整流程
单片机·keil
羽获飞2 小时前
从零开始学嵌入式之STM32——27.基于STM32F103C8T6MCU的寄存器方式实现按键调整PWM占空比,调整输出功率
stm32·单片机·嵌入式硬件
学嵌入式的小杨同学2 小时前
STM32 进阶封神之路(十五):DHT11 单总线实战 —— 温湿度检测从时序解析到代码落地(库函数 + 寄存器)
vscode·stm32·单片机·嵌入式硬件·mcu·智能硬件·pcb工艺
QYQ_11273 小时前
嵌入式学习——51单片机
嵌入式硬件·学习·51单片机
2501_937721753 小时前
stm32
stm32·单片机·嵌入式硬件
DLGXY3 小时前
STM32(二十五)——修改主频、睡眠模式、停机模式、待机模式
stm32·单片机·嵌入式硬件
BackCatK Chen4 小时前
2026年STM32新品密集发布:C5系列量产上市,低功耗无线MCU同步迭代
stm32·单片机·嵌入式硬件·stm32c5·mcu新品·stm32wl3r