基于STM32的遥控小车设计

基于STM32的遥控小车设计

一、系统概述与核心功能

1. 系统定位

基于STM32的遥控小车以“无线控制-电机驱动-环境感知-智能避障”为核心,支持红外/NRF24L01/蓝牙多种遥控方式,实现前进/后退/转向/调速等基本运动控制,可扩展循迹、避障、WiFi图传等功能,适用于机器人竞赛、智能小车教学、DIY娱乐等场景。

2. 核心功能模块

模块 功能描述
遥控通信 支持红外遥控(NEC协议)、NRF24L01无线、蓝牙HC-05三种控制方式,可切换
电机驱动 双直流电机驱动(L298N/TB6612),支持PWM无级调速、正反转、刹车
运动控制 基本运动(前进/后退/左转/右转/停止)、速度调节、运动模式切换(竞速/平稳)
环境感知 红外避障(E18-D80NK)、循迹(TCRT5000)、超声波测距(HC-SR04)可选
状态反馈 OLED显示(速度/模式/电量)、LED指示灯、蜂鸣器提示音
低功耗设计 待机电流<10mA,无操作5分钟自动休眠,遥控唤醒

二、硬件设计方案

1. 核心硬件选型

模块 型号 关键参数 接口方式
主控MCU STM32F103C8T6 72MHz Cortex-M3,64KB Flash,20KB RAM,4路PWM,多路ADC/USART 核心控制器
遥控模块 红外+蓝牙+NRF24L01 红外HS0038B(NEC协议)、蓝牙HC-05、NRF24L01(2.4GHz,100m) 红外:PA1(输入捕获)
蓝牙:USART1
NRF:SPI1
电机驱动 TB6612FNG(优选) 双H桥,1.2A/路,内置过热保护,效率>90%,体积小 IN1-4:PB0-PB3
PWMA/B:TIM2
直流电机 TT马达+减速箱 减速比1:48,额定电压6V,转速200RPM,扭矩1.2kg·cm 直流电机×2
电源模块 18650锂电池×2 7.4V/2200mAh,带充放电保护,续航>2小时 7.4V直供电机,5V降压给MCU
避障模块 E18-D80NK红外避障 检测距离3-80cm可调,数字输出,抗干扰强 GPIO输入(PC0-PC1)
显示模块 OLED 12864(I2C) 0.96寸,128×64像素,显示速度/模式/电量 I2C1(PB6=SCL,PB7=SDA)
车身结构 亚克力/铝合金底盘 四轮驱动/两轮驱动可选,万向轮×1,电机固定座,电池仓 机械结构

2. 硬件电路设计要点

2.1 核心电路连接

STM32F103C8T6
├── 电机驱动(TB6612FNG)
│   ├── IN1=PB0, IN2=PB1(左电机方向)
│   ├── IN3=PB2, IN4=PB3(右电机方向)
│   ├── PWMA=PA8(TIM1_CH1,左电机PWM)
│   └── PWMB=PA9(TIM1_CH2,右电机PWM)
├── 遥控模块
│   ├── 红外接收:PA1(TIM2_CH2输入捕获)
│   ├── 蓝牙HC-05:PA9-TX, PA10-RX(USART1)
│   └── NRF24L01:SPI1(PA5-SCK, PA6-MISO, PA7-MOSI, PA4-CS)
├── 传感器
│   ├── 红外避障×2:PC0(左), PC1(右)
│   └── 循迹TCRT5000×3:PC2-PC4
└── 电源系统
    ├── 7.4V锂电池→电机驱动
    └── LM2596降压→5V→AMS1117→3.3V(MCU/传感器)

2.2 电机驱动电路(TB6612FNG)

TB6612FNG引脚连接:
- VM:7.4V(电机电源)
- VCC:5V(逻辑电源)
- GND:共地
- STBY:PB4(高电平使能,低电平休眠)
- AIN1/AIN2:PB0/PB1(左电机方向控制)
- BIN1/BIN2:PB2/PB3(右电机方向控制)
- PWMA/PWMB:PA8/PA9(PWM调速,10kHz)
- AO1/AO2:左电机正负极
- BO1/BO2:右电机正负极

