1 Гц и 32 Гц от 32,768 кГц на ATmega328P на 8 МГц и DS3231 32K
У меня на входе ATmega328P сигнал 32,768 кГц. Мне нужно получить 2 сигнала из этого сигнала: 1 Гц и 32 Гц. Насколько хорошо это будет работать? Как это сделать с помощью таймера/счетчика ATmega328P? Какой выигрыш даст Таймер/Счетчик по сравнению с моим решением?
void setup() {
pinMode(S32768Hz_PIN, INPUT_PULLUP); // сигнал 32,768 кГц
attachInterrupt(digitalPinToInterrupt(S32768Hz_PIN), s32768Isr, FALLING); // Пин 3
}
volatile bool s32768IsrWasCalled = false; // Флаг ISR 32768 Гц
void s32768Isr() {
s32768IsrWasCalled = true;
}
uint16_t s32Counter = 0;
uint16_t s1Counter = 0;
void loop() {
if (s32768IsrWasCalled) {
s32768IsrWasCalled = false;
s32Counter++;
if (s32Counter == 1024) {
s32Counter = 0;
// 32 Гц
}
s1Counter++;
if (s1Counter == 32768) {
s1Counter = 0;
// 1 Гц
}
}
}
Обновление от 24.10.2022 Я написал свой первый код установки таймера 1. Будет ли это выполняться 32 раза в секунду бесконечно?
// ATmega328P 3,3 В 8 МГц, Таймер 1 и усилитель; DS3231 32768 Гц
void setupTimer1() {
// Отключаем все прерывания
cli();
// Сбросить биты
TCCR1A = 0;
TCCR1B = 0;
// Разрешить прерывание по переполнению Таймера 1 (TOIE1)
TIMSK1 = (1 << TOIE1);
// Режим ШИМ. Режим № 7 (быстрый ШИМ, верх фиксирован на 10 битах = 1024)
TCCR1A |= _BV(WGM10) | _BV(WGM11);
TCCR1B |= _BV(WGM12);
// Внешняя синхронизация на выводе T1 по заднему фронту (вывод DS3231 32768 Гц)
TCCR1B |= (1 << CS11) | (1 << CS12);
// Включает прерывания
sei();
// pinMode(T1_pin, INPUT_PULLUP); ?
}
void loop() {
// Получается 1 Гц из 32 Гц?
// Здесь не будет гонок данных?
if((pulse_counter % 32) == 0) {
callback1Hz();
}
if((pulse_counter % 2) == 0) {
callback16Hz();
}
}
volatile uint8_t pulse_counter;
// Переполнение таймера 1 (32 Гц)
ISR(TIMER1_OVF_vect) {
pulse_counter++;
}
Спасибо!
@Andre, 👍-2
Обсуждение1 ответ
Лучший ответ:
Я не проверял вашу программу, но, просто взглянув на нее, я ожидаю это работает... при условии, что вы не добавляете больше кода! Очевидно, вам понадобится добавить больше кода, чтобы сделать что-то полезное с этими сигналами, и затем думает, что может сломаться.
Проблема с этим подходом заключается в том, что частота прерываний довольно высока. высокий: программа прерывается каждые 30,5 мкс. Вы можете пропустить прерывать, если:
он срабатывает, когда уже есть прерывание (или другое критическое раздел) обрабатывается, и это прерывание (возможно, в сочетании с какое-либо другое ожидающее прерывание) требуется более 30,5 мкс для завершить
вы добавляете больше кода в
loop()
, и эта функция в конечном итоге занимает больше времени чем 30,5 мкс.
Первое условие должно быть очевидным, но я предполагаю, что риск довольно низким, так как только очень плохо написанный ISR займет так много времени. полный. Однако остерегайтесь библиотечного кода, так как он может преподнести сюрпризы.
Второе условие – больший риск, хотя, возможно, и менее очевидный.
проблема в том, что этот loop()
предполагает код внутри
if (s32768IsrWasCalled)
будет вызываться ровно один раз за
прерывать. Это может быть не так: если программа станет больше, вы можете
пропускать прерывания и ошибаться в частотах.
Простой способ избежать проблемы номер 2 – подсчитать количество импульсов в пределах ISR, как предлагает PMF в комментарии. Я бы использовал один счетчик для this, и сделайте его неподписанным, чтобы гарантировать, что он обертывается надежно:
volatile uint16_t pulse_counter;
void s32768Isr() {
pulse_counter++;
}
void loop() {
int signal_32Hz = (pulse_counter >> 9) & 1;
int signal_1Hz = (pulse_counter >> 14) & 1;
}
Обратите внимание, что это один из немногих случаев, когда вы можете получить доступ к многобайтовая переменная без отключения прерываний: так как вы только когда-либо заботьтесь об одном бите, нет гонки данных.
Однако для этого задания лучше использовать аппаратный таймер. Я бы предложил использовать Таймер 1 в режиме «счетчик» (внешние часы источник на контакте T1). Используя режим 7 (быстрый ШИМ, 10 бит), вы получаете Сигнал 32 Гц полностью генерируется аппаратно. И тогда ты сможешь используйте прерывание переполнения таймера для генерации 1 Гц. Прерывание ставка теперь в 1024 раза ниже. Я позволю вам проверить техническое описание подробности.
Блестящий ответ, как всегда :), @VE7JRO
Спасибо, хороший код и вариант!, @Andre
@EdgarBonet Какое условие будет верным для signal_1Hz? if (signal_32Hz == 1)
не работает раз в секунду., @Andre
@Andre: «signal_1Hz» — это сигнал частотой 1 Гц с рабочим циклом 50%: это «0» в течение половины секунды, затем «1» в течение половины секунды. Если вы хотите, чтобы условие было «истинным» только один раз в секунду (в отличие от истинного в течение целых полсекунды), вам придется выполнить _обнаружение фронта_ для этого сигнала. В качестве альтернативы выполните _обнаружение изменений_ на pulse_counter&(1U<<15)
., @Edgar Bonet
- Разница между «time_t» и «DateTime»
- Создание таймера с использованием часов реального времени с указанием времени начала и остановки
- Генерация стабильной частоты
- RtcDateTime' не называет тип
- Странная проблема. Ардуино перестает работать через несколько часов. Мнения, пожалуйста
- Как найти разницу между двумя timestamp
- Вызов функций одного класса из другого класса — Обратный вызов
- PCF8583 с задержкой Arduino в одну секунду?
Насколько хорошо это будет работать?
... разве вы не должны быть тем, кто определяет это? ... используйте аппаратный делитель, для этого есть подходящие микросхемы, @jsotolaПочему бы тебе просто не измерить? У вас есть прицел?, @PMF
О, и подсказка: не используйте флаг в ISR, вместо этого увеличивайте счетчик там., @PMF
Вы можете [отредактировать](https://arduino.stackexchange.com/posts/91054/edit) свой вопрос, чтобы включить детали и разъяснения, о которых вас спрашивают в комментариях., @timemage
Какая точность вам нужна для 1 Гц и 32 Гц? просто логический уровень?, @Tony Stewart Sunnyskyguy EE75
Я не могу использовать внешний аппаратный разделитель. Разделить 1 сигнал на 2 сигнала я могу только на ATmega328P. Я использую выход DS3231 32K с точностью ±2ppm., @Andre
@EdgarBonet Я написал свой первый код настройки Таймера 1. Обновление от 24.10.2022. Будет ли это работать 32 раза в секунду бесконечно?, @Andre
Да, похоже, это должно сработать. Время проверить это., @Edgar Bonet
@EdgarBonet Так что получите 1 Гц из 32 Гц? Здесь не будет гонок данных?
if((pulse_counter % 32) == 0) { }
Я обновил свой пример в заголовке., @AndreНет гонки данных, так как вы читаете однобайтовую переменную. Но вы не выполняете обнаружение краев., @Edgar Bonet