51單片機紅外通信及控制LED燈(LCD1602顯示)
大家好,又和大家見面了,離上一次DS18B20傳感器的文章已經過去了一個星期了,這期我將給大家帶來,基于STC89C52芯片和HS0038紅外接收探頭的紅外通信實驗
紅外通信
- 51單片機紅外通信及控制LED燈(LCD1602顯示)
- 紅外遙控電路的組成
- 信號調制和解調 及 紅外編碼協議和解碼
- NEC協議
- 資料碼
紅外遙控電路的組成
在我們生活當中,紅外遙控系統由發射裝置和接收裝置兩大部分組成,也就是遙控器(包括鍵盤電路、紅外編碼芯片、電源(我們今天使用的就是一顆小小的紐扣電池)還有紅外發射電路)和被遙控的物品(智能燈,風扇,空調,電視等等)
而紅外接收設備可由紅外接收電路、紅外解碼芯片、電源還有應用電路組成,今天我們將開發板和紅外接收探頭組合成為紅外接收電路、而STC89C52用來當作解碼芯片
此為紅外通信流程圖,其實很簡單,并沒有那么復雜,也就是遙控器發射紅外線,被控制的物品接收紅外線做我們人類編程程式中的任務,紅外線肉眼不可見,但只要我們拿手機攝像頭對準遙控器的發射端,就能看見一個紫光

這就是我們今天要用的遙控器啦
我們要用的HS0038長這個樣子

我使用的清翔51單片機將HS0038的out口接到了P3^2這個IO口上,也就是我們熟悉的INT0(外部中斷0),所以我們要通過撰寫中斷程式來接收紅外線攜帶的“1”和“0”,
因此,在紅外通信的時候我們并不需要sbit IO口,只需要利用好中斷服務程式就行,
接下來我們就要開始講信號的調制和解調及紅外編碼協議及解碼
信號調制和解調 及 紅外編碼協議和解碼
通常為了使信號能夠更好的被傳輸,發送端會將基帶二進制信號調制為脈沖串信號,通過紅外發射管發射

這樣可以讓單片機更容易區分資料“0”和“1”的脈沖時間,來通過變化的脈沖時間來識別這到底是資料“0”位還是資料“1”位
因此,我們引入一個NEC協議,讓大家更好的了解如何將紅外發射的信號接收和識別出來
NEC協議
資料格式:以下是發射端的方波圖,接收端的剛好與其相反,資料的傳輸從最低位開始,所以我們要撰寫正確的程式識別資料

大家也可以從這張圖看到,其中引導碼是我們不需要的,我們主要需要的是資料碼,通過資料碼的識別來控制單片機上的小燈或者其他東西
NEC標準下的編碼表示
其中:引導碼高電平約9000us 左右,低電平約4500us 左右;
用戶碼16 位,資料碼16 位,共32位;
資料0 是用“高電平約560us +低電平約560us”表示,
資料1 可用“高電平約560us+低電平約1680us”表示,