2.3 抗干扰设计

三、软件设计与核心代码

1. 系统架构(FreeRTOS多任务调度)

采用FreeRTOS实时操作系统,划分5个核心任务(优先级从高到低):

  1. 遥控接收任务(优先级5):解析红外/NRF/蓝牙指令,生成运动控制命令。
  2. 电机控制任务(优先级4):执行运动命令,PWM输出,速度闭环控制。
  3. 避障循迹任务(优先级3):检测障碍物/循迹线,触发避障/循迹算法。
  4. 状态显示任务(优先级2):更新OLED显示(速度/模式/电量)。
  5. 低功耗管理任务(优先级1):无操作超时检测,控制休眠/唤醒。

2. 核心代码实现(基于HAL库)

2.1 电机驱动与控制

#include "motor.h"
#include "tim.h"

// 电机方向定义
typedef enum {
    MOTOR_STOP = 0,
    MOTOR_FORWARD,
    MOTOR_BACKWARD
} MotorDir_t;

// 电机结构体
typedef struct {
    GPIO_TypeDef* in1_port;
    uint16_t in1_pin;
    GPIO_TypeDef* in2_port;
    uint16_t in2_pin;
    TIM_HandleTypeDef* tim_handle;
    uint32_t pwm_channel;
    int16_t speed;  // -100~100,负号表示反向
} Motor_t;

Motor_t motor_left = {
    .in1_port = GPIOB, .in1_pin = GPIO_PIN_0,
    .in2_port = GPIOB, .in2_pin = GPIO_PIN_1,
    .tim_handle = &htim1, .pwm_channel = TIM_CHANNEL_1
};

Motor_t motor_right = {
    .in1_port = GPIOB, .in1_pin = GPIO_PIN_2,
    .in2_port = GPIOB, .in2_pin = GPIO_PIN_3,
    .tim_handle = &htim1, .pwm_channel = TIM_CHANNEL_2
};

// 电机初始化
void Motor_Init(void) {
    // 启动PWM
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
    
    // 默认停止
    Motor_SetSpeed(&motor_left, 0);
    Motor_SetSpeed(&motor_right, 0);
}

