Chat
Danh mục
Mạch điều khiển quạt từ xa - Hẹn giờ

Mạch điều khiển quạt từ xa - Hẹn giờ

Số lượng:
Thêm vào giỏ
Mạch điều khiển quạt từ xa - Hẹn giờ đã được thêm vào giỏ hàng

Chia sẻ mạch điều khiển quạt bàn dùng Remote TV Sony, mạch dùng IC 89C2051 để lập trình nhận tín hiệu từ Remote TV. Bao gồm các chức năng cơ bản:
- On/Off
- Hẹn giờ (30', 1h, 2h, 4h)
- Đổi tốc độ (Số 1, số 2, số 3)
- Gió liu riu (Nature Wind)

1. Hình ảnh mạch thật:


2. Tìm hiểu cơ chế hoạt động Remote TV

Điều khiển từ xa phát ra các bit 0 va 1. Tuy nhiên nó không thể đưa lên LED hồng ngoại và phát trực tiếp các bit này được (không phát đi xa được). Vì vậy nó cần phải có 1 sóng mang với tần số khoảng 36KHz (giống như trong Radio ấy).


Để làm được bộ phát như trên có 2 cách:
- Tạo ra 1 tần số 36 KHz ổn định làm sóng mang (Dùng NE555) 
(36KHz) AND (Bit 0,1) = Output_Signal 
- Cách 2 đơn giản hơn. Ra chợ mua 1 cái đk về dùng

Bộ thu là 1 con IC 3 chân(VCC--GND--OUTPUT). Nó có nhiệm vụ nhận tín hiệu hồng ngoại yếu ớt từ đk phát ra, khuyêch đại tín hiệu, tách sóng mang để lọc ra những bít 0 và 1.


Mỗi 1 lần phát, đk sẽ phát đi 1 chuỗi các bít 0 và 1. IC nhận cũng sẽ nhận đúng số lượng và đúng thứ tự các bit này.


Sau khi nhân được chuỗi các bit, nhiệm vụ của người lập trình là giải mã chuỗi bit này.
Mỗi loại đktx sẽ tuân theo 1 chuẩn mã khác nhau (RC5 là 1 chuẩn phổ biến). 
Trong chuỗi bit đó người ta sẽ phân ra làm 2 phần:
- phần 1 là địa chỉ để phân biệt các thiết bị khác nhau(cùng là hãng SONY nhưng đktx của TV không làm ảnh hưởng đến đầu DVD chẳng hạn)
- phần 2 là phần lệnh (lúc này sẽ chỉ rõ lệnh phát ra là gì khi ta ấn nút trên đk)


Cuối cùng dựa vào lệnh đã nhận. Ta chỉ việc sử dụng thôi. Ví dụ ấn nút số 1 trên đktx thì sẽ nhận được số 1, ta bật cái quạt. Ấn số 2 ta nhận đc số 2, ta bật cái bóng điện v.v..


Đây là chuẩn SIRC mà đktx SONY sử dụng (ở đây mình sử dụng bộ mã 12 bits)



3. Tìm hiểu sơ đồ nguyên lý:

Sau khi đã hiểu qua về mã truyền và nhận. Ta đi xây dựng phần cứng.

Phần cứng gồm các nút bấm, các đèn LED báo, cách ly quang MOC3020 để điều khiển triac MAC97. Các triac này để đóng/mở việc cấp nguồn cho cuộn dây của quạt (quạt có 3 số --> 3 cuộn dây --> 3 triac)




NGUYÊN LÝ :
- Protel:

- Altium Designer:
Khi nhấn nút "Tải Về Máy", bạn sẽ vào trang quảng cáo, vui lòng chờ 5 giây. Nút  hiện ra ở góc phải phía trên, nhấn vào để đến trang download

Sau khi tham khảo trên mạng. tôi đã tìm được cách tối ưu mạch điều khiển từ xa sử dụng điều khiển TV. Do sử dụng trực tiếp nguồn 5V được hạ áp từ 220VAC nên bỏ qua được biến áp và opto-triac. 
Đây là 1 mạch đk đa năng, mỗi mạch sẽ điều khiển 1 thiết bị điện trong nhà. Tùy thuộc vào công suất của thiết bị cần điều khiển mà ta sẽ chọn loại triac có công suất phù hợp. Hơn nữa mạch rất nhỏ gọn nên có thể giấu trong các thiết bị (ví dụ điều khiển đèn neon thì giấu trong máng đèn, chỉ lộ con IR receiver ra ngoài)
Đây là nguyên lý của mạch (CHÚ Ý, MẠCH NÀY DO KHÔNG CÓ BIẾN ÁP CÁCH LY NÊN ĐỤNG VÀO PHẦN MẠCH KHI CÓ ĐIỆN LÀ BỊ GIẬT)
Mạch in chỉ bé bằng 2 đầu ngón tay 




4. Lập trình:

Công việc đầu tiên của lập trình là làm sao để thu được 12 bit mà đktx đã phát đi. Chân OUTPUT của con IR receiver nối với chân ngắt ngoài INT0 của 89C2051. 
Khi có ngăt ngoài xảy ra (tức là đã có tín hiệu phát ra từ bộ đktx) chương trình sẽ dừng các việc đang làm và nhảy vào trình phục vụ ngắt. 
Chương trình ngắt sẽ làm gì?
- Việc đầu tiên khi nhảy vào ngắt là chờ cho bit START về 0
- Đọc bít ngay sau đó
- Lưu vào vị trí bit đầu tiên của 2 byte (2 byte nằm trong 1 biến: int receiver;) 
- Dịch bít này sang trái 1 lần
- Đọc bít tiếp theo......
- Nhận đủ 12 bit
- Lọc ra 7 bit đầu để lấy ra lệnh (tức là đktx vừa bấm nút nào?)
- Tra trong bảng lệnh để thực hiện 1 công việc nào đó

Đây là đoạn chương trình ngắt INT0
Khi truyền, đktx sẽ phát ra các bit có frame timing như sau
- bit START gồm 4T mức "1", mỗi T là 600uS
- bit 0 gồm 2T: 1T mức "0" và 1T mức "1"
- bit 1 gồm 3T: 1T mức "0" và 2T mức "1"

Khi nhận, các bit sẽ bị đảo dấu. có frame timing như sau
- bit START gồm 4T mức "0", mỗi T là 600uS
- bit 0 gồm 2T: 1T mức "1" và 1T mức "0"
- bit 1 gồm 3T: 1T mức "1" và 2T mức "0"

Dựa vào frame timing nhận, ta có thể tiến hành lấy mẫu chân P3_2 để xác định được đâu là bit 0 và đâu là bit 1 (từ đây ta chỉ quan tâm đến frame timing nhận thôi)

Chú ý rằng dù bit "0" hay bit "1" thì đều có 1T ở đầu là mức "1". ta tạm gọi 1T đặc biệt này là "X" 

tick2=0; while(tick2<250 data-blogger-escaped-100us="" data-blogger-escaped-1="" data-blogger-escaped-g="" data-blogger-escaped-h="" data-blogger-escaped-i="" data-blogger-escaped-khi="" data-blogger-escaped-l="" data-blogger-escaped-m="" data-blogger-escaped-n="" data-blogger-escaped-ng="" data-blogger-escaped-nh="" data-blogger-escaped-o="" data-blogger-escaped-p="" data-blogger-escaped-ra="" data-blogger-escaped-t="" data-blogger-escaped-th="" data-blogger-escaped-tick1="" data-blogger-escaped-tick2="" data-blogger-escaped-timer1="" data-blogger-escaped-tr="" data-blogger-escaped-us="" data-blogger-escaped-v="" data-blogger-escaped-xem="" data-blogger-escaped-y="">


------------------------------------------------------------------------------------
void external_ISR (void) interrupt 0
{
tick2=0; //bắt đầu tính trễ
rev_IR = 0x01; // rev_IR = 000 0001 giá trị khởi tạo ban đầu cho rev_IR


bit_1:
if(rev_IR >= 128) {save_rev = rev_IR; goto end_rev;} // Nếu đã nhận đủ 7 bit thì thoát
while( (P3_2 == 0)&&(tick2<250 data-blogger-escaped-p="">
// Chờ cho hết bit START (P3_2 về "1"). Nếu vì lý do nào đó, P3_2 không về "1" thì sau (100*250)uS sẽ thoát, nếu không chương trình sẽ bị treo ở đây.


bit_0:
if(rev_IR >= 128) {save_rev = rev_IR; goto end_rev;} // Nếu đã nhận đủ 7 bit thì thoát
while( (P3_2 == 1)&&(tick2<250 data-blogger-escaped-p="">
// Chờ cho "X" về "0" và bắt đầu lấy mẫu ở chân P3_2 (đọc bit) 


tick1=0;
while(tick1<7 data-blogger-escaped-700us="" data-blogger-escaped-o="" data-blogger-escaped-p="" data-blogger-escaped-t="" data-blogger-escaped-tr="">
if(P3_2 == 0) {rev_IR <<= 1; rev_IR |= 0x01; goto bit_1;} // bit_x = 1

//Nếu chân P3_2 = 0 tức là nhận được bit "1". Nếu nhận được bit 1, ta phải chờ cho P3_2 về "1" rồi chờ tiếp cho P3_2 về "0" rồi mới đọc bit tiếp theo, chính vì thế mới "goto bit_1" (xem ảnh dưới)


if(P3_2 == 1) {rev_IR <<= 1; rev_IR |= 0x00; goto bit_0;} // bit_x = 0

//Nếu chân P3_2 = 1 tức là nhận được bit "0". Nếu nhận được bit 0, ta chỉ phải chờ cho P3_2 về "0" rồi mới đọc bit tiếp theo, chính vì thế mới "goto bit_0" (xem ảnh dưới)


 

/* 
| MSB LSB |
--------------------------------------------------
rev_IR = 0 0 0 0 0 0 0 1 
rev_IR = 0 0 0 0 0 0 1 bit1
rev_IR = 0 0 0 0 0 1 bit1 bit2
rev_IR = 0 0 0 0 1 bit1 bit2 bit3
rev_IR = 0 0 0 1 bit1 bit2 bit3 bit4
rev_IR = 0 0 1 bit1 bit2 bit3 bit4 bit5
rev_IR = 0 1 bit1 bit2 bit3 bit4 bit5 bit6
rev_IR = 1 bit1 bit2 bit3 bit4 bit5 bit6 bit7 (rev_IR >= 128 ; stop INT0 va thoat)
*/

end_rev:
EX0=0; //chi lay 7 bit command, bo qua 5 bit address
}



CODE MÃ NGUỒN:

//*****************************
// for Sony remote only

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

#include <AT89x051.H>
//#include <intrins.H>

#define pow P3_1 // in
#define speed P3_0 // in
#define timer P3_3 // in
#define nature P3_4 // in

#define power_IR 212 // IR receiver
#define speed_IR 142 // IR receiver
#define timer_IR 210 // IR receiver
#define nature_IR 244 // IR receiver

#define speed3 P1_0 //out triac
#define speed2 P1_1 //out triac
#define speed1 P1_2 //out triac

#define timer1 P1_4 //timer out led
#define timer2 P1_5 //timer out led
#define timer3 P1_6 //timer out led
#define timer4 P1_7 //timer out led

#define alarm P1_3 //loa chip out

unsigned char tocdo,hengio,kt_hengio,power;
unsigned char tick,wind,anti_vib,enable_IR;
unsigned int tick1,tick2,current_time,wind_time,time_off;
unsigned char rev_IR,save_rev;



void chip() //tao am thanh 2KHz
{
unsigned int i,m;
for(m=0;m<250;m++)
{
alarm=0;
for(i=0;i<20;i++)
{;}
alarm=1;
for(i=0;i<20;i++)
{;}
}
}



void timer1_ISR (void) interrupt 3
{
tick1++; tick2++; //100 us
}



void external_ISR (void) interrupt 0
{
tick2=0;
rev_IR = 0x01;
bit_1:
if(rev_IR >= 128) {save_rev = rev_IR; goto end_rev;}
while((P3_2 == 0)&&(tick2<250)) {;}
bit_0:
if(rev_IR >= 128) {save_rev = rev_IR; goto end_rev;}
while((P3_2 == 1)&&(tick2<250)) {;}
tick1=0;
while(tick1<7) {;}
if(P3_2 == 0) {rev_IR <<= 1; rev_IR |= 0x01; goto bit_1;}
if(P3_2 == 1) {rev_IR <<= 1; rev_IR |= 0x00; goto bit_0;}
end_rev:
//tick1=0;
//while(tick1<70) {;}
//_nop_();
EX0=0; //chi lay 7 bit lenh, bo qua 5 bit dia chi
}


void timer0_ISR (void) interrupt 1
{
// with xtal 12.000MHz
// 3CAF(15535) - 50ms
// D8EF(55535) - 10ms

// with xtal 11.0592 MHz
// DC05 - 10ms
// FC17(64535) - 1ms
// FE0B(65035) - 0.5ms
// FF37(65335) - 0.2ms

TH0 = 0xDC;
TL0 = 0x05;
tick++;
anti_vib++;
enable_IR++;
if (tick >= 100) // 1 sec
{
current_time++;
wind_time++;
tick = 0;
}
}


void main (void)
{//-------------------------

// init system
TMOD = 0x21; // (TMOD: 0010 0001) Timer_1 8 bits auto reload. Timer_0 16 bits
TH1 = 255-100;
TL1 = TH1;
ET0 = 1; // Enable Timer 0 Interrupt
ET1 = 1; // Enable Timer 1 Interrupt
// EX0 = 1; // Enable External 0 Interrupt
EA = 1; // Global Interrupt Enable
PT1 = 1; // Priority timer_1
TR0 = 1; // Start Timer 0
TR1 = 1; // Start timer 1
P1 = 0x00; // set P1 lam dau ra
P3 = 0xff; // set P3 lam dau vao

save_rev=0xff;
//enable_IR=0;

// main loop
while(1)
{//*
kt_hengio=0;
hengio=0;
wind=0;
power=0;
tocdo=1;
speed1=speed2=speed3=1;
timer1=timer2=timer3=timer4=1;
alarm=0;
EX0 = 1; // Enable External 0 Interrupt

//********** ENABLE IR INTERRUPT AGAIN *************
// while(enable_IR > 100)
// { EX0=1; enable_IR=0; }
//**************************************************



//**************** POWER ON ************************
if((!pow) || (save_rev==power_IR)) // nut on/off duoc an lan dau
{
chip();
speed1=0; // speed1 is defauled
power++;
while(!pow) {;}
anti_vib=0;
while(anti_vib<20)
{;}
save_rev=0xff;
EX0=1;
}
//*************************************************



//**************************************************
//**************************************************
while(power)
{//**



//******************* POWER OFF ************************
if((!pow) || (save_rev==power_IR)) // nut on/off duoc an lan tiep theo
{
chip();
chip();
chip();
power--;
while(!pow) {;} //anti vibration contact
anti_vib=0;
while(anti_vib<20)
{;}
save_rev=0xff;
EX0=1;

}
//**************************************************



//******************** SPEED ************************
if((!speed) || (save_rev==speed_IR)) // button speed
{///***
chip();
tocdo++;
if(tocdo > 3)
{ tocdo=1;}
dungimi:switch(tocdo)
{////
case 1: speed1=0; speed2=1; speed3=1;
break;
case 2: speed1=1; speed2=0; speed3=1;
break;
case 3: speed1=1; speed2=1; speed3=0;
break;
}////
while(!speed) {;}
anti_vib=0;
while(anti_vib<20)
{;}
save_rev=0xff;
EX0=1;

}///***
//**************************************************



//********** ENABLE IR INTERRUPT AGAIN *************
while(enable_IR > 100)
{ EX0=1; enable_IR=0; }
//**************************************************




//****************** CHECK TIMER *******************
if(kt_hengio == 1)
{
if(current_time > time_off)
{ chip(); chip(); chip(); power--; goto off; }
}
//**************************************************




//******************* TIMER ************************
if((!timer) || (save_rev==timer_IR)) //timer function
{
chip();
kt_hengio=1;
current_time=0;
hengio++;
if(hengio > 4)
{ hengio=0; }

switch(hengio)
{
case 0: kt_hengio=0;
timer1=1; timer2=1; timer3=1; timer4=1; break;
case 1: time_off=1800; // 30 minutes
timer1=0; timer2=1; timer3=1; timer4=1; break;
case 2: time_off=3600; // 1 hour
timer1=1; timer2=0; timer3=1; timer4=1; break;
case 3: time_off=7200; // 2 hours
timer1=1; timer2=1; timer3=0; timer4=1; break;
case 4: time_off=14400; // 4 hours
timer1=1; timer2=1; timer3=1; timer4=0; break;
}

while(!timer) {;}
anti_vib=0;
while(anti_vib<20)
{;}
save_rev=0xff;
EX0=1;

}
//************************************************** **




//******************** NATURE ************************
if((!nature) || (save_rev==nature_IR)) // nature wind function
{
chip();
wind++;
if(wind>1)
{
wind=0;
goto dungimi; // return speed after turn-off nature wind function
}
while(!nature) {;}
anti_vib=0;
while(anti_vib<20)
{;}
save_rev=0xff;
EX0=1;

}
//**************************************************




//**************************************************
if(wind) // if nature wind function is turned-on
{
switch(tocdo)
{
case 1: if(wind_time>5)
{speed1 =~ speed1; wind_time=0;}
break;
case 2: if(wind_time>10)
{ speed2 =1;
anti_vib=0;
while(anti_vib<5)
{;}
speed1=0;
wind_time=0;}
if(wind_time>7)
{ anti_vib=0;
while(anti_vib<5)
{;}
speed2=0;}
if(wind_time>3)
{ anti_vib=0;
while(anti_vib<5)
{;}
speed1=1;}
break;

case 3: if( wind_time>10)
{ speed3 =1;
speed1 =1;
anti_vib=0;
while(anti_vib<5)
{;}
speed2 =0; //chuyen sang so 2
wind_time=0;

}
if(wind_time>7)
{ speed3 =1;
speed2 =1;
anti_vib=0;
while(anti_vib<5)
{;}
speed1=0; //chuyen sang so 1
goto exit;
}
if(wind_time>3)
{ speed1 =1;
speed2 =1;
anti_vib=0;
while(anti_vib<5)
{;}
speed3 =0; //chuyen sang so 3
}
exit: break;

}
}
//**************************************************


}//**

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


off:
alarm=0;

}//* end while()

}//------------------end


Đây là mạch dùng điện 220VAC (không cần biến áp). Có switch gạt để đặt địa chỉ cho thiết bị



Giải thích thêm về đoạn chương trình ngắt để giải mã code

Trước hết xem lại phần frame timing



Khi truyền, đktx sẽ phát ra các bit có frame timing như sau
- bit START gồm 4T mức "1", mỗi T là 600uS
- bit 0 gồm 2T: 1T mức "0" và 1T mức "1"
- bit 1 gồm 3T: 1T mức "0" và 2T mức "1"

Khi nhận, mắt nhận hồng ngoại sẽ đảo dấu các bit. Nguyên nhân của việc phải đảu dấu là vì: chân OUT của mắt nhận nối với chân ngắt ngoài INT0 của vđk. Do vậy, khi bit START được phát ra (mức 1) thì vđk sẽ nhận được mức 0 (mức 0 là mức tác động của ngắt ngoài) báo cho vđk biết để bắt đầu quá trình giải mã. Frame timing như sau


- bit START gồm 4T mức "0", mỗi T là 600uS
- bit 0 gồm 2T: 1T mức "1" và 1T mức "0"
- bit 1 gồm 3T: 1T mức "1" và 2T mức "0"

Dựa vào frame timing nhận, ta có thể tiến hành lấy mẫu chân P3_2 để xác định được đâu là bit 0 và đâu là bit 1 (từ đây ta chỉ quan tâm đến frame timing nhận thôi)

Chú ý rằng dù bit "0" hay bit "1" thì đều có 1T ở đầu là mức "1". ta tạm gọi 1T đặc biệt này là "X"

tick2=0; while(tick2<250) {;} // là tạo ra 1 trễ, mỗi khi hết 100uS thì tick1 và tick2 tăng lên 1 (xem đoạn gắt timer1). Như vậy đoạn này tạo ra (100*250 uS)


------------------------------------------------------------------------------------
void external_ISR (void) interrupt 0
{
tick2=0; //bắt đầu tính trễ
rev_IR = 0x01; // rev_IR = 000 0001 giá trị khởi tạo ban đầu cho rev_IR


bit_1:
if(rev_IR >= 128) {save_rev = rev_IR; goto end_rev;} // Nếu đã nhận đủ 7 bit thì thoát
while( (P3_2 == 0)&&(tick2<250)) {;}
// Chờ cho hết bit START (P3_2 về "1"). Nếu vì lý do nào đó, P3_2 không về "1" thì sau (100*250)uS sẽ thoát, nếu không chương trình sẽ bị treo ở đây.


bit_0:
if(rev_IR >= 128) {save_rev = rev_IR; goto end_rev;} // Nếu đã nhận đủ 7 bit thì thoát
while( (P3_2 == 1)&&(tick2<250)) {;}
// Chờ cho "X" về "0" và bắt đầu lấy mẫu ở chân P3_2 (đọc bit)


tick1=0;
while(tick1<7) {;} //tạo trễ 700uS
if(P3_2 == 0) {rev_IR <<= 1; rev_IR |= 0x01; goto bit_1;} // bit_x = 1

//Nếu chân P3_2 = 0 tức là nhận được bit "1". Nếu nhận được bit 1, ta phải chờ cho P3_2 về "1" rồi chờ tiếp cho P3_2 về "0" rồi mới đọc bit tiếp theo, chính vì thế mới "goto bit_1" (xem ảnh dưới)


if(P3_2 == 1) {rev_IR <<= 1; rev_IR |= 0x00; goto bit_0;} // bit_x = 0

//Nếu chân P3_2 = 1 tức là nhận được bit "0". Nếu nhận được bit 0, ta chỉ phải chờ cho P3_2 về "0" rồi mới đọc bit tiếp theo, chính vì thế mới "goto bit_0" (xem ảnh dưới)



/*
| MSB LSB |
--------------------------------------------------
rev_IR = 0 0 0 0 0 0 0 1
rev_IR = 0 0 0 0 0 0 1 bit1
rev_IR = 0 0 0 0 0 1 bit1 bit2
rev_IR = 0 0 0 0 1 bit1 bit2 bit3
rev_IR = 0 0 0 1 bit1 bit2 bit3 bit4
rev_IR = 0 0 1 bit1 bit2 bit3 bit4 bit5
rev_IR = 0 1 bit1 bit2 bit3 bit4 bit5 bit6
rev_IR = 1 bit1 bit2 bit3 bit4 bit5 bit6 bit7 (rev_IR >= 128 ; stop INT0 va thoat)
*/

end_rev:
EX0=0; //chi lay 7 bit command, bo qua 5 bit address
}

DOWNLOAD FULL MÃ NGUỒN:
Khi nhấn nút "Tải Về Máy", bạn sẽ vào trang quảng cáo, vui lòng chờ 5 giây. Nút  hiện ra ở góc phải phía trên, nhấn vào để đến trang download

Nguồn bmtbd.uct.edu.vn