基于esp32s3做的麦轮小车

简述:可以实现小车前进后退左走右走,主要是搭建esp32在vscode下的环境,配置普通gpio口,以及输出4路pwm波,掌握麦轮运动的原理。

1 环境配置

下载esp-idf,下载vscode,新建基础工程,自定义工程架构和添加组件
可以跟着正点原子的官方视频做好搭建。

2 硬件设备

硬件设备只需要简单的轮趣科技tb6612电机驱动esp32s3开发板、12v电池、轮趣科技电机抱紧式麦克纳姆轮、车底板、若干杜邦线就可以实现,后续可以在此基础之上完成一些小设计和开发。

3 代码

①ledc.h文件

#ifndef __LEDC_H
#define __LEDC_H

#include "driver/gpio.h"
#include "driver/ledc.h"

// ------------------- TB6612 引脚定义 -------------------
// 四路电机方向控制引脚
#define AIN1_GPIO    GPIO_NUM_4
#define AIN2_GPIO    GPIO_NUM_5
#define BIN1_GPIO    GPIO_NUM_6
#define BIN2_GPIO    GPIO_NUM_7
#define CIN1_GPIO    GPIO_NUM_15
#define CIN2_GPIO    GPIO_NUM_16
#define DIN1_GPIO    GPIO_NUM_17
#define DIN2_GPIO    GPIO_NUM_18

// TB6612 使能引脚
#define STBY_GPIO    GPIO_NUM_8

// ------------------- 四路PWM配置 -------------------
#define LEDC_PWM_TIMER_0    LEDC_TIMER_0
#define LEDC_PWM_TIMER_1    LEDC_TIMER_1
#define LEDC_PWM_TIMER_2    LEDC_TIMER_2
#define LEDC_PWM_TIMER_3    LEDC_TIMER_3

#define LEDC_PWM_CH0_GPIO   GPIO_NUM_35  // 电机A PWM
#define LEDC_PWM_CH1_GPIO   GPIO_NUM_36  // 电机B PWM
#define LEDC_PWM_CH2_GPIO   GPIO_NUM_37  // 电机C PWM
#define LEDC_PWM_CH3_GPIO   GPIO_NUM_38  // 电机D PWM

#define LEDC_PWM_CH0_CHANNEL LEDC_CHANNEL_0
#define LEDC_PWM_CH1_CHANNEL LEDC_CHANNEL_1
#define LEDC_PWM_CH2_CHANNEL LEDC_CHANNEL_2
#define LEDC_PWM_CH3_CHANNEL LEDC_CHANNEL_3

// PWM配置结构体(兼容原有格式,扩展四路)
typedef struct ledc
{
    ledc_clk_cfg_t clk_cfg;             /* 时钟源配置 */
    ledc_timer_t  timer_num;            /* 定时器(LEDC_TIMER_0~LEDC_TIMER_3) */
    uint32_t freq_hz;                   /* 频率(系统自动计算分频系数) */
    ledc_timer_bit_t duty_resolution;   /* 占空比分辨率 */
    ledc_channel_t channel;             /* 通道(LEDC_CHANNEL_0~LEDC_CHANNEL_7) */
    uint32_t duty;                      /* 初始占空比 */
    int gpio_num;                       /* PWM输出管脚 */
}ledc_config_t;

// ------------------- 函数声明 -------------------
uint32_t ledc_duty_pow(uint32_t duty, uint8_t m, uint8_t n);
void ledc_init(ledc_config_t *ledc_config);
void ledc_pwm_set_duty(ledc_config_t *ledc_config, uint16_t duty);


// TB6612 初始化函数
void tb6612_gpio_init(void);
// 电机方向控制函数
void motor_set_direction(uint8_t motor_num, uint8_t dir); // dir:0-反转,1-正转
// 四路PWM初始化封装
void four_pwm_init(void);

#endif

②ledc.c文件

#include "ledc.h"

