Печать

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

Автор: AntonChip Опубликовано . Опубликовано в Программирование на Си

Рейтинг:   / 17
ПлохоОтлично 

Существует много схемных решений и способов измерения частоты сигнала, в том числе и при помощи микроконтроллеров. В этой статье разберем пример самого простого частотомера построенного на контроллере 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)
{
PORTD = 0x00; // Настраиваем входы/выходы
DDRD = 0b11110011;
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;		
}
}
}

Файлы:
Проект AVRStudio4
Дата 04.04.2015 Размер файла 29.72 KB Закачек 1224

Комментарии  

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

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

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

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

Роман, если Вы снимаете показания с датчика АЦП, значит Вы измеряете напряжение. Следовательно чем больше обороты, тем больше напряжение на датчике. Тогда Вы можите измерять частоту измеряя напряжение, только для этого нужно воспользоваться отдельным тахометром для калибровки устройства - найти соответствие напряжения к оборотам в нескольких точках т.к. в разном диапазоне зависимость можит быт не линейной. Это мое мнение и в том случае если датчик по принципу моторчика.
Сообщить модератору

Рекомендуем посмотреть