Как прочитать направление таймера в фазе правильного ШИМ?
Мне пришлось использовать Timer0 с корректным по фазе ШИМ и прескалером 1. Это приводит к частоте ШИМ 32 кГц и, очевидно, влияет на миллисы, задержки и микросы. (Timer1 и Timer 2 также используются, поэтому переход на них невозможен)
millis() тривиально обойти - умножить на 32, так как обработчик прерывания TOV0 вызывается в 32 раза чаще (при условии переполнения, конечно).
micros() несколько сложнее - как обычно, это счетчик переполнения Timer0 (как используется в millis (), хотя в данном случае он сдвинут) плюс текущее значение TCNT0. Это отлично работает, когда TCNT0 только увеличивается и сбрасывается до 0 после МАКСИМУМА, но в фазе правильного ШИМ он может идти вверх или вниз.
Я думаю, что мне нужно найти направление, которое в данный момент использует Таймер, но, переливаясь через таблицу данных для ATmega328P, я не могу найти способ сделать это...
Есть ли способ прочитать это, например, из реестра, который я могу опросить?
В качестве альтернативы, может ли кто - нибудь еще придумать способ получить микросекундное (или близкое-10 микросекунд было бы достаточно хорошо) разрешение, когда все таймеры находятся в фазокорректном режиме?
Я рассматриваю возможность повторного чтения TCNT0 дважды и получения его из этого-с прескалером 1I я предполагаю, что невозможно прочитать его в два раза быстрее, чем он может измениться - однако я бы предпочел более аккуратное решение, если оно есть.
@Simm, 👍2
Обсуждение1 ответ
Лучший ответ:
Я решил пойти с методом inspect и соответствующим образом модифицировал wiring.c - возможно, не подходит для каждого варианта использования, но решает мою проблему (наряду с парой настроек, изменяющих #define на volatile переменные и добавляющих сеттер для них.
микрос обновлен до:
unsigned long micros() {
unsigned long m;
uint8_t oldSREG = SREG, t1, t2;
cli();
m = timer0_overflow_count;
#if defined(TCNT0)
t1 = TCNT0;
t2 = TCNT0;
#elif defined(TCNT0L)
t1 = TCNT0L;
t2 = TCNT0L;
#else
#error TIMER 0 not defined
#endif
#ifdef TIFR0
if ((TIFR0 & _BV(TOV0)) && (t1 == timer0_phase_correct ? 0 : 255))
m++;
#else
if ((TIFR & _BV(TOV0)) && (t1 == timer0_phase_correct ? 0 : 255))
m++;
#endif
SREG = oldSREG;
if (timer0_phase_correct && t1 > t2){
t2 = 510 - t2;
}
return (m * us_per_timer0_overflow) + (clockCyclesToMicroseconds(timer0_prescale * t2));
}
Примечание: это не сработает только с этим изменением, но изменение проводки.c - это не та тема, которую я хочу обсуждать, и любой, кто способен на это, может легко сделать вывод о других изменениях из того, что я изложил выше. Здесь будут драконы., @Simm
Где определен timer0_phase_correct
?, @user172650
- Нужен сигнал ШИМ частотой 10 кГц от контактов 3 и 11 с использованием таймера 2.
- Использовать timer0, не влияя на millis() и micros().
- Можно ли сгенерировать точный тактовый импульс 15 кГц с помощью ардуино?
- Генерация сигнала частотой 38 кГц без таймеров
- Светодиод Arduino PWM с замиранием в сборке
- Отрегулируйте расчет времени после изменения частоты Timer0
- Как измерить ультразвуковой датчик без импульсного метода?
- Teensy 4.1 / 4.0 Когда использовать контакты FlexPWM и QuadTimer для стробирования светодиодов
Я боюсь, что это не просто, или возможно вообще. Поскольку каждый код для проверки направления занимает несколько часов, вам нужно посчитать их и принять во внимание. Кроме того, если вы используете прерывания, вам нужно будет отключить их в этой критической части. -- Вам действительно нужен
micros()
?, @the busybeeВаше запасное решение-лучшее, и ваше предположение верно: с прескалером на единице вы не можете считывать счетчик в два раза быстрее, чем он меняется., @Edgar Bonet
Может быть, вы можете использовать регистр "OC0x". Сейчас у меня нет под рукой atmega328p, но вы можете проверить его. Соответствующая часть в таблице данных: "В неинвертирующем режиме сравнения выходных данных значение output compare (OC0x) очищается при сопоставлении TCNT0 и OCR0x при подсчете вверх и устанавливается при сопоставлении вниз. В режиме инвертирования выходного сравнения операция инвертируется.
(заголовок
14.7.4 Корректный по фазе ШИМ-режим"), @SwedginИспользуйте флаг, чтобы следить за ним, @Swedgin
Вы можете обойти эту проблему и использовать [_delay_us](https://www.nongnu.org/avr-libc/user-manual/group__util__delay.html) вместо этого. Единственным недостатком является то, что _delay_us не учитывает время, затрачиваемое внутри ISR, так как он просто выполняет кучу инструкций NOP., @Gerben
@Swedgin, этот вопрос был задан на другом сайте stackexchange, и у меня была та же мысль. Или то же самое, насколько я могу судить. Вчера я экспериментировал с этой идеей, за исключением использования timer1 с прескалером 1024, потому что это дает около 4 секунд на колебание от одного конца диапазона до другого. По какой-то причине он, похоже, не устанавливал флаги прерывания сравнения вывода. Но меня прервало кое-что еще, прежде чем я понял, почему., @timemage