// 原有占空比转换函数
uint32_t ledc_duty_pow(uint32_t duty, uint8_t m, uint8_t n)
{
    uint32_t result = 1;
    while (n--)
    {
        result *= m;
    }
    return (result * duty) / 100;
}

// 原有PWM初始化函数
void ledc_init(ledc_config_t *ledc_config)
{
    ledc_config->duty = ledc_duty_pow(ledc_config->duty, 2, ledc_config->duty_resolution);

    ledc_timer_config_t ledc_timer = {
        .speed_mode = LEDC_LOW_SPEED_MODE,
        .duty_resolution = ledc_config->duty_resolution,
        .timer_num        = ledc_config->timer_num,         
        .freq_hz          = ledc_config->freq_hz,           
        .clk_cfg          = ledc_config->clk_cfg            
    };
    ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
    
    ledc_channel_config_t ledc_channel = {
        .speed_mode     = LEDC_LOW_SPEED_MODE,      
        .channel        = ledc_config->channel,     
        .timer_sel      = ledc_config->timer_num,   
        .intr_type      = LEDC_INTR_DISABLE,        
        .gpio_num       = ledc_config->gpio_num,    
        .duty           = ledc_config->duty,        
        .hpoint         = 0                         
    };
    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
}

// 原有占空比设置函数
void ledc_pwm_set_duty(ledc_config_t *ledc_config, uint16_t duty)
{
    ledc_config->duty = ledc_duty_pow(duty, 2, ledc_config->duty_resolution);
    ledc_set_duty(LEDC_LOW_SPEED_MODE, ledc_config->channel, ledc_config->duty);    
    ledc_update_duty(LEDC_LOW_SPEED_MODE, ledc_config->channel);                   
}

// TB6612 GPIO初始化(方向+使能)
void tb6612_gpio_init(void)
{
    // 配置方向控制引脚为输出
    gpio_config_t gpio_conf = {
        .pin_bit_mask = (1ULL << AIN1_GPIO) | (1ULL << AIN2_GPIO) |
                        (1ULL << BIN1_GPIO) | (1ULL << BIN2_GPIO) |
                        (1ULL << CIN1_GPIO) | (1ULL << CIN2_GPIO) |
                        (1ULL << DIN1_GPIO) | (1ULL << DIN2_GPIO) |
                        (1ULL << STBY_GPIO),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE
    };
    ESP_ERROR_CHECK(gpio_config(&gpio_conf));
    
    // STBY置高(使能TB6612)
    gpio_set_level(STBY_GPIO, 1);
    // 初始方向默认反转(可根据需求调整)
    gpio_set_level(AIN1_GPIO, 0);
    gpio_set_level(AIN2_GPIO, 1);
    gpio_set_level(BIN1_GPIO, 0);
    gpio_set_level(BIN2_GPIO, 1);
    gpio_set_level(CIN1_GPIO, 0);
    gpio_set_level(CIN2_GPIO, 1);
    gpio_set_level(DIN1_GPIO, 0);
    gpio_set_level(DIN2_GPIO, 1);
}

// 电机方向控制
// motor_num: 0-A电机,1-B电机,2-C电机,3-D电机
// dir: 0-反转,1-正转
void motor_set_direction(uint8_t motor_num, uint8_t dir)
{
    switch(motor_num)
    {
        case 0: // A电机
            gpio_set_level(AIN1_GPIO, dir);
            gpio_set_level(AIN2_GPIO, !dir);
            break;
        case 1: // B电机
            gpio_set_level(BIN1_GPIO, dir);
            gpio_set_level(BIN2_GPIO, !dir);
            break;
        case 2: // C电机
            gpio_set_level(CIN1_GPIO, dir);
            gpio_set_level(CIN2_GPIO, !dir);
            break;
        case 3: // D电机
            gpio_set_level(DIN1_GPIO, dir);
            gpio_set_level(DIN2_GPIO, !dir);
            break;
        default:
            break;
    }
}

