Как обойти передачу переменной в ISR

isr

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

Сейчас у меня ISR настроен следующим образом:

void vhISR()
{
  // это проверяет регистр PINE на наличие 1 или 0 в 5-м бите (или 6-м)
  // Я немного сдвигаю результат, чтобы для сравнения переменная была либо истинной, либо ложной, а не 16/0 или 32/0
  rotCurrentA = (PINE & B00010000) >> 4; 
  rotB = (PINE & B00100000) >> 5;

  if (rotCurrentA != rotLastA)
    {
      if (rotCurrentA != rotB) // cw
        {
          voltageHigh += knobResolution;
          rotLastA = rotCurrentA;
        }
      else // против часовой стрелки
        {
          voltageHigh -= knobResolution;
          rotLastA = rotCurrentA;
        }
    }
}

Код просто изменяет напряжение High с шагом 0,1 В. Проблема вот в чем: мне нужно сделать такие же вариации на 0,1 для нескольких других переменных, в зависимости от того, какую из них я выберу. Мне нужно увеличить/уменьшить напряжение Low, таймер и сопротивление, все это требует приращения +/- 0,1 (но в разных диапазонах. Например, VOLLow находится где-то между 0,1 и 5, а VH где-то между 6 и 5). 10). Обычно я бы просто передал нужную мне переменную по ссылке в функцию и заставил функцию увеличивать/уменьшать эту переменную.... но ISR не является обычной функцией, и вы не можете передать в нее переменную.

Как мне это обойти? Я не уверен, достаточно ли этой информации, чтобы сообщить мне.

, 👍4

Обсуждение

почему бы не использовать библиотеку Encoder?, @Juraj

Не вариант. Это работает отлично, за исключением этой небольшой проблемы., @HavocRC

оператор switch, основанный на глобальной переменной с текущим выбранным параметром?, @Juraj

Не меняйте переменную в ISR. Просто отслеживайте количество кликов и добавляйте их в соответствующую переменную в коде. В любом случае вам придется отключить прерывания, чтобы использовать эти переменные, поэтому просто замените этот код на чтение и измените., @Delta_G


2 ответа


Лучший ответ:

8

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

// Количество шагов вращения обновляется в ISR. Должен быть не авторизован
// чтобы избежать знакового переполнения, которое является неопределенным поведением.
volatile uint16_t rotation_count;

void vhISR()
{
    static uint8_t rotLastA;
    uint8_t rotCurrentA = (PINE >> 4) & 1;
    uint8_t rotB = (PINE >> 5) & 1;

    if (rotCurrentA != rotLastA)
        return;
    if (rotCurrentA != rotB) // cw
        rotation_count++;
    else // против часовой стрелки
        rotation_count--;
    rotLastA = rotCurrentA;
}

// Возвращаем количество шагов вращения с момента последнего вызова. Должно быть
// вызывается достаточно часто, чтобы избежать переполнения int16_t.
int16_t get_rotation()
{
    static uint16_t last_count;

    // Избегайте гонки данных: Rotate_count не следует изменять, пока мы
    // читаем это.
    noInterrupts();
    uint16_t count_copy = rotation_count;
    interrupts();
    int16_t delta = count_copy - last_count;  // это подписано!
    last_count = count_copy;
    return delta;
}

Обратите внимание, что необработанный счетчик представляет собой беззнаковое число, которое может пересчитываться по модулю. 216. Однако значение, возвращаемое функцией get_rotation(), является число со знаком, которое по правилам модульной арифметики является иммунитет к смене необработанных значений. Пока ты звонишь get_rotation() достаточно часто (чаще, чем каждые 32767 шагов), возвращаемое значение всегда будет правильным.

Затем в вашей основной программе вы можете использовать это для обновления любой переменной вы хотите:

const float knobResolution = 0.1;
float voltageHigh, voltageLow, resistance;

// Переменная, которую пользователь выбрал для изменения.
enum {VOLTAGE_HIGH, VOLTAGE_LOW, RESISTANCE} selection;

void loop()
{
    // Устанавливаем указатель на выбранную переменную.
    float *selected_var;
    switch (selection) {
    case VOLTAGE_HIGH:
        selected_var = &voltageHigh;
        break;
    case VOLTAGE_LOW:
        selected_var = &voltageLow;
        break;
    case RESISTANCE:
        selected_var = &resistance;
        break;
    }

    // Обновляем выбранную переменную.
    *selected_var += get_rotation() * knobResolution;
}

Обратите внимание, что перечисление selection, вероятно, является избыточным для выбранная_вар. Вы можете использовать последнее, чтобы отслеживать пользователя выбора, то вам не понадобится переключатель/case.

,

Это круто! Спасибо, что напечатали все это для меня. Мне придется обдумать это в ближайшее время и реализовать, как только я все пойму. Спасибо!, @HavocRC


2

Хорошо, вы не можете передать переменную в ISR...

Нет как такового, потому что ISR запускается аппаратно, и поэтому вы не передаете ему аргументы.

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

,