ESP32 - шаговый двигатель, єнкодер и чтение аналогового порта

Нет ответов
igorlab
Offline
Зарегистрирован: 11.11.2015

Доброго времени суток!

пітаюсь разобраться с модуем ESP32, а именно с многозадачностью... Идея такова, есть:

-два шаговых двигателя (ШД),

-энкодер

-экранчик

-аналоговый вход

надо чтобы:

на экранчике обновлялись показания с аналогового входа, причем даже при  команде ШД вращаться на заданное количество шагов, данные на экране должны продолжать обновляться и энкодер должен продолжать работать....

в планах еще прикрутить вебсервер и.. телеграмбота...

сейчас пособирал по нету кучу кусков кода - как-то оно работает, но многое по коду не понятно и команда на вращение двигателей рубит чтение аналогового входа и реакцию на энкодер...

буду благодарен за любые подсказки...

#include "AiEsp32RotaryEncoder.h"
#include "Arduino.h"

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3F, 16, 2);

/*
connecting Rotary encoder
CLK (A pin) - to any microcontroler intput pin with interrupt -> in this example pin 32
DT (B pin) - to any microcontroler intput pin with interrupt -> in this example pin 21
SW (button pin) - to any microcontroler intput pin -> in this example pin 25
VCC - to microcontroler VCC (then set ROTARY_ENCODER_VCC_PIN -1) or in this example pin 25
GND - to microcontroler GND
*/
#define ROTARY_ENCODER_A_PIN      26
#define ROTARY_ENCODER_B_PIN      27
#define ROTARY_ENCODER_BUTTON_PIN 25
#define ROTARY_ENCODER_VCC_PIN    -1 /*put -1 of Rotary encoder Vcc is connected directly to 3,3V; else you can use declared output pin for powering rotary encoder */

#define analog_PIN 35
int val = 0;           // variable to store the value read
int val_old = 0;
AiEsp32RotaryEncoder rotaryEncoder = AiEsp32RotaryEncoder(ROTARY_ENCODER_A_PIN, ROTARY_ENCODER_B_PIN, ROTARY_ENCODER_BUTTON_PIN, ROTARY_ENCODER_VCC_PIN);

int test_limits = 5;
int _encoderValue = 0;

TaskHandle_t get_analog_val;
const int led1 = 2;

void get_analog_val_fn( void * pvParameters ){
//  Serial.print("Task1 running on core ");
//  Serial.println(xPortGetCoreID());
  for(;;){
    
    val = analogRead(analog_PIN);    // read the input pin
    digitalWrite(led1, HIGH);
    delay(1000);
    digitalWrite(led1, LOW);
    delay(1000);
  } 
}

#include "freertos/event_groups.h"
#include "freertos/queue.h"

#if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_RUNNING_CORE 1
#endif

// DRV8825 GPIO Pins
#define M1_STEP 15
#define M1_DIR  4
#define M1_EN   23

#define M2_STEP 19
#define M2_DIR  18
#define M2_EN   5

struct mot_send {  // струкура, для хренения количества шагов и задержка мс
    int steps;
    uint32_t delayms;
};

// Handles for Multitasking

const int MOT1_BIT = BIT0;
const int MOT2_BIT = BIT1;

xQueueHandle mot1_queue;
xQueueHandle mot2_queue;
EventGroupHandle_t mot_eventgroup;

