Реле с дистанционным ИК управлением на ATtiny13a

от автора

Привет, Хабр!

Появилась необходимость выключения старой, но вполне рабочей акустической системы с пульта от телевизора, не вставая с дивана. Подумав, я решил использовать ИК приёмник, некогда выкрученный со старого телевизора. ИК приёмник оказался без опознавательных знаков. Определив выходы методом тыка выяснил, что он из серии TSOP4xxx, если верить картинке:



Погуглив и потренировавшись на Arduino UNO, используя этот код и удостоверившись в работоспособности датчика, я перешёл к переписыванию кода на ATtiny13. Перейдя к ней, понял, что очень сильно ограничен ресурсах, как во флеше, так и в оперативной памяти. По началу с трудом оптимизировал по размеру прошивки, контроллер все еще не работал, а когда понял, что памяти в коде используется намного больше 64 байт, пришлось конкретно взяться за оптимизацию. В итоге с горем пополам оптимизировал код и собрал прототип на макетке. Радовался, как ребенок! Оно мигало лампочками так, как мне надо.

Общий вид:

Макетка

Схема:

Спойлер

Пришло время переводить всю макетку в текстолит. Плату изготовлял методом ЛУТ. Первый блин, как говорится, комом. Сделав первую схему, напечатав и собрав, понял, что ничего не работает. Я неправильно подключил LM317T. К тому же, поломав пятаки и оторвав некоторые слишком тонкие дорожки, решил сделать вторую плату. В ней сделал дорожки 0,7мм и, увеличив пятаки, кое как справился с частью проблем. Тут тоже не обошлось без проблем, так как опять неправильно подключил LM317T, да еще и в прошлой версии платы сжег приёмник, подав на него 12В.

Кстати, питается это дело от 12В (был у меня маломощный трансформатор, вот его и задействовал). Выбор напряжения был обусловлен так же имеющимся в наличии реле на 12В. Для понижения напряжения для микроконтроллера до 5В используется стабилизатор LM317T, а для управления реле используется имеющийся под рукой npn транзистор КТ819.

Окончательная плата в SprintLayout:

Спойлер

Используемые детали:

  • Микроконтроллер ATtiny13A;
  • Резисторы номиналом 470, 1300, 2×330 и 90 Ом;
  • Транзистор КТ819;
  • Стабилизатор LM317T;
  • 2 светодиода красный и зеленый;
  • Приёмник серии TSOP4XXX или совместимый;
  • 2 конденсатора для фильтрации по питанию примерно на 200-220мкФ.

Что касается кода.

Исходный вариант был очень «увесистый» и на ATtiny13 ни как не мог работать. Необходимо было избавиться от тяжелого двумерного массива. Код был очень странным: записывались так же «низкие» импульсы, но они никак не использовались. В общем, выкинул двумерный массив и этим освободил как минимум 64 байта оперативной памяти. Высчитывал сигналы на лету, но этого было мало и после добавления функционала таймера пришлось максимально урезать переменные.

Код для Arduino IDE

