AVR External Interrupts - INT0 Example

Download as pdf or txt
Download as pdf or txt
You are on page 1of 5

AVR - PS/2 keyboard key readout

1 of 5

http://www.electronics-base.com/projects/complete-projects/108-avr-p...
Adding PC keyboard to your hardware design and connecting microcontroller to it is both useful and COOL!
In this project we will explain how this is done and provide complete source code and step by step tutorial how to do this with AVR
microcontroller.

This project uses interrupts to interface a PS/2 keyboard with AVR ATmega8535, but code can be very easily adjusted for any microcontroller. The simplicity of the
protocol and the asynchronous nature of the clock generated by the keyboard make this a great simple start for getting familiar with interrupts and interrupt handlers. If
you have no experience with external interrupts, see AVR external interrupts - INT0 example.
First to say few words on how PS/2 port communicates.
All signals are generated by keyboard, AVR only reads pin state at pins defined as inputs.
Here is PS/2 connector pinout :

Pin 1 Data
Pin 2 Not Connected
Pin 3 GND
Pin 4 Vcc (+5V)
Pin 5 Clock
Pin 6 Not Connected

The easiest way to connect it is to find PS/2 female connector or take one from old broken PC motherboard.

You need to use four pins and we connected four wires to these pins: Vcc (red), GND (black), Data (white), Clock (yellow).

These signals are very fast so simple pooling pin state would be too slow and we could lose some data. Because of this, we use external interrupt INT0 for clock falling
edge detection and readout of key data.

28/05/2015 18:02

AVR - PS/2 keyboard key readout

2 of 5

http://www.electronics-base.com/projects/complete-projects/108-avr-p...

On picture above we can see clock frequency is around 10 kHz. Yellow line is clock and blue one is data.
Each data packet consists of 11 bits. These are 1 start bit, 8 data bits, parity and stop bit. This is illustrated on picture below.

When we capture byte from keyboard, we have Keyboard Scan Code. These are codes that give us information which key is pressed or released. Table of all Keyboard
Scan Codes is given below :

KEY

MAKE

BREAK

KEY

MAKE

BREAK

1C

F0,1C

46

32

F0,32

21

F0,21

23

-----

KEY

MAKE

BREAK

F0,46

54

FO,54

0E

F0,0E

INSERT

E0,70

E0,F0,70

4E

F0,4E

HOME

E0,6C

E0,F0,6C

F0,23

55

FO,55

PG UP

E0,7D

E0,F0,7D

24

F0,24

5D

F0,5D

DELETE

E0,71

E0,F0,71

2B

F0,2B

BKSP

66

F0,66

END

E0,69

E0,F0,69

34

F0,34

SPACE

29

F0,29

PG DN

E0,7A

E0,F0,7A

33

F0,33

TAB

0D

F0,0D

U ARROW

E0,75

E0,F0,75

43

F0,43

CAPS

58

F0,58

L ARROW

E0,6B

E0,F0,6B

3B

F0,3B

L SHFT

12

FO,12

D ARROW

E0,72

E0,F0,72

42

F0,42

L CTRL

14

FO,14

R ARROW

E0,74

E0,F0,74

4B

F0,4B

L GUI

E0,1F

E0,F0,1F

NUM

77

F0,77

3A

F0,3A

L ALT

11

F0,11

KP /

E0,4A

E0,F0,4A

31

F0,31

R SHFT

59

F0,59

KP *

7C

F0,7C

44

F0,44

R CTRL

E0,14

E0,F0,14

KP -

7B

F0,7B

4D

F0,4D

R GUI

E0,27

E0,F0,27

KP +

79

F0,79

15

F0,15

R ALT

E0,11

E0,F0,11

KP EN

E0,5A

E0,F0,5A

2D

F0,2D

APPS

E0,2F

E0,F0,2F

KP .

71

F0,71

1B

F0,1B

ENTER

5A

F0,5A

KP 0

70

F0,70

2C

F0,2C

ESC

76

F0,76

KP 1

69

F0,69

3C

F0,3C

F1

05

F0,05

KP 2

72

F0,72

2A

F0,2A

F2

06

F0,06

KP 3

7A

F0,7A

1D

F0,1D

F3

04

F0,04

KP 4

6B

F0,6B

22

F0,22

