ps2-01.c

来自「PS2键盘接口程序,51单片机,keil c编译通过.包PS2资料(PDF)」· C语言 代码 · 共 494 行

C
494
字号
/*******************************************
  . PS/2键盘第2套编码接口程序,适用于LPC930单片机

  . 键值通过串口发送到上位机

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

#include <reg931.h> 
#include "ps2-01.h"

#define uchar unsigned char
#define uint unsigned int

// 键盘键值的宏定义
#define CTRL       0x14
#define ALT        0x11
#define CAPS_LOCK  0x58
#define LSHIFT     0x12
#define RSHIFT     0x59
#define NUM_LOCK   0x77
#define ENTER      0x5a
#define RESET      0xff         //键盘进入reset
#define SETTING    0xfa         //所有使能
#define KEY_SPEED  0xf3         //机打速率设置
#define KEY_CODE   0xf0         //设定扫描码集
#define STATE      0xed         //键盘状态

#define TXBUF_LEN   0x30    //RS232串口发送缓存的长度
#define RXBUF_LEN   0x10    //RS232串口接收缓存的长度
#define KEYBUF_LEN  0x28    //PS2键盘接收缓存的长度

//  I/O口定义
sbit BUZZ      = P1^6;      // 蜂鸣器 - 
sbit K_DATA    = P2^2;      // PS2数据线,用于输出,设置为集电极开路输出
sbit K_CLK     = P2^3;      // PS/2时钟线,用于输出,设置为集电极开路输出

bdata unsigned char get,sendto;  //get 接收字; sendto 发送字;  
sbit key=get^7;
sbit sd0=sendto^0;

bit b_odd=0;
bit b_capslock=0; //1=大写键
bit b_shift=0;     //1=shift键有效
bit b_num=1;   //1=小键盘的数字键有效
bit b_ctrl=0;  //1=ctrl键有效
bit b_alt=0;  //1=ALT键有效
bit b_esp=0;  //1=特殊键(E0?
bit b_up=0;   //1=断码(F0?
bit b_e=0;    //1=键PAUSE BREAK(E1?


uchar wrtxbuf_pointer = 0;   //写数据到232发送缓存时的指针
uchar rdtxbuf_pointer = 0;   //读232发送缓存时的指针
uchar wrrxbuf_pointer = 0;   //接收时,写数据到232接收缓存时的指针
uchar rdrxbuf_pointer = 0;   //读232接收缓存时的指针
idata uchar txbuf[TXBUF_LEN];   //232发送缓存
idata uchar rxbuf[RXBUF_LEN];   //232接收缓存

data unsigned char mode=0x02;      // mode=Caps Num Scroll(0x00 ~ 0x06); 
data unsigned char key_buf[KEYBUF_LEN];           //键值接收缓冲
data unsigned char keywrpoint=0,keyrdpoint=0; //
unsigned char addkey[3];    //地址输入法的3个键值
unsigned char addkeypoint = 0;  //地址输入法的3个键值的指针位置

void set_state(unsigned char y);
void delay_us(unsigned int degree);
unsigned char getkey(void);
unsigned char receive(void);
void question(void);
void send(unsigned char x);
void start(void);
unsigned char cc_0(unsigned char ff);
unsigned char cc_1(unsigned char ff);
unsigned char cc_2(unsigned char ff);
unsigned char cc_3(unsigned char ff);
unsigned char character(unsigned char cc);
void ps2_rev_byte(void);
void SendTo_232(void);


/*-----------------------------------
 延时子程序
指令周期为0.27126us 当内部RC时(7.373MHz)
-----------------------------------*/
void delay_n(uint xn)
{
  for(;xn > 0 ;xn--){;}
  SendTo_232();
}
/*------------------------------------------------
发声程序
------------------------------------------------*/
void sound(void)                              //发声程序                     
{
  
	BUZZ = 1;
    delay_n(3000);                  
    BUZZ = 0;
}

/*------------------------------------------------
NAME: sendto_232(void)
FUNCTION: 检测232接口,若空闲,且发送队列不空,则发送一字节
IN:   
OUT:  
其它:
------------------------------------------------*/
void SendTo_232(void)
{
 
   if(!TI) return;  //正在发送,则退出
    TI = 0;
   if(rdtxbuf_pointer != wrtxbuf_pointer)
	  {  SBUF = txbuf[rdtxbuf_pointer];
 	     rdtxbuf_pointer ++;
		 if(rdtxbuf_pointer >= TXBUF_LEN) rdtxbuf_pointer = 0;
	  }
   
}

