Дисплеи, датчики и прочие функциональные узлы, управляемые МК.
Ответить

Re: ИК протокол NEC

Чт дек 22, 2016 09:35:00

решил реализовать передачу данных на таймере, так сказать почти на аппаратном уровне, что бы не завешивать МК на 70ms при передачи одной команды.
Реализация на атмеге8 16МГц (вполне можно и на меньшей частоте, для моего проекта нужно было именно 16) на 8-bit Timer/Counter2 в режиме CTC.
Спойлер
Код:
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define PORT_output 0
#define on(X) PORTB|=(1<<(X))
#define off(X) PORTB&=~(1<<(X))
#define invert(X) PORTB^=(1<<(X))
#define pin_on(X) (PINB&(1<<(X)))

//#define ID_code 0xB9

unsigned char i,j;
unsigned char buf_NEC[4]  = {0xB9, 0x46, 0, 0};
void timer_1204_init(void);
void transmit_NEC(unsigned char comands);
void timer_off(void);
void decode_bit(void);

void decode_bit(void)
{
   if (pin_on(PORT_output) != 0)
   {
      OCR2 = 70;      //560us
   }
   else
   {
      if (((buf_NEC[i-1]&(1<<j++)) != 0)&&(pin_on(PORT_output) == 0))
      {
         OCR2 = 211;      //1690us
      }
      else
      {
         OCR2 = 70;      //560us
      }
   }
   
   if (j > 7)
   {
      i++;
      j = 0;
   }
}
//прерывание сравнения
ISR(TIMER2_COMP_vect)
{
   invert(PORT_output);
   switch (i)
   {
   case 0:
   OCR2 = 70;      //4,5ms
   i++;
      break;
   case 1:
      TCCR2&=~(1<<CS21);      //предделитель 128
      decode_bit();
      break;
   case 2:
   decode_bit();
      break;
   case 3:
   decode_bit();
      break;
   case 4:
   decode_bit();
      break;
   case 5:
      if (pin_on(PORT_output) != 0)
      {
         OCR2 = 70;      //560us
      }
      else
      {
         j = 0;
         i = 0;
         timer_off();
      }
   break;   
         
   }
   
}

void timer_1204_init(void)
{
   TCCR2 |= (1<<CS20)|(1<<CS21)|(1<<CS22)|(1<<WGM21);      //предделитель 1024
   TIMSK |= (1<<OCIE2);                  //прерывание по переполнению
   sei();
}

void timer_off(void)
{
   TCCR2&=~((1<<CS20)|(1<<CS21)|(1<<CS22));      //предделитель 1024
   TIMSK&=~(1<<OCIE2);                  //прерывание по переполнению
}            

void transmit_NEC(unsigned char comands)
{
   buf_NEC[2] = comands;
   buf_NEC[3] = ~comands;
   on(PORT_output);
   timer_1204_init();      //запускаем счетчик
   OCR2 = 140;            //9ms
   i = 0;
}



int main(void)
{
   DDRB|=(1<<0);
   off(PORT_output);      //выход на транзистор IrDa
    /* Replace with your application code */
    while (1)
    {
      transmit_NEC(0x12);
      _delay_ms(200);
      transmit_NEC(0x13);
      _delay_ms(400);
    }
}

Единственно там еще не хватает буфера для передачи!
А для подключения диода задействовать Timer1 для реализации модуляции на 38,222кГц на той же ноге PB1 (OC1A)

Re: ИК протокол NEC

Ср фев 08, 2017 09:11:17

shads, А как обработать длинное нажатие?

Re: ИК протокол NEC

Вс фев 12, 2017 10:41:33

RIMUS1989i писал(а):shads, А как обработать длинное нажатие?