// 四路PWM初始化封装
void four_pwm_init(void)
{
    // 定义四路PWM配置
    ledc_config_t pwm_config[4] = {0};
    
    // 电机A PWM配置
    pwm_config[0].clk_cfg         = LEDC_AUTO_CLK;
    pwm_config[0].timer_num       = LEDC_PWM_TIMER_0;
    pwm_config[0].freq_hz         = 1000;
    pwm_config[0].duty_resolution = LEDC_TIMER_14_BIT;
    pwm_config[0].channel         = LEDC_PWM_CH0_CHANNEL;
    pwm_config[0].duty            = 0;
    pwm_config[0].gpio_num        = LEDC_PWM_CH0_GPIO;
    
    // 电机B PWM配置
    pwm_config[1].clk_cfg         = LEDC_AUTO_CLK;
    pwm_config[1].timer_num       = LEDC_PWM_TIMER_1;
    pwm_config[1].freq_hz         = 1000;
    pwm_config[1].duty_resolution = LEDC_TIMER_14_BIT;
    pwm_config[1].channel         = LEDC_PWM_CH1_CHANNEL;
    pwm_config[1].duty            = 0;
    pwm_config[1].gpio_num        = LEDC_PWM_CH1_GPIO;
    
    // 电机C PWM配置
    pwm_config[2].clk_cfg         = LEDC_AUTO_CLK;
    pwm_config[2].timer_num       = LEDC_PWM_TIMER_2;
    pwm_config[2].freq_hz         = 1000;
    pwm_config[2].duty_resolution = LEDC_TIMER_14_BIT;
    pwm_config[2].channel         = LEDC_PWM_CH2_CHANNEL;
    pwm_config[2].duty            = 0;
    pwm_config[2].gpio_num        = LEDC_PWM_CH2_GPIO;
    
    // 电机D PWM配置
    pwm_config[3].clk_cfg         = LEDC_AUTO_CLK;
    pwm_config[3].timer_num       = LEDC_PWM_TIMER_3;
    pwm_config[3].freq_hz         = 1000;
    pwm_config[3].duty_resolution = LEDC_TIMER_14_BIT;
    pwm_config[3].channel         = LEDC_PWM_CH3_CHANNEL;
    pwm_config[3].duty            = 0;
    pwm_config[3].gpio_num        = LEDC_PWM_CH3_GPIO;
    
    // 初始化四路PWM
    for(int i=0; i<4; i++)
    {
        ledc_init(&pwm_config[i]);
    }
}

③CMakeList.txt文件

set(src_dirs
            LEDC)

set(include_dirs
            LEDC)

set(requires
            driver)

idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})

component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)

④main.c文件

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "ledc.h"

// 四路PWM配置实例(全局,方便任务中修改占空比)
ledc_config_t pwm_config[4] = {
    // 电机A
    {
        .clk_cfg         = LEDC_AUTO_CLK,
        .timer_num       = LEDC_PWM_TIMER_0,
        .freq_hz         = 1000,
        .duty_resolution = LEDC_TIMER_14_BIT,
        .channel         = LEDC_PWM_CH0_CHANNEL,
        .duty            = 0,
        .gpio_num        = LEDC_PWM_CH0_GPIO
    },
    // 电机B
    {
        .clk_cfg         = LEDC_AUTO_CLK,
        .timer_num       = LEDC_PWM_TIMER_1,
        .freq_hz         = 1000,
        .duty_resolution = LEDC_TIMER_14_BIT,
        .channel         = LEDC_PWM_CH1_CHANNEL,
        .duty            = 0,
        .gpio_num        = LEDC_PWM_CH1_GPIO
    },
    // 电机C
    {
        .clk_cfg         = LEDC_AUTO_CLK,
        .timer_num       = LEDC_PWM_TIMER_2,
        .freq_hz         = 1000,
        .duty_resolution = LEDC_TIMER_14_BIT,
        .channel         = LEDC_PWM_CH2_CHANNEL,
        .duty            = 0,
        .gpio_num        = LEDC_PWM_CH2_GPIO
    },
    // 电机D
    {
        .clk_cfg         = LEDC_AUTO_CLK,
        .timer_num       = LEDC_PWM_TIMER_3,
        .freq_hz         = 1000,
        .duty_resolution = LEDC_TIMER_14_BIT,
        .channel         = LEDC_PWM_CH3_CHANNEL,
        .duty            = 0,
        .gpio_num        = LEDC_PWM_CH3_GPIO
    }
};
/**
 * @brief       程序入口
 * @param       无
 * @retval      无
 */
