单片机与PS/2键盘应用实例
2012-07-31
标签: 键盘 PS/2

SMC1602A(16*2)模拟口线接线方式

连接线图:

------------------------------------------------

  |LCM-----51   | LCM-----51   | LCM------51      |

  |-------------|--------------|------------------|

  |DB0-----P1.0 | DB4-----P1.4 | RW-------P2.0    |

  |DB1-----P1.1 | DB5-----P1.5 | RS-------P2.1    |

  |DB2-----P1.2 | DB6-----P1.6 | E--------P2.2    |

  |DB3-----P1.3 | DB7-----P1.7 | VLCD接1K电阻到GND|

  -------------------------------------------------

PS/2的电气特性如下(电脑端):

Keyboard接线

   PS/2--------51单片机

   1 DATA------P3.4

   2 不接

   3 GND

   4 VCC

   5 CLK-------P3.3 接在51的外部中断,触发方式为低电平

   6 不接

本程序源码只供学习参考,不得应用于商业用途,如有需要请联系作者。

[注:AT89x51使用12M或11.0592M晶振,实测使用11.0592M]

[Keil uV2 7.01编译运行通过 程序中没有做键盘数据的奇偶校验]

[站长在AT89S51+12M晶振+JHD 162A液晶上调试通过]

=============================================================*/


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
#include "reg51.h"
#include "scancodes.h"
#define LCM_RW P2_0//定义LCD引脚
#define LCM_RS P2_1
#define LCM_E P2_2
#define LCM_Data P1
#define Key_Data P3_4//定义Keyboard引脚
#define Key_CLK P3_3
#define Busy 0x80//用于检测LCM状态字中的Busy标识
void LCMInit(void);
void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData);
void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData);
void Delay5Ms(void);
void Delay400Ms(void);
void Decode(unsigned char ScanCode);
void WriteDataLCM(unsigned char WDLCM);
void WriteCommandLCM(unsigned char WCLCM, BuysC);
unsigned char ReadDataLCM(void);
unsigned char ReadStatusLCM(void);
unsigned char code cdle_net[] = {"www.cdle.net--"};
unsigned char code email[] = {"pnzwzw@cdle.net"};
unsigned char code Cls[] = {" "};
static unsigned char IntNum = 0;//中断次数计数
static unsigned char KeyV;//键值
static unsigned char DisNum = 0;//显示用指针
static unsigned char Key_UP = 0, Shift = 0; //Key_UP是键松开标识,Shift是Shift键按下标识
static unsigned char BF = 0;//标识是否有字符被收到
void main(void)
{
    unsigned char TempCyc;
 
    Delay400Ms();//启动等待,等LCM讲入工作状态
    LCMInit();//LCM初始化
    Delay5Ms();//延时片刻(可不要)
 
    DisplayListChar(0, 0, cdle_net);
    DisplayListChar(0, 1, email);
    ReadDataLCM();//测试用句无意义
    for (TempCyc = 0; TempCyc < 10; TempCyc++) {
        Delay400Ms();    //延时
    }
    DisplayListChar(0, 1, Cls);
 
    IT1 = 0;//设外部中断1为低电平触发
    EA = 1;
    EX1 = 1;//开中断
 
    do {
        if (BF) {
            Decode(KeyV);
        }
        else {
            EA = 1;    //开中断
        }
    }
    while (1);
}
//写数据
void WriteDataLCM(unsigned char WDLCM)
{
    ReadStatusLCM();//检测忙
    LCM_Data = WDLCM;
    LCM_RS = 1;
    LCM_RW = 0;
    LCM_E = 0;//若晶振速度太高可以在这后加小的延时
    LCM_E = 0;//延时
    LCM_E = 1;
}
//写指令
void WriteCommandLCM(unsigned char WCLCM, BuysC) //BuysC为0时忽略忙检测
{
    if (BuysC) {
        ReadStatusLCM();    //根据需要检测忙
    }
    LCM_Data = WCLCM;
    LCM_RS = 0;
    LCM_RW = 0;
    LCM_E = 0;
    LCM_E = 0;
    LCM_E = 1;
}
//读数据
unsigned char ReadDataLCM(void)
{
    LCM_RS = 1;
    LCM_RW = 1;
    LCM_E = 0;
    LCM_E = 0;
    LCM_E = 1;
    return (LCM_Data);
}
//读状态
unsigned char ReadStatusLCM(void)
{
    LCM_Data = 0xFF;
    LCM_RS = 0;
    LCM_RW = 1;
    LCM_E = 0;
    LCM_E = 0;
    LCM_E = 1;
    while (LCM_Data & Busy);//检测忙信号
    return (LCM_Data);
}
void LCMInit(void)//LCM初始化
{
    LCM_Data = 0;
    WriteCommandLCM(0x38, 0); //三次显示模式设置,不检测忙信号
    Delay5Ms();
    WriteCommandLCM(0x38, 0);
    Delay5Ms();
    WriteCommandLCM(0x38, 0);
    Delay5Ms();
 
    WriteCommandLCM(0x38, 1); //显示模式设置,开始要求每次检测忙信号
    WriteCommandLCM(0x08, 1); //关闭显示
    WriteCommandLCM(0x01, 1); //显示清屏
    WriteCommandLCM(0x06, 1); // 显示光标移动设置
    WriteCommandLCM(0x0F, 1); // 显示开及光标设置
}
//按指定位置显示一个字符
void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData)
{
    Y &= 0x1;
    X &= 0xF;//限制X不能大于15,Y不能大于1
    if (Y) {
        X |= 0x40;    //当要显示第二行时地址码+0x40;
    }
    X |= 0x80;//算出指令码
    WriteCommandLCM(X, 1);//发命令字
    WriteDataLCM(DData);//发数据
}
//按指定位置显示一串字符
void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData)
{
    unsigned char ListLength;
 
    ListLength = 0;
    Y &= 0x1;
    X &= 0xF;//限制X不能大于15,Y不能大于1
    while (DData[ListLength] > 0x19) { //若到达字串尾则退出
        if (X <= 0xF) { //X坐标应小于0xF
            DisplayOneChar(X, Y, DData[ListLength]);//显示单个字符
            ListLength++;
            X++;
        }
    }
}
//5ms延时
void Delay5Ms(void)
{
    unsigned int TempCyc = 5552;
    while (TempCyc--);
}
//400ms延时
void Delay400Ms(void)
{
    unsigned char TempCycA = 5;
    unsigned int TempCycB;
    while (TempCycA--) {
        TempCycB = 7269;
        while (TempCycB--);
    };
}
void Keyboard_out(void) interrupt 2
{
    if ((IntNum > 0) && (IntNum < 9)) {
        KeyV = KeyV >> 1;//因键盘数据是低>>高,结合上一句所以右移一位
        if (Key_Data) {
            KeyV = KeyV | 0x80;    //当键盘数据线为1时为1到最高位
        }
    }
    IntNum++;
    while (!Key_CLK);//等待PS/2CLK拉高
 
    if (IntNum > 10) {
        IntNum = 0;//当中断10次后表示一帧数据收完,清变量准备下一次接收
        BF = 1;//标识有字符输入完了
        EA = 0;//关中断等显示完后再开中断 (注:如这里不用BF和关中断直接调Decode()则所Decode中所调用的所有函数要声明为再入函数)
    }
}
void Decode(unsigned char ScanCode)//注意:如SHIFT+G为12H 34H F0H 34H F0H 12H,也就是说shift的通码+G的通码+shift的断码+G的断码
{
    unsigned char TempCyc;
 
    if (!Key_UP) { //当键盘松开时
        switch (ScanCode) {
        case 0xF0 :// 当收到0xF0,Key_UP置1表示断码开始
            Key_UP = 1;
            break;
 
        case 0x12 :// 左 SHIFT
            Shift = 1;
            break;
 
        case 0x59 :// 右 SHIFT
            Shift = 1;
            break;
 
        default:
            if (DisNum > 15) {
                DisplayListChar(0, 1, Cls);//清LCD第二行
                DisNum = 0;
            }
            if (!Shift) { //如果SHIFT没按下
                for (TempCyc = 0; (UnShifted[TempCyc][0] != ScanCode) && (TempCyc < 59); TempCyc++); //查表显示
                if (UnShifted[TempCyc][0] == ScanCode) {
                    DisplayOneChar(DisNum, 1, UnShifted[TempCyc][1]);
                }
                DisNum++;
            }
            else { //按下SHIFT
                for (TempCyc = 0; (Shifted[TempCyc][0] != ScanCode) && (TempCyc < 59); TempCyc++); //查表显示
                if (Shifted[TempCyc][0] == ScanCode) {
                    DisplayOneChar(DisNum, 1, Shifted[TempCyc][1]);
                }
                DisNum++;
            }
 
            break;
        }
    }
    else {
        Key_UP = 0;
        switch (ScanCode) { //当键松开时不处理判码,如G 34H F0H 34H 那么第二个34H不会被处理
        case 0x12 :// 左 SHIFT
            Shift = 0;
            break;
 
        case 0x59 :// 右 SHIFT
            Shift = 0;
            break;
        }
    }
    BF = 0;//标识字符处理完了
}
以下为 scancodes.h 文件
unsigned char code UnShifted[59][2] = {
    0x1C, 'a',
    0x32, 'b',
    0x21, 'c',
    0x23, 'd',
    0x24, 'e',
    0x2B, 'f',
    0x34, 'g',
    0x33, 'h',
    0x43, 'i',
    0x3B, 'j',
    0x42, 'k',
    0x4B, 'l',
    0x3A, 'm',
    0x31, 'n',
    0x44, 'o',
    0x4D, 'p',
    0x15, 'q',
    0x2D, 'r',
    0x1B, 's',
    0x2C, 't',
    0x3C, 'u',
    0x2A, 'v',
    0x1D, 'w',
    0x22, 'x',
    0x35, 'y',
    0x1A, 'z',
    0x45, '0',
    0x16, '1',
    0x1E, '2',
    0x26, '3',
    0x25, '4',
    0x2E, '5',
    0x36, '6',
    0x3D, '7',
    0x3E, '8',
    0x46, '9',
    0x0E, '`',
    0x4E, '-',
    0x55, '=',
    0x5D, '\\',
    0x29, ' ',
    0x54, '[',
    0x5B, ']',
    0x4C, ';',
    0x52, '\'',
    0x41, ',',
    0x49, '.',
    0x4A, '/',
    0x71, '.',
    0x70, '0',
    0x69, '1',
    0x72, '2',
    0x7A, '3',
    0x6B, '4',
    0x73, '5',
    0x74, '6',
    0x6C, '7',
    0x75, '8',
    0x7D, '9',
};
unsigned char code Shifted[59][2] = {
    0x1C, 'A',
    0x32, 'B',
    0x21, 'C',
    0x23, 'D',
    0x24, 'E',
    0x2B, 'F',
    0x34, 'G',
    0x33, 'H',
    0x43, 'I',
    0x3B, 'J',
    0x42, 'K',
    0x4B, 'L',
    0x3A, 'M',
    0x31, 'N',
    0x44, 'O',
    0x4D, 'P',
    0x15, 'Q',
    0x2D, 'R',
    0x1B, 'S',
    0x2C, 'T',
    0x3C, 'U',
    0x2A, 'V',
    0x1D, 'W',
    0x22, 'X',
    0x35, 'Y',
    0x1A, 'Z',
    0x45, '0',
    0x16, '1',
    0x1E, '2',
    0x26, '3',
    0x25, '4',
    0x2E, '5',
    0x36, '6',
    0x3D, '7',
    0x3E, '8',
    0x46, '9',
    0x0E, '~',
    0x4E, '_',
    0x55, '+',
    0x5D, '|',
    0x29, ' ',
    0x54, '{',
    0x5B, '}',
    0x4C, ':',
    0x52, '"',
    0x41, '<',
    0x49, '>',
    0x4A, '?',
    0x71, '.',
    0x70, '0',
    0x69, '1',
    0x72, '2',
    0x7A, '3',
    0x6B, '4',
    0x73, '5',
    0x74, '6',
    0x6C, '7',
    0x75, '8',
    0x7D, '9',
};


可能会用到的工具/仪表
本站简介 | 意见建议 | 免责声明 | 版权声明 | 联系我们
CopyRight@2024-2039 嵌入式资源网
蜀ICP备2021025729号