Существует много схемных решений и способов измерения частоты сигнала, в том числе и при помощи микроконтроллеров. В этой статье разберем пример самого простого частотомера построенного на контроллере Atmega8. Схема частотомера показана на рисунке 1. Для обеспечения хорошей точности измерения микроконтроллер тактируется от генератора с внешним кварцем частотой 8MHz. Измеренные показания выводятся на LCD 1602 с контроллером HD44780. VD1 стабилитрон ограничивает амплитуду входного сигнала, R2 токоограничительный резистор.

Измерение частоты сигнала с помощью микроконтроллеров AVR. Простой частотомер.

Рисунок 1.

Чтобы измерить частоту сигнала необходимо подсчитать количество импульсов, поступающих на вход микроконтроллера, за единицу времени. Для этого в нашей программе используем два типа перерывания: прерывание по переполнению таймера T0 и внешнее прерывание по изменению сигнала на входе INT0. Количество поступающих на вход сигналов будем подсчитывать за время - 1 секунда.  Восьмибитный таймер T0 будет работать с частотой 1MHz, для этого включим предделитель на 8. Обработчик прерывания по переполнению таймера вызывается 4000 раз в секунду, при этом переменная counter увеличивает свое значение на единицу. Как только эта переменная станет равна 4000, т.е. пройдет 1 секунда, на дисплей уйдет информация о переменной edgecounter, затем обе переменные обнуляются. Все это происходит уже в главном цикле. Переменная edgecounter увеличивает свое значение на единицу каждый раз когда на входе INT0 происходит смена фронта сигнала, т.е. поступает 1 импульс.

Исходный код программы с подробными комментариями показан ниже:

// Измерение частоты сигнала с помощью микроконтроллеров AVR. Простой частотомер.
#include<avr/io.h>
#include<avr/interrupt.h>
#include<util/delay.h>
		 
volatile unsigned int edgecounter = 0, counter = 0;

// Обработчик прерывания по переполнению Т0, вызывается 4000 раз в секунду
ISR(TIMER0_OVF_vect)
{
TCNT0 = 6; // Счетчик Т0 начинает считать с 6, т.к. 1MHz/(256-6) = 4000Hz
counter++;
}

// Обработчик внешнего прерывания
ISR(INT0_vect)
{
edgecounter++;
}
// Функции работы с LCD 
#define RS PD0 
#define EN PD1

// Функция передачи команды
void lcd_com(unsigned char p)
{
PORTD &= ~(1 << RS); // RS = 0 (запись команд)
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
}

// Функция передачи данных
void lcd_data(unsigned char p)
{
PORTD |= (1 << RS)|(1 << EN); // RS = 1 (запись данных), EN - 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
}

// Функция вывода строки на LCD
void lcd_string(unsigned char command, char *string)
{
lcd_com(0x0C);
lcd_com(command);
while(*string != '\0')
{  
lcd_data(*string);
string++;
}
}

// Функция вывода переменной
void lcd_num_to_str(unsigned int value, unsigned char nDigit)
{
 switch(nDigit)
 {
 case 4: lcd_data((value/1000)+'0');
 case 3: lcd_data(((value/100)%10)+'0');
 case 2: lcd_data(((value/10)%10)+'0');
 case 1: lcd_data((value%10)+'0');
 }
}

// Функция инициализации LCD
void lcd_init(void)
{
_delay_ms(50); // Ожидание готовности ЖК-модуля
// Конфигурирование четырехразрядного режима
PORTD |= (1 << PD5);
PORTD &= ~(1 << PD4);
// Активизация четырехразрядного режима
PORTD |= (1 << EN);
PORTD &= ~(1 << EN);
_delay_ms(5); 
lcd_com(0x28); // шина 4 бит, LCD - 2 строки
lcd_com(0x08); // полное выключение дисплея
lcd_com(0x01); // очистка дисплея
_delay_us(100);
lcd_com(0x06); // сдвиг курсора вправо
lcd_com(0x0C); // включение дисплея, курсор не видим
}