// 定义四种方向状态的配置表(每行对应一种状态,列对应A/B/C/D电机:1=正转,0=反转)
const uint8_t motor_dir_states[4][4] = {
    {1, 1, 1, 1},  // 状态1:全正转 (A正、B正、C正、D正)
    {0, 0, 0, 0},  // 状态2:全反转 (A反、B反、C反、D反)
    {1, 0, 1, 0},  // 状态3:AC正、BD反 (A正、B反、C正、D反)
    {0, 1, 0, 1}   // 状态4:AC反、BD正 (A反、B正、C反、D正)
};

// 状态名称(用于打印提示)
const char* state_names[4] = {
    "全正转",
    "全反转",
    "AC正、BD反",
    "AC反、BD正"
};

void app_main(void)
{
    uint8_t current_state = 0;     // 当前状态索引(0-3对应四种状态)
    uint16_t pwm_duty = 20;        // 四路PWM固定20%占空比
    const TickType_t delay_2s = pdMS_TO_TICKS(2000); // 2秒延时

    // 初始化TB6612 GPIO和四路PWM
    tb6612_gpio_init();
    four_pwm_init();

    printf("四路电机多状态循环启动!占空比:%d%%\n", pwm_duty);
    printf("状态循环:全正转→全反转→AC正BD反→AC反BD正(每种状态持续2秒)\n\n");

    while(1)
    {
        // 1. 应用当前状态的方向配置
        for(int i=0; i<4; i++)
        {
            // 设置第i个电机的方向(从状态表中取值)
            motor_set_direction(i, motor_dir_states[current_state][i]);
            // 设置固定20%占空比
            ledc_pwm_set_duty(&pwm_config[i], pwm_duty);
        }

        // 2. 打印当前状态信息
        printf("当前状态:%s,占空比:%d%%\n", state_names[current_state], pwm_duty);

        // 3. 保持当前状态运行2秒
        vTaskDelay(delay_2s);

        // 4. 切换到下一个状态(循环:0→1→2→3→0...)
        current_state = (current_state + 1) % 4;
    }
}

4 引脚连接

可以把pwm配置后接在板子的板载led的引脚上,看一下led有没有点亮,改变pwm占空比,是否出现呼吸灯,全部检查没问题后就可以接tb6612了。
在这里插入图片描述

引脚配置主要在ledc.h文件里面可以更改。
在这里插入图片描述

在这里插入图片描述

使用杜邦线连接好tb6612和esp32即可

5 麦克纳姆轮原理

内八型麦克纳姆轮是指四个麦克纳姆轮安装在平台上时,其辊子的倾斜方向朝内,从上方看呈 “内八” 形状。以下是其原理及相关图示说明:
内八型麦克纳姆轮的原理与普通麦克纳姆轮一致,都是利用辊子与地面接触产生的斜向推力来实现全向移动。当轮毂旋转时,斜向辊子与地面接触产生摩擦力,该摩擦力方向与辊子轴线垂直,形成与轮毂轴线呈 45° 的斜向推力,通过对四个轮子的转速和转向进行不同组合的控制,就可以合成不同方向的合力,从而实现各种运动。

例如,当四个轮子都正向同速旋转时,各个轮子的纵向分力相加,横向分力相互抵消,车辆向前移动,如
在这里插入图片描述

当左前轮和右后轮反转,右前轮和左后轮正转,且转速相同时,各个轮子的纵向分力相互抵消,横向分力相加,车辆向左平移,如
在这里插入图片描述

更多推荐