一、前置准备

         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.小车可拓展加上超声波避障,蓝牙控制等。

希望对你有所帮助。

 

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