Срабатывание ложного прерывания Arduino Mega

Я использую Arduino mega с 4 прерываниями, чтобы найти скорость вращения 4 двигателей . Для этого я использовал INT2, INT3, INT4, INT5, и код для этого приведен ниже

struct Pulse {
  uint32_t last_toggle;
  uint32_t width;
  bool stateHigh;
  uint32_t get_width() {
    noInterrupts();
    uint32_t width_copy = width;
    interrupts();
    return width_copy;
  }
};

Pulse ch1, ch2, ch3, ch4;





void pin_initialization() {
  // Four engine configuration
  pinMode(19, INPUT_PULLUP);
  pinMode(18, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);

}

volatile uint32_t ovfCount = 0;
ISR(TIMER3_OVF_vect) {
  ovfCount++;
}


void timer3_initialization() {
  TCCR3A = 0x00;

  TCNT3 = 0x1FF; // из таблицы данных
  TIFR3 = _BV(TOV3);
  TIMSK3 = _BV(TOIE3); // включить переполнение
  TCCR3B = B00000011; // 64 предварительная настройка
}

void pulseWidthCalculator(Pulse *channel){
  unsigned char sreg;
  sreg = SREG;
  cli();
  uint16_t tcnt3 = TCNT3;
  SREG = sreg;
  uint32_t ovf_count = ovfCount;

  if (bit_is_set(TIFR3, TOV3) && tcnt3 < 32768) {
    ovf_count++;
  }

  uint32_t time = ovf_count << 16 | tcnt3;

  if (!channel->stateHigh) {  // первый максимум
    channel->stateHigh = true;
  } else if ( channel->stateHigh) {  // вторая высота
    channel->width = time - channel->last_toggle;
    channel->stateHigh = false;
  }
  channel->last_toggle = time;
}


ISR(INT2_vect) {
  pulseWidthCalculator(&ch1);
}

ISR(INT3_vect) {
  pulseWidthCalculator(&ch2);
}

ISR(INT4_vect) {
  pulseWidthCalculator(&ch3);
}

ISR(INT5_vect) {
  pulseWidthCalculator(&ch4);
}




void externalInterrputInitialization() {
  EIFR = _BV(INTF2) | _BV(INTF3) | _BV(INTF4) | _BV(INTF5); 
  EICRA = 0; EICRB = 0;
  EICRA = B10100000;
  EICRB = B00001010;
  EIMSK = B00111100;
}

void setup() {
  Serial.begin(115200);

  pin_initialization();
  delayMicroseconds(10);

  externalInterrputInitialization();
  delayMicroseconds(10);

  timer3_initialization();
  delayMicroseconds(10);

}


void loop() {
  String a = String(15e6/ch1.get_width());
  a += " ";
  a += String(15e6/ch2.get_width());
  a += " ";
  a += String(15e6/ch3.get_width());
  a += " ";
  a += String(15e6/ch4.get_width());

  Serial.println(a);

}

Проблема , с которой я сталкиваюсь, заключается в том, что всякий раз, когда я даю сигнал INT2, INT3 также срабатывает, то же самое с INT4, INT5 также срабатывает без сигнала .

Чего мне здесь не хватает, ?

Должен ли я сделать свои каналы ch1, ch2 , ch3, ch4 изменчивыми или нет? Нравится летучий импульс сн1, сн2, сн3, сн4

  • Все, что не так с OUTPUT_PULLUP

, 👍3

Обсуждение

изменчивость необходима для того, чтобы убедиться, что вносимые изменения заметны между контекстами ISR и не-ISR. Тем не менее, не похоже, что это полностью объясняет то, что вы видите. Вы должны сделать их "изменчивыми" и сообщить нам, каково поведение в этом случае., @timemage

почему вы считаете, что это проблема программирования?, @jsotola

@timemage, переход на volatile не помогает., @Lawliet

В этом случае, вероятно, было бы хорошей идеей просто сделать переменные "изменчивыми" в вашем коде и удалить ту часть, где спрашивают об этом, чтобы отвлечь внимание от основного вопроса., @timemage

Это не имеет никакого отношения к вашему вопросу как таковому, но ваш "последовательный" поток объединяет вещи для вас как нечто само собой разумеющееся. Все ваши назначения и добавления к "Строке a" являются неэффективной и подверженной ошибкам альтернативой серии вызовов "печать" /`println"., @timemage


1 ответ


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

1

У вас есть правильная идея, чтобы атомарно получить копию данных, обновляемых прерыванием:

  unsigned char sreg;
  sreg = SREG;
  cli();
  uint16_t tcnt3 = TCNT3;
  SREG = sreg;
  uint32_t ovf_count = ovfCount;

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

  unsigned char sreg;
  sreg = SREG; // Сохранить состояние прерывания.
  cli(); // Отключить прерывания.
  uint16_t tcnt3 = TCNT3; // Атомарные операции.
  uint32_t ovf_count = количество овалов;
  SREG = sreg; // Восстановить состояние прерывания.

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

Ложное прерывание может быть вызвано перекрестными помехами между номерами INT2 и INT3. Вы можете подтвердить это с помощью осциллографа. Держите сигнальные провода отдельно, чтобы избежать перекрестных помех.

Вы также можете попробовать более сильные внешние подтягивания, такие как 10 К или 1 К. Внутренние подтягивания Arduino слабее примерно на 20-50 Ком.

,

Спасибо Тиму , из осциллографа я понял, что это проблема INPUT_PULLUP, я буду выполнять внешние подтягивания., @Lawliet

используется резистор 1K и его работа. Есть ли какие-либо проблемы с использованием внешнего подтягивания вместе с INPUT_PULLUP.?, @Lawliet