Во время длинного нажатия пульты NEC передают какой то простой (уже точно не помню) периодический импульс, это означает что нужно повторять действие последней принятой команды...
Т.е. нужно сделать так... завести программный счетчик который внутри регулярного прерывания всегда стремится обнулится. Когда поступила нормальная команда мы выполняем ее действие и одновременно взводим этот счетчик на время большее чем время между импульсами повторения. Затем если приходит импульс повтора, то проверяем состояние счетчика, если он 0 то игнорируем команду повтора, если он не 0 то выполняем последнюю принятую команду и снова взводим счетчик.
Как то так... если нужно, могу накидать примерчик...

Re: ИК протокол NEC

Вт фев 14, 2017 14:13:01

Было бы не плохо, если бы вы примерчик набросали!

Re: ИК протокол NEC

Пт дек 22, 2017 16:50:03

RIMUS1989i писал(а):shads, А как обработать длинное нажатие?

Как то так... если нужно, могу накидать примерчик...

Добрый вечер, было бы очень хорошо. МК только только пытаюсь освоить, только на уровне поморгать светодиодом :?

Re: ИК протокол NEC

Вт май 14, 2019 07:16:36

Ну вот, домучил, обработку IR NEC, вот что получилось:
Спойлер
Код:
//***************************************************************************************************************************
//БИБЛИОТЕКА ОБРАБОТКИ СИГНАЛА ИК ПУЛЬТА ДИСТАНЦИОННОГО УПРАВЛЕНИЯ СТАНДАРТА NEC
//для частоты контроллера 8000000 Hz
//при работе IR NEC задействован таймер\счетчик T0.
//выход IR сенсора подать на внешнее прерывание INT0
//программа создана SHADS

//НАСТРОЙКА ПОРТА И ВЫВОДА IR СЕНСОРА
#define IR_NEC_PORT    PORTD
#define IR_NEC_DDR    DDRD
#define IR_NEC_Line    (1<<2)


//***************************************************************************************************************************
//переменные и флаги библиотеки
char IrNecData [4];                //массив байтов для приема данных
char IrNecCounBites;            //счетчик принятых битов данных

volatile char IrNecFlags;        //байт флагов
#define bIrNecStreamEnb    (1<<0)    /*флаг наличия запущеннного процесса приема серии*/
#define bIrNecRcvCmpltd    (1<<1)    /*флаг завершения приема данных*/
#define bIrNecTmrOvf     (1<<2)    /*флаг фиксации переполнения счетчика\таймера*/


//***************************************************************************************************************************
//инициализация работы библиотеки
void IrNecInit (void)
{
    //настройка порта
    IR_NEC_DDR &= ~IR_NEC_Line;    //линия на ввод
    IR_NEC_PORT |= IR_NEC_Line;    //включить подтяжку

    //Настройка таймера/счетчика T0
    TCCR0 = 0x05;                 //настройка тактирования T0 (0x01-clk, 0x02-clk/8, 0x03-clk/64, 0x04-clk/256, 0x05-clk/1024)    
    TIMSK |= (1<<TOIE0);        //включить прерывание по переполнению T0

    //Настройка внешнего прерывания
    GICR  |= (1<<INT0);            //включить внешнее прерывание с вывода INT0
    MCUCR |= (1<<ISC01);         //генерация прерывания по спадающему фронту 
}


//***************************************************************************************************************************
//проверка готовности данных IR NEC
//ЗНАЧЕНИЕ - результат проверки на готовность принятых данных к чтению (0x00 - данные не готовы, 0xFF - данные готовы)
//ВАЖНО!!!!! если с помощью этой функции определено что имеются поступившие данные, то следом необходимо вызвать
//функцию чтения команды IrNecCommandGet (), (или сначала IrNecDevNumGet (), а потом IrNecCommandGet ()), 
//только после этого может быть принята следующая команда.
char IrNecDataCheck (void)
{
    if (IrNecFlags & bIrNecRcvCmpltd)
        return 0xff;
    return 0x00;
}


//***************************************************************************************************************************
//чтение данных адреса IR NEC
//ЗНАЧЕНИЕ - номер адресуемого устройства.
unsigned char IrNecDevNumGet (void)
{
    volatile char* pAddr = &IrNecData[0];            //адрес байта номера устройства в буфере данных
    return *pAddr;
}


