基于esp-idf v5.0.1驱动五线四相步进电机(成果记录)

文章介绍了如何使用ESP32的gptimer库来驱动28BYJ-48步进电机,解决了延时函数驱动速度慢的问题。通过配置GPIO、初始化硬件定时器并编写中断回调函数,实现了电机的精确控制。此外,还提供了设置步进电机步数和转速的接口。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

前言

一、API库

二、gpio和timer初始化

1.配置GPIO

2.配置硬件定时器

 三、编写驱动接口

总结


前言

在使用esp-idf v5.0.1框架开发28BYJ-48步进电机驱动时,发现使用延时产生脉冲驱动uln2003,其最快延时到10ms,再小一点都无法驱动步进电机,gpio口也没有输出,相比之下51单片机使用延时可以正常的工作,esp32驱动的步进电机太慢了。其具体原因尚不明白,后来采用定时器产生脉冲,最终得以解决。以下内容则包含硬件通用定时器的简单驱动程序。(注:使用的v5.0.1版本,低版本可能无法使用)


一、API库

使用组件库:

#include "driver/gptimer.h"

可参考IDF中的gptimr的例程 

二、gpio和timer初始化

1.配置GPIO

五线四相步进电机需使用4个gpio口,选用如下,可自定义

#define MOTOR_PIN_A     14
#define MOTOR_PIN_B     27
#define MOTOR_PIN_C     26
#define MOTOR_PIN_D     25

接下来配置初始化gpio,及全局变量:

// 目标步数(剩余步数)
static uint32_t target_step = 0;
// 电机方向
static int8_t direction = -1;


// 定义步进电机的时序(一二相励磁方式)
static const uint8_t step_sequence[] = { 
    (1<<0)|(0<<1)|(0<<2)|(0<<3), // Step 1
    (1<<0)|(1<<1)|(0<<2)|(0<<3), // Step 2
    (0<<0)|(1<<1)|(0<<2)|(0<<3), // Step 3
    (0<<0)|(1<<1)|(1<<2)|(0<<3), // Step 4
    (0<<0)|(0<<1)|(1<<2)|(0<<3),
    (0<<0)|(0<<1)|(1<<2)|(1<<3),
    (0<<0)|(0<<1)|(0<<2)|(1<<3),
    (1<<0)|(0<<1)|(0<<2)|(1<<3)
};


/**
 * @brief 步进电机初始化函数,初始化GPIO口
 * 
 */
void motor_gpio_init()
{
    ESP_LOGI(TAG,"configured motor GPIO !\n");

    gpio_config_t io_conf;

    // 禁用中断
    io_conf.intr_type = GPIO_INTR_DISABLE;
    // 设置为输出模式
    io_conf.mode = GPIO_MODE_OUTPUT;
    // 设置输出电平为低电平
    io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
    // 设置输出电平为高电平
    io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
    // 配置GPIO
    io_conf.pin_bit_mask = (1ULL << MOTOR_PIN_A) | (1ULL << MOTOR_PIN_B) |
                           (1ULL << MOTOR_PIN_C) | (1ULL << MOTOR_PIN_D);
    gpio_config(&io_conf);

}


2.配置硬件定时器

定时器初始化:

//定时器操作句柄
gptimer_handle_t gptimer = NULL;

void step_timer_init()
{
    ESP_LOGI(TAG, "Create timer handle");

    gptimer_config_t timer_config = {
        .clk_src = GPTIMER_CLK_SRC_DEFAULT,
        .direction = GPTIMER_COUNT_UP,
        .resolution_hz = 1000000, // 1MHz, 1 tick=1us
    };
    ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));

    gptimer_event_callbacks_t cbs = {
        .on_alarm = step_timer_inr,
    };
    ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, NULL));

    ESP_ERROR_CHECK(gptimer_enable(gptimer));

    ESP_LOGI(TAG, "Start timer, stop it at alarm event");
    gptimer_alarm_config_t alarm_config1 = {
        .reload_count = 0,
        .alarm_count = 10000, // period = 10ms
        .flags.auto_reload_on_alarm = true,
    };
    ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config1));
    ESP_ERROR_CHECK(gptimer_start(gptimer));

}

定时器中断回调:

static bool IRAM_ATTR step_timer_inr(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *args)
{
    BaseType_t high_task_awoken = pdFALSE;

    // 相序下标
    static int8_t phase_index = 0;
    switch(direction) {
        case 1: {   // 顺时针旋转
            phase_index--;
            if(phase_index < 0) {
                phase_index = 7;
            }
            gpio_set_level(MOTOR_PIN_A, (step_sequence[phase_index] >> 0) & 0x1);
            gpio_set_level(MOTOR_PIN_B, (step_sequence[phase_index] >> 1) & 0x1);
            gpio_set_level(MOTOR_PIN_C, (step_sequence[phase_index] >> 2) & 0x1);
            gpio_set_level(MOTOR_PIN_D, (step_sequence[phase_index] >> 3) & 0x1);

            target_step--;
            if(target_step <= 0) {
                direction = -1;
            }
            break;
        }
        case 0: {   //逆时针旋转
            phase_index++;
            if(phase_index >= 8) {
                phase_index = 0;
            }
            gpio_set_level(MOTOR_PIN_A, (step_sequence[phase_index] >> 0) & 0x1);
            gpio_set_level(MOTOR_PIN_B, (step_sequence[phase_index] >> 1) & 0x1);
            gpio_set_level(MOTOR_PIN_C, (step_sequence[phase_index] >> 2) & 0x1);
            gpio_set_level(MOTOR_PIN_D, (step_sequence[phase_index] >> 3) & 0x1);

            target_step--;
            if(target_step <= 0) {
                direction = -1;
            }
            break;
        }
        default : break;
    }

    // return whether we need to yield at the end of ISR
    return (high_task_awoken == pdTRUE);
}

 三、编写驱动接口

写了两个步进电机控制函数:

// 设置步进电机步数及方向
void step_set_steps(uint32_t step, int8_t dire)
{
    // 步数乘以相序节拍
    target_step = step * 8;
    direction = dire;
}

// 设置步进电机转速 (freq ms)
void step_update_freq(uint16_t freq)
{
    ESP_LOGI(TAG, "Stop timer");
    ESP_ERROR_CHECK(gptimer_stop(gptimer));

    ESP_LOGI(TAG, "Start timer, update alarm value ");
    gptimer_alarm_config_t alarm_config = {
        .reload_count = 0,
        .alarm_count = freq * 1000, // period = freq ms
        .flags.auto_reload_on_alarm = true,
    };
    ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
    ESP_ERROR_CHECK(gptimer_start(gptimer));
}

总结

使用以上程序只能简单驱动电机旋转,实际应用还得根据需求作出调整和优化,现只是简单记录学习成果。

江畔何人初见月?江月何年初照人?

### 使用 ESP32-IDF 驱动线步进电机 为了实现使用 ESP32 控制两线步进电机的功能,可以采用硬件脉冲发生器来控制步进电机的运动。下面是一个基于 ESP32-IDF 的简单示例程序,用于驱动线步进电机。 #### 初始化 GPIO 和配置定时器 首先定义连接到步进电机的 GPIO 引脚,并初始化这些引脚作为输出模式: ```c #include "driver/ledc.h" #include "esp_err.h" #define STEP_PIN 18 // 步进信号引脚 #define DIR_PIN 19 // 方向信号引脚 void setup_gpio(void){ gpio_config_t io_conf; // 设置GPIO为输出模式 io_conf.intr_type = GPIO_INTR_DISABLE; io_conf.mode = GPIO_MODE_OUTPUT; io_conf.pin_bit_mask = (1ULL<<STEP_PIN)|(1ULL<<DIR_PIN); io_conf.pull_down_en = 0; io_conf.pull_up_en = 0; gpio_config(&io_conf); } ``` 接着设置 LEDC 脉宽调制通道参数并启动它,这有助于生成精确的时间间隔以触发步进动作[^1]。 #### 定义旋转函数 创建两个辅助方法 `rotate_clockwise` 和 `rotate_counterclockwise` 来分别处理顺时针和逆时针方向上的转动操作: ```c static void rotate_clockwise(int steps, int delay_us){ for(int i=0;i<steps;i++){ gpio_set_level(STEP_PIN, 1); ets_delay_us(delay_us / 2); gpio_set_level(STEP_PIN, 0); ets_delay_us(delay_us / 2); } } static void rotate_counterclockwise(int steps, int delay_us){ gpio_set_level(DIR_PIN, !gpio_get_level(DIR_PIN)); rotate_clockwise(steps,delay_us); gpio_set_level(DIR_PIN, !gpio_get_level(DIR_PIN)); } ``` 上述代码片段展示了如何利用软件延时的方式模拟出适合于步进马达工作的电平变化序列[^2]。 #### 主循环逻辑 最后,在应用程序入口处编写主循环部分,这里可以根据实际需求调整转过的总步数以及每次等待时间长短等参数: ```c void app_main(){ setup_gpio(); while(true){ // 向前走100步,每一步之间延迟50微秒 rotate_clockwise(100, 50); vTaskDelay(pdMS_TO_TICKS(1000)); // 返回原位 rotate_counterclockwise(100, 50); vTaskDelay(pdMS_TO_TICKS(1000)); } } ``` 这段完整的例子说明了怎样借助 ESP32 开发板配合 IDF SDK 实现基本的步进电机控制功能。当然还可以进一步优化此方案比如加入限位开关检测防止越界等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值