int main(void)
{
// Настройка портов ввода/вывода
DDRD = 0b11110011;
PORTD = 0x00;
// Настройка таймера T0
TCCR0 |= (1 << CS01); // Предделитель на 8, частота таймера 1 MHz
TIMSK |= (1 << TOIE0); // Разрешаем прерывание от таймера Т0
// Настройка внешнего прерывания
GICR |= (1 << INT0); // Разрешаем внешнее прерывание на входе INT0
MCUCR |= (1 << ISC01)|(1 << ISC00); // Внешнее прерывание формируется по переднему фронту

sei(); // Глобально разрешаем прерывания
	
lcd_init(); // Инициализация дисплея
lcd_com(0x01);
lcd_string(0x80, "Frequency Meter");
lcd_string(0xC0, "F =      Hz");
while(1)
{
// Выводим показания на дисплей
if(counter == 4000)
{
lcd_com(0xC4);
lcd_num_to_str(edgecounter, 4);
counter = 0;
edgecounter = 0;		
}
}
}


Архив для статьи "Измерение частоты сигнала с помощью микроконтроллеров AVR. Простой частотомер."
Описание: Проект AVRStudio4
Размер файла: 29.72 KB Количество загрузок: 3 318 Скачать

Комментарии  

#1 Александр_Т 18.11.2013 00:06
Здравствуйте.
Подскажите пожалуйста, как можно упростить данный код ( radioparty.ru/.../... ), чтобы он был без LCD, а результат выдавал на конкретный светодиод.
Например: при частоте меньше 20Гц горит один светодиод, при частоте более 40Гц горит другой светодиод, в диапазоне 20-40 не горят.
Заранее благодарю.
#2 AntonChip 18.11.2013 08:51
Достаточно убрать все строки связанные с LCD и заменить код в главном цикле на:
Код:
while(1)
{
if(counter == 4000)
{
if(edgecounter < 20) // Если частота меньше 20
PORTD |= (1 << PD0); // Включаем зеленый светодиод
else
PORTD &= ~(1 << PD0); // Выключаем зеленый светодиод

if(edgecounter > 40) // Если частота больше 40
PORTD |= (1 << PD1); // Включаем красный светодиод
else
PORTD &= ~(1 << PD1); // Включаем красный светодиод

counter = 0;
edgecounter = 0;
}
}
#3 Александр_Т 18.11.2013 13:50
Спасибо за быстрый ответ.
Но все же код работает не так как надо.
Светодиод загорается при любой частоте, единственно раньше или позже - как-будто он ждет пока насобирается определенное число тиков.
Я примерно так тоже экспериментиров ал вчера и результаты были такие же.
Может я выбрал не тот тип генератора в Протеусе (DCLOCK)?
#4 Александр_Т 18.11.2013 16:57
Кое-что заработало если поменять цифры в коде:
if(counter == 4000) на f(counter == 1000)
и частоты с 20 на 200 и с 40 на 400.
В этом случае работает, но при частоте, допустим, 300 все равно загорается PD1.
Может еще надо поиграться с настройкой таймера или прерываний или еще что подстроить?
#5 Александр_Т 18.11.2013 18:23
Убрав с основной программы:
counter = 0;
edgecounter = 0;
заработал промежуток, в котором не должены гореть светодиоды.
Единственно, что пока работает на тех частотах, не те которые мне нужны.
Но и за это спасибо.
#6 AntonChip 18.11.2013 21:34
Код проверял в Протеусе, использовал VSM Signal Generator, амплитуда 5,5Вольт. Срабатывает четко, при уменьшении частоты ниже 20Гц загорается зел. светодиод, при увеличении больше 40Гц загорается красный, в промежутке от 20 до 40 оба не горят, единственное включаются с небольшой задержкой, может комп у меня тормозит
#7 Александр_Т 19.11.2013 00:16
У меня тоже с запозданием работает.
А какие настройки ставили в генераторе, кроме вольтажа?
Когда стоит отметк "Bi" - не зависимо от частоты загорается PD1
Когда ставлю "Uni" - всегда загорается PD0
Почему так?
#8 AntonChip 19.11.2013 06:37
Нет, у меня на Bi/Uni не реагирует, создай тему в форуме, я скину туда свой проект
#9 Alex_och 19.11.2013 11:12
Ок. Тему создал:
radioparty.ru/.../...
#10 Alex_och 21.11.2013 23:41
Вникаю в код программы. Откуда взялась цифра 256?
"TCNT0 = 6; // Счетчик Т0 начинает считать с 6, т.к. 1MHz/(256-6) = 4000Hz"
Можно ли сделать, чтобы таймер тикал не каждую секунду, а каждые две?
#11 AntonChip 22.11.2013 22:42
Цитирую Alex_och:
Вникаю в код программы. Откуда взялась цифра 256?
"TCNT0 = 6; // Счетчик Т0 начинает считать с 6, т.к. 1MHz/(256-6) = 4000Hz"