F4

0C

F0,0C

KP 5

73

F0,73

35

F0,35

F5

03

F0,03

KP 6

74

F0,74

1A

F0,1A

F6

0B

F0,0B

KP 7

6C

F0,6C

45

F0,45

F7

83

F0,83

KP 8

75

F0,75

16

F0,16

F8

0A

F0,0A

KP 9

7D

28/05/2015
F0,7D

-----

18:02

AVR - PS/2 keyboard key readout

3 of 5

http://www.electronics-base.com/projects/complete-projects/108-avr-p...

45

F0,45

F7

83

F0,83

KP 8

75

F0,75

16

F0,16

F8

0A

F0,0A

KP 9

7D

F0,7D

1E

F0,1E

F9

01

F0,01

5B

F0,5B

26

F0,26

F10

09

F0,09

4C

F0,4C

25

F0,25

F11

78

F0,78

'

52

F0,52

2E

F0,2E

F12

07

F0,07

41

F0,41

36

F0,36

PRNT
SCRN

E0,12,
E0,7C

E0,F0,
7C,E0,
F0,12

49

F0,49

3D

F0,3D

SCROLL

7E

F0,7E

4A

F0,4A

PAUSE

E1,14,77,
E1,F0,14,
F0,77

-NONE8

3E

F0,3E

And you can download this table along with some extra codes for special function buttons at this link.
In this example we implemented conversion of Keyboard Scan Codes to their ASCII codes. As you can see, any key sends its code when it is pressed or byte 0xf0
followed by taster code when it is released.We will use this to detect whether shift taster is pressed or not. Since scan codes for a and A are the same we have to
detect when shift is pressed.
External interrupt on falling edge of clock signal is used. Usage of external interrupts on AVR is explained more at AVR external interrupts - INT0 example.Reception of
data from keyboard is done in INT0 ISR.
Here is code that does this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

interrupt [EXT_INT0] void ext_int0_isr(void)


