Мне нужно измерить частоту прямоугольной волны, которая может варьироваться от 0 до 1 МГц, и имеет разрешение 0,25 Гц.
Я еще не определился с тем, какой контроллер, но скорее всего это будет один из 20-контактных Attiny.
Обычно я измерял бы низкочастотные сигналы, используя два таймера, один из которых сконфигурирован в режиме захвата таймера, чтобы прерывать, например, нарастающие фронты внешнего сигнала, и другой таймер, настроенный на прерывание каждую секунду, поэтому значение счетчика бывших таймеров регистрируется через 1 секунду. будет равна частоте сигнала.
Однако этот метод, очевидно, не будет работать для захвата сигналов в диапазоне от 0 до 1 МГц с разрешением 0,25 Гц, для этого мне понадобится 22-битный счетчик (8-битные микросхемы AFAIK имеют только 8/16-битные счетчики).
Одна из идей, которая у меня была, заключалась в том, чтобы разделить сигнал, прежде чем применять его к микро, но это было бы нецелесообразно, так как сигнал пришлось бы делить на 61, поэтому частоту можно было обновлять только каждые 61 секунду, где бы я хотел, чтобы она была каждые несколько секунд. ,
Есть ли другой метод, который позволил бы обновлять частоту, скажем, каждые 4 секунды?
Обновить:
Самое простое решение - использовать внешнее прерывание или захват таймера для прерывания по переднему фронту сигнала и isr
увеличения значения переменной типа long int
. Считывайте переменную каждые 4 секунды (чтобы можно было измерять частоты до 0,25 Гц).
Обновление 2:
Как указал JustJeff, 8-битный MCU не сможет справиться с сигналом 1 МГц, так что исключается прерывание на каждом переднем фронте и увеличение long int
...
Я выбрал метод, предложенный Тиморорром. Как только я приступлю к его реализации, я отправлю ответ и поделюсь результатами. Спасибо всем за ваши предложения.
Отчет о проделанной работе:
Iv'e начал тестировать некоторые идеи, представленные здесь. Сначала я попробовал код Викацу. Была очевидная проблема TCNT1, которая не была очищена после того, как была рассчитана частота - ничего страшного ...
Затем я заметил, что при отладке кода примерно каждые 2-7 раз вычислялась частота, когда счетчик переполнения таймера 1 (таймера, настроенного на подсчет внешних событий) был бы коротким на два. Я отложил это до времени ожидания таймера 0 ISR и решил переместить блок оператора if из ISR в главное (см. Фрагмент ниже) и просто установить флаг в ISR. Некоторая отладка показала, что первое измерение будет в порядке, но при каждом последующем чтении счетчик переполнения Таймера 1 будет превышен на 2., что я не могу объяснить - я ожидал, что оно будет ниже, чем не более ...
int main()
{
while(1)
{
if(global_task_timer_ms > 0 && (T0_overflow == 1))
{
global_task_timer_ms--;
T0_overflow = 0;
}
.....
}
}
Затем я решил, что попытаюсь реализовать предложение тиморра. Чтобы сгенерировать необходимый интервал (около 15 мс между каждым прерыванием timer_isr), мне пришлось бы каскадировать два 8-битных таймера, поскольку единственный 16-битный таймер на Atmega16 используется для захвата нарастающих фронтов внешнего сигнала.
Я думал, что это решение будет работать и будет гораздо более эффективным, поскольку большая часть служебных данных переносится на таймеры, и для обработки процессором остается только один короткий isr. Однако это было не так точно, как я надеялся, измерения сместились назад и вперед примерно на 70 Гц, что я не возражал бы на высоких частотах, но это определенно не приемлемо на низких частотах. Я не потратил много времени на анализ проблемы, но я предполагаю, что механизм каскадирования таймера не настолько точен, поскольку я реализовал механизм, подобный предложению тиморра, на гораздо более медленном контроллере 8051, который имел 2 16-разрядных таймера, и результаты были довольно точными.
Теперь я вернулся к предложению Викачу, но я перенес расчет частоты в таймер 0 (см. Фрагмент ниже ), этот код дал последовательные и достаточно точные измерения. При небольшой калибровке точность должна быть примерно +/- 10 Гц.
ISR(TIMER0_OVF_vect)
{
TCNT0 = TIMER0_PRELOAD; //Reload timer for 1KHz overflow rate
if(task_timer_ms > 0)
{
task_timer_ms--;
}
else
{
frequency_hz = 1.0 * TCNT1;
TCNT1 = 0;
frequency_hz += global_num_overflows * 65536.0;
global_num_overflows = 0;
frequency_hz /= (TASK_PERIOD_MS / 1000.0);
task_timer_ms = TASK_PERIOD_MS;
}
}
Если у кого-то есть какие-либо другие предложения, я им открыта, хотя, но мне, скорее, не нужно использовать диапазоны ... Я также больше не намерен получать разрешение 0,25%, похоже, что уровень точности, который я имею сейчас, не имеет особого смысла ,