Измеряемая частота это количество импульсов за 1 секунду, рабочая частота Atmega8 - 8MHz, настроен делитель таймера на 8, рабочая частота таймера 1MHz, считает таймер до максимума т.е. до 0xFF(255), за 1 секунду он переполнится 1000000/255 = 3921,56, получается нецелое число, это нас не устраивает, поетому таймер можно заставить считать не с начала, а например с 5, 1000000/(255-5) = 4000.

Почему в статье стоит TCNT0 = 6; потому что из-за переходных процессов счетчик добавляет 3 тика, число 6 можно смело увеличить до 8
Цитирую Alex_och:
Можно ли сделать, чтобы таймер тикал не каждую секунду, а каждые две?

Таймер не тикает каждую секунду, за 1 секунду просто обновляются данные, если за 2 секунды, то можно здесь
Код:
if(counter == 4000)

увеличить число 4000 до 8000
#12 Ольга 23.12.2013 20:56
Спроектировать измеритель частоты сети с точностью до десятых долей герца при времени измерения не более одной секунды. Информация должна дублироваться на выносном табло, связь с которым осуществляется с помощью трехпроводной линии связи/ что значит трехпроводной линии связи
#13 Сава 07.01.2014 23:46
Здравствуйте. А можно ли вместо кварца 8 мгц, подключить ОГ Гиацинт на 5 Мгц ???
#14 AntonChip 08.01.2014 00:12
Цитирую Сава:
Здравствуйте. А можно ли вместо кварца 8 мгц, подключить ОГ Гиацинт на 5 Мгц ???

Можно, только считать будет не правильно
#15 Игорь 14.02.2014 15:59
Здравствуйте! Можно ли и как изменить код, чтобы измерялся временной интервал (или измеритель периода) по входу INT0. Т.е. при "1" на INT0 запускался внутренний генератор и счетчик, а по прерыванию ("1" на входе INT1) счетчик останавливался и выводил информацию на LCD, допустим в микросекундах.
Зараннее спасибо
#16 Сергей Ф 12.03.2014 23:41
Здравствуйте!
Помогите пожалуйста совместить два проекта: ЧАСТОТОМЕР и ТЕРМОМЕТР (DS18B20). Необходима контролировать одновременно температуру радиатора и частоту оборотов кулера. Два проекта я совместил, но работает только что-то одно частотомер или термометр (видимо мешают друг другу).
#17 AntonChip 13.03.2014 07:56
Цитирую Сергей Ф:
Здравствуйте!
Помогите пожалуйста совместить два проекта: ЧАСТОТОМЕР и ТЕРМОМЕТР (DS18B20). Необходима контролировать одновременно температуру радиатора и частоту оборотов кулера. Два проекта я совместил, но работает только что-то одно частотомер или термометр (видимо мешают друг другу).

Как и на какой индикатор выводите данные?
#18 AntonChip 13.03.2014 08:05
Цитирую Игорь:
Здравствуйте! Можно ли и как изменить код, чтобы измерялся временной интервал (или измеритель периода) по входу INT0. Т.е. при "1" на INT0 запускался внутренний генератор и счетчик, а по прерыванию ("1" на входе INT1) счетчик останавливался и выводил информацию на LCD, допустим в микросекундах.
Зараннее спасибо