/*------------------------------------------------
NAME: write_txbuf(uchar xd)
FUNCTION: 写一个数据到发送队列中
IN:   xd - 数据
OUT:  
其它: 写数据前若数据满,则等待
------------------------------------------------*/
void write_txbuf(uchar xd)
{ uint xi; 
  uchar xa;
  for(xi = 0; xi < 0xfff0; xi ++) //约140ms
    { xa = wrtxbuf_pointer + 1; if(xa >= TXBUF_LEN) xa = 0;
      if(xa != rdtxbuf_pointer) break;
    }
   txbuf[wrtxbuf_pointer] = xd;
   wrtxbuf_pointer ++;
   if(wrtxbuf_pointer >= TXBUF_LEN) wrtxbuf_pointer = 0;

}

/*------------------------------------------------
主程序
------------------------------------------------*/
main()
{ 

  P0 = 0xff;
  P1 = 0xff;
  P2 = 0xff;
  P3 = 0xff;

  BRGCON = 0x00;
  SCON   = 0x50;      //串口配置:模式1;115.2k, 专用波特率发生器.
  BRGR0  = 48;    
  BRGR1  = 0;
  BRGCON = 0x03;
  EA = 1;  

  BUZZ = 0;

    start();
   keyrdpoint=0;
   key_buf[keyrdpoint]=0;

   K_CLK = 1;K_DATA = 1; 
 
   while(1)
     { 
      ps2_rev_byte();
      question();
      SendTo_232();
     }
}


void question(void) //查询键盘是否有键按下
{
   unsigned char kk=0;

   kk=getkey();
   if(kk > 0)  //键值有效,则通过COM口发送到主机
   {  sound();
      write_txbuf(kk); 
   }

}

