Как прочитать 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
)? Я не верю, что это так.
@Greenonline, 👍1
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);
.
Предварительное замечание: Если вы используете любой из 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.
- Генерация импульса 200 кГц на Arduino Uno в обычном режиме
- Как правильно настроить ШИМ для управления углом сервопривода в AVR?
- Справка - Atmega328p, преобразование ШИМ-сервокода из pin9 в pin6
- Об управлении светодиодом с помощью кнопки с помощью ATmega328P
- Установите частоту ШИМ на 25 кГц.
- Какова частота PWM-выхода на Arduino
- Управление скоростью вентилятора с помощью библиотеки Arduino PID
- Как устранить шум от вентилятора 12 В с ШИМ-управлением на низкой скорости