void setup() {
  Serial.begin(115200);
  Serial.println("Hello");
  pinMode(ROTARY_ENCODER_A_PIN,      INPUT_PULLUP);
  pinMode(ROTARY_ENCODER_B_PIN,      INPUT_PULLUP);
  pinMode(ROTARY_ENCODER_BUTTON_PIN, INPUT_PULLUP);

  pinMode(led1, OUTPUT);
    xTaskCreatePinnedToCore(
                    get_analog_val_fn,   /* Task function. */
                    "Task1",     /* name of task. */
                    10000,       /* Stack size of task */
                    NULL,        /* parameter of the task */
                    1,           /* priority of the task */
                    &get_analog_val,      /* Task handle to keep track of created task */
                    1);          /* pin task to core 0 */                  
  delay(500); 
  pinMode(M1_DIR, OUTPUT);
  pinMode(M1_STEP, OUTPUT);
  pinMode(M1_EN, OUTPUT);
  digitalWrite(M1_DIR, LOW);   
  digitalWrite(M1_STEP, LOW); 
  digitalWrite(M1_EN, LOW);  

  pinMode(M2_DIR, OUTPUT);
  pinMode(M2_STEP, OUTPUT);
  pinMode(M2_EN, OUTPUT);
  digitalWrite(M2_DIR, LOW);   
  digitalWrite(M2_STEP, LOW); 
  digitalWrite(M2_EN, LOW);  

  // Multitasking setup  
  mot1_queue = xQueueCreate(10, sizeof(mot_send)); // что это?
  mot2_queue = xQueueCreate(10, sizeof(mot_send));
  mot_eventgroup = xEventGroupCreate();

  xTaskCreatePinnedToCore(mot_task_step,     /* Указатель на функцию, которая реализует задачу. */
                          "mot_task_step1",  /* Текстовое имя задачи. Этот параметр нужен только для упрощения отладки. */
              4096,              /* Глубина стека */
              (void *)1,         /* ?? Мы не используем параметр задачи. */ // что это?
              1,                 /* Задача будет запущена с приоритетом 1. */
              NULL,              /* Мы не будем использовать хендл задачи. */
              ARDUINO_RUNNING_CORE);
              
  xTaskCreatePinnedToCore(mot_task_step, "mot_task_step2", 4096, (void *)2, 1, NULL, ARDUINO_RUNNING_CORE);

  //we must initialize rorary encoder 
  rotaryEncoder.begin();
  rotaryEncoder.setup([]{rotaryEncoder.readEncoder_ISR();});
  //optionally we can set boundaries and if values should cycle or not
  rotaryEncoder.setBoundaries(0, 20, true); //minValue, maxValue, cycle values (when max go to min and vice versa)

  Wire.begin (21, 22);   // sda= GPIO_21 /scl= GPIO_22
//  pinMode(SDA,INPUT_PULLUP);
//  pinMode(SCL,INPUT_PULLUP);
  
  lcd.init(); // initialize the lcd
  // Print a message to the LCD.
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Hello world");
  lcd.setCursor(0,1);
  lcd.print("ESP32 I2C LCD");
}

void loop() {
  //in loop call your custom function which will process rotary encoder values
  rotary_loop();
   if (val_old != val) {
      val_old = val;
      Serial.print("analog =    ");
      Serial.println(val);
      lcd.setCursor(0,0);
      lcd.print("analog =   ");
      lcd.setCursor(5,0);
      lcd.print(val);
   }
    if (Serial.available()) {
    char c = Serial.read();
    if (c == 'm') {
        dubmot_step(-1000, 1, -1000, 1);
    } else if (c == 'l') {
        dubmot_step(1000, 2, 1000, 1);
    } else if (c == 'h') {
        dubmot_step(1000, 1, 0, 1);
    } else if (c == 'c') {
        dubmot_step(0, 1, 1000, 1);
    } 
  }
    

  //delay(50);                               
  //if (millis()>20000) rotaryEncoder.enable (); // для чего это?
}

void rotary_loop() {
  //first lets handle rotary encoder button click
  if (rotaryEncoder.currentButtonState() == BUT_RELEASED) {
    //we can process it here or call separate function like:
    rotary_onButtonClick();
  }

  //lets see if anything changed
  int16_t encoderDelta = rotaryEncoder.encoderChanged();
  
  //optionally we can ignore whenever there is no change
  if (encoderDelta == 0) return;
  
  //for some cases we only want to know if value is increased or decreased (typically for menu items)
  if (encoderDelta>0) Serial.print("+");
  if (encoderDelta<0) Serial.print("-");

  //for other cases we want to know what is current value. Additionally often we only want if something changed
  //example: when using rotary encoder to set termostat temperature, or sound volume etc
  
  //if value is changed compared to our last read
  if (encoderDelta!=0) {
    //now we need current value
    int16_t encoderValue = rotaryEncoder.readEncoder();
    _encoderValue = encoderValue;
    //process new value. Here is simple output.
    Serial.print("Value: ");
    Serial.println(encoderValue);
    lcd.setCursor(14,0);
    lcd.print("   ");
    lcd.setCursor(14,0);
    lcd.print(encoderValue);    
  }  
}

void rotary_onButtonClick() {
  //rotaryEncoder.reset();
  //rotaryEncoder.disable();
//  rotaryEncoder.setBoundaries(-test_limits, test_limits, false);
//  test_limits *= 1;
//  lcd.setCursor(0,0);
//  lcd.print("analog =   ");
  lcd.setCursor(0,1);
  lcd.print("encoderVal = "); lcd.print("   ");
  lcd.setCursor(0,1);
  lcd.print("encoderVal = "); lcd.print(_encoderValue);
  
  lcd.setCursor(14,0);
  lcd.print("   ");
}
/*
typedef enum {
    BUT_DOWN = 0,
    BUT_PUSHED = 1,
    BUT_UP = 2,
    BUT_RELEASED = 3,
    BUT_DISABLED = 99, //this state is after you call rotaryEncoder.disable(); 
  } ButtonState;
*/