unsigned char getkey(void)   //处理键盘接收缓存,把扫描码转换为键值
{
  unsigned char f,char_ok=0;
  if(keyrdpoint == keywrpoint) return 0;
  f=key_buf[keyrdpoint];
  keyrdpoint++;
  if(keyrdpoint>=KEYBUF_LEN) keyrdpoint=0;

  if(f==0)
    return 0;
  else
  {
    if(f==0x83)      //F7
       { if(b_up) char_ok = 0;
         else     char_ok=7; 
         b_up = 0;
       } 
    else if(f&0x80)
    {
       switch(f)
       {
          case 0xe1: b_esp=1;b_up=1;b_e=1;break;   //PAUSE
          case 0xe0: b_esp=1;b_e=0;break;          //特殊键
          case 0xf0: b_up=1;b_e=0;break;           //断码
          default: break;
       }
    }
    else
    {
       if(!b_esp)                               //b_esp==0;
       {
         if(!b_up) char_ok=cc_0(f);             //b_esp==0;b_up==0;
         else {b_up=0;cc_1(f);}                 //b_esp==0;b_up==1;
       }
       else                                   //b_esp==1;
       {
         b_esp=0;
         if(!b_up) char_ok=cc_2(f);             //b_esp==1;b_up==0;
         else {b_up=0;char_ok=cc_3(f);}         //b_esp==1;b_up==1;
       }
    }
    return (char_ok);
  }
}
unsigned char cc_0(unsigned char ff)
{
  unsigned char ch=0;
  switch(ff)
  {
    case LSHIFT:
    case RSHIFT: b_shift=1;break;
    case CTRL: b_ctrl=1; addkeypoint = 0; break;           //
    case ALT: b_alt=1;addkeypoint = 0; break;             //
    case 0x6e:
    case NUM_LOCK: b_num=~b_num;
                   if(b_num)
                   { mode = mode | 0x02;   //位1 - 点亮数字灯
                   }
                   else
                   {  mode = mode & 0xfd;  //位1 - 关闭数字灯
                   }
                   set_state(mode);

                   ch=0;
                   break;

    case 0x58: //CAPS_LOCK键 
                   b_capslock=~b_capslock;
                    if(b_capslock)
                    {
                       mode = mode | 0x04;  //位2为点亮大写灯
                    }
                    else
                    {
                       mode = mode & 0xfb; //位2 - 关闭大写灯
                    }
                    set_state(mode);
                    ch=31;  //发送大/小写状态到ARM
                    break;
    default: ch=character(ff);break;
  }
  return (ch);
}
unsigned char cc_1(unsigned char ff)
{ unsigned char ch=0;
  switch(ff)
  {
    case LSHIFT:
    case RSHIFT: b_shift=0;break;
    case CTRL: b_ctrl=0;addkey_ascii();break;
    case ALT:  b_alt=0;addkey_ascii();break;
    default: break;
  }
  return (ch);
}
unsigned char cc_2(unsigned char ff)
{
  unsigned char ch=0;
  switch(ff)
  {
    case CTRL: b_ctrl=1;addkeypoint = 0;break;
    case ALT: b_alt=1;addkeypoint = 0; break;
    case 0x12: ch=ASCII_0[61];break;              //Print Screen
    case 0x7c: break;
    default: ch=ASCII_0[ff];
             break;
  }
  return (ch);
}
unsigned char cc_3(unsigned char ff)
{
  unsigned char ch=0;
  switch(ff)
  {
    case CTRL: if(b_e){b_esp=1;b_up=1;b_e=0;ch=ASCII_0[ff];}
               else {addkey_ascii();b_ctrl=0;}
               break;
    case ALT: b_alt=0;addkey_ascii();break;
    default: b_e=0;break;
  }
  return (ch);
}
unsigned char character(unsigned char cc)      //字符键的处理
{
  data unsigned char hh=0;
  if(b_shift)
  {
    switch(cc)
    {
      case 0x0e: hh='~';break;
      case 0x16: hh='!';break;
      case 0x1e: hh='@';break;
      case 0x26: hh='#';break;
      case 0x25: hh='$';break;
      case 0x2e: hh='%';break;
      case 0x36: hh='^';break;
      case 0x3d: hh='&';break;
      case 0x3e: hh='*';break;
      case 0x46: hh='(';break;
      case 0x45: hh=')';break;
      case 0x4e: hh='_';break;
      case 0x55: hh='+';break;
      case 0x5d: hh='|';break;
      case 0x54: hh='{';break;
      case 0x5b: hh='}';break;
      case 0x4c: hh=':';break;
      case 0x52: hh='"';break;
      case 0x41: hh='<';break;
      case 0x49: hh='>';break;
      case 0x4a: hh='?';break;
      default: switch(mode)
               {
                 case 0: hh=ASCII_0[cc];
                            if((hh > 96) && (hh < 123)) hh-= 0x20; //把小写转换为大写
                         break;
                 case 0x02: hh=ASCII_1[cc];
                            if((hh > 96) && (hh < 123)) hh-= 0x20; //把小写转换为大写
                         break;
                 case 0x04: hh=ASCII_0[cc];break;
                 case 0x06: hh=ASCII_1[cc];break;
                 default: break;
               }
               break;
    }
  }
  else
  {
    switch(mode)
    {
      case 0: hh=ASCII_0[cc];break;
      case 0x02: hh=ASCII_1[cc];break;
      case 0x04: hh=ASCII_0[cc];
                 if((hh > 96) && (hh < 123)) hh-= 0x20; //把小写转换为大写
           break;
      case 0x06: hh=ASCII_1[cc];
                 if((hh > 96) && (hh < 123)) hh-= 0x20; //把小写转换为大写
                break;
      default: break;
    }
  }
  return (hh);
}

void set_state(unsigned char y)   //设置键盘状态
{
 
   send(STATE);
   send(y);
 
}

void start(void)
{
   send(RESET);   //键盘复位:第2套,标准机打,使能机打/通码/断码
   send(0xf6);   //(Set Default) 载入缺省的机打速率/延时10.9cps/500ms 按键类型(所有按键都使能机打/通码/断码) 以及第二套扫描码集
   set_state(mode);
}

