решил реализовать передачу данных на таймере, так сказать почти на аппаратном уровне, что бы не завешивать МК на 70ms при передачи одной команды. Реализация на атмеге8 16МГц (вполне можно и на меньшей частоте, для моего проекта нужно было именно 16) на 8-bit Timer/Counter2 в режиме CTC. Спойлер
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)
Во время длинного нажатия пульты NEC передают какой то простой (уже точно не помню) периодический импульс, это означает что нужно повторять действие последней принятой команды... Т.е. нужно сделать так... завести программный счетчик который внутри регулярного прерывания всегда стремится обнулится. Когда поступила нормальная команда мы выполняем ее действие и одновременно взводим этот счетчик на время большее чем время между импульсами повторения. Затем если приходит импульс повтора, то проверяем состояние счетчика, если он 0 то игнорируем команду повтора, если он не 0 то выполняем последнюю принятую команду и снова взводим счетчик. Как то так... если нужно, могу накидать примерчик...
Качественное и безопасное устройство, работающее от аккумулятора, должно учитывать его физические и химические свойства, профили заряда и разряда, их изменение во времени и под влиянием различных условий, таких как температура и ток нагрузки. Мы расскажем о литий-ионных аккумуляторных батареях EVE и нескольких решениях от различных китайских компаний, рекомендуемых для разработок приложений с использованием этих АКБ. Представленные в статье китайские аналоги помогут заменить продукцию западных брендов с оптимизацией цены без потери качества.
Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре.
Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.
Ну вот, домучил, обработку 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;//включить подтяжку
//Настройка внешнего прерывания 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; } } }
Давным-давно и многократно я выкладывал универсальный алгоритм (практически код готовый), который позволяет принимать почти ЛЮБЫЕ коды пультов, и NEC в том числе. В простейшем варианте вообще без прерываний и задействования таймеров и т.п.
Но всем интересно идти своим путём, плодя "библиотеки", которые даже внутри одного семейства МК перенести сложно, не говоря уже о межплатформерности...
Мыши плакали, кололись, но продолжали жрать кактус.
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
Здравствуйте, я пока учусь, не ругайте если что не так. Пытаюсь разобраться с кодом из этой темы. Объясните, пожалуйста, как рассчитываются числа из этой строчки:
if ((val > 70) && (val < 140)){ //105 - длительность периода импульса старта (берем шире 70 - 140)
Почему 105 - это длительность периода импульса старта, откуда эта цифра?
Ilya_2006, Начните с того, что расскажите нам, откуда Вы вообще всё это взяли ? Додумываться за Вас не охота
Да, извините. Это строчка из кода, который выложил 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; //включить подтяжку
//Настройка внешнего прерывания 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; } } }
Возился пару дней, всё работает четко. Стенд для испытания: Atmega8, 12МГц, TSOP1736, безымянный IR датчик от телевизора,самодельный IR датчик на lm358. Все датчики работают одинаково. Снял логическим анализатором диаграммы кнопки включения трех разных пультов, от ТВ, от DVB T2 приставки и от спутникового приемника, все оказались NEC. В CVAVR написал прогу для приема NEC кодов и выброса по UART. Так же там посылает и кол-во "тиков" между спадами до нуля (Пересчитал для тактовой контроллера 12Мгц , получился один "тик" =0,0853mS, и смог синхронизировать стартовую посылку увидев её в терминале) Скорость UART 57600бод, иначе на 9600 пропускает посылки. В приложении проект в CVAVR, в той же папке проект Proteus 7,10.
А мне вот очень любопытно: 100500 лет назад я опубликовал алгоритм, принимающий коды (на глазок) 99% всех стандартов пультов... Алгоритм прост, как три рубля. Да, в моем описании он "синхронный", на "тупых задержках", но описание и простота алгоритма элементарно позволяют сделать его и асинхронным (хотя лично я тому причин не вижу).
Однако, еще ни разу не встретил никого, кто попробовал бы мой подход... Интересно, почему? Неужели кактус так вкусен?!
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
Я его то же смотрел Возможно потому, что NEC самый распространенный формат пультов, и алгоритм на прерываниях "вылизан" дальше некуда. Ну и получается какая то психологическая "стандартизация" полученных задержек, не хочется оперировать понятными только себе цифрами полученными из вашего кода. Хотя, конечно, это чистая вкусовщина- "масло масленое"
Что ж... Если упомянутый вариант и с любыми другими пультами так же успешно сработает, то он и вправду хорош. Не видя исходника, сделать более обоснованные выводы не могу.
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
Я видел исходник… программа занимает 273 слова, свободно 239 слов (46%)… исходник на АВ, но Вы при желании можете прошивку дизассемблировать и получить исходник на классическом ассемблере…
За все пульты не скажу… но те которые у меня были, все работали уверенно, даже вот этот…
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 9
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения