Проблема с внутренним компаратором ATmega328P - ISR (ANALOG_COMP_vect) не срабатывает

После ознакомления с этим кодом на githubи изучения руководства electronoobs я был вынужден попытаться воссоздать его с помощью Arduino Nano и использовать аналоговые входы компаратора A0-A3. В настоящее время я просто пытаюсь добиться распознавания переднего фронта приемного преобразователя в прерывании. Таким образом, я думаю, что моя проблема заключается в ISR(ANALOG_COMP_vect)

Использование осциллографа подтверждает, что ультразвуковой преобразователь срабатывает каждые 100 мс, и я могу определить пинг с приемником как небольшое напряжение выше GND, что должно вызвать прерывание ISR с DIO6, подключенным к GND... но этого не происходит...

Вот мой код:

#include  <avr/interrupt.h>

#include  <stdint.h>
#include  <stdlib.h>
#include  <stdio.h>
#include  <stdbool.h>

#include <Arduino.h>
#include  "fastio.h"


#define F_CPU 16000000UL

// контакты для драйвера TX
#define PING0   PB1
#define PING1   PB2

#define LED     DIO13

#ifndef MASK
  #define MASK(m) (1UL << m)
#endif

#define DEBUG 0
#define INTERVAL 100
#define diff(a,b) (((a) >= (b))?((a)-(b)):((b)-(a)))

inline bool ge(uint32_t a, uint32_t b) {
  return ((a - b) & 0x80000000UL) == 0;
}

inline bool lt(uint32_t a, uint32_t b) {
  return ((a - b) & 0x80000000UL) != 0;
}

volatile uint32_t milliseconds = 0;

uint16_t starttime = 0;
volatile uint16_t envelopetime = 0;
volatile uint16_t phasetime = 0;

#define PULSECOUNT_X2 16
volatile uint8_t edges = 0;

void pins_init(void) {
  WRITE(PING0, 0);
  SET_OUTPUT(PING0);

  WRITE(PING1, 0);
  SET_OUTPUT(PING1);

  WRITE(LED, 0);
  SET_OUTPUT(LED);

}

void timer_init(void) {
  // таймеры инициализации
  GTCCR = MASK(TSM) | MASK(PSRSYNC); // часы приостановки

  PRR &= ~MASK(PRTIM1) & ~MASK(PRTIM0) & ~MASK(PRADC); // включить таймеры 0 и 1 и АЦП

  // используйте timer1 для генерации выходного сигнала и синхронизации аналогового компаратора
  TCCR1A = 0;                       // нормальный режим, поведение не соответствует
  TCCR1B = MASK(CS10);              // максимальная скорость (без предварительной настройки)

  // используйте timer0 для наших миллисекундных часов
  TCCR0A = MASK(WGM01);             // Режим CTC
  TCCR0B = MASK(CS00) | MASK(CS01); // предварительный масштаб = 64, т.е. Частота составляет 250 кГц
  OCR0A  = 250;                     // прерывание на частоте 1 кГц, т.е. каждые 1 мс
  TIMSK0 = MASK(OCIE0A);            // прерывание при сравнении

  // настройка аналогового компаратора
  ADCSRA = (0 << ADEN);
  ADCSRB = (1 << ACME);
  ADMUX = 0;
  ACSR |= 0x03;
  
  GTCCR = 0; // повторно включить часы
}

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

void loop() {
  timer_init();
  sei();
  Serial.println("Start");

  uint32_t pingtime = INTERVAL;

  for (;;) {
    if (ge(milliseconds, pingtime) && (edges == 0)) {
      // обратите внимание, когда мы должны выполнить следующий пинг
      pingtime += INTERVAL;

      // начните через миллисекунду, чтобы избежать условий гонки
      starttime = TCNT1 + (F_CPU / 1000);
      OCR1A = OCR1B = starttime;

      // сброс счетчика границ
      edges = PULSECOUNT_X2;

      // установите A и снимите B при совпадении
      TCCR1A = MASK(COM1A0) | MASK(COM1A1) | MASK(COM1B1);

      // отключить прерывание на компараторе
      TIMSK1 &= ~MASK(ICIE1);

      // снимите флажок на случай, если он сработал во время запуска
      TIFR1 = MASK(OCF1A);

      // включить прерывание
      TIMSK1 |= MASK(OCIE1A);
    }
    if (phasetime != 0) {
      uint16_t eoff = envelopetime - starttime; // время, когда сигнал Rx стал достаточно громким, чтобы его можно было рассмотреть, т.е. Начало огибания
      uint16_t poff = phasetime - starttime;    // время первого пересечения нуля после огибающей, используется для вычисления смещения фазы

      //printf_P(PSTR("огибающая +%u (%uus) фаза +%u (%uus) \n"), eoff, eoff >> 4, poff, poff >> 4);

     Serial.println(eoff);
     //Serial.println(poff);

      // ноль, поэтому мы печатаем только один раз, обрабатываем данные перед этим
      phasetime = 0;
    }
  }
}

