生活在大學的同學們在經過一天的學習和忙碌之后,到晚上是否想看一場電影。有時累了一天了,都不愿自己動手去操作播放電影了,這時是否想過用一個遙控器來代替鍵盤去操作電腦呢?下面的這個例子,將帶著大家去實現具備這樣功能的電腦遙控器。
首先你需要一個遙控器,需要知道他的按鍵碼,按鍵碼可以用程序測試出來。將這個按鍵碼和你要設置的鍵盤的案件的掃描碼建立一個對應的關系。接下來你需要一個單片機開發板,也可以自己做。自己做需要話PCB,選擇好芯片并設計電路。最后需要做的就是了解PS/2通信協會并根據這個協議來編寫程序。如果是要設置像ALT+F4這種的快捷鍵的朋友,這種鍵我們可以想象按下的順序,首先按下ALT鍵,是發送他的通碼,這時并沒有釋放,再按下F4的通碼,接著松開F4,這時發送的是F4的斷碼,接著松開ALT,發送的是ALT的斷碼,這個數據是這樣的。下面給出程序,此例直接在
電子人單片機開發板 上實現。
/***********************這是main.c文件中的代碼。**********************************************************************************
** 文 件 名: 無線通信主文件
********************************************************************************************************/
#include "config.h"
#define MAXSIZE 33
UCHAR receive_data;
UCHAR sp; //奇偶校驗
UCHAR start;
bit changbit=0; //改變位
UCHAR code InfraredData[MAXSIZE] = {
//0-14----------------------------------------------------------------------------------------
0x00/*0 */, 0x01/*1 */, 0x02/*2 */, 0x03/*3 */, 0x04/*4 */,
0x05/*5 */, 0x06/*6 */, 0x07/*7 */, 0x08/*8 */, 0x09/*9 */,
0x0A/*F5 */, 0x1C/*TAB */, 0x4F/*空格 */, 0x56/*回車 */,
0x5C/*BackSpace*/,
//15-26----------------------------------------------------------------------------------------
0x10/*靜音 */, 0x12/*WINDOWS*/,0x1A/*音加 */, 0x1B/*上一曲 */,
0x1E/*音減 */, 0x1F/*下一曲 */, 0x4E/*delete */,0x53/*鼠標右*/, 0x5A/*上 */,
0x5B/*右 */, 0x5E/*下 */, 0x5F/*左 */,
//27-32----------------------------------------------------------------------------------------
0x16/*ctrl+v */, 0x51/*alt+esc */, 0x54/*ctrl+c */,0x57/*ALT+F4*/,
0x0F/*win+E */, 0x0B/*wins+D */,
};
/* 0x17 切換 */
/*********************************************************************************************************************/
/* 鍵盤掃描碼 */
/*********************************************************************************************************************/
uchar code Table1[15][3]= { 0x70,0xf0,0x70,/*0 */0x69,0xf0,0x69,/*1 */0x72,0xf0,0x72,/*2 */
0x7a,0xf0,0x7a,/*3 */0x6b,0xf0,0x6b,/*4 */0x73,0xf0,0x73,/*5 */
0x74,0xf0,0x74,/*6 */0x6c,0xf0,0x6c,/*7 */0x75,0xf0,0x75,/*8 */
0x7d,0xf0,0x7d,/*9 */0x03,0xf0,0x03,/*F5 *//*0x76,0xf0,0x76,CAPSLK 58*/
0x0d,0xf0,0x0d,/*TAB */0x29,0xf0,0x29,/*空格 */0x5a,0xf0,0x5a,/*回車 */
0x66,0xf0,0x66,/*BackS */
};
uchar code Table2[12][5]= {
0xe0,0x23,0xe0,0xf0,0x23,/*【靜音】 */
0xe0,0x27,0xe0,0xf0,0x27,/*【wins】 */
0xe0,0x32,0xe0,0xf0,0x32,/*【音加】 */
0xe0,0x15,0xe0,0xf0,0x15,/*【上一曲】 */
0xe0,0x21,0xe0,0xf0,0x21,/*【音減】 */
0xe0,0x4d,0xe0,0xf0,0x4d,/*【下一曲】 */
0xe0,0x71,0xe0,0xf0,0x07,/*【delete】 */
0xe0,0x2f,0xe0,0xf0,0x2f,/*【鼠標右鍵】 */
0xe0,0x75,0xe0,0xf0,0x75,/*【上】 */
0xe0,0x74,0xe0,0xf0,0x74,/*【右】 */
0xe0,0x72,0xe0,0xf0,0x72,/*【下】 */
0xe0,0x6b,0xe0,0xf0,0x6b,/*【左】 */
};
uchar code Table3[4][6]= { 0x14,0x2a,0xf0,0x2a,0xf0,0x14,/*ctrl +v */
0x11,0x76,0xf0,0x76,0xf0,0x11,/*alt +ESC */
0x14,0x21,0xf0,0x21,0xf0,0x14,/*ctrl +c */
0x11,0x0c,0xf0,0x0c,0xf0,0x11,/*ALT+F4 */
};
uchar code Table4[2][8]={ 0xe0,0x27,0x24,0xf0,0x24,0xe0,0xf0,0x27/*【windows】+E */,
0xe0,0x27,0x23,0xf0,0x23,0xe0,0xf0,0x27/*【windows】+D */};
UCHAR code Table5[10][3]={
0x34,0xf0,0x34,/*G*/0x33,0xf0,0x33,/*H*/0x24,0xf0,0x24,/*E*/0x4b,0xf0,0x4b,/*L*/
0x44,0xf0,0x44,/*O*/0x3b,0xf0,0x3b,/*J*/0x43,0xf0,0x43,/*I*/0x3c,0xf0,0x3c,/*U*/
0x1c,0xf0,0x1c,/*A*/0x31,0xf0,0x31,/*N*/
};
UCHAR Select(UCHAR Infrared);
void SendPS2(UCHAR Infrared);
void send_byte(uchar dat); //PS/2 協議 由鍵盤發送給主機
void init_keyboard(); //PS/2開機初始化
UCHAR Select(UCHAR Infrared) //查找按鍵序號,從MAXSIZE里比較
{
UCHAR i;
for(i=0;i<MAXSIZE;i++)
{
if(Infrared == InfraredData
)
break;
}
return i;
}
void SendPS2(UCHAR Infrared)//輸入紅外按鍵碼值
{
UCHAR num;
num = Select(Infrared); //查找對應的PS/2 掃描碼值
if(num<15)
{
if(changbit&&(num<10))
{
send_byte(Table5[num][0]); //發送字母
send_byte(Table5[num][1]);
send_byte(Table5[num][2]);
}
else
{
send_byte(Table1[num][0]); //發送數字
send_byte(Table1[num][1]);
send_byte(Table1[num][2]);
}
}
else if(num<27)
{
send_byte(Table2[num-15][0]);
send_byte(Table2[num-15][1]); //發送媒體鍵
send_byte(Table2[num-15][2]);
send_byte(Table2[num-15][3]);
send_byte(Table2[num-15][4]);
}
else if(num<31)
{
send_byte(Table3[num-27][0]);
send_byte(Table3[num-27][1]); //發送普通的快捷鍵
send_byte(Table3[num-27][2]);
send_byte(Table3[num-27][3]);
send_byte(Table3[num-27][4]);
send_byte(Table3[num-27][5]);
}
else
{
send_byte(Table4[num-31][0]);
send_byte(Table4[num-31][1]); //帶wins的快捷鍵
send_byte(Table4[num-31][2]);
send_byte(Table4[num-31][3]);
send_byte(Table4[num-31][4]);
send_byte(Table4[num-31][5]);
send_byte(Table4[num-31][6]);
send_byte(Table4[num-31][7]);
}
send_byte(0xAA); //鍵盤控制器自檢
}
/*********************************************************************************************************************/
/*********************************************************************************************************
** 函數名稱: void Delay(unsigned int s)
** 功能描述: 延時程序
********************************************************************************************************/
void delay(uint z) //延時 毫秒級
{
uint x,y;
for(x=z;x>0;x--)
for(y=124;y>0;y--);
}
void micsec_delay(uchar z)//延時 10微秒級
{
uchar i,j;
for(i=z;i>0;i--)
for(j=3;j>0;j--);
}
void send_byte(uchar dat) //PS/2 協議 由鍵盤發送給主機 ,接收的數據
{
uchar i;
EX0 = 0; //從設備總是在時鐘線為高時改變數據線狀態,主設備在時鐘下降沿讀入數據線狀態
while(!clk); //從設備向主設備發送數據時,首先檢查時鐘線,以確認時鐘線是否為高電平
micsec_delay(5); //如果是高電平,從設備就可以開始傳輸數據
while(!clk);
if(da==1) // DA==1 表示從設備到主設備的通信有11位數據,為0表示主設備到從設備的通信有12位數據
{
da=0;clk=0;
micsec_delay(4);
for(i=0;i<8;i++) //數據位:8位
{
LED=~LED;
clk=1;
micsec_delay(2);
da=dat&0x01;
if(da==1)
sp++;
dat>>=1; //獲取數據
micsec_delay(1);
clk=0;
micsec_delay(4);
}
clk=1;
micsec_delay(2);
switch(sp%2) //奇校驗位
{
case 0 : {da=1;sp=0;} // 如果數據位中1的個數為偶數,校驗位就為1;如果數據位中1的個數為奇數,
break;
case 1 : {da=0;sp=0;} //校驗位就為0;總之,數據位中1的個數加上校驗位中1的個數總為奇數,因此總進行奇校驗。
break;
}
micsec_delay(1);
clk=0;
micsec_delay(4);
clk=1;
micsec_delay(2);
da=1;
micsec_delay(1);
clk=0;
micsec_delay(4);
clk=1;
EX0 = 1;
}
}
void init_keyboard() //PS/2開機初始化
{
delay(5);
switch(receive_data)
{
case 0xFF :{send_byte(0xFA);send_byte(0xAA);} //引起鍵盤進入Reset模式
break;
case 0xF2 :{send_byte(0xFA);send_byte(0xAB);send_byte(0x83);} //鍵盤回應2個字節設備的ID
break;
case 0xFE : send_byte(0xFE); //用于只是在接收中出現的錯誤,從發送最后的掃描碼或者命令回應給主機
break;
case 0xCC : send_byte(0xFE);
break;
case 0x60 : send_byte(0xFE); //讀輸入緩沖區的內容就是讀0x60端口的數據
break;
case 0xEE : send_byte(0xEE); //鍵盤用"Echo"(0xEE)回應
break;
case 0xF1 : send_byte(0xFE);
break;
default : send_byte(0xFA); //缺省設置,所有鍵的通碼、斷碼都使能
break;
}
}
void PS2_receive() interrupt 0 //PS/2中斷接收主機數據
{
uchar i; //主設備首先將時鐘線和數據線設置為"請求發送"狀態
EX0 = 0; //具體方式為:首先下拉時鐘線至少100us抑制通信,然后下拉數據線"請求發送",最后釋
if(clk==0) //放時鐘線.在此過程中,從設備在不超過10us的間隔內必須檢查這個狀態,當設備檢測到
{ //這個狀態時,設備將開始產生時鐘信號.此時數據傳輸的每一幀由12位構成
micsec_delay(11);
if(clk==0)
{
while(!clk);
if(da==0)
{
micsec_delay(1);
for(i=0;i<8;i++) //接收八位的數據
{
LED=~LED;
clk=0; //主設備總是在時鐘線為低電平時改變數據線的狀態,從設備在時鐘上升沿讀人數據線狀態
receive_data>>=1;
micsec_delay(3);
clk=1;
micsec_delay(2);
if(da==1)
receive_data|=0x80;
micsec_delay(1);
}
clk=0;
micsec_delay(3);
clk=1;
micsec_delay(3);
clk=0;
micsec_delay(3);
clk=1;
micsec_delay(2);
while(!da);
da=0;
micsec_delay(1);
clk=0;
micsec_delay(3);
clk=1;
micsec_delay(1);
da=1;
init_keyboard();
}
}
}
EX0 = 1;
}
uchar chang(uchar cent) //把一個0-9,10-15轉ASCII碼‘0’-‘9’,‘A’-'F'
{
if( cent <= 9 )
cent = cent + 0x30; //顯示數字0-9
else
cent = cent + 0x37; //顯示字符A-F
return( cent );
}
/*----------------------------------------*/
void xianshi(uchar time11,uchar time22,uchar j,uchar k)
{ //解碼顯示兩個16進制數據
uchar a,b,c,d;
a=time11/16;
b=time11%16;
c=time22/16;
d=time22%16;
LCD1602_write_char(j,k,chang(a));
LCD1602_write_char(j,k+1,chang(b));
LCD1602_write_char(j,k+2,' ');
LCD1602_write_char(j,k+3,chang(c));
LCD1602_write_char(j,k+4,chang(d));
}
/*********************************************************************************************************
** 函數名稱: void main(void)
** 功能描述: 主函數
********************************************************************************************************/
void main(void)
{
UCHAR count = 0;
LCD_initial();
LCD_cls();
LCD1602_write_string(1,3,"KeyBoard...");
Init_main();
init_keyboard();
delay(1000);
start=0;
send_byte(0xAA);
while(1)
{
if(succes)
{
xianshi(infrared_R_DATA[0],infrared_R_DATA[1],2,2); //第2行,3列開始顯示
xianshi(infrared_R_DATA[2],infrared_R_DATA[3],2,8); //第2行,9列
if(infrared_R_DATA[0] == InfraredUserCode)
{
if(infrared_R_DATA[2]==0x17) //切換鍵
changbit= !changbit; //changbit為0,發送數字;為1,發送字母
else
SendPS2(infrared_R_DATA[2]);
}
succes = 0;
}
}
}
#ifndef __HS0038_h__
#define __HS0038_h__
/*---------------------------------------------------------
HS0038控制口定義
---------------------------------------------------------*/
#define Inte_Int 1 //外部中斷0/1
#define Inte_Time 1 //定時器0/1
#define Infrared_LED P13 //led燈引腳定義
bit succes = 0; //接收成功位
UCHAR infrared_Count = 0; //接收成功值,其值代表連按的次數值。
UCHAR infrared_R_DATA[4]; //接收后數據存入的數組
/*用戶碼:0x40 0xBF*/
#define InfraredUserCode 0x40//用戶碼位
/*---------------------------------------------------------
HS0038控制口及中斷控制位定義
---------------------------------------------------------*/
#if Inte_Int==0 //中斷0
#define Receive P32 //中斷0引腳
#define Inte_I_Num 0 //中斷0中斷號
#define IT IT0
#define EX EX0
#define PX PX0
#endif
#if Inte_Int==1 //中斷1
#define Receive P33 //中斷1引腳
#define Inte_I_Num 2 //中斷1中斷號
#define IT IT1
#define EX EX1
#define PX PX1
#endif
#if Inte_Time==0 //定時器0
#define ET ET0 //允許T/C0中斷
#define TR TR0 //關定時器中斷
#define TH TH0
#define TL TL0
#endif
#if Inte_Time==1 //定時器1
#define ET ET1 //允許T/C1中斷
#define TR TR1 //關定時器中斷
#define TH TH1
#define TL TL1
#endif
/*---------------------------------------------------------
紅外線的0、1電平區分的最低與最高值定義
---------------------------------------------------------*/
/*--晶振頻率:12MHZ---------------------------------------
03 05 07 09 2B 34 7B FF
|_______|---|__|---|_______|--------|____|----------------|
| 0 1 碼頭 第一個下降延
---------------------------------------------------------*/
/*紅外參數:*/
#define TH_0L 0x03 //0的低位定時判斷值
#define TH_0H 0x05 //0的高位定時判斷值
#define TH_1L 0x07 //1的低位定時判斷值
#define TH_1H 0x09 //1的高位定時判斷值
#define CodeStartL 0x30 //碼頭低位定時判斷值
#define CodeStartH 0x40 //碼頭高位定時判斷值
#define CodeOverL 0x7B //碼結束低位定時判斷值
/*---------------------------------------------------------
函數初始化Init_main();
---------------------------------------------------------*/
void Init_main(void)
{
IP = 0x01; //外部中斷為最大級
IT1 = 1; //INT下降沿中斷
EX1 = 1; //允許INT中斷
TMOD=0x10; //定時器1的方式為一
TH1 = 0x00;
TL1 = 0x00;
ET1 = 0; //關閉T/C0中斷
TR1 = 0; //關閉定時器
ET0 =1;
IT0 =1;
EA = 1; //開總中斷
}
/*---------------------------------------------------------
利用中斷接收數據程序
---------------------------------------------------------*/
void Int(void) interrupt 2
{
static UCHAR cont_R = 0xFF; //定義紅外接收中斷個數的計數器
UCHAR num; //定義用來存數組位置的變量
UCHAR list;
EX1 = 0;
TR = 0;
cont_R++; //中斷計數開始
num = (cont_R) / 8; //計算保存到哪一個數組
list = (cont_R) % 8; //計算保存到數組的哪一位置
//-------------------------------------------------------------------------------------------
if( TH >=TH_1L && TH <= TH_1H ) //讀取數據,如果為1
{
infrared_R_DATA[ num ] |= 0x01<<list; //在計算出的數組向右移動一位
}
else if( TH >=TH_0L && TH <= TH_0H ) //讀取數據,如果為0
{
infrared_R_DATA[ num ] &= ~(0x01<<list); //在計算出的數組向右移動一位
}
else if(TH >= CodeStartL && CodeStartH) //紅外碼頭檢測
{
infrared_Count++; //紅外重復碼加
if(cont_R>=31&&infrared_R_DATA[0]==0x40)succes = 1; //只接收0X40的頭碼,大于32,接收成功
cont_R = 0xff; //計數初始值
}
else if(TH >= CodeOverL) //紅外碼頭的結束標志
{
infrared_Count = 0;
cont_R = 0xff;
}
else
succes = 0; //接收失敗
//-------------------------------------------------------------------------------------------
Infrared_LED = !Infrared_LED; //led燈取反閃爍
TH = 0x00; //開定時器部分
TL = 0x00;
TR = 1;
EX1 = 1;
}
/*----------------------------------------------------------------------*/
#endif
*---------------------配置文件-------------------------------------------------*/
#ifndef _CONFIG_H_
#define _CONFIG_H_
#include <reg52.h>
#include ".\\Liquid Crystal LCD1602\\LCD1602_8.h"
#include "HS0038.h"
#define LED P13
sbit clk = P3^2; //定義鍵盤的時鐘腳(接中斷口0)
sbit da = P3^1; //定義鍵盤的數據腳
#define KEY P24
#endif