文章目录
-
- 一、前言
-
- [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 核心知识点
- FreeRTOS多任务:任务优先级、调度策略
- PWM电机控制:精确调速、方向控制
- 传感器融合:红外+超声波协同工作
- 任务通信:队列、事件标志
7.2 扩展方向
- 编码器测速:添加PID速度闭环
- 路径规划:实现更复杂的导航算法
- 视觉识别:集成OpenMV摄像头
- ROS集成:接入机器人操作系统