Часовой механизм на основе шагового двигателя с DS3231

Я работаю над часовым механизмом, который питается от шагового двигателя и платы драйвера GSM2. Задержка между шагами, необходимая для достижения 1 оборотов в минуту на секундной стрелке, составляет 25 мс. В моей текущей настройке я посылаю импульсы на плату драйвера с помощью функции задержки (). Это работает достаточно хорошо, хотя для меня это недостаточно точно. Чтобы устранить эту проблему, я заказал модуль DS3231 RTC, думая, что смогу использовать его для замены функции delay()/millis(). Однако я не могу понять, как достичь миллисекундного разрешения с помощью RTC. Похоже, что это должно быть прямолинейно, учитывая, что генератор на борту RTC составляет 32 кГц. Я новичок в программировании на arduino и нахожусь вне своей зоны комфорта с этим.

Кто - нибудь знает способ достижения необходимых нам импульсов в 25 мс при сохранении точности RTC? Можно ли использовать RTC для коррекции выходных данных, создаваемых встроенной функцией delay()/millis ()? Как бы вы затем преобразовали этот вывод в импульсы, посылаемые на плату драйвера шагового двигателя?

Интернет-исследования дают некоторые неоднозначные результаты относительно того, возможно ли это или нет. Я еще не видел примера часов с разрешением в миллисекунду. Теория, лежащая в ее основе, имеет смысл, но я не знаю, с чего начать.

, 👍0


2 ответа


1

Непонятно, зачем вам нужна высокая точность, и вы не включили свой код. Поэтому мои объяснения будут такими же широкими, как и ваш вопрос.

"разрешение в миллисекунду" - это широкий термин. Как правило, вы должны учитывать 2 различные ошибки.

  1. Дрожание для каждого значения millis()
  2. часы дрейфуют во времени

Вы можете довольно легко устранить второй момент, используя RTC с библиотекой времени. Он использует RTC в качестве эталона, так что у вас не будет такого большого смещения часов. Тем не менее, вам придется использовать функции библиотек для всех вещей, связанных со временем, поскольку это не изменяет значение millis() или его братьев и сестер.

Кроме того, для небольших различий в диапазоне нескольких миллисекунд вам лучше использовать micros() вместо millis() (если вы не используете функции RTCs).


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

Непонятно, зачем именно вам нужно "разрешение в миллисекунду", и вы не показали нам свой код. Но в Интернете есть несколько шаговых библиотек, например шаговая библиотека или стандартная библиотека AccelStepper (Accelstepper более способен, не знаю, если вам это нужно). С помощью библиотек AccelStepper вы можете установить определенную скорость и целевую позицию, а затем просто вызвать метод then run () (не посмотрел название, но определенно есть такая функция), пока не будет достигнута желаемая позиция. Сам метод будет запускать шаговый двигатель только в том случае, если для этого придет время. Таким образом, вы освобождаетесь от выполнения всего этого с помощью millis (), поскольку библиотека делает это за вас.

Хотя библиотеки используют внутреннее время ардуино: millis() и братья и сестры. Чтобы изменить это, вам придется изменить библиотеку.

,

0

Рассмотрим контур фазовой автоподстройки частоты (ФАПЧ) для синхронизации импульсов 40x25 мс с RTC 1pps.

Если вам нужно 25 мс/импульс, чтобы получить 1 об/мин, это 2400ppm или 1pulse / 0.025 ms=40pps. Если вы получаете импульс 1pps от RTC, вы можете использовать его, чтобы дисциплинировать свой хронометраж. https://www.romanblack.com/one_sec.htm это хороший ориентир.

Основной трюк состоит в том, чтобы отслеживать несоответствие и бежать быстрее, когда оно медленное, и бежать медленнее, когда оно слишком быстрое.

Попробуйте что-то вроде приведенного ниже, но замените импульсный светодиод своим шаговым кодом.

// pps_pll.ino for https://arduino.stackexchange.com/questions/67699/stepper-motor-based-clock-movement-with-ds3231
//
// Adapting code from https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/
// to do a PPS PLL per https://www.romanblack.com/one_sec.htm

const int pps = 40;
const int pulse_us = 1;
const int pulse_interval = (1000L / pps);
int count = 0; //
int count_at_sync = 0; //
unsigned long next_pulse = 0; //
unsigned long pulse_off = 0;

const byte led_pin = LED_BUILTIN;
const byte interrupt_pin = 2; // Choose pin per https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/
volatile byte state = LOW;

void setup() {
  pinMode(led_pin, OUTPUT);
  pinMode(interrupt_pin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interrupt_pin), sync_ISR, RISING);
}

void loop() {
  unsigned long loop_micros = micros();  // capture start of loop time
  if (loop_micros >= next_pulse) {
    // Schedule pulse
    digitalWrite(led_pin, HIGH);
    next_pulse += pulse_interval ;
    pulse_off = loop_micros + pulse_us; // schedule off time
    // PLL accounting
    count++;
    if (count_at_sync > 0) next_pulse++; // too fast: slow down
    else if (count_at_sync < 0) next_pulse--; // too slow: speed up
  }
  if (state == HIGH && loop_micros >= pulse_off) {
    state = LOW;
  }
  digitalWrite(led_pin, state);
}

void sync_ISR() { // called by 1pps interrupt
  count_at_sync = (count -= pps); // PLL accounting and record synchronization state
}
,