Мне нужно измерить частоту прямоугольной волны, которая может варьироваться от 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%, похоже, что уровень точности, который я имею сейчас, не имеет особого смысла ,
Ответы:
Если возможно, я бы предложил выбрать микроконтроллер, который поддерживает работу счетчика с использованием входов таймера; вместо того, чтобы вручную увеличивать счетчик внутри ISR (который на высоких частотах быстро заканчивает насыщение активности микроконтроллера), вы позволяете аппаратному обеспечению обрабатывать счет. В этот момент ваш код просто становится вопросом ожидания вашего периодического прерывания, а затем вычисления частоты.
Чтобы расширить диапазон и сделать частотомер более обобщенным (устраняя необходимость в нескольких диапазонах за счет немного больше работы для MCU), вы можете использовать следующую технику.
Выберите периодическую частоту прерываний, которая учитывает точность измерения на самой высокой входной частоте; это должно учитывать размер вашего счетчика (вам нужно выбрать период таймера, чтобы счетчик таймера не переполнялся при максимальной входной частоте). В этом примере я предполагаю, что значение входного счетчика можно прочитать из переменной «timer_input_ctr».
Включить переменную для подсчета периодических прерываний (при запуске следует инициализировать 0); для этого примера я буду ссылаться на эту переменную как «isr_count». Период прерывания содержится в константе "isr_period".
Ваше периодическое прерывание должно быть реализовано как (C псевдокод):
Очевидно, что этот грубый пример опирается на некоторую математику с плавающей запятой, которая может быть несовместима с низкоуровневыми микроконтроллерами, есть способы преодолеть это, но они выходят за рамки этого ответа.
источник
Возможно, вы захотите рассмотреть два (или более) диапазона. Проблемы с захватом очень низких частот несколько отличаются от проблем с более высокими. Как вы уже заметили, на верхнем уровне вашего диапазона возникают проблемы с переполнением счетчика.
Но обратите внимание на нижнюю границу вашего диапазона, ваша точность пострадает от недостатка подсчетов в реестре. Не уверен, что вы действительно хотите различать между 0,25 Гц и 0,5 Гц, но если вы это сделаете, то вам действительно придется рассчитывать на четыре секунды, чтобы сделать это.
Кроме того, если указать строгое интерпретированное плоское разрешение 0,25 Гц, вы сможете отличить 500 000,00 Гц от 500 000,25 Гц, что является довольно высокой степенью точности.
По этим причинам проектирование для различных диапазонов может облегчить проблему размера счетчика. Вытягивание случайных чисел, например, для нижнего уровня, скажем, от 0 до 100 Гц, считается за 10-секундные интервалы, и вы получаете разрешение 0,1 Гц, и вашему счетчику нужно только подняться до 1000, а не до 10 бит. Затем от 100 Гц до 10 кГц считайте интервалы в 1 секунду; вы получаете разрешение только 1 Гц, но ваш счетчик должен работать до 10000, все же меньше 16 бит. Верхний диапазон от 10 кГц до 1 МГц может считаться всего за 0,01 с, а максимальный счет будет по-прежнему только 10 000, и хотя ваше разрешение будет 100 Гц, это будет разумной точностью.
источник
Вы можете смешать аппаратный и программный счетчик, посчитав переполнения аппаратного счетчика в ISR.
Подсчет каждого фронта сигнала в ISR будет слишком медленным для сигнала 1 МГц. Я думаю, что вы можете сделать до 50 кГц таким образом.
источник
Вместо того чтобы делать 1-секундный счетчик, сделайте его 0,1-секундным счетчиком и умножьте счет на 10?
Если это просто вопрос сохранения номера счетчика, разве вы не можете использовать дополнительный код для отслеживания того, когда счетчик собирается переполниться, и записи в другое место в памяти, чтобы сохранить счет?
источник
Разве вы не можете использовать прерывания захвата и переполнения 16-битного таймера (плюс переменная) для измерения? Вот как я мог бы сделать это с ATTiny24A с AVR-GCC (непроверенный и, возможно, с ошибками):
... во всяком случае, он компилируется :)
РЕДАКТИРОВАТЬ Я посмотрел на вывод файла lss из моего кода, и в сгенерированном коде слишком много инструкций, чтобы не отключиться на частоте 1 МГц с тактовой частотой 8 МГц ... даже простое увеличение на одну строку в TIM1_OVF_vect генерирует 19 инструкций! Таким образом, для обработки событий с частотой 1 МГц вам, безусловно, необходимо оптимизировать, вероятно, зарегистрировать выделение некоторых вещей (например, num_overflows и capture_value_ticks), использовать встроенный ассемблер (украсть важные вещи из файла lss) и перенести обработку из прерываний в главное петля везде, где это возможно.
источник
Публикация этого кода в качестве альтернативы предложению @ timrorr для моего предыдущего поста. Он компилируется для ATTiny24A с использованием стандарта языка c99, но я на самом деле не тестировал его никоим образом.
Это приятное небольшое использование аппаратных возможностей Timer1 и освобождает тонну циклов обработки по сравнению с моим первоначальным постом.
источник
При использовании прескалеров даже можно измерять ГГц. Это простой 40МГц частотомер с ATMEL AVR AT90S2313: http://www.myplace.nu/avr/countermeasures/index.htm
Вот некоторые другие подобные проекты:
http://www.ikalogic.com/freq_meter_2.php
http://www.saturn.dti.ne.jp/~khr3887/lfcd_e.html
http://www.circuitlake.com/rs232-frequency-meter-and-pulse-generator.html
http://www.ulrichradig.de/home/index.php/avr/frequenzcounter
http://www.triplespark.net/elec/analysis/FreqCnt/
http://www.cappels.org/dproj/30MHzfmeter/30MhzFmtr.html
http://www.qsl.net/pa3ckr/bascom%20and%20avr/rfcounter/index.html
http://www.sump.org/projects/counter
http://digilander.libero.it/alfred73/eprojects.htm#1300%20Mhz%20Frequencymeter%20with%20prescaler
источник