Появилась необходимость выключения старой, но вполне рабочей акустической системы с пульта от телевизора, не вставая с дивана. Подумав, я решил использовать ИК приёмник, некогда выкрученный со старого телевизора. ИК приёмник оказался без опознавательных знаков. Определив выходы методом тыка выяснил, что он из серии 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 байта оперативной памяти. Высчитывал сигналы на лету, но этого было мало и после добавления функционала таймера пришлось максимально урезать переменные.
#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МГц конфигурацию.
Фотографии почти не делал, но что есть, то есть:
Вторая плата сбоку:
Вторая плата сверху (ик датчик перенес):
Вторая плата снизу:
Конечное устройство:
Исходники и прошивка на яндекс диске.
Используемые материалы
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/
Добавить комментарий