sadr0b0t / stepper_h

Stepper motor control library for ChipKIT/Arduino
GNU Lesser General Public License v3.0
3 stars 1 forks source link

Мотор всегда делает первый шаг в цикле, даже если есть причины его не делать #1

Closed sadr0b0t closed 7 years ago

sadr0b0t commented 8 years ago

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

Например:

sadr0b0t commented 8 years ago

Код обработчика таймера handle_interrupts https://github.com/1i7/stepper_h/blob/master/stepper_h/stepper_timer.cpp#L824

на одном шаге работает в 3 приема:

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

    cstatuses[i].stopped

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

В чем проблема, пока непонятно. Пока единственное логичное объяснение - на первой итерации код не попадает в первых проход, а сразу отправляется во второй-третий. Теоретически, это возможно при определенных значениях частоты таймера (в задержку между двумя шагами должны уместиться как минимум 3 импульса). Но это вроде как не этот случай, плюс - последующие шаги отрабатывают и проверки срабатывают, значит бага скорее всего где-то еще.

sadr0b0t commented 7 years ago

Короче, понятно.

Если готовить вращение таким образом (указывать в prepare_whirl скорость 0):

    // подготовить вращение
    prepare_whirl(sm, dir, 0);//sm->pulse_delay);

(для прошивки rraptor_mpide3 вполнить команду "rr_go x -1")

то получаем выход за границы на 1 шаг

X.pos=0.0000000000, Y.pos=0.0000000000, Z.pos=0.0000000000
ok
X.***WARNING: fixing step_delay to match minimal pulse_delay step_delay=0us, pulse_delay=1000us
***ERROR: timer handler takes longer than timer period: cycle time=98499us, timer period=200us
pos=-7.5000000000, Y.pos=0.0000000000, Z.pos=0.0000000000

это сообщение будет при включенной отладке в stepper_lib_config.h

#define DEBUG_SERIAL

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

Если готовить вращение так (указывать задержку sm->pulse_delay)

    // подготовить вращение
    prepare_whirl(sm, dir, sm->pulse_delay);

То выхода за границы нет.

Сообщение с предупреждением выводится в stepper_timer.h https://github.com/1i7/stepper_h/blob/master/stepper_h/stepper_timer.cpp#L1072

// проверим, корректна ли задержка
                if(step_delay < smotors[i]->pulse_delay) {
                    // посмотрим, что делать с ошибкой
                    if(small_pulse_delay_handle == FIX) {
                        // попробуем исправить:
                        // не будем делать шаги чаще, чем может мотор
                        // (следует понимать, что корректность вращения уже нарушена)
                        step_delay = smotors[i]->pulse_delay;

                        #ifdef DEBUG_SERIAL     
                            Serial.print("***WARNING: fixing step_delay to match pulse_delay ");
                            Serial.print("step_delay=");
                            Serial.print(step_delay);
                            Serial.print("us, pulse_delay=");
                            Serial.print(smotors[i]->pulse_delay);
                            Serial.println("us");
                        #endif // DEBUG_SERIAL
                    } else if(small_pulse_delay_handle == STOP_MOTOR) {
                        // останавливаем мотор
                        cstatuses[i].stopped = true;

                        if(cstatuses[i].stepper_info != NULL) {
                            cstatuses[i].stepper_info->status = STEPPER_STATUS_FINISHED;
                        }
                    } else if(small_pulse_delay_handle == CANCEL_CYCLE) {
                        // завершаем весь цикл
                        canceled = true;
                    }
                    // иначе, игнорируем

                    // в любом случае, обозначим ошибку
                    if(cstatuses[i].stepper_info != NULL) {
                        cstatuses[i].stepper_info->error_pulse_delay_small = true;
                    }
}

Инересно, что в случае, если задержка step_delay=0, то ее значение исправляется на тот же step_delay = smotors[i]->pulse_delay динамически. Но при варианте с автоматическим исправлением, почему-то, происходит выход за границу, нужно понять, почему, и добавить тесты на эту ситуацию.

sadr0b0t commented 7 years ago

Короче, вот исправление (комент к коммиту не корректен - это исправление текущего тикета #1 , а не #3 ) https://github.com/1i7/stepper_h/commit/2979e6483eea7fcab50c68de8a316aa723ffc7ee

объяснение в коменте к тесту

// а вот и объяснение: если указываем step_delay=0 в prepare_whirl, то первый шаг будет
// сделан без всех проверок сразу на 1й тик таймера (этапы tick1 и tick2 будут пропущены),
// т.к. первый тик сразу получается наиближайшим к фронту шага (т.е. нулю).
// Решение: во всех местах, где устанавливаем значение step_delay для очередного шага,
// нужно делать проверку, что в нее уместится как минимум 3 тика таймера 
// (сравнения с аппаратными ограничениями задержки для шагов мотора достаточно, 
// т.к. она должна заведомо включать как минимум 3 тика).
// В нашем случае - это метод prepare_whirl, но другие места (особенно там, где есть динамическая
// задержка) нужно тоже все проверить.

этот же коммит исправляет тикет (нужно проверить на контроллере, а лучше как-нибудь через отдельный тест-кейс) https://github.com/1i7/stepper_h/issues/2

добавил тесты для prepare_whirl и prepare_steps, для остальных вариантов подготовки шагов еще предстоит написать тесты