//***************************************************************************************************************************
//чтение данных команды IR NEC
//ЗНАЧЕНИЕ - принятая команда.
//ВАЖНО!!!!! также тут происходит сброс флага наличия готовых данных bIrNecRcvCmpltd,
//если с помощью функции IrNecDataCheck() определено, что данные готовы,
//то пока не будет вызвана функция IrNecCommandGet (), новые данные не будут приниматся!
unsigned char IrNecCommandGet (void)
{
    volatile char* pCommand = &IrNecData[2];        //адрес байта команды в буфере данных
    IrNecFlags &= ~bIrNecRcvCmpltd;                    //сброс флага окончания приема данных
    return *pCommand;
}


//***************************************************************************************************************************
//Внешнее прерывание INT0 (по спаду сигнала)
ISR (INT0_vect)
{
    uint8_t val = TCNT0;                            //копируем и обнуляем счетчик таймера
    TCNT0 = 0;
    char flags = IrNecFlags;                        //копия флагов из волатильной переменной (для увеличения скорости работы )
    if (flags & bIrNecRcvCmpltd)                    //если предыдущие данные не обработаны - выход
        return;                                        //флаг сбрасывается функцией забирающей данные

    //проверка на переполнение
    if (flags & bIrNecTmrOvf){                        
        flags 
&= ~(bIrNecTmrOvf | bIrNecStreamEnb);    //сбросить флаги переполнения и приема (т.е. начать с начала)
        IrNecFlags = flags;
        return;
    }

    //проверка на заголовок пакета
    if ((val > 70) && (val < 140)){                    //105 - длительность периода импульса старта (берем шире 70 - 140)
        IrNecCounBites = 0;                            //обнулить счетчик принятых битов
        flags |= bIrNecStreamEnb;                    //установить флаг приема
        IrNecFlags = flags;                
        return
;
    }    

    
//если прием активен (т.е. заголовок пакета уже был)
    if (flags & bIrNecStreamEnb){                    
    
        
//проверка на правильность длины импульса
        if ((val < 6) || (val >= 24)){                //ошибочный размер периода
            flags &= ~bIrNecStreamEnb;                //сбросить флаг приема (т.е. начать с начала)
            IrNecFlags = flags;    
            return
;
        }

        //вычисляем место в буфере, куда поместим принятый бит
        char* pIrNecData = IrNecData + (IrNecCounBites >> 3);    
        char BitNum 
= 0x80 >> (IrNecCounBites & 0x07);
        if (val >= 12)                                                
            
*pIrNecData |= BitNum;                    //принятый бит = 1 (бит = 1 если значение 12-23)    
        else
            
*pIrNecData &= ~BitNum;                    //принятый бит = 0 (бит = 0 если значение 6-11)
            
        
//проверка, принята ли вся серия IR NEC
        if (++IrNecCounBites == 32){                //приняты все 32 бита    
            if ((char)~IrNecData[0] == IrNecData[1]){        //сравниваем прямую и инверсную части адреса устройства
                if ((char)~IrNecData[2] == IrNecData[3])    //сравниваем прямую и инверсную части команды

                    //данные верны - установить флаг наличия готовых данных    
                    flags |= bIrNecRcvCmpltd;        
            
}

            flags &= ~bIrNecStreamEnb;                //сбросить флаг приема (т.е. начать с начала)
            IrNecFlags = flags;    
        
}
    }
}


//***************************************************************************************************************************
//Прерывание по переполнению T0
ISR (TIMER0_OVF_vect)
{
    IrNecFlags |= bIrNecTmrOvf;                        //флаг переполнения
}


//***************************************************************************************************************************      


Пока что для одной частоты (8000000) и константы заданы жестко. Потом надо будет докумекать автоматический рассчет для разных частот.


Увжаемий "shads" помогите ваш код расчитать для частоти 16MHz и меги32

Re: ИК протокол NEC

Вт май 14, 2019 15:08:58

