Проблема с внутренним компаратором 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++;
}
@NRav, 👍2
Обсуждение1 ответ
Вы не включили прерывание компаратора.
Просто нужно изменить строку, в которой вы устанавливаете 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 и был бы отличным местом для начала, поскольку его легко использовать в качестве периферийного устройства.
- Чтение квадратурного энкодера в реальном времени с полным разрешением только с одним прерыванием на ATmega328
- Недопустимое использование выражения void (как использовать указатель функции с вводом)
- Проблема прерываний с датчиком потока
- Значение понижающего резистора для прерываний
- Прерывания таймера Arduino для PID
- Правильное использование SPI с ISR
- Контакты внешнего прерывания на наноклонах
- Работа двигателя в течение 3 секунд непрерывно с прерыванием и без него
1. Какова цель
ge ()
иlt ()
? Почему бы не>=
и<
? 2. В этом коде многое происходит. Не могли бы вы свести это к минимальному примеру, который показывает проблему, с которой вы столкнулись, и ничего больше? 3. Вы недостаточно объясняете, как это должно работать. Должны ли мы прочитать полный учебник, чтобы понять это? 4. Такие выражения, как "ACSR & B00100000", очень трудно расшифровать.ACSR & MASK (ACO)
было бы более читабельным. 5. Возможно, вы захотите использовать макросы avr-libc, такие как_BV()
(эквивалент вашейMASK()
) иbit_is_set()
., @Edgar Bonet6.
ISR (ANALOG_COMP_vect)
никогда не сработает, если вы не включите прерывание (битACIE
вACSR
)., @Edgar BonetВсе, что сказал Эдгар Бонет. Если бы я работал над этим, я бы, вероятно, удалил "МАСКУ" по той же причине, по которой я не стал бы использовать "_BV
. Если вы не используете их экстраординарно, они существуют в основном для того, чтобы другие люди, читающие код, могли иметь *"WTF?.... о, и это все?"* момент. Трудно сказать, происходит ли то же самое с
ge ()и
lt ()`., @timemage