Как прочитать OC0A в режиме CTC (переключение)?

У меня есть этот код, который переводит Arduino в режим CTC с прерываниями и выводит прямоугольный сигнал на PD6 (OC0A):

void setup_timer(double p_ms, double duty){
  DDRD |= (1 << 6);  // установить вывод 3 как выход
  TCCR0A = _BV(COM0A0); // переключить OC0A на Compare Match
  TCCR0B = _BV(WGM01); // установить режим CTC WGM0[2,1,0] = 0b010
  TIMSK0 |= _BV(OCIE0A); // Включить прерывание CTC
  OCR0A = 128;  // установить время включения и выключения
  TCCR0B |= ( (0 << CS02) | (0 << CS01) | ( 1 << CS00)); // CS02:0 - Без предварительного масштабирования
  sei();
}

В ISR (который не показан) я хочу иметь возможность видеть, является ли OC0A (т. е. PD6) HIGH или LOW.

Я предполагал, что смогу сделать digitalRead(), но в этом сообщении, Re: Как прочитать состояние выходного контакта? говорится:

Глядя на код, функция digitalRead() просто считывает соответствующий бит в соответствующем регистре PINx, ничего не изменяя, так что это должно работать.

Одна загвоздка: если это вывод ШИМ, то ШИМ будет остановлен.

Итак, будет ли прекращено переключение OCR0A?

Этот ответ на Как я могу выполнить цифровое чтение контакта, который находится в режиме вывода pinMode? предлагает напрямую читать PORTD, с

bitRead(PORTD,6);

Однако, будет ли это работать для выхода ШИМ или ШИМ также будет остановлен путем чтения PORTD?


Приложение

Я планирую использовать этот код либо как (в первую очередь) автономный AVR µController (48/88/168/328 IC), но, возможно, в Nano/Uno (если он будет работать) для тестовых целей. Я понимаю, что использование Timer0 повлияет на работу millis(), delay() и т. д., но остальная часть кода не будет полагаться ни на одну из этих функций... поэтому я предполагаю (возможно, неправильно), что код будет работать так, как и ожидалось. Это верно?

Кроме того, я не собираюсь использовать программную структуру setup() и loop(), а вместо этого main(). Просто из интереса, будет ли работать loop() (т.е. зависит ли он от Timer0)? Я не верю, что это так.

, 👍1


2 ответа


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

2

Если вы заглянете в Arduino Core, то найдете этот код для цифрового считывания:

int digitalRead(uint8_t pin)
{
    uint8_t timer = digitalPinToTimer(pin);
    uint8_t bit = digitalPinToBitMask(pin);
    uint8_t port = digitalPinToPort(pin);

    if (port == NOT_A_PIN) return LOW;

    // Если вывод поддерживает выход ШИМ, нам нужно его отключить
    // перед получением цифрового показания.
    if (timer != NOT_ON_TIMER) turnOffPWM(timer);

    if (*portInputRegister(port) & bit) return HIGH;
    return LOW;
}

Это значит, что отключение ШИМ — просто странная функция в digitalRead.

Для чтения bitRead(PORTB,3); я не уверен, что вы получите выходное значение OC0A, это может быть другая сигнальная линия внутри. Однако вы получите логический уровень на выводе порта, прочитав bitRead(PINB,3);.

,

2

Предварительное замечание: Если вы используете любой из ATmega48/88/168/328, тогда вывод OC0A — это PD6, а не PB3.

Как и ваша идея чтения регистра PORT (PORTD, а не PORTB), это тоже не сработает, так как это скажет вам только последнее значение который был записан в порт с помощью digitalWrite(). Вот схема показывая, как таймер управляет выходным пином. Это взято из ATmega328P datasheet, но другие AVR этого семейства ведут себя одинаково в этом отношении:

Сравнить единицу вывода соответствия

Как показано на схеме, значение, записанное в порт, поступает из мультиплексор и управляется либо:

  • триггером PORT, когда таймер не генерирует выходной сигнал ШИМ
  • или триггер OC0A, когда он есть.

Довольно важно, что таймер не записывает в регистр PORT, он только переопределяет его.

Таким образом, это будет выглядеть так, как будто вы хотите прочитать триггер OC0A в вашем ISR. Увы, эта схема также показывает, что, в отличие от PORT триггер, триггер OC0A не подключен к шине данных. Таким образом вы не можете его прочитать, и у него даже нет адреса.

Я бы рекомендовал вместо этого прочитать регистр PIN-кода. Это не сказать вам, что таймер пишет в порт, но что вы можете прочитать назад. Однако, если штифт не нагружен намного больше своего абсолютного максимума рейтинг, что вы читаете обратно что было написано. Есть небольшая задержка между операцией записи и значением, доступным для обратного считывания (a пару циклов ЦП (IIRC), но это намного короче, чем выполнение время пролога ISR.

,