STM32超简易的循迹小车
本文介绍了基于STM32的循迹小车实现方案。主要内容包括:1)硬件准备(TCRT5000红外传感器、DRV8833电机驱动模块等);2)PWM驱动代码实现;3)电机控制封装(正反转调速);4)动作函数封装(前进、转向等);5)循迹逻辑设计(三路红外检测黑线);6)常见问题解决方案(供电不足、电机调试等)。项目采用模块化编程,通过PWM控制四轮电机,结合红外传感器实现自动循迹功能,并指出可通过PID
一、前置准备
1.本小车基于江科大的PWM而来,当你学完江科大STM32入门课程的PWM驱动电机就可以做这个循迹小车了。
2.需要用到的模块:TCRT5000传感器(红外循迹模块),DRV8833电机驱动模块,电源。
二、功能实现
1.接线图
1.1如果是TB6612的话,就需要接PWMA,PWMB接口,如下图所示。 

1.2如果是DRV8833的话,NC不用接线,直接接在AIN1,AIN2或者BIN1,BIN2上,前者为正转。


2.PWM代码
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_0 | GPIO_Pin_8 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);//配置GPIO
TIM_InternalClockConfig(TIM1);
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; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
TIM_ARRPreloadConfig(TIM1, ENABLE); //高级定时器
TIM_CtrlPWMOutputs(TIM1, ENABLE);
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_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_Cmd(TIM1, ENABLE);
TIM_Cmd(TIM2, ENABLE);//上电
}
void PWM_SetCompare_TIM1CH1(uint16_t Compare)
{
TIM_SetCompare1(TIM1, Compare);
}
void PWM_SetCompare_TIM2CH3(uint16_t Compare)
{
TIM_SetCompare3(TIM2, Compare);
}
void PWM_SetCompare_TIM2CH1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare);
}
void PWM_SetCompare_TIM2CH2(uint16_t Compare)
{
TIM_SetCompare2(TIM2, Compare);
}
3.Motor.c(封装,独立控制4个电机)
#include "stm32f10x.h" // Device header
#include "PWM.h"
//#define RightForth Pin3 | Pin4
//#define RightBack Pin9 | Pin10
//#define LeftForth Pin5 | Pin6
//#define LeftBack Pin11 | Pin12
void Motor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 |GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_9 | GPIO_Pin_10 |GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
PWM_Init();
}
void Motor1_SetSpeed(int8_t Speed1)
{
if (Speed1 >= 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_3);
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
PWM_SetCompare_TIM1CH1(Speed1);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_3);
GPIO_SetBits(GPIOA, GPIO_Pin_4);
PWM_SetCompare_TIM1CH1(-Speed1);
}
}
void Motor2_SetSpeed(int8_t Speed2)
{
if (Speed2 >= 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_5);
GPIO_ResetBits(GPIOA, GPIO_Pin_6);
PWM_SetCompare_TIM2CH3(Speed2);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
GPIO_SetBits(GPIOA, GPIO_Pin_6);
PWM_SetCompare_TIM2CH3(-Speed2);
}
}
void Motor3_SetSpeed(int8_t Speed3)
{
if (Speed3 >= 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_9);
GPIO_ResetBits(GPIOA, GPIO_Pin_10);
PWM_SetCompare_TIM2CH1(Speed3);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_9);
GPIO_SetBits(GPIOA, GPIO_Pin_10);
PWM_SetCompare_TIM2CH1(-Speed3);
}
}
void Motor4_SetSpeed(int8_t Speed4)
{
if (Speed4 >= 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_11);
GPIO_ResetBits(GPIOA, GPIO_Pin_12);
PWM_SetCompare_TIM2CH2(Speed4);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_11);
GPIO_SetBits(GPIOA, GPIO_Pin_12);
PWM_SetCompare_TIM2CH2(-Speed4);
}
}
4.Action.c(封装驱动)
#include "stm32f10x.h" // Device header
#include "Motor.h"
void Action_Init(void)
{
Motor_Init();
}
void Stop(void)
{
Motor1_SetSpeed(0); //右前
Motor2_SetSpeed(0); //左前
Motor4_SetSpeed(0); //右后
Motor3_SetSpeed(0); //左后
}
void Go_Ahead(void)
{
Motor1_SetSpeed(90); //右前
Motor2_SetSpeed(90); //左前
Motor4_SetSpeed(90); //右后
Motor3_SetSpeed(90); //左后
}
void Go_Back(void)
{
Motor1_SetSpeed(-90); //右前
Motor2_SetSpeed(-90); //左前
Motor4_SetSpeed(-90); //右后
Motor3_SetSpeed(-90); //左后
}
void Go_Left(void)
{
Motor1_SetSpeed(80); //右前
Motor2_SetSpeed(30); //左前
Motor4_SetSpeed(80); //右后
Motor3_SetSpeed(30); //左后
}
void Go_Right(void)
{
Motor1_SetSpeed(30); //右前
Motor2_SetSpeed(80); //左前
Motor4_SetSpeed(30); //右后
Motor3_SetSpeed(80); //左后
}
5. Track.c(循迹模块)
#include "stm32f10x.h" // Device header
#include "Action.h"
//7为左,8为中,9为右
void Track_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 |GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
uint8_t Get_Left(void)
{
return GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7);
}
uint8_t Get_Mid(void)
{
return GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8);
}
uint8_t Get_Right(void)
{
return GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9);
}
void Track_Run(void) {
// 正常:中间黑线,左右白色 → 前进
if (Get_Left() == 0 && Get_Mid() == 1 && Get_Right() == 0) {
Go_Ahead();
}
// 偏左:左或左+中检测到黑线 → 右转调整
else if (Get_Left() == 1 && Get_Mid() == 0 && Get_Right() == 0
|| Get_Left() == 1 && Get_Mid() == 1 && Get_Right() == 0) {
Go_Right();
}
// 偏右:右或中+右检测到黑线 → 左转调整
else if (Get_Left() == 0 && Get_Mid() == 0 && Get_Right() == 1
|| Get_Left() == 0 && Get_Mid() == 1 && Get_Right() == 1) {
Go_Left();
}
// 完全偏离(全白或全黑)→ 停止
else {
Stop();
}
}
6.主函数调用
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Action.h"
#include "Track.h"
int main(void)
{
Action_Init();
Track_Init();
while (1)
{
Track_Run();
Delay_ms(100);
}
}
三、可能遇到的问题及可修改部分
1.用ST-Link烧录的时候,5V供电连接到电机驱动模块的VM可能会不转,若不转,但靠近听到微弱的电流滋滋声,可能是电流不足造成,可外接电源加稳压模块。
2.切勿将正负极接错,可能会烧坏芯片。外接电源,稳压模块,STBY接3.3V,VM接5V。
3.循迹模块的接线和使用比较简单,可以搜索看。
4.需要自己买车的模块组装好,然后接好线,在组装前建议测试好车轮是否按代码所示旋转。
代码部分别写错,定时器通道,各种引脚等。
5.所有IO,数字等建议宏定义处理,不然后续不好维护和更改,本文章旨在详细介绍接线部分。
6.若完成后,发现其占空比设置一样,但是仍不能直行,与直流电机内部有关,建议购买同一批次的直流电机,否则就需要慢慢调试占空比,其左右转都是需要慢慢调整。
7.针对6所示问题,可以去学习PID算法。
8.小车可拓展加上超声波避障,蓝牙控制等。
希望对你有所帮助。
更多推荐



所有评论(0)