При помощи режима "захват" таймера можно измерять не только частоту сигнала но и длительность импульса сигнала, о так же скважность. Например в устройствах радиоуправления моделями для передачи цифровых команд используется широтно модулированный сигнал, чтобы раскодировать команду необходимо вычислить ширину или скважность импульса.

Задача измерения длины импульса, периода и частоты сводится к следующему:

1. Настраиваем шестнадцатиразрядный таймер/счетчик 1

Рабочая частота микроконтроллера(Atmega8) 8 МГц, используем предделитель на 8 для таймера/счетчика 1, т.е. частота таймера 1 МГц и 1 тик таймера будет равен 1/1000000 Гц = 1 мкс., т.е. за 65536 мкс произойдет одно прерывание таймера. Также используем входной подавитель шума, который активируется записью лог. 1 в бит ICNC1 регистра TCCR1B. Разрешаем прерывание по захвату и переполнению таймера.

2. Обрабатываем прерывания по таймеру/счетчику 1

Наш таймер считает от 0 до 65536, потом происходит прерывание по переполнению, регистр TCNT1 обнуляется и начинает тикать снова. Необходимо считать количество этих прерываний для дальнейшего подсчета длительности. С каждым прерыванием увеличиваем переменную OVF_counter.

Измеряемый сигнал подается на вывод ICP1(PB0). Текущее значение TCNT1 при каждом изменении на выводе ICP1 копируется в ICR1 и остается там до следующего изменения. Если на входе ICP1 нарастающий фронт импульса, текущее значение ICR1 помещаем в буфер rising, после обнуляем счетчик прерываний по переполнению и переключаем режим захвата по спадающему фронту.  Если на входе ICP1 спадающий фронт импульса, текущее значение ICR1 помещаем в буфер falling, после переключаем режим захвата по нарастающему фронту. Вычисляем длину импульса по формуле:

t = falling - rising_1 + (OVF_counter * 65536);

Для измерения периода необходимо сделать те же операции за исключением того что переключать режим захвата не надо, здесь оба измерения делаем по положительному импульсу. Чтобы вычислить частоту сигнала единицу делим на значение периода импульса. Скважность это отношение длины импульса высокого уровня к периоду его следования и выражается в процентах.

3. Выводим значения длительности и периода импульса(мкс) и частоты(Гц) на LCD дисплей.

Принцип измерения всех величин показан на графике:

Измерение ширины, скважности и частоты сигнала

Принципиальная схема измерителя:

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

// Использование таймера в режиме захвата. Измерение ширины, 
// скважности импульса и частоты сигнала
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
unsigned char OVF_counter, zamer_1, zamer_T, zamer_t, duty;
unsigned long t, T, f;
unsigned int rising_1, rising_2, falling;
// Прерывание по переполнению Таймера/счетчика 1
ISR(TIMER1_OVF_vect)
{
OVF_counter++; // Увеличиваем счетчик переполнений
}
// Прерывание по захвату Таймера/счетчика 1
ISR(TIMER1_CAPT_vect)
{
switch(zamer_1)
  {
    case 0: // Вычисляем ширину импульса
      switch(zamer_t)
	  {
      case 0:
	  rising_1 = ICR1; // Запоминаем значениие счётчика 
      TCCR1B &= ~(1 << ICES1); // Устанавливаем прерывание по спадающему фронту импульса
      OVF_counter = 0; // Обнуляем количество переполнений счётчика
      zamer_t = 1; // Переходим к следующему вычислению
	  break;
	  
	  case 1:	  
      falling = ICR1;           // Запоминаем значение счётчика
      TCCR1B |= (1 << ICES1);   // Устанавливаем прерывание по нарастающему фронту импульса
// Приводим все переменные к одному типу и вычисляем длительность импульса
      t = (unsigned long)falling - (unsigned long)rising_1 + ((unsigned long)OVF_counter * 65536);
      zamer_t = 0;
	  zamer_1 = 1; // Переходим к следующему вычислению
	  break;      
	  }
    break;
    case 1: // Вычисляем период импульса
      switch(zamer_T)  
      {
      case 0:
      rising_1 = ICR1; // Запоминаем значение счётчика
      OVF_counter = 0; // Обнуляем количество переполнений счётчика
      zamer_T = 1; // Переходим к следующему вычислению
      break;
  
      case 1: 
      rising_2 = ICR1; // Запоминаем значение счётчика
// Приводим все переменные к одному типу и вычисляем период импульса
      T = (unsigned long)rising_2 - (unsigned long)rising_1 + ((unsigned long)OVF_counter * 65536);
      zamer_T = 0;
      zamer_1 = 2; // Переходим к следующему вычислению
      break;
      }
    break;
  case 2:                   
  f = 1000000/T; // Вычисляем частоту сигнала в Гц
  duty = (t*100)/T; // Вычисляем скважность импульса в процентах
  zamer_1 = 0; // Переходим к следующему вычислению
  break;
  }
}
// Функции работы с LCD 
#define RS PD0 
#define EN PD2
// Функция установки курсора в указанную точку
#define lcd_gotoxy(x, y) lcd_com(0x80|(x)+((y)*0x40))
// Функция передачи команды
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(char *string)
{
lcd_com(0x0C);
while(*string != '\0')
{  lcd_data(*string);
string++;
}
}
// Функция вывода переменной
void lcd_num_to_str(unsigned long value, char nDigit)
{
 switch(nDigit)
 {
  case 6: lcd_data(((value/100000)%10)+'0');
  case 5: lcd_data(((value/10000)%10)+'0');
  case 4: lcd_data(((value/1000)%10)+'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)
{
PORTD = 0x00;
DDRD = 0xFF;
_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)
{
TCCR1B |= (1 << ICNC1)|(1 << CS11); // Активируем входной подавитель шума, предделитель на 8
TIMSK |= (1 << TICIE1)|(1 << TOIE1); // Разрешаем прерывание по захвату и переполнению
lcd_init(); // Инициализация дисплея
_delay_ms(50);
lcd_gotoxy(0, 0);
lcd_string("F=      Hz  DUTY");
lcd_gotoxy(0, 1);
lcd_string("t=      us     %");
sei(); // Глобально разрешаем прерывания
while (1)
{
lcd_gotoxy(2, 0);
lcd_num_to_str(f, 6); // Выводим частоту сигнала на экран
lcd_gotoxy(2, 1);
lcd_num_to_str(t, 6); // Выводим ширину импульса на экран
lcd_gotoxy(12, 1);
lcd_num_to_str(duty, 3); // Выводим скважность импульса на экран
_delay_ms(250);
}
}

Обсуждение статьи на форуме


Архив для статьи "Использование таймера в режиме захвата. Измерение ширины, скважности и частоты сигнала"
Описание: Проект AVRStudio4
Размер файла: 36.34 KB Количество загрузок: 2 181 Скачать