Прибор измеряет интервалы от 10мкс до 30мин elektor-magazine.com/.../...
#19 Сергей Ф 13.03.2014 11:47
Цитирую admin:
Цитирую Сергей Ф:
Здравствуйте!
Помогите пожалуйста совместить два проекта: ЧАСТОТОМЕР и ТЕРМОМЕТР (DS18B20). Необходима контролировать одновременно температуру радиатора и частоту оборотов кулера. Два проекта я совместил, но работает только что-то одно частотомер или термометр (видимо мешают друг другу).

Как и на какой индикатор выводите данные?

На LCD 1602 HD44780.
#20 AntonChip 13.03.2014 15:10
Цитирую Сергей Ф:
Здравствуйте!
Помогите пожалуйста совместить два проекта: ЧАСТОТОМЕР и ТЕРМОМЕТР (DS18B20).

Пиши и выкладывай свой исходник в форуме, помогу
#21 Sergey F 13.03.2014 15:43
Цитирую admin:
Цитирую Сергей Ф:
Здравствуйте!
Помогите пожалуйста совместить два проекта: ЧАСТОТОМЕР и ТЕРМОМЕТР (DS18B20).

Пиши и выкладывай свой исходник в форуме, помогу

Тему создал radioparty.ru/.../...
#22 jogik9 29.04.2014 22:14
какой у него предел?
#23 AntonChip 30.04.2014 08:36
Цитирую jogik9:
какой у него предел?
Для этого проекта 9999 Гц
#24 Игорь М. 31.03.2015 12:29
Здравствуйте!
Интересный проект!
Хотел бы попробовать да нет прошивки и установки фьюзов.
Я не спец в этом
#25 Alex_och 01.12.2015 14:44
Для себя код немного упростил - убрал дисплей и поставил светодиоды т.к. диапазон изменения не большой и особой точности не требует. Решил я сделать анемометр - прибор для измерения скорости ветра. Вентилятор крутится, геркон щелкает.
Но задача усложняется. Вентилятор с герконом нужно подключить к мобильному телефону (микрофону), а сама схема измерителя частоты к другому мобильному в гнездо наушников. Но возникла проблемка - получается как бы дребезг от геркона и дополнительный шум в эфире, что дает на устройстве дополнительные прерывания и ошибочный результат. Добавил в коде пару строчек для маленькой задержки, помогло, но хотелось бы лучше.
Пришла идея, чтобы на вентиляторе тоже поставить такую схему, а результат выдавался бы звуком определенной частоты в зависимости от скорости ветра и вращения вентилятора. Думаю, звук будет передаваться точнее. Получится что-то типа старого модема.
Или Вы посоветуете как-то по-другому сделать, может сталкивались с подобным вопросом.
Заранее благодарю.
#26 AntonChip 01.12.2015 16:08
Вместо геркона лучше использовать оптопару, а если вентилятор от охлаждения процессора, то его встроенный датчик Холла
#27 Юрий_ 09.01.2016 11:11
Доброго времени суток, реально ли сделать чтоб показывало еще и сотые от частоты?
#28 AntonChip 11.01.2016 16:14
Цитирую Юрий_:
Доброго времени суток, реально ли сделать чтоб показывало еще и сотые от частоты?

Можно, но тогда измерение будет происходить 1 раз в 10 секунд
#29 Ромаn 26.11.2016 19:44
подскажите например снимаем показания сдатчика через АЦП , а как узнать еще и частоту ?
#30 Alex_och 27.11.2016 17:21
Цитирую Ромаn:
подскажите например снимаем показания сдатчика через АЦП , а как узнать еще и частоту ?

Роман, если Вы снимаете показания с датчика АЦП, значит Вы измеряете напряжение. Следовательно чем больше обороты, тем больше напряжение на датчике. Тогда Вы можите измерять частоту измеряя напряжение, только для этого нужно воспользоваться отдельным тахометром для калибровки устройства - найти соответствие напряжения к оборотам в нескольких точках т.к. в разном диапазоне зависимость можит быт не линейной. Это мое мнение и в том случае если датчик по принципу моторчика.
#31 gulevsky 02.03.2017 21:04
Здравствуйте подскажите если сделать четыре частотомена на одном микроконтроллер е какой микроконтроллер нужно и сложно это?
#32 AntonChip 05.03.2017 09:52
Цитирую gulevsky:
Здравствуйте подскажите если сделать четыре частотомена на одном микроконтроллере какой микроконтроллер нужно и сложно это?

