Глобальная переменная не обновляется до следующего цикла

Я пытаюсь создать тахометр на С++ для своего ESP32. Когда я раскомментирую Serial.printf("outside rev: %d \n", rev); вне условного оператора, он работает, но когда я его комментирую, я получаю значения, которые на порядок больше, чем должны быть (700 оборотов без, против 7 оборотов с). Мое лучшее предположение состоит в том, что оператор печати замедляет loop() ровно настолько, чтобы incrementRevolutions() переключал глобальную переменную passedMagnet с true на false перед следующим циклом. Это имело бы смысл, так как задержка обновления passMagnet позволила бы newRevCount++; запускаться несколько раз. Но это, очевидно, то, что я не могу отладить ни с помощью операторов печати, ни с помощью пошаговой отладки, учитывая чувствительный ко времени характер состояния гонки.

bool passedMagnet = true;
int incrementRevolutions(int runningRevCount, bool passingMagnet)
{
//    Serial.printf("passedMagnet: %d ,  passingMagnet %d , runningRevCount: %d \n", passedMagnet, passingMagnet, runningRevCount);
    int newRevCount = runningRevCount;
    if (passedMagnet && passingMagnet)
    { //Started a new pass of the magnet
        passedMagnet = false;
        newRevCount++;
    }
    else if (!passedMagnet && !passingMagnet)
    { //The new pass of the magnet is complete
        passedMagnet = true;
    }
    return newRevCount;
}

unsigned long elapsedTime = 0;
unsigned long intervalTime = 0;
int rev = 0;
void loop()
{
    intervalTime = millis() - elapsedTime;
    rev = incrementRevolutions(rev, digitalRead(digitalPin));

//    Serial.printf("outside rev: %d \n", rev);
    if (intervalTime > 1000)
    {
        Serial.printf("rev: %d \n", rev);
        rev = 0;
        elapsedTime = millis();
    }
}

Это известная проблема с программированием на Arduino или C++? Что мне делать, чтобы это исправить?


person James Jordan Taylor    schedule 14.02.2021    source источник
comment
Да, это известная ошибка программирования на C, а именно: неопределенное поведение. Где-то в коде есть ошибка, которую вы не показали, которая в конечном итоге приводит к повреждению памяти, что проявляется в случайном поведении, которое меняется, когда добавляется что-то столь же невинное, как оператор печати. Вам нужно будет найти ошибку где-то в коде, которая не показана, и исправить ее.   -  person Sam Varshavchik    schedule 14.02.2021
comment
Полная кодовая база не намного больше. Он доступен здесь: github.com/jamesjmtaylor/esp32-ftms-server У вас есть любые предложения о том, как я могу найти ошибку? Есть ли профилировщик или другие инструменты отладки, которые вы бы порекомендовали?   -  person James Jordan Taylor    schedule 14.02.2021
comment
Почему бы вам не профилировать его с помощью micros()? Посмотрите, сколько времени это займет с Serial.printf() и без него, и вы должны знать, является ли это узким местом   -  person Jack Lilhammers    schedule 14.02.2021
comment
Используя micros(), я обнаружил, что цикл занял 33334 микросекунды с Serial.printf() и 17708 микросекунд без него, то есть почти в два раза дольше.   -  person James Jordan Taylor    schedule 14.02.2021
comment
язык, который вы используете, не C. Это компилятор C++, используемый в сочетании с пользовательской стандартной библиотекой.   -  person Antti Haapala    schedule 14.02.2021
comment
Обновленный вопрос, чтобы соответствовать.   -  person James Jordan Taylor    schedule 14.02.2021


Ответы (1)


Думаю тест виноват. Мне пришлось немного переименовать и переместить вещи, чтобы визуализировать логику, извините за это.

bool magStateOld = false;  // initialize to digitalRead(digitalPin) in setup()

int incrementRevolutions(int runningRevCount, bool magState)
{
    int newRevCount = runningRevCount;

    // detect positive edge.
    if (magState && !magStateOld)      // <- was eq. to if (magState && magStateOld)
                                       // the large counts came from here.  
    { 
        newRevCount++;
    }
    magStateOld = magState; // record last state unconditionally

    return newRevCount;
}

Вы также можете написать это как...

int incrementRevolutions(int n, bool magState)
{
    n += (magState && !magStateOld);
    magStateOld = magState;
    return n;
}

Но самый экономичный (и самый быстрый) способ сделать то, что вы хотите, это:

bool magStateOld;

inline bool positiveEdge(bool state, bool& oldState)
{
    bool result = (state && !oldState);
    oldState = state;
    return result;
}  

void setup()
{
  // ...

  magStateOld = digitalRead(digitalPin);
}

void loop()
{
    // ...

    rev += (int)positiveEdge(digitalRead(digitalPin), magStateOld);

    // ...
}

Его можно использовать повторно, и он экономит место в стеке и ненужные назначения.

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

Пример:

constexpr byte debounce_delay = 50; // ms, you may want to play with
                                    // this value, smaller is better.
                                    // but must be high enough to 
                                    // avoid issues on expected
                                    // RPM range. 
                                    // 50 ms is on the high side.
    
byte debounce_timestamp;            // byte is large enough for delays 
                                    // up to 255ms. 

// ...

