Как обойти передачу переменной в 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 не является обычной функцией, и вы не можете передать в нее переменную.
Как мне это обойти? Я не уверен, достаточно ли этой информации, чтобы сообщить мне.
@HavocRC, 👍4
Обсуждение2 ответа
Лучший ответ:
Я бы последовал совету 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
Хорошо, вы не можете передать переменную в ISR...
Нет как такового, потому что ISR запускается аппаратно, и поэтому вы не передаете ему аргументы.
Однако ничто не мешает вам иметь глобальную переменную, к которой ISR может получить доступ (предпочтительно объявленную VOLUty
), чтобы ISR мог изменить свое поведение в зависимости от того, что содержит глобальная переменная.
- Почему необходимо использовать ключевое слово volatile для глобальных переменных при обработке прерываний в ардуино?
- Серийное прерывание
- Прерывание ардуино при смене контакта
- Влияет ли `millis()` на длинные ISR?
- Как прервать функцию цикла и перезапустить ее?
- Задержка Arduino внутри прерывания
- Аппаратное прерывание срабатывает случайным образом
- Какой правильный способ запроса устройства I2C из процедуры обслуживания прерывания?
почему бы не использовать библиотеку Encoder?, @Juraj
Не вариант. Это работает отлично, за исключением этой небольшой проблемы., @HavocRC
оператор
switch
, основанный на глобальной переменной с текущим выбранным параметром?, @JurajНе меняйте переменную в ISR. Просто отслеживайте количество кликов и добавляйте их в соответствующую переменную в коде. В любом случае вам придется отключить прерывания, чтобы использовать эти переменные, поэтому просто замените этот код на чтение и измените., @Delta_G