Поможет использование более продвинутого контроллера, например ATmega1280 имеет 4 16 битных таймера/счетчик а
#33 gulevsky 09.03.2017 20:07
Цитирую AntonChip:
Цитирую gulevsky:
Здравствуйте подскажите если сделать четыре частотомена на одном микроконтроллере какой микроконтроллер нужно и сложно это?

Поможет использование более продвинутого контроллера, например ATmega1280 имеет 4 16 битных таймера/счетчика

дороговат он дешевле четыре таких собрать только дисплей бы сегментный сделать и все. Ладно спасибо за совет.
#34 АлексЕ 11.05.2017 22:58
Здравствуйте. Попробовал в Вашей программе предделитель поставить на 64 вместо 8-и. Соответственно и
counter в 8 раз убавил. Частотомер работает. А если предделитель на 64 оставить, а тайметр Т2 использовать, то частотомер в два раза меньше реальной частоты показывает. Моделировал в AVRStudio4.19. Подскажите что не так.
+1 #35 AntonChip 12.05.2017 08:32
Цитирую АлексЕ:
А если предделитель на 64 оставить, а тайметр Т2 использовать, то частотомер в два раза меньше реальной частоты показывает. Моделировал в AVRStudio4.19. Подскажите что не так.
проверьте все ли регистры перенастроены под таймер 2
#36 Андрей Иванович 13.10.2018 23:04
А как увеличить измеряемую частот, например до 10МГц?
#37 Андрей Иванович 13.10.2018 23:05
Что нужно поменять в исходнике?
#38 AntonChip 14.10.2018 08:54
Цитирую Андрей Иванович:
Что нужно поменять в исходнике?

Вот, почитайте, метод позволяет измерять частоту входного сигнала до Timer_Clock_fre quency/2.5 ww1.microchip.com/.../...
-1 #39 Veronika 04.12.2018 19:40
Здравствуйте, почему-то не работает схема, вроде все правильно собрано, пишет F=0000Hz и ничего не происходит.
И как можно увеличить измеряемую частоту и, чтобы еще можно было измерять период(время)?
#40 mastech 31.12.2019 06:01
Как получилось время измерения в одну секунду?
+1 #41 Тарас 16.07.2020 06:27
Зачем было настраивать внещнее прерывание , счетчик настроить и все. Он бы и так считал.
В этой статье например такого не делают
engineersgarage.com/.../...
-1 #42 AntonChip 18.07.2020 16:24
Цитирую Тарас:
Зачем было настраивать внещнее прерывание , счетчик настроить и все. Он бы и так считал.
В этой статье например такого не делают
engineersgarage.com/.../...

Здравствуйте. Существует несколько способов измерения частоты сигнала с помощью AVR, у каждого есть свои недостатки и преимущества,
в данном примере таймер/счетчик отсчитывает только интервал времени равным 1 с.
#43 Artur-K 16.10.2020 18:47
Добрый день. А что надо подправить, что бы работало с другим кварцем?
У меня есть 16 и 12 МГц, но 16-й я хочу в другое устройство на atmega64...
+1 #44 AntonChip 17.10.2020 16:24
Для частоты 12 МГц частота таймера 0 равна 12000000/8=1500 000 Гц
1500000/(256-6) = 6000 переполнений нужно для подсчета 1 секунды
4000 меняем на 6000
#45 ДенисD 02.02.2021 23:25
Добрый день. А скажите как переправить программу, чтоб можно было измерять до 10 мегагерц и показывало на lcd? Я уже долго голову ломаю, и немного не могу понять итот момент. Может кто-то поможет и напишет код. За ранее спасибо.
#46 AntonChip 03.02.2021 22:28
Приветствую. Чтобы AVR смог измерить частоту 10 МГц его тактовая частота должна быть как минимум 25 МГц

У Вас недостаточно прав для добавления комментариев. Возможно, Вам необходимо зарегистрироваться на сайте.