Существует много схемных решений и способов измерения частоты сигнала, в том числе и при помощи микроконтроллеров. В этой статье разберем пример самого простого частотомера построенного на контроллере 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 246 Скачать

Комментарии  

+1 #41 Тарас 16.07.2020 06:27
Зачем было настраивать внещнее прерывание , счетчик настроить и все. Он бы и так считал.
В этой статье например такого не делают
engineersgarage.com/.../...
Сообщить модератору
-1 #42 AntonChip 18.07.2020 16:24
Цитирую Тарас:
Зачем было настраивать внещнее прерывание , счетчик настроить и все. Он бы и так считал.
В этой статье например такого не делают
engineersgarage.com/.../...

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