STM32实战:基于FreeRTOS的智能小车多任务调度(循迹+避障+通信)

文章目录

    • 一、前言
      • [1.1 技术背景](#1.1 技术背景)
      • [1.2 本文目标](#1.2 本文目标)
      • [1.3 适合读者](#1.3 适合读者)
    • 二、环境准备
      • [2.1 硬件清单](#2.1 硬件清单)
      • [2.2 系统架构](#2.2 系统架构)
    • 三、核心实现
      • [3.1 项目结构](#3.1 项目结构)
      • [3.2 配置文件](#3.2 配置文件)
      • [3.3 电机驱动](#3.3 电机驱动)
      • [3.4 循迹模块](#3.4 循迹模块)
      • [3.5 避障模块](#3.5 避障模块)
      • [3.6 蓝牙通信](#3.6 蓝牙通信)
      • [3.7 主程序](#3.7 主程序)
    • 四、系统架构
    • 五、测试验证
      • [5.1 硬件连接](#5.1 硬件连接)
      • [5.2 串口输出示例](#5.2 串口输出示例)
    • 六、故障排查
      • [6.1 电机不转](#6.1 电机不转)
      • [6.2 循迹不稳定](#6.2 循迹不稳定)
      • [6.3 超声波测距不准](#6.3 超声波测距不准)
    • 七、总结
      • [7.1 核心知识点](#7.1 核心知识点)
      • [7.2 扩展方向](#7.2 扩展方向)
      • [7.3 学习资源](#7.3 学习资源)

一、前言

1.1 技术背景

智能小车是嵌入式学习和机器人入门的经典项目。一个功能完善的智能小车需要同时处理多种任务:循迹行驶避障检测速度控制无线通信状态显示等。在裸机环境下,这些任务的协调会变得复杂且难以维护。

FreeRTOS实时操作系统提供了多任务调度同步机制中断管理等功能,能够优雅地解决多任务并发问题。通过合理划分任务优先级和使用同步原语,可以构建出响应迅速、结构清晰的智能小车控制系统。

1.2 本文目标

通过本教程,你将学会:

  • 使用FreeRTOS构建多任务智能小车系统
  • 实现红外循迹和超声波避障功能
  • 使用PWM精确控制电机速度
  • 通过蓝牙/WiFi实现远程控制
  • 掌握任务间同步和通信机制

技术栈:

  • 硬件平台:STM32F103C8T6
  • 操作系统:FreeRTOS V10.3.1
  • 开发环境:STM32CubeIDE
  • 驱动模块:L298N电机驱动
  • 传感器:红外循迹模块、HC-SR04超声波
  • 通信:HC-05蓝牙模块
  • 调试工具:ST-Link V2

1.3 适合读者

本教程适合初级实践级读者,假设你已具备:

  • 基础C语言编程能力
  • STM32 GPIO、TIM、PWM的基本了解
  • FreeRTOS基础概念

二、环境准备

2.1 硬件清单

组件 型号/规格 数量 说明
主控 STM32F103C8T6 1 主控制器
电机驱动 L298N 1 双H桥驱动
减速电机 TT马达+轮子 4 带编码器(可选)
循迹模块 TCRT5000×5 1组 五路红外循迹
超声波 HC-SR04 1-2 避障检测
蓝牙模块 HC-05 1 无线通信
电池 18650×2 1组 7.4V供电
稳压模块 LM2596 1 5V/3.3V输出
底盘 亚克力/金属 1 小车底盘
ST-Link V2 1 程序下载

2.2 系统架构

复制代码
┌─────────────────────────────────────────────────────────┐
│                    智能小车控制系统                        │
├─────────────────────────────────────────────────────────┤
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐   │
│  │ 循迹任务  │ │ 避障任务  │ │ 运动控制  │ │ 通信任务  │   │
│  │TrackTask │ │AvoidTask │ │MotorTask │ │CommTask  │   │
│  │ Priority:3│ │Priority:4│ │Priority:5│ │Priority:2│   │
│  └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘   │
│       │            │            │            │          │
│       └────────────┴────────────┴────────────┘          │
│                         │                               │
│       ┌─────────────────┴─────────────────┐             │
│       ▼                                   ▼             │
│  ┌──────────┐                      ┌──────────┐        │
│  │ 事件标志组 │                      │ 消息队列  │        │
│  │EventGroup│                      │  Queue   │        │
│  └──────────┘                      └──────────┘        │
│                         │                               │
│  ┌─────────────────────────────────────────────────┐   │
│  │              FreeRTOS Kernel                     │   │
│  └─────────────────────────────────────────────────┘   │
│                         │                               │
│  ┌─────────────────────────────────────────────────┐   │
│  │              HAL Driver Layer                    │   │
│  │  GPIO | TIM/PWM | UART | EXTI | ADC             │   │
│  └─────────────────────────────────────────────────┘   │
│         │              │              │                │
│    ┌────┴────┐    ┌────┴────┐   ┌────┴────┐          │
│    │  电机    │    │ 传感器   │   │ 蓝牙模块 │          │
│    │ L298N   │    │循迹/超声 │   │ HC-05   │          │
│    └─────────┘    └─────────┘   └─────────┘          │
└─────────────────────────────────────────────────────────┘

三、核心实现

3.1 项目结构

复制代码
SmartCar_Project/
├── Core/
│   ├── Inc/
│   │   ├── main.h
│   │   ├── car_config.h
│   │   ├── motor.h
│   │   ├── track.h
│   │   ├── avoid.h
│   │   └── bluetooth.h
│   └── Src/
│       ├── main.c
│       ├── motor.c
│       ├── track.c
│       ├── avoid.c
│       └── bluetooth.c
└── README.md

3.2 配置文件

📄 创建文件:Core/Inc/car_config.h

c 复制代码
/**
 * @file car_config.h
 * @brief 智能小车配置文件
 * @version 1.0
 */

#ifndef __CAR_CONFIG_H
#define __CAR_CONFIG_H

#include "main.h"

/* ==================== 引脚配置 ==================== */

// 电机控制引脚
#define MOTOR_LEFT_EN_PORT      GPIOA
#define MOTOR_LEFT_EN_PIN       GPIO_PIN_0   // TIM2_CH1
#define MOTOR_LEFT_IN1_PORT     GPIOB
#define MOTOR_LEFT_IN1_PIN      GPIO_PIN_0
#define MOTOR_LEFT_IN2_PORT     GPIOB
#define MOTOR_LEFT_IN2_PIN      GPIO_PIN_1

#define MOTOR_RIGHT_EN_PORT     GPIOA
#define MOTOR_RIGHT_EN_PIN      GPIO_PIN_1   // TIM2_CH2
#define MOTOR_RIGHT_IN1_PORT    GPIOB
#define MOTOR_RIGHT_IN1_PIN     GPIO_PIN_10
#define MOTOR_RIGHT_IN2_PORT    GPIOB
#define MOTOR_RIGHT_IN2_PIN     GPIO_PIN_11

// 循迹传感器(5路)
#define TRACK_SENSOR_1_PORT     GPIOA
#define TRACK_SENSOR_1_PIN      GPIO_PIN_3
#define TRACK_SENSOR_2_PORT     GPIOA
#define TRACK_SENSOR_2_PIN      GPIO_PIN_4
#define TRACK_SENSOR_3_PORT     GPIOA
#define TRACK_SENSOR_3_PIN      GPIO_PIN_5
#define TRACK_SENSOR_4_PORT     GPIOA
#define TRACK_SENSOR_4_PIN      GPIO_PIN_6
#define TRACK_SENSOR_5_PORT     GPIOA
#define TRACK_SENSOR_5_PIN      GPIO_PIN_7

// 超声波(前)
#define ULTRASONIC_TRIG_PORT    GPIOB
#define ULTRASONIC_TRIG_PIN     GPIO_PIN_12
#define ULTRASONIC_ECHO_PORT    GPIOB
#define ULTRASONIC_ECHO_PIN     GPIO_PIN_13

// 蓝牙串口
#define BLUETOOTH_UART          huart2

/* ==================== 参数配置 ==================== */

// PWM配置
#define PWM_FREQUENCY           10000   // 10kHz
#define PWM_MAX_DUTY            1000    // 最大占空比

// 速度等级
#define SPEED_STOP              0
#define SPEED_LOW               300
#define SPEED_MEDIUM            600
#define SPEED_HIGH              900

// 循迹阈值
#define TRACK_THRESHOLD         500     // ADC阈值

// 避障距离(cm)
#define AVOID_DISTANCE_NEAR     10
#define AVOID_DISTANCE_MID      20
#define AVOID_DISTANCE_FAR      30

// 任务优先级
#define TASK_PRIORITY_IDLE      1
#define TASK_PRIORITY_COMM      2
#define TASK_PRIORITY_TRACK     3
#define TASK_PRIORITY_AVOID     4
#define TASK_PRIORITY_MOTOR     5

// 任务堆栈
#define STACK_SIZE_MOTOR        256
#define STACK_SIZE_TRACK        256
#define STACK_SIZE_AVOID        256
#define STACK_SIZE_COMM         256

/* ==================== 运行模式 ==================== */

typedef enum {
    MODE_STOP = 0,
    MODE_MANUAL,        // 手动控制
    MODE_TRACKING,      // 循迹模式
    MODE_AVOIDANCE,     // 避障模式
    MODE_AUTO           // 自动模式(循迹+避障)
} CarMode_t;

/* ==================== 运动命令 ==================== */

typedef enum {
    CMD_STOP = 0,
    CMD_FORWARD,
    CMD_BACKWARD,
    CMD_TURN_LEFT,
    CMD_TURN_RIGHT,
    CMD_ROTATE_LEFT,
    CMD_ROTATE_RIGHT,
    CMD_SPEED_UP,
    CMD_SPEED_DOWN,
    CMD_MODE_TRACK,
    CMD_MODE_AVOID,
    CMD_MODE_AUTO,
    CMD_MODE_MANUAL
} CarCommand_t;

/* ==================== 数据结构 ==================== */

typedef struct {
    int16_t leftSpeed;      // 左轮速度 (-1000 ~ 1000)
    int16_t rightSpeed;     // 右轮速度 (-1000 ~ 1000)
    CarMode_t mode;         // 当前模式
    uint8_t trackStatus;    // 循迹状态 (5位)
    uint16_t distance;      // 前方距离(cm)
    bool obstacleDetected;  // 检测到障碍物
} CarStatus_t;

#endif /* __CAR_CONFIG_H */

3.3 电机驱动

📄 创建文件:Core/Inc/motor.h

c 复制代码
/**
 * @file motor.h
 * @brief 电机驱动头文件
 * @version 1.0
 */

#ifndef __MOTOR_H
#define __MOTOR_H

#include "car_config.h"
#include "cmsis_os.h"

// 初始化电机
void Motor_Init(void);

// 设置电机速度
void Motor_SetLeftSpeed(int16_t speed);
void Motor_SetRightSpeed(int16_t speed);
void Motor_SetSpeed(int16_t leftSpeed, int16_t rightSpeed);

// 运动控制
void Motor_Forward(uint16_t speed);
void Motor_Backward(uint16_t speed);
void Motor_TurnLeft(uint16_t speed);
void Motor_TurnRight(uint16_t speed);
void Motor_RotateLeft(uint16_t speed);
void Motor_RotateRight(uint16_t speed);
void Motor_Stop(void);

// 启动电机控制任务
void MotorTask_Start(void);

// 发送电机命令
bool Motor_SendCommand(CarCommand_t cmd, uint16_t speed);

#endif /* __MOTOR_H */

📄 创建文件:Core/Src/motor.c

c 复制代码
/**
 * @file motor.c
 * @brief 电机驱动实现
 * @version 1.0
 */

#include "motor.h"
#include <stdio.h>
#include <string.h>

// 外部定时器句柄
extern TIM_HandleTypeDef htim2;

// 任务句柄
static osThreadId_t motorTaskHandle;
static osMessageQueueId_t motorCmdQueue;

// 当前速度
static int16_t currentLeftSpeed = 0;
static int16_t currentRightSpeed = 0;

// 命令结构
typedef struct {
    CarCommand_t cmd;
    uint16_t speed;
} MotorCmd_t;

/**
 * @brief 初始化电机PWM
 */
void Motor_Init(void)
{
    // 启动PWM
    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);  // 左轮
    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);  // 右轮
    
    // 初始状态:停止
    Motor_Stop();
    
    printf("[Motor] Initialized\r\n");
}

/**
 * @brief 设置左轮速度
 * @param speed -1000~1000,负值表示后退
 */
void Motor_SetLeftSpeed(int16_t speed)
{
    // 限幅
    if (speed > PWM_MAX_DUTY) speed = PWM_MAX_DUTY;
    if (speed < -PWM_MAX_DUTY) speed = -PWM_MAX_DUTY;
    
    currentLeftSpeed = speed;
    
    if (speed >= 0) {
        // 正转
        HAL_GPIO_WritePin(MOTOR_LEFT_IN1_PORT, MOTOR_LEFT_IN1_PIN, GPIO_PIN_SET);
        HAL_GPIO_WritePin(MOTOR_LEFT_IN2_PORT, MOTOR_LEFT_IN2_PIN, GPIO_PIN_RESET);
        __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, speed);
    } else {
        // 反转
        HAL_GPIO_WritePin(MOTOR_LEFT_IN1_PORT, MOTOR_LEFT_IN1_PIN, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(MOTOR_LEFT_IN2_PORT, MOTOR_LEFT_IN2_PIN, GPIO_PIN_SET);
        __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, -speed);
    }
}

/**
 * @brief 设置右轮速度
 * @param speed -1000~1000,负值表示后退
 */
void Motor_SetRightSpeed(int16_t speed)
{
    // 限幅
    if (speed > PWM_MAX_DUTY) speed = PWM_MAX_DUTY;
    if (speed < -PWM_MAX_DUTY) speed = -PWM_MAX_DUTY;
    
    currentRightSpeed = speed;
    
    if (speed >= 0) {
        // 正转
        HAL_GPIO_WritePin(MOTOR_RIGHT_IN1_PORT, MOTOR_RIGHT_IN1_PIN, GPIO_PIN_SET);
        HAL_GPIO_WritePin(MOTOR_RIGHT_IN2_PORT, MOTOR_RIGHT_IN2_PIN, GPIO_PIN_RESET);
        __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, speed);
    } else {
        // 反转
        HAL_GPIO_WritePin(MOTOR_RIGHT_IN1_PORT, MOTOR_RIGHT_IN1_PIN, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(MOTOR_RIGHT_IN2_PORT, MOTOR_RIGHT_IN2_PIN, GPIO_PIN_SET);
        __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, -speed);
    }
}

/**
 * @brief 设置双轮速度
 */
void Motor_SetSpeed(int16_t leftSpeed, int16_t rightSpeed)
{
    Motor_SetLeftSpeed(leftSpeed);
    Motor_SetRightSpeed(rightSpeed);
}

/**
 * @brief 前进
 */
void Motor_Forward(uint16_t speed)
{
    if (speed > PWM_MAX_DUTY) speed = PWM_MAX_DUTY;
    Motor_SetSpeed(speed, speed);
    printf("[Motor] Forward: %d\r\n", speed);
}

/**
 * @brief 后退
 */
void Motor_Backward(uint16_t speed)
{
    if (speed > PWM_MAX_DUTY) speed = PWM_MAX_DUTY;
    Motor_SetSpeed(-(int16_t)speed, -(int16_t)speed);
    printf("[Motor] Backward: %d\r\n", speed);
}

/**
 * @brief 左转(差速)
 */
void Motor_TurnLeft(uint16_t speed)
{
    if (speed > PWM_MAX_DUTY) speed = PWM_MAX_DUTY;
    Motor_SetSpeed(speed / 2, speed);
    printf("[Motor] Turn Left\r\n");
}

/**
 * @brief 右转(差速)
 */
void Motor_TurnRight(uint16_t speed)
{
    if (speed > PWM_MAX_DUTY) speed = PWM_MAX_DUTY;
    Motor_SetSpeed(speed, speed / 2);
    printf("[Motor] Turn Right\r\n");
}

/**
 * @brief 原地左转
 */
void Motor_RotateLeft(uint16_t speed)
{
    if (speed > PWM_MAX_DUTY) speed = PWM_MAX_DUTY;
    Motor_SetSpeed(-(int16_t)speed, speed);
    printf("[Motor] Rotate Left\r\n");
}

/**
 * @brief 原地右转
 */
void Motor_RotateRight(uint16_t speed)
{
    if (speed > PWM_MAX_DUTY) speed = PWM_MAX_DUTY;
    Motor_SetSpeed(speed, -(int16_t)speed);
    printf("[Motor] Rotate Right\r\n");
}

/**
 * @brief 停止
 */
void Motor_Stop(void)
{
    Motor_SetSpeed(0, 0);
    printf("[Motor] Stop\r\n");
}

/**
 * @brief 电机控制任务
 */
static void MotorTask(void *argument)
{
    MotorCmd_t cmd;
    uint16_t currentSpeed = SPEED_MEDIUM;
    
    printf("[MotorTask] Started\r\n");
    
    for (;;) {
        // 接收命令
        if (osMessageQueueGet(motorCmdQueue, &cmd, NULL, 10) == osOK) {
            switch (cmd.cmd) {
                case CMD_FORWARD:
                    Motor_Forward(cmd.speed);
                    break;
                case CMD_BACKWARD:
                    Motor_Backward(cmd.speed);
                    break;
                case CMD_TURN_LEFT:
                    Motor_TurnLeft(cmd.speed);
                    break;
                case CMD_TURN_RIGHT:
                    Motor_TurnRight(cmd.speed);
                    break;
                case CMD_ROTATE_LEFT:
                    Motor_RotateLeft(cmd.speed);
                    break;
                case CMD_ROTATE_RIGHT:
                    Motor_RotateRight(cmd.speed);
                    break;
                case CMD_SPEED_UP:
                    currentSpeed += 100;
                    if (currentSpeed > PWM_MAX_DUTY) currentSpeed = PWM_MAX_DUTY;
                    Motor_SetSpeed(currentSpeed, currentSpeed);
                    printf("[Motor] Speed up: %d\r\n", currentSpeed);
                    break;
                case CMD_SPEED_DOWN:
                    currentSpeed -= 100;
                    if (currentSpeed < 100) currentSpeed = 100;
                    Motor_SetSpeed(currentSpeed, currentSpeed);
                    printf("[Motor] Speed down: %d\r\n", currentSpeed);
                    break;
                case CMD_STOP:
                default:
                    Motor_Stop();
                    break;
            }
        }
        
        osDelay(50);
    }
}

/**
 * @brief 启动电机任务
 */
void MotorTask_Start(void)
{
    motorCmdQueue = osMessageQueueNew(10, sizeof(MotorCmd_t), NULL);
    
    const osThreadAttr_t taskAttributes = {
        .name = "MotorTask",
        .priority = TASK_PRIORITY_MOTOR,
        .stack_size = STACK_SIZE_MOTOR * 4
    };
    
    motorTaskHandle = osThreadNew(MotorTask, NULL, &taskAttributes);
}

/**
 * @brief 发送电机命令
 */
bool Motor_SendCommand(CarCommand_t cmd, uint16_t speed)
{
    MotorCmd_t motorCmd = {.cmd = cmd, .speed = speed};
    osStatus_t status = osMessageQueuePut(motorCmdQueue, &motorCmd, 0, 100);
    return (status == osOK);
}

3.4 循迹模块

📄 创建文件:Core/Inc/track.h

c 复制代码
/**
 * @file track.h
 * @brief 循迹模块头文件
 * @version 1.0
 */

#ifndef __TRACK_H
#define __TRACK_H

#include "car_config.h"

// 启动循迹任务
void TrackTask_Start(void);

// 获取循迹状态
uint8_t Track_GetStatus(void);

#endif /* __TRACK_H */

📄 创建文件:Core/Src/track.c

c 复制代码
/**
 * @file track.c
 * @brief 循迹模块实现
 * @version 1.0
 */

#include "track.h"
#include "motor.h"
#include <stdio.h>

// 任务句柄
static osThreadId_t trackTaskHandle;

// 循迹传感器引脚表
static struct {
    GPIO_TypeDef *port;
    uint16_t pin;
} trackSensors[5] = {
    {TRACK_SENSOR_1_PORT, TRACK_SENSOR_1_PIN},
    {TRACK_SENSOR_2_PORT, TRACK_SENSOR_2_PIN},
    {TRACK_SENSOR_3_PORT, TRACK_SENSOR_3_PIN},
    {TRACK_SENSOR_4_PORT, TRACK_SENSOR_4_PIN},
    {TRACK_SENSOR_5_PORT, TRACK_SENSOR_5_PIN}
};

// 当前循迹状态
static uint8_t trackStatus = 0;

/**
 * @brief 读取循迹传感器
 * @return 5位状态值,1表示检测到黑线
 */
static uint8_t Track_ReadSensors(void)
{
    uint8_t status = 0;
    
    for (int i = 0; i < 5; i++) {
        GPIO_PinState state = HAL_GPIO_ReadPin(trackSensors[i].port, trackSensors[i].pin);
        // 假设低电平表示检测到黑线
        if (state == GPIO_PIN_RESET) {
            status |= (1 << i);
        }
    }
    
    return status;
}

/**
 * @brief 循迹控制算法
 * @param status 传感器状态
 */
static void Track_Control(uint8_t status)
{
    // 状态解析(5个传感器,从左到右:4-3-2-1-0)
    // 0b00100 = 中间传感器检测到线,直行
    // 0b01000 = 偏左,需要右转
    // 0b00010 = 偏右,需要左转
    
    switch (status) {
        case 0b00100:  // 中间
        case 0b01110:  // 中间三个
        case 0b11111:  // 全部(十字路口)
            Motor_SendCommand(CMD_FORWARD, SPEED_MEDIUM);
            break;
            
        case 0b01000:  // 左偏
        case 0b11000:  // 左偏严重
        case 0b11100:  // 左偏很严重
            Motor_SendCommand(CMD_TURN_RIGHT, SPEED_MEDIUM);
            break;
            
        case 0b00010:  // 右偏
        case 0b00011:  // 右偏严重
        case 0b00111:  // 右偏很严重
            Motor_SendCommand(CMD_TURN_LEFT, SPEED_MEDIUM);
            break;
            
        case 0b10000:  // 最左
            Motor_SendCommand(CMD_ROTATE_RIGHT, SPEED_LOW);
            break;
            
        case 0b00001:  // 最右
            Motor_SendCommand(CMD_ROTATE_LEFT, SPEED_LOW);
            break;
            
        case 0b00000:  // 全部未检测到(脱线)
            // 保持上次动作或停止
            printf("[Track] Lost line!\r\n");
            Motor_SendCommand(CMD_STOP, 0);
            break;
            
        default:
            Motor_SendCommand(CMD_FORWARD, SPEED_LOW);
            break;
    }
}

/**
 * @brief 循迹任务
 */
static void TrackTask(void *argument)
{
    printf("[TrackTask] Started\r\n");
    
    for (;;) {
        // 读取传感器
        trackStatus = Track_ReadSensors();
        
        // 只有在循迹模式下才控制电机
        // 实际应用中通过事件标志或全局变量判断模式
        Track_Control(trackStatus);
        
        osDelay(50);  // 20Hz控制频率
    }
}

/**
 * @brief 启动循迹任务
 */
void TrackTask_Start(void)
{
    const osThreadAttr_t taskAttributes = {
        .name = "TrackTask",
        .priority = TASK_PRIORITY_TRACK,
        .stack_size = STACK_SIZE_TRACK * 4
    };
    
    trackTaskHandle = osThreadNew(TrackTask, NULL, &taskAttributes);
}

/**
 * @brief 获取循迹状态
 */
uint8_t Track_GetStatus(void)
{
    return trackStatus;
}

3.5 避障模块

📄 创建文件:Core/Inc/avoid.h

c 复制代码
/**
 * @file avoid.h
 * @brief 避障模块头文件
 * @version 1.0
 */

#ifndef __AVOID_H
#define __AVOID_H

#include "car_config.h"

// 启动避障任务
void AvoidTask_Start(void);

// 获取距离
uint16_t Avoid_GetDistance(void);

#endif /* __AVOID_H */

📄 创建文件:Core/Src/avoid.c

c 复制代码
/**
 * @file avoid.c
 * @brief 避障模块实现
 * @version 1.0
 */

#include "avoid.h"
#include "motor.h"
#include <stdio.h>

// 任务句柄
static osThreadId_t avoidTaskHandle;

// 外部定时器句柄
extern TIM_HandleTypeDef htim3;

// 当前距离
static uint16_t currentDistance = 0;

/**
 * @brief 超声波测距
 * @return 距离(cm)
 */
static uint16_t Ultrasonic_Measure(void)
{
    uint32_t timeout;
    uint32_t startTime, endTime;
    uint32_t duration;
    
    // 发送触发信号
    HAL_GPIO_WritePin(ULTRASONIC_TRIG_PORT, ULTRASONIC_TRIG_PIN, GPIO_PIN_SET);
    
    // 延时10us
    __HAL_TIM_SET_COUNTER(&htim3, 0);
    while (__HAL_TIM_GET_COUNTER(&htim3) < 10);
    
    HAL_GPIO_WritePin(ULTRASONIC_TRIG_PORT, ULTRASONIC_TRIG_PIN, GPIO_PIN_RESET);
    
    // 等待ECHO变高
    timeout = 10000;
    while (HAL_GPIO_ReadPin(ULTRASONIC_ECHO_PORT, ULTRASONIC_ECHO_PIN) == GPIO_PIN_RESET) {
        if (--timeout == 0) return 0;
    }
    
    // 记录开始时间
    __HAL_TIM_SET_COUNTER(&htim3, 0);
    startTime = HAL_GetTick();
    
    // 等待ECHO变低
    timeout = 10000;
    while (HAL_GPIO_ReadPin(ULTRASONIC_ECHO_PORT, ULTRASONIC_ECHO_PIN) == GPIO_PIN_SET) {
        if (--timeout == 0) return 0;
    }
    
    // 计算持续时间
    duration = __HAL_TIM_GET_COUNTER(&htim3);
    
    // 计算距离:duration(us) / 58 = cm
    uint16_t distance = duration / 58;
    
    return distance;
}

/**
 * @brief 避障控制
 */
static void Avoid_Control(uint16_t distance)
{
    if (distance < AVOID_DISTANCE_NEAR) {
        // 紧急避障:后退并转向
        printf("[Avoid] Obstacle too close: %d cm\r\n", distance);
        Motor_SendCommand(CMD_BACKWARD, SPEED_LOW);
        osDelay(300);
        Motor_SendCommand(CMD_ROTATE_LEFT, SPEED_MEDIUM);
        osDelay(500);
    } else if (distance < AVOID_DISTANCE_MID) {
        // 减速并转向
        printf("[Avoid] Obstacle near: %d cm\r\n", distance);
        Motor_SendCommand(CMD_TURN_LEFT, SPEED_LOW);
    } else if (distance < AVOID_DISTANCE_FAR) {
        // 轻微转向
        printf("[Avoid] Obstacle detected: %d cm\r\n", distance);
        Motor_SendCommand(CMD_TURN_LEFT, SPEED_MEDIUM);
    }
    // 否则继续前进
}

/**
 * @brief 避障任务
 */
static void AvoidTask(void *argument)
{
    printf("[AvoidTask] Started\r\n");
    
    for (;;) {
        // 测量距离
        currentDistance = Ultrasonic_Measure();
        
        // 避障控制
        if (currentDistance > 0 && currentDistance < AVOID_DISTANCE_FAR) {
            Avoid_Control(currentDistance);
        }
        
        osDelay(100);  // 10Hz测量频率
    }
}

/**
 * @brief 启动避障任务
 */
void AvoidTask_Start(void)
{
    const osThreadAttr_t taskAttributes = {
        .name = "AvoidTask",
        .priority = TASK_PRIORITY_AVOID,
        .stack_size = STACK_SIZE_AVOID * 4
    };
    
    avoidTaskHandle = osThreadNew(AvoidTask, NULL, &taskAttributes);
}

/**
 * @brief 获取距离
 */
uint16_t Avoid_GetDistance(void)
{
    return currentDistance;
}

3.6 蓝牙通信

📄 创建文件:Core/Inc/bluetooth.h

c 复制代码
/**
 * @file bluetooth.h
 * @brief 蓝牙通信头文件
 * @version 1.0
 */

#ifndef __BLUETOOTH_H
#define __BLUETOOTH_H

#include "car_config.h"

// 启动蓝牙任务
void BluetoothTask_Start(void);

#endif /* __BLUETOOTH_H */

📄 创建文件:Core/Src/bluetooth.c

c 复制代码
/**
 * @file bluetooth.c
 * @brief 蓝牙通信实现
 * @version 1.0
 */

#include "bluetooth.h"
#include "motor.h"
#include <stdio.h>
#include <string.h>

// 任务句柄
static osThreadId_t bluetoothTaskHandle;

// 外部串口句柄
extern UART_HandleTypeDef huart2;

/**
 * @brief 解析蓝牙命令
 * @param cmd 命令字符
 */
static void Bluetooth_ParseCommand(char cmd)
{
    switch (cmd) {
        case 'F':  // 前进
        case 'f':
            Motor_SendCommand(CMD_FORWARD, SPEED_MEDIUM);
            break;
            
        case 'B':  // 后退
        case 'b':
            Motor_SendCommand(CMD_BACKWARD, SPEED_MEDIUM);
            break;
            
        case 'L':  // 左转
        case 'l':
            Motor_SendCommand(CMD_TURN_LEFT, SPEED_MEDIUM);
            break;
            
        case 'R':  // 右转
        case 'r':
            Motor_SendCommand(CMD_TURN_RIGHT, SPEED_MEDIUM);
            break;
            
        case 'S':  // 停止
        case 's':
            Motor_SendCommand(CMD_STOP, 0);
            break;
            
        case '+':  // 加速
            Motor_SendCommand(CMD_SPEED_UP, 0);
            break;
            
        case '-':  // 减速
            Motor_SendCommand(CMD_SPEED_DOWN, 0);
            break;
            
        default:
            break;
    }
}

/**
 * @brief 蓝牙任务
 */
static void BluetoothTask(void *argument)
{
    uint8_t rxData;
    
    printf("[BluetoothTask] Started\r\n");
    
    // 启动接收中断
    HAL_UART_Receive_IT(&huart2, &rxData, 1);
    
    for (;;) {
        // 接收数据(使用队列或全局变量)
        // 这里简化处理,实际应该使用中断+队列
        
        osDelay(100);
    }
}

/**
 * @brief 启动蓝牙任务
 */
void BluetoothTask_Start(void)
{
    const osThreadAttr_t taskAttributes = {
        .name = "BluetoothTask",
        .priority = TASK_PRIORITY_COMM,
        .stack_size = STACK_SIZE_COMM * 4
    };
    
    bluetoothTaskHandle = osThreadNew(BluetoothTask, NULL, &taskAttributes);
}

/**
 * @brief UART接收回调
 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART2) {
        static uint8_t rxData;
        
        // 解析命令
        Bluetooth_ParseCommand((char)rxData);
        
        // 重新启动接收
        HAL_UART_Receive_IT(&huart2, &rxData, 1);
    }
}

3.7 主程序

📄 创建文件:Core/Src/main.c

c 复制代码
/**
 * @file main.c
 * @brief 智能小车主程序
 * @version 1.0
 */

#include "main.h"
#include "cmsis_os.h"
#include "motor.h"
#include "track.h"
#include "avoid.h"
#include "bluetooth.h"
#include <stdio.h>

// 外设句柄
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim3;

/**
 * @brief 重定向printf到串口
 */
int _write(int file, char *ptr, int len)
{
    HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY);
    return len;
}

/**
 * @brief 系统入口
 */
int main(void)
{
    // HAL初始化
    HAL_Init();
    
    // 配置系统时钟
    SystemClock_Config();
    
    // 初始化外设
    MX_GPIO_Init();
    MX_TIM2_Init();
    MX_TIM3_Init();
    MX_USART1_UART_Init();
    MX_USART2_UART_Init();
    
    printf("\r\n========================================\r\n");
    printf("   Smart Car Control System\r\n");
    printf("   FreeRTOS Multi-Task\r\n");
    printf("========================================\r\n\r\n");
    
    // 初始化电机
    Motor_Init();
    
    // 启动FreeRTOS
    osKernelInitialize();
    
    // 创建任务
    printf("[Main] Creating tasks...\r\n");
    MotorTask_Start();
    TrackTask_Start();
    AvoidTask_Start();
    BluetoothTask_Start();
    
    printf("[Main] Starting scheduler...\r\n\r\n");
    
    // 启动调度器
    osKernelStart();
    
    while (1);
}

// 外设初始化函数...
// MX_TIM2_Init(), MX_TIM3_Init(), MX_USART1_UART_Init(), MX_USART2_UART_Init()
// SystemClock_Config(), MX_GPIO_Init(), Error_Handler()

四、系统架构

🔌 Hardware
🔄 FreeRTOS Kernel
避障命令
循迹命令
遥控命令
PWM
触发/回波
GPIO
UART
MotorTask

Priority:5
AvoidTask

Priority:4
TrackTask

Priority:3
CommTask

Priority:2
L298N

电机驱动
红外传感器

×5
超声波

HC-SR04
蓝牙

HC-05


五、测试验证

5.1 硬件连接

复制代码
STM32F103          L298N
─────────          ─────
PA0 (TIM2_CH1)  ──> ENA
PA1 (TIM2_CH2)  ──> ENB
PB0             ──> IN1
PB1             ──> IN2
PB10            ──> IN3
PB11            ──> IN4

STM32F103          红外循迹
─────────          ─────────
PA3-PA7         <── 5路传感器

STM32F103          超声波
─────────          ─────────
PB12            ──> TRIG
PB13            <── ECHO

STM32F103          HC-05
─────────          ─────
PA2 (USART2_TX) ──> RX
PA3 (USART2_RX) <── TX

5.2 串口输出示例

复制代码
========================================
   Smart Car Control System
   FreeRTOS Multi-Task
========================================

[Motor] Initialized
[MotorTask] Started
[TrackTask] Started
[AvoidTask] Started
[BluetoothTask] Started
[Main] Starting scheduler...

[Motor] Forward: 600
[Track] Lost line!
[Motor] Stop
[Avoid] Obstacle detected: 25 cm
[Motor] Turn Left

六、故障排查

6.1 电机不转

  • 检查L298N供电(5V逻辑电源+电机电源)
  • 检查PWM频率和占空比
  • 检查IN1/IN2控制逻辑

6.2 循迹不稳定

  • 调整传感器高度(距地面1-2cm)
  • 调整阈值或改用ADC读取
  • 降低车速

6.3 超声波测距不准

  • 检查触发信号时序
  • 增加测量间隔(避免余波干扰)
  • 检查定时器精度

七、总结

7.1 核心知识点

  1. FreeRTOS多任务:任务优先级、调度策略
  2. PWM电机控制:精确调速、方向控制
  3. 传感器融合:红外+超声波协同工作
  4. 任务通信:队列、事件标志

7.2 扩展方向

  • 编码器测速:添加PID速度闭环
  • 路径规划:实现更复杂的导航算法
  • 视觉识别:集成OpenMV摄像头
  • ROS集成:接入机器人操作系统

7.3 学习资源

相关推荐
俊基科技2 小时前
FT‑02 全双工语音通话消回音测试底座:设计原理、性能特性与应用场景
嵌入式硬件·音频测试·语音处理模块·全双工通话·声学回声消除
zmj3203242 小时前
I2C总线协议详细介绍
单片机·嵌入式硬件·i2c·总线协议
SDAU20052 小时前
Arduino编程CH552
c语言·开发语言·单片机
快乐的划水a3 小时前
单片机仿Linux驱动开发(一)
linux·驱动开发·单片机
实在太懒于是不想取名3 小时前
STM32N6的开发日记(5):数字摄像头接口像素流水线DCMIPP让MCU拥有高性能摄像头资源
stm32·单片机·嵌入式硬件
天涯铭3 小时前
深入浅出:单片机I/O模式与上拉电阻
单片机·上拉电阻·gpio输出
iCxhust4 小时前
8088汇编测试程序 (MASM/TASM) — 显示 “HELLO 8088!“ + “LCD1602 OK“
汇编·单片机·嵌入式硬件·51单片机·微机原理
Bryce_Zhou4 小时前
STM32U5A9J-DK 介绍
stm32·单片机·嵌入式硬件
Bryce_Zhou4 小时前
stm32cubex创建freertos
单片机