ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

STM32小车(蓝牙,红外循迹,超声波避障)

2026/9/1 15:11:06 拓冰建站 浏览量
STM32小车(蓝牙,红外循迹,超声波避障) 项目背景做这辆小车就是想把手头几个常用模块串起来PS:买的江科的套件大部分都有了把STM32的GPIO、定时器、PWM、串口中断都练一遍。智能小车是嵌入式入门的经典项目通过整合电机驱动、蓝牙无线通信超声波模块等模块可以系统性地学习STM32的GPIO、定时器、PWM、串口通信等外设使用。功能特性蓝牙遥控模式手机APP无线控制小车运动红外循迹模式自动沿黑线行驶超声波避障模式自动检测障碍物并绕行OLED显示实时显示当前状态和数据模式切换蓝牙命令切换运行模式硬件清单STM32F103C8T6最小系统板1主控芯片TB6612FNG电机驱动板1 / 2直流减速电机4小车动力小车底盘含轮胎1套机械结构HC-06蓝牙模块1无线通信HC-SR04超声波模块1距离检测红外循迹传感器4黑线检测SG90舵机1超声波扫描方向OLED显示屏0.96寸1状态显示7.4V锂电池1供电面包板、杜邦线若干-接线用PS电池尽量选择可以充电的锂电池续航强选择硬件连接引脚分配表STM32引脚连接模块功能说明PA9HC-06TX/RX串口1发送接蓝牙RXPA10HC-06 RX/TX串口1接收接蓝牙TXPB0SG90舵机信号线TIM3_CH3 PWM输出PB6红外传感器1左内侧传感器PB7红外传感器2右内侧传感器PB8红外传感器3左外侧传感器PB10红外传感器4右外侧传感器PB13HC-SR04 Echo超声波回响PB14HC-SR04 Trig超声波触发PB12/PB13L298N IN1/IN2左电机方向控制PB14/PB15L298N IN3/IN4右电机方向控制PA0/PA1L298N ENA/ENB电机PWM调速I2C/SPIOLED显示屏根据模块类型重要提示L298N和电机需要单独供电7.4V锂电池不要用STM32的3.3V/5V供电舵机建议单独供电或者确保5V输出电流足够所有模块的GND必须与STM32共地接线前仔细检查避免短路烧坏芯片电机驱动/PWM调数电机驱动是小车的动力核心通过TB6612FNG电机驱动芯片控制两路直流电机的正反转和转速。使用STM32的PWM输出实现调速通过GPIO控制电机方向。TB6612FNG电机驱动原理TB6612FNG是一款基于MOS管的双H桥直流电机驱动芯片相比传统的L298N达林顿管效率更高、发热更小、体积更紧凑AIN1/AIN2控制A电机左电机方向BIN1/BIN2控制B电机右电机方向PWMAA电机PWM调速输入PWMBB电机PWM调速输入STBY待机控制引脚高电平正常工作低电平待机PWM调速原理PWM脉冲宽度调制通过改变高电平占空比来调节电机平均电压从而控制转速占空比 高电平时间 / 周期时间占空比越大电机转速越快PWM频率建议在1kHz以上避免电机产生噪音定时器配置TIM3时钟源72MHz预分频器PSC720 → 计数频率 72MHz / 720 100kHz自动重装载值ARR100 → PWM频率 100kHz / 100 1kHz速度范围0~100对应占空比0%~100%电机调速 PWM 代码 pwm.h/pwm.c#ifndef __PWMSERVO_H #define __PWMSERVO_H void PWMServo_Init(void); void PWM_2SetCompare3(uint16_t Compare); #endif#include stm32f10x.h // Device header void PWMServo_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); TIM_InternalClockConfig(TIM3); TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period 20000 - 1; //ARR TIM_TimeBaseInitStructure.TIM_Prescaler 72 - 1; //PSC TIM_TimeBaseInitStructure.TIM_RepetitionCounter 0; TIM_TimeBaseInit(TIM3, TIM_TimeBaseInitStructure); TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit(TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 0; //CCR TIM_OC3Init(TIM3, TIM_OCInitStructure); TIM_Cmd(TIM3, ENABLE); } void PWM_2SetCompare3(uint16_t Compare) { TIM_SetCompare3(TIM3, Compare); }#ifndef __PWM_H #define __PWM_H void PWM_Init(void); void PWM_SetCompare3(uint16_t Compare); void PWM_SetCompare2(uint16_t Compare); void PWM_SetCompare4(uint16_t Compare); #endif#include stm32f10x.h // Device header void PWM_Init(void) { //开启时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //配置GPIO GPIO_InitTypeDef GPIO_Initstructure; GPIO_Initstructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_Initstructure.GPIO_Pin GPIO_Pin_1|GPIO_Pin_2; GPIO_Initstructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA,GPIO_Initstructure); //选择时钟单元 TIM_InternalClockConfig(TIM2); //配置时钟单元 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period 100 - 1; //ASR TIM_TimeBaseInitStructure.TIM_Prescaler 36 - 1; //PSC TIM_TimeBaseInitStructure.TIM_RepetitionCounter 0; TIM_TimeBaseInit(TIM2, TIM_TimeBaseInitStructure); //初始化 输出比较单元 TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit(TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 0 ; //CCR TIM_OC3Init(TIM2,TIM_OCInitStructure); TIM_OC2Init(TIM2,TIM_OCInitStructure); //开启定时器 TIM_Cmd(TIM2, ENABLE); } void PWM_SetCompare2(uint16_t Compare) { TIM_SetCompare2(TIM2,Compare); } void PWM_SetCompare3(uint16_t Compare) { TIM_SetCompare3(TIM2,Compare); }小车运动控制接口 Car.h/ Car.c速度参数正负代表方向正数前进负数后退0 停止。#ifndef __Car_H #define __Car_H void Car_Init(void); void Car_Go(void); void Car_Back(void); void Car_Stop(void); void Car_Left(void); void Car_Right(void); void Car_ClockLeft(void); void Car_ClockRight(void); #endif#include stm32f10x.h // Device header #include Motor.h #include PWM.h void Car_Init(void) { Motor_Init(); } void Car_Go(void) { LeftMotor_Speed(50); RightMotor_Speed(50); } void Car_Back(void) { LeftMotor_Speed(-50); RightMotor_Speed(-50); } void Car_Stop(void) { LeftMotor_Speed(0); RightMotor_Speed(0); } void Car_Left(void) { LeftMotor_Speed(30); RightMotor_Speed(50); } void Car_Right(void) { LeftMotor_Speed(50); RightMotor_Speed(30); } void Car_ClockLeft(void) { LeftMotor_Speed(-50); RightMotor_Speed(50); } void Car_ClockRight(void) { LeftMotor_Speed(50); RightMotor_Speed(-50); }PS由于代码过于多 这里全部都写入的话文章过于啰嗦所以以下都不会加入只会介绍模块思路蓝牙遥控我使用的是HC-06,HC-06和HC-05不太一样HC-06是纯从机模式不能配参数出厂固定9600波特率连上就能用比HC-05省事。接收逻辑串口中断收到一个字符后存入全局变量cmd主循环里判断执行。每个命令执行完最好回一条消息到手机比如OK或者FWD这类方便调试。舵机控制收到的7、8、9分别对应0°、90°、180°你需要把角度换算成PWM比较值。舵机是50Hz20ms周期0.5ms脉宽对应0°1.5ms对应90°2.5ms对应180°。你的定时器是72预分频、20000周期所以计数值范围是500~2500对应关系角度/180 * 2000 500。模式切换收到A切避障B切循迹。切换时先把车停下来舵机回中OLED刷新再给手机回个Mode: OBS或Mode: TRK。红外循迹:传感器布局从左到右依次是左外(PB8)、左内(PB6)、右内(PB7)、右外(PB10)。检测到黑线输出低电平0白底输出高电平1。超声波避障舵机扫描策略前进中每隔200ms测一次前方距离。如果小于30cm安全阈值立即停车。舵机先转到0°最左等300ms稳定测左前方距离。如果左前方也小于阈值舵机转到180°最右等300ms测右前方距离。左右都堵死了就后退50cm再重新判断。如果有一侧安全就向那一侧转弯转够角度后舵机回正继续前进。电源与接线注意事项电机驱动和舵机需要7.4V锂电池单独供电STM32系统板与OLED、蓝牙、传感器等使用板载3.3V/5V供电。所有模块的GND必须与STM32共地避免总线电平漂移。接线前仔细核对引脚定义防止电源反接或信号短路损坏芯片。电池建议选用可充电锂电保证长时间调试与续航。电池容量问题我一开始用的南孚电池改的放电能力不够四个电机一起转电压掉到5V以下单片机重启。后来换了航模用的7.4V 2200mAh锂电池放电倍率25C问题解决。实体图