void loop()
{
    // ...

    byte now = (byte)millis();

    if (now - debounce_timestamp >= debounce_delay)
    {
        debounce_timestamp = now;
        rev += (int)positiveEdge(digitalRead(digitalPin), magStateOld);
    }

    // ...
}
person Michaël Roy    schedule 14.02.2021
comment
Я реализовал ваше первое предложение и получил отчет о регистрации, аналогичный тому, что у меня был раньше: rev: 796 , instantaneousRpm: 7016.470588235 . Это происходило раз в секунду. - person James Jordan Taylor; 14.02.2021
comment
@JamesJordanTaylor Что вы измеряете? Трещина в руке или в моторе? И не забудьте ! в тесте - person Michaël Roy; 14.02.2021
comment
@JamesJordanTaylor Я предлагаю вам попробовать повернуть двигатель вручную или использовать магнит для срабатывания датчика, чтобы проверить показания. Если у вас есть прицел, проверьте сигнал на шум и уровень сигнала. Оно должно быть чистым с очень быстрым подъемом до 2 вольт и более. Если нет, вам придется либо использовать задержку для устранения дребезга, либо усилить сигнал до надлежащего цифрового уровня 5 В. - person Michaël Roy; 14.02.2021
comment
@JamesJordanTaylor В вашем расчете RPM тоже что-то не так. 796 тиков в секунду это 796 * 60 = 47760 об/мин, а не 7016 об/мин. - person Michaël Roy; 14.02.2021
comment
На данный момент это просто магнит, который я двигаю рукой вперед и назад перед датчиком холла. Я скопировал и вставил верхний блок кода точно так, как он был у вас, затем добавил magStateOld = digitalRead(digitalPin) к setup() и получил тот же результат. Меня меньше беспокоит ошибочность RPM, чем ошибочность революций. Один шаг за раз :) - person James Jordan Taylor; 14.02.2021
comment
Этот тест — защелка, я использую его уже 30 лет, так что я знаю, что он работает. Вот почему я спрашиваю. Инициализация в настройках предназначена для того, чтобы избежать считывания ложного включения при загрузке. Просто чтобы быть уверенным, тест является состоянием, а не старым состоянием. а затем сохранить состояние в старом состоянии, что бы ни случилось. Это необходимо для предотвращения увеличения счета, когда магнит находится близко и/или в правильной ориентации. Существует разница между вращающимся магнитом и приближающимся магнитом в том, что поле вращающегося магнита вращается вместе с осью, что важно, и оно очень быстро приближается к датчику. - person Michaël Roy; 14.02.2021
comment
@JamesJordanTaylor Может быть, ваш датчик слишком чувствителен. Медленно водя магнитом перед датчиком рукой, ожидаемое значение должно быть как минимум близко к 1. Вот почему я подозреваю, что это может быть проблема с электрикой. Какую часть датчика вы используете? Я посмотрю на это. - person Michaël Roy; 14.02.2021
comment
В настоящее время я использую линейный магнитный датчик Холла (номер детали SE014) allelectronics.com/mas_assets/media/allelectronics2018/spec/ - person James Jordan Taylor; 14.02.2021
comment
Ok. Он должен работать. У вас есть прицел или вольтметр? Лучше всего будет прицел, но обо всем по порядку: я полагаю, ваш вход подключен к D0, а не к A0. Вы можете настроить чувствительность датчика. Для этого было бы лучше, если бы вы могли установить какую-то установку, которая будет удерживать пурпур на повторяемом расстоянии от датчика. Вы можете приклеить его к палке и найти что-нибудь, что будет удерживать его на месте, пока вы его поворачиваете, лучше всего это будут какие-нибудь конструкторы Meccano или lego, поворотный стол, все, что вращается или скользит, и что будет удерживать магнит на повторяемом расстоянии. образуют датчик. - person Michaël Roy; 14.02.2021
comment
Не используйте слишком большой магнит, чем меньше, тем лучше. Затем вы можете настроить модуль, поместив магнит прямо под (или над) датчиком, на расстоянии от 3 до 5 мм от него. Вы настраиваете датчик, подстраивая VR1. В идеале вам нужен сигнал +5 В, когда магнит находится ближе всего к датчику, и отсутствие сигнала как можно скорее, когда он удаляется от этого положения. Вам придется снова настроиться для окончательной сборки. Сделайте довольно прочную установку, так как вы будете использовать ее на протяжении всего проекта. Если у вас есть осциллограф, сигнал от D0 должен показывать четкие и быстрые переходы. когда магнит проходит под - person Michaël Roy; 14.02.2021
comment
У меня дома есть мультиметр. Я уменьшил чувствительность до предела. Я подтвердил, что вход подключен к D0, а не к A0. Полезно знать о размере магнита. Я выберу самую маленькую, которую смогу найти. - person James Jordan Taylor; 14.02.2021
comment
датчик. Если вы не можете получить чистые переходы, вам придется добавить некоторую задержку при чтении. Я опубликую код для этого в ответе. Добавить задержку немного сложно, так как она ограничит максимальное число оборотов в минуту, которое может прочитать ваше приложение. Удачи! @ДжеймсДжорданТейлор - person Michaël Roy; 14.02.2021
comment
Да, я уже начал играть с sampleTime. Спасибо тебе за помощь! - person James Jordan Taylor; 14.02.2021
comment
пожалуйста. Мне очень нравится делать все, что связано с электроникой. :) - person Michaël Roy; 14.02.2021