Давным-давно и многократно я выкладывал универсальный алгоритм (практически код готовый), который позволяет принимать почти ЛЮБЫЕ коды пультов, и NEC в том числе. В простейшем варианте вообще без прерываний и задействования таймеров и т.п.

Но всем интересно идти своим путём, плодя "библиотеки", которые даже внутри одного семейства МК перенести сложно, не говоря уже о межплатформерности...

Мыши плакали, кололись, но продолжали жрать кактус.

Re: ИК протокол NEC

Чт апр 08, 2021 22:25:00

Здравствуйте, я пока учусь, не ругайте если что не так. Пытаюсь разобраться с кодом из этой темы. Объясните, пожалуйста, как рассчитываются числа из этой строчки:

if ((val > 70) && (val < 140)){ //105 - длительность периода импульса старта (берем шире 70 - 140)

Почему 105 - это длительность периода импульса старта, откуда эта цифра?

Re: ИК протокол NEC

Пт апр 09, 2021 01:46:59

Ilya_2006, Начните с того, что расскажите нам, откуда Вы вообще всё это взяли ?
Додумываться за Вас не охота :roll:

Re: ИК протокол NEC

Пт апр 09, 2021 19:25:22

Ilya_2006, Начните с того, что расскажите нам, откуда Вы вообще всё это взяли ?
Додумываться за Вас не охота :roll:

Да, извините. Это строчка из кода, который выложил shads (спойлер), та часть, которая обозначена как //проверка на заголовок пакета
Спойлер//***************************************************************************************************************************
//БИБЛИОТЕКА ОБРАБОТКИ СИГНАЛА ИК ПУЛЬТА ДИСТАНЦИОННОГО УПРАВЛЕНИЯ СТАНДАРТА NEC
//для частоты контроллера 8000000 Hz
//при работе IR NEC задействован таймер\счетчик T0.
//выход IR сенсора подать на внешнее прерывание INT0
//программа создана SHADS

//НАСТРОЙКА ПОРТА И ВЫВОДА IR СЕНСОРА
#define IR_NEC_PORT PORTD
#define IR_NEC_DDR DDRD
#define IR_NEC_Line (1<<2)


//***************************************************************************************************************************
//переменные и флаги библиотеки
char IrNecData [4]; //массив байтов для приема данных
char IrNecCounBites; //счетчик принятых битов данных

volatile char IrNecFlags; //байт флагов
#define bIrNecStreamEnb (1<<0) /*флаг наличия запущеннного процесса приема серии*/
#define bIrNecRcvCmpltd (1<<1) /*флаг завершения приема данных*/
#define bIrNecTmrOvf (1<<2) /*флаг фиксации переполнения счетчика\таймера*/


//***************************************************************************************************************************
//инициализация работы библиотеки
void IrNecInit (void)
{
//настройка порта
IR_NEC_DDR &= ~IR_NEC_Line; //линия на ввод
IR_NEC_PORT |= IR_NEC_Line; //включить подтяжку

//Настройка таймера/счетчика T0
TCCR0 = 0x05; //настройка тактирования T0 (0x01-clk, 0x02-clk/8, 0x03-clk/64, 0x04-clk/256, 0x05-clk/1024)
TIMSK |= (1<<TOIE0); //включить прерывание по переполнению T0

//Настройка внешнего прерывания
GICR |= (1<<INT0); //включить внешнее прерывание с вывода INT0
MCUCR |= (1<<ISC01); //генерация прерывания по спадающему фронту
}


//***************************************************************************************************************************
//проверка готовности данных IR NEC
//ЗНАЧЕНИЕ - результат проверки на готовность принятых данных к чтению (0x00 - данные не готовы, 0xFF - данные готовы)
//ВАЖНО!!!!! если с помощью этой функции определено что имеются поступившие данные, то следом необходимо вызвать
//функцию чтения команды IrNecCommandGet (), (или сначала IrNecDevNumGet (), а потом IrNecCommandGet ()),
//только после этого может быть принята следующая команда.
char IrNecDataCheck (void)
{
if (IrNecFlags & bIrNecRcvCmpltd)
return 0xff;
return 0x00;
}


//***************************************************************************************************************************
//чтение данных адреса IR NEC
//ЗНАЧЕНИЕ - номер адресуемого устройства.
unsigned char IrNecDevNumGet (void)
{
volatile char* pAddr = &IrNecData[0]; //адрес байта номера устройства в буфере данных
return *pAddr;
}


//***************************************************************************************************************************
//чтение данных команды IR NEC
//ЗНАЧЕНИЕ - принятая команда.
//ВАЖНО!!!!! также тут происходит сброс флага наличия готовых данных bIrNecRcvCmpltd,
//если с помощью функции IrNecDataCheck() определено, что данные готовы,
//то пока не будет вызвана функция IrNecCommandGet (), новые данные не будут приниматся!
unsigned char IrNecCommandGet (void)
{
volatile char* pCommand = &IrNecData[2]; //адрес байта команды в буфере данных
IrNecFlags &= ~bIrNecRcvCmpltd; //сброс флага окончания приема данных
return *pCommand;
}


//***************************************************************************************************************************
//Внешнее прерывание INT0 (по спаду сигнала)
ISR (INT0_vect)
{
uint8_t val = TCNT0; //копируем и обнуляем счетчик таймера
TCNT0 = 0;
char flags = IrNecFlags; //копия флагов из волатильной переменной (для увеличения скорости работы )
if (flags & bIrNecRcvCmpltd) //если предыдущие данные не обработаны - выход
return; //флаг сбрасывается функцией забирающей данные

//проверка на переполнение
if (flags & bIrNecTmrOvf){
flags &= ~(bIrNecTmrOvf | bIrNecStreamEnb); //сбросить флаги переполнения и приема (т.е. начать с начала)
IrNecFlags = flags;
return;
}

//проверка на заголовок пакета
if ((val > 70) && (val < 140)){ //105 - длительность периода импульса старта (берем шире 70 - 140)
IrNecCounBites = 0; //обнулить счетчик принятых битов
flags |= bIrNecStreamEnb; //установить флаг приема
IrNecFlags = flags;
return;
}

//если прием активен (т.е. заголовок пакета уже был)
if (flags & bIrNecStreamEnb){

//проверка на правильность длины импульса
if ((val < 6) || (val >= 24)){ //ошибочный размер периода
flags &= ~bIrNecStreamEnb; //сбросить флаг приема (т.е. начать с начала)
IrNecFlags = flags;
return;
}

//вычисляем место в буфере, куда поместим принятый бит
char* pIrNecData = IrNecData + (IrNecCounBites >> 3);
char BitNum = 0x80 >> (IrNecCounBites & 0x07);
if (val >= 12)
*pIrNecData |= BitNum; //принятый бит = 1 (бит = 1 если значение 12-23)
else
*pIrNecData &= ~BitNum; //принятый бит = 0 (бит = 0 если значение 6-11)

//проверка, принята ли вся серия IR NEC
if (++IrNecCounBites == 32){ //приняты все 32 бита
if ((char)~IrNecData[0] == IrNecData[1]){ //сравниваем прямую и инверсную части адреса устройства
if ((char)~IrNecData[2] == IrNecData[3]) //сравниваем прямую и инверсную части команды

//данные верны - установить флаг наличия готовых данных
flags |= bIrNecRcvCmpltd;
}

flags &= ~bIrNecStreamEnb; //сбросить флаг приема (т.е. начать с начала)
IrNecFlags = flags;
}
}
}


//***************************************************************************************************************************
//Прерывание по переполнению T0
ISR (TIMER0_OVF_vect)
{
IrNecFlags |= bIrNecTmrOvf; //флаг переполнения
}


//***************************************************************************************************************************



Спойлер//проверка на заголовок пакета
if ((val > 70) && (val < 140)){ //105 - длительность периода импульса старта (берем шире 70 - 140)
IrNecCounBites = 0; //обнулить счетчик принятых битов
flags |= bIrNecStreamEnb; //установить флаг приема
IrNecFlags = flags;
return;
}

Re: ИК протокол NEC

Сб апр 10, 2021 23:45:35

Скорее всего, это какие-то "попугаи".

Добавлено after 2 minutes 2 seconds:
Ага, вот что написано в первом посте :
Пока что для одной частоты (8000000) и константы заданы жестко.

Re: ИК протокол NEC

Пт фев 10, 2023 12:36:59

Возился пару дней, всё работает четко. Стенд для испытания: Atmega8, 12МГц, TSOP1736, безымянный IR датчик от телевизора,самодельный IR датчик на lm358. Все датчики работают одинаково.
Снял логическим анализатором диаграммы кнопки включения трех разных пультов, от ТВ, от DVB T2 приставки и от спутникового приемника, все оказались NEC. В CVAVR написал прогу для приема NEC кодов и выброса по UART. Так же там посылает и кол-во "тиков" между спадами до нуля (Пересчитал для тактовой контроллера 12Мгц , получился один "тик" =0,0853mS, и смог синхронизировать стартовую посылку увидев её в терминале)
Скорость UART 57600бод, иначе на 9600 пропускает посылки. В приложении проект в CVAVR, в той же папке проект Proteus 7,10.Изображение
Вложения
irda_bc.zip
(103.19 KiB) Скачиваний: 46
Последний раз редактировалось polli123456 Пт фев 10, 2023 12:47:40, всего редактировалось 1 раз.

Re: ИК протокол NEC

Пт фев 10, 2023 12:45:08

А мне вот очень любопытно: 100500 лет назад я опубликовал алгоритм, принимающий коды (на глазок) 99% всех стандартов пультов... Алгоритм прост, как три рубля. Да, в моем описании он "синхронный", на "тупых задержках", но описание и простота алгоритма элементарно позволяют сделать его и асинхронным (хотя лично я тому причин не вижу).

Однако, еще ни разу не встретил никого, кто попробовал бы мой подход... Интересно, почему? Неужели кактус так вкусен?!

Re: ИК протокол NEC

Пт фев 10, 2023 13:11:07

Я его то же смотрел :) Возможно потому, что NEC самый распространенный формат пультов, и алгоритм на прерываниях "вылизан" дальше некуда. Ну и получается какая то психологическая "стандартизация" полученных задержек, не хочется оперировать понятными только себе цифрами полученными из вашего кода. Хотя, конечно, это чистая вкусовщина- "масло масленое"