/**@brief Function for handling one Steppermotor with multitasking.
 * Runs forever waiting for orders in the queue and after finishing 
 * set the eventgroup bit for the motor.
 *
 * @param[in] pvParameters motor number.
 */
void mot_task_step(void *pvParameters) {
  int m_step,m_dir,m_en;
  int mot = (int)pvParameters;
  int steps;
  uint32_t delayms;
  xQueueHandle mot_queue;           // что это?
  struct mot_send oMotSendPara;

  if (mot==2) {
    mot_queue=mot2_queue;
    m_step=M2_STEP;
    m_dir=M2_DIR;
    m_en=M2_EN;
  } else {
    mot_queue=mot1_queue; // xQueueHandle mot1_queue;
    m_step=M1_STEP;
    m_dir=M1_DIR;
    m_en=M1_EN;
    
  }

  while (1) {
    if(xQueueReceive(mot_queue,&oMotSendPara,60000/portTICK_RATE_MS)==pdTRUE) {  // max wait 60s // что это все такое?
      steps = oMotSendPara.steps;
      delayms = oMotSendPara.delayms;
      Serial.print("task motor ");
      Serial.print(mot);
      digitalWrite(m_en, LOW);  
      if (steps > 0) {
        Serial.print(" CCW Steps ");
         digitalWrite(m_dir, HIGH);   
      } else {
        Serial.print(" CW  Steps ");
          digitalWrite(m_dir, LOW);   
      }
      steps = abs(steps);
      
      Serial.print(steps);
      Serial.print(" Speed ");
      Serial.println(delayms);
      Serial.print("task motor ");
      Serial.print(mot);
      Serial.println(" start");
      for (int pos=0;pos<steps;pos++) {
        digitalWrite(m_step, HIGH); 
        vTaskDelay(delayms/portTICK_RATE_MS); // delay x 1ms // что это такое?
        digitalWrite(m_step, LOW); 
        vTaskDelay(delayms/portTICK_RATE_MS); // delay x 1ms
      }
      Serial.print("task motor ");
      Serial.print(mot);
      Serial.println(" stop");
     if (mot==2) {
       Serial.println("mot_task_step xEventGroupSetBits 2");
       xEventGroupSetBits(mot_eventgroup, MOT2_BIT);  // что это за MOT2_BIT?
      } else {
       Serial.println("mot_task_step xEventGroupSetBits 1");
        xEventGroupSetBits(mot_eventgroup, MOT1_BIT);
      }
    }
    if (uxQueueMessagesWaiting(mot_queue)==0) { // no message? take a break
      vTaskDelay(100 / portTICK_RATE_MS); // delay 100ms // что это, зачем делим на 1??
    }
  }
  Serial.println("mot_task_step ends");  
}

int dubmot_step(int steps1, uint32_t delayms1,int steps2, uint32_t delayms2) {
  EventBits_t bits;
  struct mot_send oMotSendPara1,oMotSendPara2;
  oMotSendPara1.steps=steps1;
  oMotSendPara1.delayms=delayms1;
  oMotSendPara2.steps=steps2;
  oMotSendPara2.delayms=delayms2;

  // inter task information exchange
  Serial.println("dubmot_step xQueueSendToBack 1 ");

  if(xQueueSendToBack(mot1_queue,&oMotSendPara1,1000/portTICK_RATE_MS)!=pdTRUE) {  // что это такое?(((
    Serial.println("dubmot_step error stop 1");
     return 1;
    }
   Serial.println("dubmot_step xQueueSendToBack 2 ");
   if(xQueueSendToBack(mot2_queue,&oMotSendPara2,1000/portTICK_RATE_MS)!=pdTRUE) {
    Serial.println("dubmot_step error stop 2");
      return 1; // что это возвращается и для чего?
    }

  // sync

  while (1) {
    bits=xEventGroupWaitBits(mot_eventgroup, MOT1_BIT|MOT2_BIT,pdTRUE, pdTRUE, 60000 / portTICK_RATE_MS); // max wait 60s   // что это, откуда 60 секунд??
    if(bits==(MOT1_BIT|MOT2_BIT)) {  // xWaitForAllBits == pdTRUE, so we wait for MOT1_BIT and MOT2_BIT so all other is timeout
      Serial.println("dubmot_step sync 1+2");
      break;
    } else {
      if (uxQueueMessagesWaiting(mot1_queue)==0 && uxQueueMessagesWaiting(mot2_queue)==0 ) { // no message? take a break // что это за сравнение??
        break;
      }
      Serial.println("dubmot_step wait sync");
    }
  }
  return 0;  // что это возвращается и для чего?
}