// 设置电机速度(-100~100)
void Motor_SetSpeed(Motor_t* motor, int16_t speed) {
    motor->speed = speed;
    
    if (speed > 0) {
        // 正转
        HAL_GPIO_WritePin(motor->in1_port, motor->in1_pin, GPIO_PIN_SET);
        HAL_GPIO_WritePin(motor->in2_port, motor->in2_pin, GPIO_PIN_RESET);
    } else if (speed < 0) {
        // 反转
        HAL_GPIO_WritePin(motor->in1_port, motor->in1_pin, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(motor->in2_port, motor->in2_pin, GPIO_PIN_SET);
        speed = -speed;  // PWM占空比为正数
    } else {
        // 停止
        HAL_GPIO_WritePin(motor->in1_port, motor->in1_pin, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(motor->in2_port, motor->in2_pin, GPIO_PIN_RESET);
    }
    
    // 设置PWM占空比(0-1000对应0-100%)
    uint16_t pwm_val = (speed * 10) > 1000 ? 1000 : (speed * 10);
    __HAL_TIM_SET_COMPARE(motor->tim_handle, motor->pwm_channel, pwm_val);
}

2.2 红外遥控解码(NEC协议)

#include "ir_remote.h"
#include "tim.h"

// NEC解码状态
typedef enum {
    IR_IDLE = 0,
    IR_LEADER_HIGH,
    IR_LEADER_LOW,
    IR_DATA
} IR_State_t;

volatile IR_State_t ir_state = IR_IDLE;
volatile uint32_t ir_data = 0;
volatile uint8_t ir_bit_count = 0;
volatile uint8_t ir_received = 0;

// TIM2输入捕获中断回调(PA1)
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
    if (htim->Instance == TIM2 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2) {
        uint32_t pulse_width = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
        
        switch (ir_state) {
            case IR_IDLE:
                if (pulse_width > 8500 && pulse_width < 9500) {
                    ir_state = IR_LEADER_LOW;  // 引导码高电平9ms
                }
                break;
                
            case IR_LEADER_LOW:
                if (pulse_width > 4000 && pulse_width < 5000) {
                    ir_state = IR_DATA;  // 引导码低电平4.5ms
                    ir_data = 0;
                    ir_bit_count = 0;
                } else {
                    ir_state = IR_IDLE;
                }
                break;
                
            case IR_DATA:
                if (pulse_width > 1400 && pulse_width < 1800) {
                    ir_data = (ir_data << 1) | 0x01;  // 逻辑1
                } else if (pulse_width > 400 && pulse_width < 700) {
                    ir_data = (ir_data << 1);  // 逻辑0
                }
                
                if (++ir_bit_count >= 32) {
                    ir_received = 1;  // 32位数据接收完成
                    ir_state = IR_IDLE;
                }
                break;
        }
    }
}

// 解析红外键值
uint8_t IR_GetKey(void) {
    if (!ir_received) return 0xFF;
    
    ir_received = 0;
    
    uint8_t addr = (ir_data >> 24) & 0xFF;      // 地址码
    uint8_t addr_inv = (ir_data >> 16) & 0xFF;  // 地址反码
    uint8_t cmd = (ir_data >> 8) & 0xFF;       // 命令码
    uint8_t cmd_inv = ir_data & 0xFF;           // 命令反码
    
    // 校验
    if ((addr ^ addr_inv) == 0xFF && (cmd ^ cmd_inv) == 0xFF) {
        return cmd;  // 返回命令码
    }
    return 0xFF;  // 校验失败
}

2.3 运动控制逻辑

#include "motion_control.h"

// 运动命令定义
typedef enum {
    CMD_STOP = 0,
    CMD_FORWARD,
    CMD_BACKWARD,
    CMD_LEFT,
    CMD_RIGHT,
    CMD_SPEED_UP,
    CMD_SPEED_DOWN
} MotionCmd_t;

// 系统状态
typedef struct {
    int16_t base_speed;      // 基础速度(0-100)
    uint8_t motion_mode;     // 0=竞速,1=平稳
    uint8_t obstacle_detected; // 障碍物检测标志
} SystemState_t;

SystemState_t sys_state = {50, 0, 0};

// 运动控制任务
void Motion_Control_Task(void *pvParameters) {
    uint8_t key;
    int16_t left_speed, right_speed;
    
    while (1) {
        // 1. 获取遥控命令
        key = IR_GetKey();
        
        // 2. 处理避障(优先级最高)
        if (sys_state.obstacle_detected && key != CMD_BACKWARD) {
            // 检测到障碍物,自动后退
            Motor_SetSpeed(&motor_left, -30);
            Motor_SetSpeed(&motor_right, -30);
            vTaskDelay(pdMS_TO_TICKS(500));
            sys_state.obstacle_detected = 0;
            continue;
        }
        
        // 3. 解析运动命令
        switch (key) {
            case 0x45:  // 电源键 -> 停止
                key = CMD_STOP;
                break;
            case 0x46:  // 音量+ -> 前进
                key = CMD_FORWARD;
                break;
            case 0x47:  // 音量- -> 后退
                key = CMD_BACKWARD;
                break;
            case 0x44:  // 左键 -> 左转
                key = CMD_LEFT;
                break;
            case 0x40:  // 右键 -> 右转
                key = CMD_RIGHT;
                break;
            case 0x43:  // 上键 -> 加速
                key = CMD_SPEED_UP;
                break;
            case 0x44:  // 下键 -> 减速
                key = CMD_SPEED_DOWN;
                break;
        }
        
        // 4. 执行运动控制
        switch (key) {
            case CMD_STOP:
                left_speed = 0;
                right_speed = 0;
                break;
                
            case CMD_FORWARD:
                left_speed = sys_state.base_speed;
                right_speed = sys_state.base_speed;
                break;
                
            case CMD_BACKWARD:
                left_speed = -sys_state.base_speed;
                right_speed = -sys_state.base_speed;
                break;
                
            case CMD_LEFT:
                if (sys_state.motion_mode == 0) {  // 竞速模式:差速转向
                    left_speed = sys_state.base_speed * 0.5;
                    right_speed = sys_state.base_speed;
                } else {  // 平稳模式:原地转向
                    left_speed = -sys_state.base_speed;
                    right_speed = sys_state.base_speed;
                }
                break;
                
            case CMD_RIGHT:
                if (sys_state.motion_mode == 0) {
                    left_speed = sys_state.base_speed;
                    right_speed = sys_state.base_speed * 0.5;
                } else {
                    left_speed = sys_state.base_speed;
                    right_speed = -sys_state.base_speed;
                }
                break;
                
            case CMD_SPEED_UP:
                sys_state.base_speed = (sys_state.base_speed + 10) > 100 ? 100 : sys_state.base_speed + 10;
                break;
                
            case CMD_SPEED_DOWN:
                sys_state.base_speed = (sys_state.base_speed - 10) < 0 ? 0 : sys_state.base_speed - 10;
                break;
        }
        
        // 5. 设置电机速度
        Motor_SetSpeed(&motor_left, left_speed);
        Motor_SetSpeed(&motor_right, right_speed);
        
        vTaskDelay(pdMS_TO_TICKS(50));  // 50ms控制周期
    }
}

2.4 避障循迹功能

#include "obstacle.h"

// 避障检测任务
void Obstacle_Task(void *pvParameters) {
    uint8_t left_obstacle, right_obstacle;
    
    while (1) {
        // 读取红外避障传感器
        left_obstacle = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_0);
        right_obstacle = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_1);
        
        // 检测到障碍物
        if (left_obstacle == GPIO_PIN_RESET || right_obstacle == GPIO_PIN_RESET) {
            sys_state.obstacle_detected = 1;
        }
        
        vTaskDelay(pdMS_TO_TICKS(100));  // 100ms检测周期
    }
}