#define IRpin_PIN PINB #define IRpin 2  #define rLedPin 3 #define gLedPin 4 #define relayPin 1  #define MAXPULSE 5000 #define NUMPULSES 32 #define RESOLUTION 2  #define timeN1 1800000 #define timeN2 3600000  #define timerInterval 500   bool relayState = false; unsigned long timer = 0; unsigned long shift = timeN1;//30 min timer by default unsigned long previousMillis = 0; bool timerN = false; byte i = 0;   void setup() {   //default states   DDRB |= (1<<relayPin);   DDRB |= (1<<rLedPin);   DDRB |= (1<<gLedPin);    PORTB &= ~(1<<relayPin);//relay off   PORTB &= ~(1<<rLedPin);//red led off   PORTB |=  (1<<gLedPin);//green led on    /*   //for debug   Serial.begin(9600);   Serial.println("Start | "+String(millis()));   //*/    /*   //for debug without ir receiver   pinMode(5, INPUT);   pinMode(6, INPUT);   //*/   }  void shutDown(){   relayState = true;   PORTB |=  (1<<relayPin);   PORTB &= ~(1<<gLedPin);   PORTB |=  (1<<rLedPin);   //Serial.println("turining off |"+String(millis())); }  void startUp(){   relayState = false;   PORTB &= ~(1<<relayPin);   PORTB |=  (1<<gLedPin);   PORTB &= ~(1<<rLedPin);   //Serial.println("turining on |"+String(millis())); }  void loop() {   unsigned long irCode = listenForIR(); // Wait for an IR Code   //Serial.println("ir code: "+String(irCode));   if(irCode == 3359105948){//green button     //Serial.println("Pressed green btn |"+String(millis()));     if(timer == 0){//on off mode       if(relayState == true){         startUp();       }else{         shutDown();       }     }else{//cancel timer mode       timer = 0;       PORTB &= ~(1<<rLedPin);//turn off red led       //Serial.println("timer canceled |"+String(millis()));     }   }//end green btn     if(3359101868 == irCode){//red btn     //Serial.println("pressed red btn |"+String(millis()));     if(timer == 0){       if(relayState == 0){         timer = millis();         //Serial.println("timer started |"+String(millis()));       }/*else{         Serial.println("already shutdown |"+String(millis()));       }       //*/      }else{//changing time mode       timerN = !timerN;       if(timerN){         //Serial.println("change 30sec |"+String(millis()));         shift = timeN1;//30 min       }else{         //Serial.println("change 60sec |"+String(millis()));         shift = timeN2;//60 min       }     }   }//end red btn } // loop end  void checkTimer(){   unsigned long time = millis();   if(time - previousMillis >= timerInterval || previousMillis > time ) {     previousMillis =time;     timer1();   } }  unsigned long  listenForIR() {// IR receive code   byte currentpulse = 0; // index for pulses we're storing   unsigned long irCode = 0; // Wait for an IR Code   irCode = irCode << 1;    while (true) {     unsigned int pulse = 0;// temporary storage timing     //bool true (HIGH)     while (IRpin_PIN & _BV(IRpin)) { // got a high pulse (99% standby time have HIGH)       if(++i > 150){//check timer every 150 iterations (high frequency break ir code timing)         i = 0;         checkTimer();       }       pulse++;       delayMicroseconds(RESOLUTION);       if (((pulse >= MAXPULSE) && (currentpulse != 0)) || currentpulse == NUMPULSES ) {         return irCode;       }     }  	//make irCode     irCode = irCode << 1;     if ((pulse * RESOLUTION) > 0 && (pulse * RESOLUTION) < 500) {       irCode |= 0;     }else {       irCode |= 1;     }     currentpulse++; 	pulse = 0;         //bool false (LOW)     while (!(IRpin_PIN & _BV(IRpin))) {//wait before new pulse       //checkTimer();       pulse++;       delayMicroseconds(RESOLUTION);       if (pulse >= MAXPULSE || currentpulse == NUMPULSES ) {         //Serial.println(irCode);         return irCode;       }     }   }//end while(1)    }//end listenForIR    //executing every timerInverval void timer1() {   if(timer != 0){     if(timerN == true){//timeN1 or timeN2       PORTB |= (1<<rLedPin);     }else{//blinking 30min	       PORTB ^= (1<<rLedPin);//invert     }     //Serial.println(String((timer+shift - millis())/1000));   }    if(timer != 0 &&(timer+shift < millis() || timer > millis())){     timer = 0;     shutDown();   } }  

Видеодемонстрация:

Прошивал ATtiny13 я с помощью Arduino UNO, используя его как программатор, руководствуясь публикацией «Прошивка и программирование ATtiny13 при помощи Arduino». Для прошивки использовал 9,6МГц конфигурацию.

Фотографии почти не делал, но что есть, то есть:

Фото

Из за другой распиновки запасного TSOP на второй версии, пришлось его перенести на проводки и закрепить клеем (позже просто прикрепил к корпусу).

Вторая плата сбоку:

Вторая плата сверху (ик датчик перенес):

Вторая плата снизу:

Конечное устройство:

Исходники и прошивка на яндекс диске.

Используемые материалы

myrobot.ru/wiki/index.php?n=Components.TSOP Всё об ИК-приёмнике «TSOP»
www.atmel.com/images/doc2535.pdf Даташит по ATtiny13
habrahabr.ru/post/234477/ Инструкция по прошивке ATtiny13
payalo.at.ua/c_fuse/calc.html?part=ATtiny13A калькулятор фьюзов для ATtiny13
github.com/nathanchantrell/TinyPCRemote/tree/master/TinyPCRemote_CodeReader, код читалки кодов пульта который я взял за основу.

ссылка на оригинал статьи http://habrahabr.ru/post/244349/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *