http://habrahabr.ru/post/244349/
Привет, Хабр!
Появилась необходимость выключения старой, но вполне рабочей акустической системы с пульта от телевизора, не вставая с дивана. Подумав, я решил использовать ИК приёмник, некогда выкрученный со старого телевизора. ИК приёмник оказался без опознавательных знаков. Определив выходы методом тыка выяснил, что он из серии TSOP4xxx, если верить картинке:
Погуглив и потренировавшись на Arduino UNO, используя
этот код и удостоверившись в работоспособности датчика, я перешёл к переписыванию кода на ATtiny13. Перейдя к ней, понял, что очень сильно ограничен ресурсах, как во флеше, так и в оперативной памяти. По началу с трудом оптимизировал по размеру прошивки, контроллер все еще не работал, а когда понял, что памяти в коде используется намного больше 64 байт, пришлось конкретно взяться за оптимизацию. В итоге с горем пополам оптимизировал код и собрал прототип на макетке. Радовался, как ребенок! Оно мигало лампочками так, как мне надо.
Общий вид:
Схема:
Пришло время переводить всю макетку в текстолит. Плату изготовлял методом ЛУТ. Первый блин, как говорится, комом. Сделав первую схему, напечатав и собрав, понял, что ничего не работает. Я неправильно подключил LM317T. К тому же, поломав пятаки и оторвав некоторые слишком тонкие дорожки, решил сделать вторую плату. В ней сделал дорожки 0,7мм и, увеличив пятаки, кое как справился с частью проблем. Тут тоже не обошлось без проблем, так как опять неправильно подключил LM317T, да еще и в прошлой версии платы сжег приёмник, подав на него 12В.
Кстати, питается это дело от 12В (был у меня маломощный трансформатор, вот его и задействовал). Выбор напряжения был обусловлен так же имеющимся в наличии реле на 12В. Для понижения напряжения для микроконтроллера до 5В используется стабилизатор LM317T, а для управления реле используется имеющийся под рукой npn транзистор КТ819.
Окончательная плата в SprintLayout:
Используемые детали:
- Микроконтроллер ATtiny13A;
- Резисторы номиналом 470, 1300, 2x330 и 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, код читалки кодов пульта который я взял за основу.