Как управлять шаговым двигателем, и одновременно выполнять другие задачи?

Плата драйвера Polulo A4988 ожидает сигнала LOW -> ВЫСОКИЙ импульс на цифровом выводе для включения шагового двигателя.

Простой способ создать импульс с задержкой, скажем, 100 мкс, — использовать функцию delayMicroсекунды:

#define STEP_PIN  3
#define DELAY     100

void setup(){
    pinMode(STEP_PIN, OUTPUT);
}

void loop(){
    //тяжелые вычисления, требующие времени
    digitalWrite(STEP_PIN, LOW);
    digitalWrite(STEP_PIN, HIGH);
    delayMicroseconds(DELAY);
}

Хотя это работает, проблема в том, что все время, которое процессор тратит на функцию delayMicroсекунды, тратится впустую. Это значит, что если бы я захотел, например, обрабатывать входы с последовательной линии, у меня не было бы на это времени. Кроме того, время выполнения этих вычислений приведет к увеличению времени импульса, что не идеально, поскольку скорость шагового двигателя больше не будет одинаковой.

Как я могу эффективно подавать импульсы степперу в нужное время, оставляя при этом себе место для выполнения других задач между импульсами?


Ps Это для этого проекта: https://github.com/joeiddon/chess_robot.

, 👍0

Обсуждение

Используйте библиотеку, которая генерирует импульсы за вас: https://www.airspayce.com/mikem/arduino/AccelStepper/ Какую плату Arduino вы используете?, @Jot

@Jot Я использую Arduino Nano. Что касается библиотеки, то меня больше интересует понимание того, как работает шаговое управление, а не конечный результат. :), @Joe Iddon

В этом случае вы могли бы взглянуть на исходный код [AccelStepper](https://github.com/adafruit/AccelStepper/blob/master/AccelStepper.cpp). При беглом взгляде кажется, что они используют тот же метод, который Джо Иддон описывает ниже, с некоторыми дополнениями, позволяющими увеличивать и уменьшать скорость двигателя. Совершенно другое решение — использовать таймер и таймер-прерывание для запуска кода через заданный интервал., @Gerben

@Gerben Спасибо за ссылку, я посмотрю на источник. Кроме того, система, управляемая прерываниями, звучит интересно, я об этом не подумал. Однако сейчас он работает хорошо, поэтому я сомневаюсь, что потрачу слишком много времени на его совершенствование. Хотя спасибо за вклад :), @Joe Iddon


1 ответ


1

Вместо того, чтобы делать задержку между импульсами, выполните цикл основной функции loop() как можно быстрее, постоянно проверяя время, чтобы увидеть, стоит ли нам подавать импульс на двигатель. р>

Это означает, что вычисления могут выполняться в цикле (при условии, что они достаточно быстрые).

Для реализации этого можно использовать функцию micros() для получения беззнакового значения long, представляющего микросекунды, прошедшие с момента запуска программы.

Затем в каждом цикле цикла нам нужно проверять, составляет ли разница между текущим временем и последним временем, когда мы подали импульс, >= задержку импульса. В этом случае мы посылаем импульс на двигатель

Однако вместо установки переменной времени последнего импульса на текущее время мы увеличиваем переменную последнего импульса на время задержки. Это приводит к тому, что даже если мы немного опоздали с пульсацией , мы притворяемся, что действительно пульсировали в нужное время, чтобы они не смещались со временем.

Это будет выглядеть примерно так:

#define STEP_PIN  3
#define DELAY     100

uint32_t last_pulse;

void setup(){
    pinMode(STEP_PIN, OUTPUT);
    last_pulse = micros();
}

void loop(){
    //тяжелые вычисления, требующие времени
    if (micros() - last_pulse >= DELAY){
        last_pulse += DELAY;
        digitalWrite(STEP_PIN, LOW);
        digitalWrite(STEP_PIN, HIGH);
    }
}

Обратите внимание, что здесь есть небольшая тонкость: результат micros() и, следовательно, значение last_pulse переполнится после ~70 минут. Однако, поскольку мы берем разницу двух беззнаковых чисел, этот метод все равно будет работать, если micros() никогда не опережает на целый цикл last_pulse, который в практика никогда не будет иметь место.

,