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

目录

前言

一、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));
}

总结

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

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

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值