{
// Place your code here
// function entered at falling edge of the kbd clock signal
// if data bit is the next bit to be read
// (bit 3 to 10 is data, start, stop & parity bis are ignored
if((bitcount < 11) && (bitcount > 2)) {
data = (data >> 1);
if (KBD_DATA==1)
// if next bit is 1
data = data | 0x80;
// store a '1'
else
data = data & 0x7f; // else store a '0'
}
if(--bitcount == 0) {
// all bits received ?
decode(data);
// decode received byte
bitcount = 11;
// reset bit counter
}

It is very simple code that receives bit by bit and adds it to appropriate place in previously declared data variable. Also, start, parity and stop bits are ignored so only scan
code will be placed in data variable.When received, scan code needs to be converted into ASCII value.To be able to do this we defined constant strings that contains all
ASCII codes and its keyboard scan codes. Here are those strings:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

?
// keyboard scan codes (without & with shift key pressed)
flash unsigned char unshifted[67][2] = {
//0x0d,9,
0x0e,'`',0x15,'q',0x16,'1',0x1a,'z',0x1b,'s',0x1c,'a',0x1d,'w',0x1e,'2',0x21,'c',0x22,'x',0x23,'d',0x24,'e',
0x25,'4',0x26,'3',0x29,' ',0x2a,'v',0x2b,'f',0x2c,'t',0x2d,'r',0x2e,'5',0x31,'n',0x32,'b',0x33,'h',0x34,'g',
0x35,'y',0x36,'6',0x39,',',0x3a,'m',0x3b,'j',0x3c,'u',0x3d,'7',0x3e,'8',0x41,',',0x42,'k',0x43,'i',0x44,'o',
0x45,'0',0x46,'9',0x49,'.',0x4a,'/',0x4b,'l',0x4c,';',0x4d,'p',0x4e,'-',0x52,'`',0x54,'[',0x55,'=',0x5a,13,
0x5b,']',0x5d,'/',0x61,'<',0x66,8, 0x69,'1',0x6b,'4',0x6c,'7',0x70,'0',0x71,',',0x72,'2',0x73,'5',0x74,'6',
0x75,'8',0x79,'+',0x7a,'3',0x7b,'-',0x7c,'*',0x7d,'9',0,0 };
flash unsigned char shifted[67][2] = {
//0x0d,9,
0x0e,'`',0x15,'Q',0x16,'!',0x1a,'Z',0x1b,'S',0x1c,'A',0x1d,'W',0x1e,'@',0x21,'C',0x22,'X',0x23,'D',0x24,'E',
0x25,'$',0x26,'#',0x29,' ',0x2a,'V',0x2b,'F',0x2c,'T',0x2d,'R',0x2e,'%',0x31,'N',0x32,'B',0x33,'H',0x34,'G',
0x35,'Y',0x36,'^',0x39,'L',0x3a,'M',0x3b,'J',0x3c,'U',0x3d,'&',0x3e,'*',0x41,'<',0x42,'K',0x43,'I',0x44,'O',
0x45,')',0x46,'(',0x49,'>',0x4a,'?',0x4b,'L',0x4c,':',0x4d,'P',0x4e,'_',0x52,'"',0x54,'{',0x55,'+',0x5a,13,
0x5b,'}',0x5d,'|',0x61,'>',0x66,8, 0x69,'1',0x6b,'4',0x6c,'7',0x70,'0',0x71,',',0x72,'2',0x73,'5',0x74,'6',
0x75,'8',0x79,'+',0x7a,'3',0x7b,'-',0x7c,'*',0x7d,'9',0,0 };

Conversion of keyboard scan code to ASCII code is done with function decode(data).
Here is its source code:

1
2
3
4
5
6
7
8

//***********************************************
// decode scan code
void decode(unsigned char sc) {
static unsigned char is_up=0, shift = 0, mode = 0;
unsigned char i;
if (!is_up) {
switch (sc) {

28/05/2015 18:02

AVR - PS/2 keyboard key readout

4 of 5

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

http://www.electronics-base.com/projects/complete-projects/108-avr-p...

unsigned char i;
if (!is_up) {
switch (sc) {
case 0xF0 :// The up-key identifier
is_up = 1;
break;
case 0x12 :// Left SHIFT
shift = 1;
break;
case 0x59 :// Right SHIFT
shift = 1;
break;
case 0x05 :// F1
if(mode == 0)
mode = 1;// Enter scan code mode
if(mode == 2)
mode = 3;// Leave scan code mode
break;
default:
if(mode == 0 || mode == 3) {// If ASCII mode
if(!shift) {// If shift not pressed, do a table look-up
for(i = 0; unshifted[i][0]!=sc && unshifted[i][0]; i++);
if (unshifted[i][0] == sc) {
putchar(unshifted[i][1]);
}
}
else {// If shift pressed
for(i = 0; shifted[i][0]!=sc && shifted[i][0]; i++);
if (shifted[i][0] == sc) {
putchar(shifted[i][1]);
}
}
}
else putchar(sc);
// scan code mode (debugging mode)
break;
}
}
else {
is_up = 0;// Two 0xF0 in a row not allowed
switch (sc) {
case 0x12 :// Left SHIFT
shift = 0;
break;
case 0x59 :// Right SHIFT
shift = 0;
break;
case 0x05 :// F1 -- F1 puts you in debugging mode
// pressing F1 again gets you out of debugging mode
// in debugging mode hex code of the scan codes
// are stored in the buffer instead of their ascii codes
if(mode == 1)
mode = 2;
if(mode == 3)
mode = 0;
break;
}
}
}
//***********************************************

It is simple state machine that keeps track if shift key is pressed or not and finds appropriate ASCII code for every received keyboard scan code received from PS/2 port.
By pressing F1 key you can choose to send scan codes directly to UART without conversion. This is done for you to understand better how this conversion is done.
Because of its simplicity whole code is done in interrupt, so while(1) loop is empty and can execute other tasks. Implementation in interrupt guarantees that no data will be
lost.
Here is video showing how this example works in practice:

28/05/2015 18:02

AVR - PS/2 keyboard key readout

5 of 5

http://www.electronics-base.com/projects/complete-projects/108-avr-p...

You can download complete project for connection of AVR with PS/2 tastature and source code from this link.
Below is provided source code with key lines for this example highlighted:

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

?
/*****************************************************
This program was produced by the
CodeWizardAVR V2.04.4a Advanced
Automatic Program Generator
Copyright 1998-2009 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project :
Version :
Date
: 12/26/2011
Author :
Company :
Comments:
Chip type
: ATmega8535
Program type
: Application
AVR Core Clock frequency: 8.000000 MHz
Memory model
: Small
External RAM size
: 0
Data Stack size
: 128
*****************************************************/
#include <mega8535.h>
#include <delay.h>
// Standard Input/Output functions
#include <stdio.h>
#define KBD_DATA PIND.3
// External Interrupt 0 service routine
unsigned char data,bitcount=11;
// holds the received scan code
// keyboard scan codes (without & with shift key pressed)
flash unsigned char unshifted[67][2] = {
//0x0d,9,
0x0e,'`',0x15,'q',0x16,'1',0x1a,'z',0x1b,'s',0x1c,'a',0x1d,'w',0x1e,'2',0x21,'c',0x22,'x',0x23,'d',0x24,'e'
0x25,'4',0x26,'3',0x29,' ',0x2a,'v',0x2b,'f',0x2c,'t',0x2d,'r',0x2e,'5',0x31,'n',0x32,'b',0x33,'h',0x34,'g'
0x35,'y',0x36,'6',0x39,',',0x3a,'m',0x3b,'j',0x3c,'u',0x3d,'7',0x3e,'8',0x41,',',0x42,'k',0x43,'i',0x44,'o'
0x45,'0',0x46,'9',0x49,'.',0x4a,'/',0x4b,'l',0x4c,';',0x4d,'p',0x4e,'-',0x52,'`',0x54,'[',0x55,'=',0x5a,13,
0x5b,']',0x5d,'/',0x61,'<',0x66,8, 0x69,'1',0x6b,'4',0x6c,'7',0x70,'0',0x71,',',0x72,'2',0x73,'5',0x74,'6'
0x75,'8',0x79,'+',0x7a,'3',0x7b,'-',0x7c,'*',0x7d,'9',0,0 };
flash unsigned char shifted[67][2] = {
//0x0d,9,
0x0e,'`',0x15,'Q',0x16,'!',0x1a,'Z',0x1b,'S',0x1c,'A',0x1d,'W',0x1e,'@',0x21,'C',0x22,'X',0x23,'D',0x24,'E'
0x25,'$',0x26,'#',0x29,' ',0x2a,'V',0x2b,'F',0x2c,'T',0x2d,'R',0x2e,'%',0x31,'N',0x32,'B',0x33,'H',0x34,'G'
0x35,'Y',0x36,'^',0x39,'L',0x3a,'M',0x3b,'J',0x3c,'U',0x3d,'&',0x3e,'*',0x41,'<',0x42,'K',0x43,'I',0x44,'O'
0x45,')',0x46,'(',0x49,'>',0x4a,'?',0x4b,'L',0x4c,':',0x4d,'P',0x4e,'_',0x52,'"',0x54,'{',0x55,'+',0x5a,13,
0x5b,'}',0x5d,'|',0x61,'>',0x66,8, 0x69,'1',0x6b,'4',0x6c,'7',0x70,'0',0x71,',',0x72,'2',0x73,'5',0x74,'6'
0x75,'8',0x79,'+',0x7a,'3',0x7b,'-',0x7c,'*',0x7d,'9',0,0 };

//***********************************************
// decode scan code
void decode(unsigned char sc) {
static unsigned char is_up=0, shift = 0, mode = 0;
unsigned char i;
if (!is_up) {
switch (sc) {
case 0xF0 :// The up-key identifier
is_up = 1;
break;
case 0x12 :// Left SHIFT
shift = 1;
break;
case 0x59 :// Right SHIFT
shift = 1;
break;
case 0x05 :// F1
if(mode == 0)
mode = 1;// Enter scan code mode
if(mode == 2)
mode = 3;// Leave scan code mode
break;
default:
if(mode == 0 || mode == 3) {// If ASCII mode
if(!shift) {// If shift not pressed, do a table look-up
for(i = 0; unshifted[i][0]!=sc && unshifted[i][0]; i++);
if (unshifted[i][0] == sc) {
putchar(unshifted[i][1]);
}
}
else {// If shift pressed
for(i = 0; shifted[i][0]!=sc && shifted[i][0]; i++);
if (shifted[i][0] == sc) {
putchar(shifted[i][1]);
}

28/05/2015 18:02

You might also like