Re: ИК протокол NEC

Пт фев 10, 2023 13:15:31

Ну, хоть какой-то аргумент... Хотя вылизанный алгоритм на прерываниях в attiny13 никак не влезет, например.

Re: ИК протокол NEC

Пт фев 10, 2023 16:26:58

о ! пультики))
NEC.jpg
(89.09 KiB) Скачиваний: 50

надо будет добавить в свою схему...
схема_1.jpg
(196.87 KiB) Скачиваний: 46

:tea:

Re: ИК протокол NEC

Пт фев 10, 2023 17:35:20

Хотя вылизанный алгоритм на прерываниях в attiny13 никак не влезет, например.

Ещё и место остаётся… :)
http://www.getchip.net/posts/076-upravl ... -attiny13/

Re: ИК протокол NEC

Пт фев 10, 2023 21:37:09

Что ж... Если упомянутый вариант и с любыми другими пультами так же успешно сработает, то он и вправду хорош. Не видя исходника, сделать более обоснованные выводы не могу.

Re: ИК протокол NEC

Пт фев 10, 2023 23:42:20

Я видел исходник… программа занимает 273 слова, свободно 239 слов (46%)… исходник на АВ, но Вы при желании можете прошивку дизассемблировать и получить исходник на классическом ассемблере… :)

За все пульты не скажу… :dont_know: но те которые у меня были, все работали уверенно, даже вот этот…
P1010416.JPG
(37.78 KiB) Скачиваний: 45
Ответить