基于esp32s3做的麦轮小车
·
基于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° 的斜向推力,通过对四个轮子的转速和转向进行不同组合的控制,就可以合成不同方向的合力,从而实现各种运动。
例如,当四个轮子都正向同速旋转时,各个轮子的纵向分力相加,横向分力相互抵消,车辆向前移动,如

当左前轮和右后轮反转,右前轮和左后轮正转,且转速相同时,各个轮子的纵向分力相互抵消,横向分力相加,车辆向左平移,如

更多推荐
所有评论(0)