因此,我們可以通過不同的脈沖寬度來識別是“0”還是“1”
這里我們就要用到一個定時中斷函式和中斷服務程式來增加時間,好讓我們接收正確的脈沖寬度并且識別出來
void Init_timer0()
{
EA = 1;
TR0 = 1;
TMOD = 0X02; //八位自動重裝
ET0 = 1;
TH0 = 0;
TL0 = 0;
}
void timer0() interrupt 1
{
time_num++; //256us
}
這是我的定時中斷函式及中斷服務程式,其中,每隔256us就會進一次中斷函式,并且讓我的計時變數time_num加1,這樣我們就很容易接收正確的脈沖寬度了,
例如:資料0的脈沖寬度是1.12ms(如圖)資料1的脈沖寬度就是2.25ms
我們只需要用2250/256 = 8.78 及我們只需要判斷我們接收的時間是不是大于7,如果大于7,就認為我們接收到的資料是1,這樣我們就可以成功的接收32位資料,也就是四個位元組,
接下來就是看代碼的時候啦,學到紅外通信的小伙伴應該不缺乏看代碼能力和理解能力,當然大家不懂的都可以私信博主,我會一一為大家解答的哦,
#include<reg52.h>
#define uchar unsigned char
#define uint unsigned int
sbit RS = P3^5;
sbit RW = P3^6;
sbit EN = P3^4;
sbit dula = P2^6;
sbit wela = P2^7;
sbit LED1 =P1^0;
uchar time_num,extern_num;
uchar timerecord[33];
uchar cord[4];
uchar flag_ok;
uchar count;
/* LCD1602 */
void Read_Busy()
{
uchar busy;
P0 = 0XFF; //將P0復位
RS = 0;
RW = 1;
do
{
EN = 1;
busy = P0;
EN = 0; //以便下一次產生上升沿
}while(busy & 0x80);
}
void LCD_Write_cmd(uchar cmd) //寫入操控lcd的指令
{
Read_Busy();
RS = 0;
RW = 0;
P0 = cmd;
EN = 1;
EN = 0;
}
void LCD_Write_dat(uchar dat)
{
Read_Busy();
RS = 1;
RW = 0;
P0 = dat;
EN = 1;
EN = 0;
}
void LCD_Init()
{
LCD_Write_cmd(0x38);
LCD_Write_cmd(0x0c);
LCD_Write_cmd(0x06);
LCD_Write_cmd(0x01);
}
/* LCD1602 */
void Init_INT0()
{
EA = 1;
EX0 = 1;
IT0 = 1;
}
void Init_timer0()
{
EA = 1;
TR0 = 1;
TMOD = 0X02; //八位自動重裝
ET0 = 1;
TH0 = 0;
TL0 = 0;
}
void processing_jiema()
{
uchar i,j,k = 1,jiema;
for(j=0;j<4;j++)
{
for(i=0;i<8;i++)
{
jiema >>= 1;
if(timerecord[k] > 6)
{
jiema|=0x80;
}
k++;
}
cord[j] = jiema;
// jiema = 0; //可寫可不寫
}
}
void LCD1602_Display()
{
uchar i;
LCD_Write_cmd(0x80+0x04); //第一行第五個
for(i=0;i<4;i++)
{
if(cord[i]/16<10)
{
LCD_Write_dat(cord[i]/16 + 0x30);
}
else
{
LCD_Write_dat(cord[i]/16 + 0x37);
}
if(cord[i]%16<10)
{
LCD_Write_dat(cord[i]%16 + 0x30);
}
else
{
LCD_Write_dat(cord[i]%16 + 0x37);
}
}
}
void main()
{
Init_INT0();
Init_timer0();
LCD_Init();
dula = 0;
wela = 0;
while(1)
{
if(flag_ok == 1)
{
processing_jiema();
LCD1602_Display();
flag_ok = 0;
}
switch(cord[2])
{
case 0x0c:LED1 = 0; break;
case 0x18:LED1 = 1; break;
}
}
}
void INT_0() interrupt 0
{
extern_num++;
if(extern_num == 1)
{
time_num = 0;
}
else
{
if(time_num > 32) //起始碼判斷
{
count = 0;
}
timerecord[count] = time_num; //第一個是起始碼,不需要
time_num = 0;
count++;
if(count == 33)
{
extern_num = 0;
flag_ok = 1;
}
}
}
void timer0() interrupt 1
{
time_num++; //256us
}
/* 1碼的脈沖寬度為2.25ms
0碼的脈沖寬度為1.12ms
起始碼的脈沖寬度為9ms */
在我的代碼中,我用了LCD1602來顯示接收到的32位資料,這樣我就可以知道按鍵的各個部分的資料碼是什么,通過資料碼的識別來控制小燈的亮和熄滅,
資料碼
以下是我的遙控器各個按鍵的資料碼,僅供參考
| 按鍵名稱 | 資料碼 |
|---|---|
| 0 | 0x16 |
| 1 | 0x0c |
| 2 | 0x18 |
| 3 | 0x5e |
| 4 | 0x08 |
| 5 | 0x1c |
| 6 | 0xaa |
| 7 | 0x42 |
| 8 | 0x52 |
| 9 | 0xab |
| - | 0x07 |
| + | 0x15 |
| eq | 0x09 |
| next | 0x40 |
| prev | 0x44 |
| play | 0x87 |
感謝大家的收看!喜歡博主的就點個贊還有點個關注吧!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/277411.html
標籤:其他
