Печать

Декодирование сигналов протокола NEC

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

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

Продолжаем знакомиться с различными протоколами передачи данных по ИК связи, на этот раз разберем протокол NEC, который в настоящее время стал очень популярным среди радиолюбителей. Информация в этом стандарте кодируется паузой разной длины между импульсами, если длительность паузы меньше это лог. ноль, если больше - лог. единица. Длина периода лог. нуля составляет 1,12 мс, а длина лог. единицы 2,25 мс, длина паузы лог. нуля составляет 560 мкс, а длина паузы лог. единицы 1,675 мс.

Команды передаются пакетами. Каждый пакет начинается со стартовой последовательности - импульса длиной 9 мс и паузы длиной 4,5 мс. В каждой посылке передается 8 бит адреса и 8 бит команды, причем адрес и команда передаются дважды - в прямом и инверсном виде (это кроме проверки валидности передачи, делает одинаковой общую длительность любой посылки). Каждый байт пакета передаётся младшим битом вперёд.

При непрерывном нажатии на кнопку, только первая посылка передается как показано на рисунке выше. Все остальные передаются в виде преамбулы с укороченной паузой и завершающим импульсом, с периодом 110мс:


Кодирование бит - длительностью паузы ( 1.6мс или 0.56мс) между импульсами фиксированной длительности (0.56мс):

Существует несколько способов декодировать принятый с фотоприемника сигнал (сигнал с фотоприемника поступает уже демодулированным, т.е. чистый логический)
- без перываний;
- с помощью внешнего прерывания;
- по захвату таймера.

Декодирование сигналов протокола NEC

Мы же разберем пример обработки ИК сигнала с помощью внешнего прерывания и прерывания по таймеру. Микроконтроллер Atmega8 тактируется от внутреннего RC-генератора частотой 1МГц. Демодулированный сигнал поступает на вход INT0. Внешнее прерывание будет происходить по заднему фронту импульса. Таймер/счетчик 1 настроен на прерывание по совпадению, в этом прерывании каждые 560мкс инкрементируется счетчик интервалов signal_time, который сбрасывается в обработчике внешнего прерывания. Если счетчик интервалов успел насчитать больше 9 раз это указывает на начало приема посылки. Если счетчик не успеет обновится, то записанный бит будет равен нулю, если он насчитает больше 4 раз - это значение можно считать лог. единицей. Как только в массив будут записаны все 33 значения, установится флаг окончания приема посылки ir_ok и можно приступать к декодированию принятых команд.

Декодирование происходит по принципу разделения принятого массива данных на 4 байта - байт адреса, байт инвертированного значения адреса, байт команды и байт инвертированного значения команды. На дисплей выводим данные уже в шестнадцатиричном виде, путем преобразования их со значениями из массива HEX.

Ниже представлен код программы с подробными комментариями:

// Декодирование сигналов протокола NEC

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include "hd44780.h"

const unsigned char HEX[16] PROGMEM="0123456789ABCDEF";

unsigned char signal_time;
static unsigned char ir_ok,ir_decode_ok;
unsigned char ir_code[4]; // Массив для хранения значений адресов и команд
unsigned char ir_signal[33]; // Массив для хранения значений интервалов

// Прерывание по совпадению T1(каждые 560мкс)
ISR(TIMER1_COMPA_vect)
{
signal_time++; // Счетчик интервалов 
}

// Внешнее прерывание по INT0
ISR(INT0_vect)
{
static unsigned char i;
// Определяем начало приема посылки
if(signal_time > 9) // (4,5ms+562us)/560 = 9 
i = 0; // Выбираем первый интервал

ir_signal[i] = signal_time; // Записываем в буфер значения интервалов 
signal_time = 0; // Обнуляем счетчик интервалов
i++; // Следующий интервал 

if(i == 33) // Если все интервалы приняты
ir_ok = 1; // Устанавливаем флаг окончания приема сигнала 
}

// Функция декодирования сигнала
void ir_decode(void)
{ 
unsigned char k = 0;
unsigned char signal_length,value = 0;

for(unsigned char i = 0; i < 4; i++) // Обработка байтов адреса или команды
  {	
  for(unsigned char j = 0; j < 8; j++) // Обработка 8-ми битов адреса или команды
    {
	k++;
	value = value >> 1; // Сдвигаем биты вправо
    signal_length = ir_signal[k]; // Выбираем следущее значение интервала
    if(signal_length > 4) // Если интервал больше (1,675ms+562us)/560 = 4
	  value = value | 0x80; // Добавляем к старшему разряду единицу
    }
  ir_code[i] = value; // Запоминаем в буфере байт адреса или команды
  value = 0; // Обнуляем значение адреса или команды    
  } 
// Производим явное приведение типов и проверяем принятые байты
if(((unsigned char)ir_code[0] == (unsigned char)~ir_code[1]) && 
   ((unsigned char)ir_code[2] == (unsigned char)~ir_code[3]))
  {
  ir_decode_ok = 1; // Устанавливаем флаг окончания декодирования сигнала
  ir_ok = 0; // Сбрасываем флаг окончания приема сигнала
  }
else
  {
  ir_decode_ok = 0; // Сбрасываем флаг окончания декодирования сигнала
  ir_ok = 0; // Сбрасываем флаг окончания приема сигнала
  }
}

int main(void)
{
MCUCR |= (1 << ISC01); // Внешнее прерывание по заднему фронту
GICR |= (1 << INT0); // Разрешение внешнего прерывния по INT0

TCCR1B |= (1 << WGM12)|(1 << CS10); // Режим CTC, предделитель 1 
OCR1A = 279; // 1000000/2*(279+1) = 1786Hz(560us)
TIMSK |= (1 << OCIE1A); // Разрешаем прерывание по совпадению Т1 

lcd_init(); // Инициализация ЖК дисплея
sei(); // Глобально разрешаем прерывания

lcd_gotoxy(0, 0);
lcd_string("NEC PROTOCOL    ",16);
lcd_gotoxy(0, 1);
lcd_string("DECODER         ",16);
_delay_ms(250);
_delay_ms(250);
_delay_ms(250);
_delay_ms(250);
lcd_clr(); // Очищаем дисплей
lcd_gotoxy(0, 0);
lcd_string("Adr: 00  Inv: 00",16);
lcd_gotoxy(0, 1);
lcd_string("Com: 00  Inv: 00",16);

while(1)
{
  if(ir_ok) ir_decode(); // Если сигнал принят, декодируем его
	if(ir_decode_ok) // Если обработка сигнала завершена, выводим данные на дисплей
	{
      lcd_gotoxy(5, 0);
      lcd_data(pgm_read_byte(HEX+ir_code[0]/16)); // Выводим данные в шестнадцатиричном виде
	  lcd_data(pgm_read_byte(HEX+ir_code[0]%16));
      lcd_gotoxy(14, 0);
	  lcd_data(pgm_read_byte(HEX+ir_code[1]/16));
	  lcd_data(pgm_read_byte(HEX+ir_code[1]%16));
      lcd_gotoxy(5, 1);	    
	  lcd_data(pgm_read_byte(HEX+ir_code[2]/16));
	  lcd_data(pgm_read_byte(HEX+ir_code[2]%16));
      lcd_gotoxy(14, 1);	    
	  lcd_data(pgm_read_byte(HEX+ir_code[3]/16));
	  lcd_data(pgm_read_byte(HEX+ir_code[3]%16));
      ir_decode_ok = 0; // Сбрасываем флаг окончания декодирования сигнала
	}
   }
}

Файлы:
Проект AVRStudio4
Дата 18.10.2015 Размер файла 33.63 KB Закачек 270

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