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 + -
显示快捷键?