/*------------------------------------------------
NAME: void send(unsigned char x) 
FUNCTION: 发送一个字节到键盘
IN:   x - 发送的数据
  
发送数据到键盘的步骤:
1) 把时钟线拉低至少100 微秒
2) 把数据线拉低
3) 释放数据线
4) 等待设备把时钟线拉低
5) 设置/复位数据线发送第一个数据位
6) 等待设备把时钟拉高
7) 等待设备把时钟拉低
8) 重复 5-7 步 发送剩下的7 个数据位和校验位
9) 释放数据线
10) 等待设备把数据线拉低
11) 等待设备把时钟线拉低
12) 等待设备释放数据线和时钟线
------------------------------------------------*/
void send(unsigned char x)       //发送一个字节到键盘
{ unsigned char xi;
  unsigned int xn;
   sendto=x;

   K_CLK = 1;
   K_DATA = 1;
    SendTo_232();
   K_CLK=0;            //把时钟和数据线设置为请求发送状态:时钟线拉低至少100 微秒
   delay_n(120);                //延时100us
   K_DATA=0;            //把时钟和数据线设置为请求发送状态:把数据线拉低
   delay_n(40);
   K_CLK=1;            //把时钟和数据线设置为请求发送状态:释放数据线
    delay_n(40);
  
   b_odd = 0;
   
   for(xn = 0; xn < 0x4000; xn++) { if(K_CLK == 0) break;}
    if(xn >= 0x4000) return;
   for(xi = 0; xi < 10; xi++)
    { 
      for(xn = 0; xn < 0x1f0; xn++) { if(K_CLK == 0) break;}
       if(xi < 8) //发送8位数据
       { K_DATA=sd0;
         if(sd0)b_odd=~b_odd;
         sendto= sendto >> 1;
       } 
       else if(xi == 8) //发送校验位
        { K_DATA = ~b_odd;  }
       else if(xi == 9) //发送停止位
        { K_DATA = 1; }
       for(xn = 0; xn < 0x1f0; xn++) {  if(K_CLK) break;}
    }
     delay_n(10); K_DATA = 1; 
    for(xn = 0; xn < 0xf0; xn++)  {  if(K_DATA == 0) break; }
    for(xn = 0; xn < 0xf0; xn++)  {  if(K_CLK == 0) break;  }
    for(xn = 0; xn < 0x1f0; xn++) {  if(K_DATA) break; }
    for(xn = 0; xn < 0x1f0; xn++) {  if(K_CLK) break;  }
   delay_n(60000);  //延时10ms
}

void ps2_rev_byte(void)  //从PS2键盘接收一字节数据,查询方式
{ unsigned char xi;
  unsigned int xn;

    K_CLK = 1; K_DATA = 1; 
  for(xn = 0; xn < 0xfff0; xn++) {  if(K_CLK == 0) break; } //检测时钟线是否为低
  if(xn >= 0xfff0)    return; 
  for(xn = 0; xn < 0x1f0; xn++)  {  if(K_DATA == 0) break; }//检测数据线是否为低
  if(xn >= 0x1f0)     return; 
   for(xn = 0; xn < 0x1f0; xn++) {  if(K_CLK) break;}

  //已收到开始位
    get = 0; b_odd = 0;
   for(xi = 0; xi < 9; xi++)
   {
    for(xn = 0; xn < 0x1f0; xn++) { if(K_CLK == 0) break;}
     if(xi < 8)
     { get = get >> 1;
       key=K_DATA;
       if(key) b_odd=~b_odd;
     }
     else
     {  if(K_DATA) b_odd=~b_odd;
     }
    for(xn = 0; xn < 0x1f0; xn++) {  if(K_CLK) break;}
   }
    for(xn = 0; xn < 0x1f0; xn++) {  if(K_CLK == 0) break;}
    for(xn = 0; xn < 0x1f0; xn++) {  if(K_DATA) break;}
    for(xn = 0; xn < 0x1f0; xn++) {  if(K_CLK) break;}
  
    K_CLK = 0; //抑制键盘立即发送下一字节,(键盘在CLK变高50ms后再发送)

        //前后两个值相同,则不写入.即取消 机打功能
        xi = keywrpoint;
        if(xi==0) xi = KEYBUF_LEN;
         xi--;
        if(key_buf[xi] == get)
         { if(keywrpoint != keyrdpoint) return;    //上一键还未处理
           if((b_ctrl) && (get == 0x14)) return;   //前后两个键盘为ctrl
         }

        key_buf[keywrpoint]=get;
        keywrpoint++;
        if(keywrpoint>=KEYBUF_LEN) keywrpoint=0;

}


⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?