ISR(TIMER1_COMPA_vect) {
  if (edges == PULSECOUNT_X2) {
    TCCR1B |= MASK(ICES1);   // прослушивание восходящего фронта
    TIFR1 = MASK(ICF1);      // очистить флаги на случай, если он сработал во время запуска
    TIMSK1 |= MASK(ICIE1);   // прерывание на компараторе

    TCCR1A = MASK(COM1A0) | MASK(COM1B0); // включить сопоставление
    //Serial.println("ИМПУЛЬС");
    WRITE(LED, 1);
  }
  else if (edges == 1) {
    TCCR1A = MASK(COM1A1) | MASK(COM1B1); // очистить оба при совпадении
  }
  else if (edges == 0) {
    //putc('>');

    TIMSK1 &= ~MASK(OCIE1A);   // отключить прерывание

    return;
  }
  edges--;
  OCR1A = OCR1B += F_CPU / 80000; // установить следующий тайм-аут
}

ISR (ANALOG_COMP_vect)
  {
  phasetime = ICR1; 
  if((ACSR & B00100000)){
    envelopetime = ICR1;      // сохранить временную метку
    WRITE(LED, 0);
    TIMSK1 &= ~MASK(ICIE1);   // отключить прерывание на компараторе
  }
}
  
ISR(TIMER0_COMPA_vect) {
  milliseconds++;
}

, 👍2

Обсуждение

1. Какова цель ge () и lt ()? Почему бы не >= и <? 2. В этом коде многое происходит. Не могли бы вы свести это к минимальному примеру, который показывает проблему, с которой вы столкнулись, и ничего больше? 3. Вы недостаточно объясняете, как это должно работать. Должны ли мы прочитать полный учебник, чтобы понять это? 4. Такие выражения, как "ACSR & B00100000", очень трудно расшифровать. ACSR & MASK (ACO) было бы более читабельным. 5. Возможно, вы захотите использовать макросы avr-libc, такие как _BV() (эквивалент вашей MASK()) и bit_is_set()., @Edgar Bonet

6. ISR (ANALOG_COMP_vect) никогда не сработает, если вы не включите прерывание (бит ACIE в ACSR)., @Edgar Bonet

Все, что сказал Эдгар Бонет. Если бы я работал над этим, я бы, вероятно, удалил "МАСКУ" по той же причине, по которой я не стал бы использовать "_BV. Если вы не используете их экстраординарно, они существуют в основном для того, чтобы другие люди, читающие код, могли иметь *"WTF?.... о, и это все?"* момент. Трудно сказать, происходит ли то же самое с ge () и lt ()`., @timemage


1 ответ


3

Вы не включили прерывание компаратора.

Просто нужно изменить строку, в которой вы устанавливаете ACSR.

//Прерывание компаратора при нарастающем выходном фронте.
ACSR |= (1 << ACIS0) | (1 << ACIS1);   

//Включить прерывание компаратора
ACSR |= (1 << ACIE)

Также может быть достигнуто с помощью.

ACSR = 0x0B

Если вы хотите изменить то, что вызывает прерывание. Биты 0 и 1 управляют тем, как запускается прерывание (переключение, понижение или повышение).

//Переключение (по умолчанию, требуется только в том случае, если уже выбран другой режим прерывания)
ACSR &= ~(0b11)

//Падение
ACSR &= ~(1 << ACIS0); //Требуется только в том случае, если задано значение ACIS0
ACSR |= (1 << ACIS1);

// Восходящий
ACSR |= (1 << ACIS0) | (1 << ACIS1);
//Или
ACSR | = 0x03;

Вам также нужно будет включить глобальные прерывания. (Что вы и сделали) Что можно сделать, позвонив...

interrupts(); // только для Arduino
sei();        // AVR и Arduino

Я вижу, что вы вызываете "sei" в цикле, его нужно вызвать только один раз (если вы не отключили их с помощью "cli ()", и я считаю, что это вообще не требуется для Arduino, поскольку я думаю, что прерывания уже включены.

Таблица данных - ваш друг. Учебные пособия - отличный способ учиться, но если вы действительно хотите повысить свой набор навыков, вам следует попытаться как можно больше освободиться от учебных пособий, читая таблицу данных и экспериментируя самостоятельно. Компаратор начинается на странице 202 и был бы отличным местом для начала, поскольку его легко использовать в качестве периферийного устройства.

,