2.5 主程序框架

#include "stm32f1xx_hal.h"
#include "FreeRTOS.h"
#include "task.h"
#include "motor.h"
#include "ir_remote.h"
#include "obstacle.h"
#include "oled.h"

int main(void) {
    HAL_Init();
    SystemClock_Config();  // 72MHz
    
    // 初始化外设
    MX_TIM1_Init();    // PWM电机控制
    MX_TIM2_Init();    // 红外输入捕获
    MX_GPIO_Init();     // GPIO初始化
    MX_I2C1_Init();    // OLED I2C
    
    // 初始化模块
    Motor_Init();
    IR_Init();
    OLED_Init();
    
    // 创建FreeRTOS任务
    xTaskCreate(Motion_Control_Task, "MotionCtrl", 256, NULL, 4, NULL);
    xTaskCreate(Obstacle_Task, "Obstacle", 128, NULL, 3, NULL);
    xTaskCreate(Display_Task, "Display", 128, NULL, 2, NULL);
    xTaskCreate(LowPower_Task, "LowPower", 128, NULL, 1, NULL);
    
    vTaskStartScheduler();
    
    while (1);
}

参考代码 基于STM32遥控小车设计 www.youwenfan.com/contentcnt/133609.html

四、关键技术与优化

1. 运动控制优化

2. 遥控可靠性优化

3. 低功耗设计

五、系统调试与扩展

1. 调试步骤

阶段 操作 工具
硬件调试 测量电机驱动电压,检查PWM波形 万用表、示波器
遥控测试 验证红外/NRF/蓝牙指令接收和解码 串口打印键值
运动测试 测试前进/后退/转向,检查电机响应速度 实际运行测试
避障测试 放置障碍物,验证自动避障功能 障碍物(书本/墙壁)

2. 扩展功能

 

专注于matlab/simulink,电子电路,编程