当前位置:   article > 正文

基于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口,选用如下,可自定义

  1. #define MOTOR_PIN_A 14
  2. #define MOTOR_PIN_B 27
  3. #define MOTOR_PIN_C 26
  4. #define MOTOR_PIN_D 25

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

  1. // 目标步数(剩余步数)
  2. static uint32_t target_step = 0;
  3. // 电机方向
  4. static int8_t direction = -1;
  5. // 定义步进电机的时序(一二相励磁方式)
  6. static const uint8_t step_sequence[] = {
  7. (1<<0)|(0<<1)|(0<<2)|(0<<3), // Step 1
  8. (1<<0)|(1<<1)|(0<<2)|(0<<3), // Step 2
  9. (0<<0)|(1<<1)|(0<<2)|(0<<3), // Step 3
  10. (0<<0)|(1<<1)|(1<<2)|(0<<3), // Step 4
  11. (0<<0)|(0<<1)|(1<<2)|(0<<3),
  12. (0<<0)|(0<<1)|(1<<2)|(1<<3),
  13. (0<<0)|(0<<1)|(0<<2)|(1<<3),
  14. (1<<0)|(0<<1)|(0<<2)|(1<<3)
  15. };
  16. /**
  17. * @brief 步进电机初始化函数,初始化GPIO口
  18. *
  19. */
  20. void motor_gpio_init()
  21. {
  22. ESP_LOGI(TAG,"configured motor GPIO !\n");
  23. gpio_config_t io_conf;
  24. // 禁用中断
  25. io_conf.intr_type = GPIO_INTR_DISABLE;
  26. // 设置为输出模式
  27. io_conf.mode = GPIO_MODE_OUTPUT;
  28. // 设置输出电平为低电平
  29. io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
  30. // 设置输出电平为高电平
  31. io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
  32. // 配置GPIO
  33. io_conf.pin_bit_mask = (1ULL << MOTOR_PIN_A) | (1ULL << MOTOR_PIN_B) |
  34. (1ULL << MOTOR_PIN_C) | (1ULL << MOTOR_PIN_D);
  35. gpio_config(&io_conf);
  36. }


2.配置硬件定时器

定时器初始化:

  1. //定时器操作句柄
  2. gptimer_handle_t gptimer = NULL;
  3. void step_timer_init()
  4. {
  5. ESP_LOGI(TAG, "Create timer handle");
  6. gptimer_config_t timer_config = {
  7. .clk_src = GPTIMER_CLK_SRC_DEFAULT,
  8. .direction = GPTIMER_COUNT_UP,
  9. .resolution_hz = 1000000, // 1MHz, 1 tick=1us
  10. };
  11. ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
  12. gptimer_event_callbacks_t cbs = {
  13. .on_alarm = step_timer_inr,
  14. };
  15. ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, NULL));
  16. ESP_ERROR_CHECK(gptimer_enable(gptimer));
  17. ESP_LOGI(TAG, "Start timer, stop it at alarm event");
  18. gptimer_alarm_config_t alarm_config1 = {
  19. .reload_count = 0,
  20. .alarm_count = 10000, // period = 10ms
  21. .flags.auto_reload_on_alarm = true,
  22. };
  23. ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config1));
  24. ESP_ERROR_CHECK(gptimer_start(gptimer));
  25. }

定时器中断回调:

  1. static bool IRAM_ATTR step_timer_inr(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *args)
  2. {
  3. BaseType_t high_task_awoken = pdFALSE;
  4. // 相序下标
  5. static int8_t phase_index = 0;
  6. switch(direction) {
  7. case 1: { // 顺时针旋转
  8. phase_index--;
  9. if(phase_index < 0) {
  10. phase_index = 7;
  11. }
  12. gpio_set_level(MOTOR_PIN_A, (step_sequence[phase_index] >> 0) & 0x1);
  13. gpio_set_level(MOTOR_PIN_B, (step_sequence[phase_index] >> 1) & 0x1);
  14. gpio_set_level(MOTOR_PIN_C, (step_sequence[phase_index] >> 2) & 0x1);
  15. gpio_set_level(MOTOR_PIN_D, (step_sequence[phase_index] >> 3) & 0x1);
  16. target_step--;
  17. if(target_step <= 0) {
  18. direction = -1;
  19. }
  20. break;
  21. }
  22. case 0: { //逆时针旋转
  23. phase_index++;
  24. if(phase_index >= 8) {
  25. phase_index = 0;
  26. }
  27. gpio_set_level(MOTOR_PIN_A, (step_sequence[phase_index] >> 0) & 0x1);
  28. gpio_set_level(MOTOR_PIN_B, (step_sequence[phase_index] >> 1) & 0x1);
  29. gpio_set_level(MOTOR_PIN_C, (step_sequence[phase_index] >> 2) & 0x1);
  30. gpio_set_level(MOTOR_PIN_D, (step_sequence[phase_index] >> 3) & 0x1);
  31. target_step--;
  32. if(target_step <= 0) {
  33. direction = -1;
  34. }
  35. break;
  36. }
  37. default : break;
  38. }
  39. // return whether we need to yield at the end of ISR
  40. return (high_task_awoken == pdTRUE);
  41. }

 三、编写驱动接口

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

  1. // 设置步进电机步数及方向
  2. void step_set_steps(uint32_t step, int8_t dire)
  3. {
  4. // 步数乘以相序节拍
  5. target_step = step * 8;
  6. direction = dire;
  7. }
  8. // 设置步进电机转速 (freq ms)
  9. void step_update_freq(uint16_t freq)
  10. {
  11. ESP_LOGI(TAG, "Stop timer");
  12. ESP_ERROR_CHECK(gptimer_stop(gptimer));
  13. ESP_LOGI(TAG, "Start timer, update alarm value ");
  14. gptimer_alarm_config_t alarm_config = {
  15. .reload_count = 0,
  16. .alarm_count = freq * 1000, // period = freq ms
  17. .flags.auto_reload_on_alarm = true,
  18. };
  19. ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
  20. ESP_ERROR_CHECK(gptimer_start(gptimer));
  21. }

总结

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

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

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号