ATtiny85 дает многократное пробуждение от простоя, если INT0 удерживается на низком уровне

Желаемое поведение:

  1. Перейдите в спящий режим ожидания.
  2. Когда PB2 (синий след) становится низким, просыпайтесь и дайте 10 мс на PB1, потянув за низкий уровень (желтый след на рисунке 1).
  3. Перейдите к 1.

Что я получаю:

Oscilloscope view.

Рис. 1. (1) Входные триггеры, (2) процессор выходит из спящего режима, (3) процессор снова переходит в спящий режим и снова просыпается, пока (9) вход снова не увеличится.

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

Несмотря на то, что я установил MCUCR на понижающийся край INT0, устройство, похоже, постоянно просыпается, когда PB2 удерживается на низком уровне. Мой код ниже пытается решить эту проблему, переключившись на прерывание с восходящим краем, если входной сигнал низкий, и прерывание с падающим краем, если входной сигнал высокий. Результат показан на рисунке 1.

void setup(){
  ADCSRA &= ~_BV(ADEN);               // ADC off. ADC not used.
  pinMode(reedPin, INPUT);
}

void loop(){
  system_sleep();                     // Go straigth to sleep.
  // ---- System woken up by interrupt. ---------
  if (!digitalRead(reedPin)) {        // Check that input is low.
    pinMode(outPin, OUTPUT);          // Enable output.
    digitalWrite(outPin, HIGH);       // Pull motor sensor input low by MOSFET.
    delay(pulseTime);                 // Pulse time.
    digitalWrite(outPin, LOW);        // Pulse off.
  }  
}

// From http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/
void system_sleep() {
  MCUCR |= _BV(ISC01);                    // Required for both rising and falling edge interrupt.
  if(digitalRead(reedPin)) {              // Input is high so set interrupt for detecting low.
    MCUCR &= ~_BV(ISC00);                 // Clear MCUCR bit 0 for falling edge interrupt.
  } else {                                // Input is low so set interrupt for detecting high.
    MCUCR |= _BV(ISC00);                  // Set MCUCR bit 0 for rising edge interrupt.
  }
  GIMSK |= _BV(INT0);                     // Enable Pin Change Interrupts
  set_sleep_mode(SLEEP_MODE_IDLE);        // Leaves the clock running for elapsedMillis.
  sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
  sei();                                  // Enable interrupts
  sleep_cpu();                            // sleep
  // ZZZZZZZZZZZzzzzzzzzzzz .......
  // ------- CPU is now asleep and awaiting interrupt. ----------------------
  cli();                                  // Disable interrupts on wake up.
  sleep_disable();                        // Clear SE bit.
}

ISR(INT0_vect){                           // Wake up.
  GIMSK = 0;                              // Disable external interrupts. (Only need one to wake up.)
}

Кто-нибудь может провести меня через это?

Большое спасибо.

, 👍2

Обсуждение

@jsotola, никакой радости. Все то же самое., @Transistor

вот некоторый код сна, который вы могли бы попробовать ... понятия не имею, действительно ли это работает (у меня нет возможности проверить) ... https://github.com/DaveCalaway/ATtiny/blob/master/Examples/AT85_sleep_interrupt/AT85_sleep_interrupt.ino, @jsotola

Библиотека в этом коде многое упрощает. Теперь у меня это срабатывает на грани падения и снова на подъеме. Я буду продолжать копать. Спасибо., @Transistor

раздел 9.2 может помочь (стр. 49) ... https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf, @jsotola


1 ответ


1

Ядро Arduino настраивает таймер для периодического прерывания. Это прерывание используется для реализации millis(), delay () и т.д. Ваш код предполагает, что внешнее прерывание является единственным , которое может получить микроконтроллер. Я предполагаю, что прерывания таймера могут быть причиной наблюдаемого поведения.

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

volatile bool pulse_active;     // whether the pulse is on
volatile uint32_t pulse_start;  // time when it started

ISR(INT0_vect) {
    // Start the pulse.
    digitalWrite(outPin, HIGH);
    pulse_active = true;
    pulse_start = millis();
}

void loop() {
    // End the pulse if it's time to do so.
    if (pulse_active && millis() - pulse_start >= pulseTime) {
        digitalWrite(outPin, LOW);
        pulse_active = false;
    }

    // Take a little nap.
    sleep_mode();
}

Обратите внимание, что небольшой сон в приведенном выше коде может быть прерван либо внешним прерыванием, либо прерыванием таймера.

,