Столкновение прерываний таймера

Я работаю с MEGA2560, пытаясь реализовать скоростные рампы для трех степперов одновременно. Цель состоит в том, чтобы двигаться в разные позиции, применяя ускорения и замедления, одновременно завершая их все.

Чтобы сделать это, исходя из расстояний, на которые должен двигаться каждый из двигателей, я вычисляю количество шагов, которые нужно сделать, и скорости для каждого из этих шагов, чтобы гарантировать, что все двигатели закончат работу одновременно. Для этого я использую таймеры 3, 4 и 5 в режиме сравнения. Таким образом, при каждом перерыве я заставляю двигатели делать один шаг и после этого устанавливаю новое значение сравнения для таймера, чтобы достичь новой скорости движения.

Моя проблема в том, что в какой-то момент мои прерывания терпят крах, и один из двигателей перестает работать. Это происходит только при одном условии: когда двигатель 1 должен сделать 18 шагов, а двигатель 2-34. В этом случае оба двигателя 1 и 3 заканчивают работу, но двигатель 2 застревает и больше не движется. Хотя, если его мотор 1, который должен сделать 34 шага, и мотор 2, 18, то все работает. Двигатель 1 связан с таймером 3, а двигатель 2-с таймером 4. Это наводит меня на мысль, что это должно быть связано с приоритетами прерываний.

В следующем коде вы можете увидеть мои прерывания:

ISR(TIMER3_COMPA_vect) {
    m1_step();
    m1_steps_done++;
    m1_configure_vel();
}
ISR(TIMER4_COMPA_vect) {
    m2_step();
    m2_steps_done++;
    m2_configure_vel();
}
ISR(TIMER5_COMPA_vect) {
    m3_step();
    m3_steps_done++;
    m3_configure_vel();
} 

В то время как в конфигурации скорости я имею:


void m1_configure_vel() {

  if (m1_steps_done < m1_acceleration_steps) {
    m1_temp_vel = m1_temp_vel + acceleration;
  }
  else if (m1_steps_done <= (m1_steps - m1_acceleration_steps)) {
    m1_temp_vel = m1_temp_vel;
  }
  else if (m1_steps_done < m1_steps) {
    m1_temp_vel = m1_temp_vel - acceleration;
  }
  else {
    m1_temp_vel = 0;
    m1_finished = true;
  }
  if (m1_temp_vel <=0){
    m1_temp_vel = 0;
  }

  OCR3A = frequency_to_compare_value(m1_temp_vel);
}

Как вы можете видеть, все, что я делаю здесь, зависит от шагов, выполняемых двигателем, и количества шагов, которые нужно сделать, я увеличиваю, поддерживаю или уменьшаю скорость двигателя и устанавливаю значение сравнения для таймера.

Все дело в том, что этот код работает нормально, пандусы рассчитаны правильно, а двигатели двигаются правильно. Проблема в том, что при условии, которое я объяснил ранее, прерывания могут сталкиваться из-за их приоритетов, и один из них просто перестает происходить.

Мне было интересно, имел ли кто-нибудь представление или сталкивался с чем-то подобным раньше.

, 👍1

Обсуждение

Это странная проблема. Как я знаю, на чипах AVR нет приоритета прерывания. Если происходит прерывание, в то время как другой ISR в настоящее время работает, соответствующий флаг установлен, и выполнение ISR произойдет после завершения первого ISR. У вас есть доступ к осциллографу или логическому анализатору? Было бы неплохо проверить, выводится ли ожидаемый сигнал на вывод motor2. Возможно, есть аппаратная проблема. Или у вас есть какой-то код, который может использовать один из таймеров?, @chrisl

Спасибо за ваш ответ @christl . Я отбрасываю аппаратные проблемы, так как вся система работает во всех других ситуациях. Теперь я попытался изменить прескалер таймеров. Они были установлены на 1, но теперь я настроил их на 8, так что таймеры теперь имеют частоту 2 МГц. Удивительно, но теперь система не дает сбоев, вся последовательность выполняется без ошибок, и ни одно прерывание не замерзает. Я полагаю, что, уменьшив частоту таймеров, они могут теперь работать под меньшим "давлением", так что они могут не ударять друг друга., @Izar Thomson

@chrisl Существует то, что называется "естественным приоритетом" - каждый интрерупт имеет свой номер, и они проверяются последовательно - прерывания с более низкими номерами происходят раньше прерываний с более высокими номерами. Если прерывание с более низким номером повторится до того, как появится шанс запустить прерывание с более высоким номером, то оно будет ответлено предпочтительнее, чем прерывание с более высоким номером. Вот почему ISR должны быть как можно короче., @Majenko

@Majenko Я реализовал эти прерывания таймера на частоте, намного превышающей ту, о которой я упоминал ранее. Когда это происходит и мои прерывания прекращаются, ни один из них не имеет частоты выше 500 Гц, в то время как с тем же кодом они могут совершать одновременные движения на 4 кГц. При этом я имею в виду, что мой код внутри прерываний не длится достаточно долго, чтобы не допустить других прерываний. В моем критическом случае частоты ISR достаточно низки, чтобы запустить весь код внутри, но без смысла один из них просто застревает., @Izar Thomson

@Majenko О, интересно. Я этого не знал, спасибо :), @chrisl

Я удивлен, что никто не попросил вас опубликовать компилируемый код. У нас даже нет соответствующих типов данных., @timemage


2 ответа


1

Я думаю, что проблема вызвана размером ваших ISR. При использовании ISR важно сделать код как можно короче, и вы, конечно же, должны избегать использования каких-либо функций!

Я бы изменил каждый из ваших ISR на:

ISR(TIMER3_COMPA_vect) {
    m1_steps_done++;
}
ISR(TIMER4_COMPA_vect) {
    m2_steps_done++;
}
ISR(TIMER5_COMPA_vect) {
    m3_steps_done++;
} 

затем в главном цикле кода вы можете изменить его так, чтобы, если m1_steps_done, m2_steps_done или m3_steps_done увеличились, выполнить шаг и настроить функции

,

0

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

,