1. Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.
    Dismiss Notice
Pommie

RS232 keypad with autorepeat and 2 key rollover.

This project uses a 12F508 (or 12F509) to read a keypad and send the results over RS232. It is most

  1. Pommie
    This project uses a 12F508 (or 12F509) to read a keypad and send the results over RS232. It is most useful as an add-on to other pic projects that require some way to input numbers. This keypad acts the same way as your PC keyboard in that when a key is held down, it has the short delay before it repeats the key. Both the delay and repeat speed are settable in software just like your PC keyboard.

    Many people will wonder what "2 key rollover" means. The best answer is with a little demonstration. Open your favourite text editor, hold down the "A" key and when it starts repeating press the "B" key. Even though 2 keys are now held down the keyboard sends the last key pressed. This is useful because many people will press the keys so fast that they inadvertently press two keys at once.

    How does it work.
    The Schematic,
    [​IMG]

    The 12F508 only has 6 input/output (I/O) pins and we need 1 to send the serial data out which leaves only 5 left to read the keypad. The keypad has 4 rows and 3 columns which is too many for our little pic chip. The way we get around this problem is by using the same pins twice. You see the three resistors in the diagram, well they will make the three pic pins low by connecting them to ground. If we now make GP4 high the diode on row 1 will conduct and if any keys are pressed in row 1 then the corresponding pin GP0, GP1 or GP3 will also go high. We have successfully read row 1. We can repeat this with GP4 and read row 3.

    Now comes the tricky part because the diodes on rows 0 and 2 are backward and so the only way to read those rows is by outputting a zero on GP4 and GP5 thus making the input pins low. But, wait a minute, they are already low due to those resistors and so we won't see any change. Luckily for us the pics have internal resistors that connect the (input) pins to the 5V line that we can turn on and off in software. These resistors have a value around 20k and so will overpower the 200k external ones and make the input pins 5V. So, with these internal resistors turned on we can make GP4 an output, set it low and any pins that are now low will indicate a key pressed on row 0. We can repeat this using GP5 to read row 2.

    The board,
    [​IMG]

    [​IMG]

    Closer,
    [​IMG]
    The two sets of connectors are just for convenience, I added the second set to enable me to use it on a breadboard.

    The finished article,
    [​IMG]

    The code,
    Code (text):

    ;*************************************************  ******************
    ;        Title    12F509 RS232 KeyPad
    ;        Author    Mike Webb
    ;        Date     12th January 2009
    ;        Version    1.0
    ;*************************************************  ******************
    ;
        processor    12F509

        include    "p12f509.inc"

        errorlevel    -302
        radix    dec

        __CONFIG   _MCLRE_OFF & _CP_OFF & _WDT_OFF & _IntRC_OSC

    ;uncomment the following line to invert the RS232 signal.
    ;when inverted it can connect straight to a PC without a MAX232

    ;#define        Inverted

    #define        GPIO0    0
    #define        GPIO1    1
    #define        GPIO2    2
    #define        GPIO3    3
    #define        GPIO4    4
    #define        GPIO5    5

    #define        b_GPIO0    GPIO,0
    #define        b_GPIO1    GPIO,1
    #define        b_GPIO2    GPIO,2
    #define        b_GPIO3    GPIO,3
    #define        b_GPIO4    GPIO,4
    #define        b_GPIO5    GPIO,5


    #define        RS232    2
    #define        b_RS232Out GPIO,RS232

            cblock    07h
    OutByte
    Temp
    Count
    Key0
    Key1
    Old0
    Old1
    Edge0
    Edge1
    KeyCount
            endc

    KeyDelay    equ    35    ;delay in 100th second until repeat kicks in
    KeyRepeat    equ    10    ;delay between repeated keys


            org     0h

    ;*************************************************  *******************
    ;    4 meg clock = 1.0 meg instructions


            org     0h
            movwf    OSCCAL        ;write calibration value

    start        movlw    0
            OPTION
        #ifdef    Inverted
            bcf    b_RS232Out    ;ensure RS232 is high
        #else
            bsf    b_RS232Out    ;ensure RS232 is high
        #endif
            movlw    b'11111111'-(1<<RS232)
            TRIS    GPIO        ;all input except RS232 output


            call    Wait100th
            call    Wait100th

    Loop        Call    Wait100th    ;needed for timing and debounce
            movfw    Key0
            movwf    Old0        ;keep copy of previous keys
            movfw    Key1
            movwf    Old1
            call    ReadKeys    ;read the key matrix into Key0 and Key1
            movfw    Key0
            iorwf    Key1,W        ;any key pressed
            btfsc    STATUS,Z
            goto    Loop
            movfw    Key0        ;get key state
            xorwf    Old0,W        ;keep changed bits (pressed and released)
            andwf    Key0,W        ;keep only new presses
            btfss    STATUS,Z
            goto    NewKeyDown
            movfw    Key1
            xorwf    Old1,W
            andwf    Key1,W
            btfsc    STATUS,Z
            goto    KeySame
    NewKeyDown    movlw    KeyDelay    ;start key delay
            movwf    KeyCount
            movfw    Key0        ;edge = (key^old)&key
            xorwf    Old0,W
            andwf    Key0,W
            movwf    Edge0        ;edges contains a 1 for each new key press
            movfw    Key1
            xorwf    Old1,W
            andwf    Key1,W
            movwf    Edge1
            goto    HaveKey
    KeySame        decfsz    KeyCount,F    ;key delay
            goto    Loop
            movlw    KeyDelay    ;if KeyDelay is zero
            iorlw    0        ;then no repeat allowed
            btfsc    STATUS,Z
            goto    Loop
            movlw    KeyRepeat
            movwf    KeyCount
    HaveKey
    ;    having got to here then a key is pressed for the first time
    ;    or the repeat time is up and so it needs repeating.
    ;    Edge0 or Edge1 contain the key pressed.

            movfw    Edge0        ;does edge0 have a bit set
            btfsc    STATUS,Z
            goto    IsEdge1        ;no so must be edge1
            movwf    Temp
            movlw    255        ;set count to -1
            goto    CountBits
    IsEdge1        movfw    Edge1        ;move edge1
            movwf    Temp        ;into temp and
            movlw    7        ;count to 7
    CountBits    movwf    Count
            bsf    STATUS,C    ;ensure it can't get stuck in loop
    CountBitsL    incf    Count,F        ;increment counter
            rrf    Temp,F        ;until we find a set bit
            btfss    STATUS,C
            goto    CountBitsL
            movfw    Count        ;W=key number 0-15
            Call    Bit2Key        ;convert to key character
            call    Send        ;send key via RS232
            goto    Loop



    ReadKeys    movlw    0<<NOT_GPPU        ;   WPUs ON
            OPTION
            movlw    b'11111111'-(1<<GPIO5)-(1<<RS232)
            tris    GPIO            ;make I/O 5 output
            bcf    b_GPIO5            ;and make it low
            call    BitDelay        ;avoid RMW
            clrf    Key0            ;will hold key state
            btfss    b_GPIO0            ;if I/O 0 low then key pressed
            bsf    Key0,0            ;so set the bit
            btfss    b_GPIO1            ;repeat
            bsf    Key0,1            ;for
            btfss    b_GPIO3            ;other
            bsf    Key0,2            ;keys
            movlw    b'11111111'-(1<<GPIO4)-(1<<RS232)
            tris    GPIO            ;make I/O 4 output
            bcf    b_GPIO4            ;and make it low
            Call    BitDelay
            btfss    b_GPIO0            ;read next row
            bsf    Key0,4
            btfss    b_GPIO1
            bsf    Key0,5
            btfss    b_GPIO3
            bsf    Key0,6
            movlw    1<<NOT_GPPU        ;WPUs OFF
            OPTION
            movlw    b'11111111'-(1<<GPIO5)-(1<<RS232)
            tris    GPIO            ;make I/O 5 output
            bsf    b_GPIO5            ;and make it high
            call    BitDelay
            clrf    Key1
            btfsc    b_GPIO0            ;I/O 0 will be pulled low by
            bsf    Key1,0            ;220k resistors unless key pressed
            btfsc    b_GPIO1
            bsf    Key1,1            ;same for rest of row
            btfsc    b_GPIO3
            bsf    Key1,2
            movlw    b'11111111'-(1<<GPIO4)-(1<<RS232)
            tris    GPIO            ;make I/O 4 output
            bsf    b_GPIO4            ;and high
            call    BitDelay
            btfsc    b_GPIO0
            bsf    Key1,4            ;read next row
            btfsc    b_GPIO1
            bsf    Key1,5
            btfsc    b_GPIO3
            bsf    Key1,6
            retlw    0
         
    Bit2Key        andlw    15
    KeyTable    addwf    PCL,F
            retlw    '7'
            retlw    '8'
            retlw    '9'
            retlw    'x'
            retlw    '1'
            retlw    '2'
            retlw    '3'
            retlw    'x'
            retlw    '*'
            retlw    '0'
            retlw    '#'
            retlw    'x'
            retlw    '4'
            retlw    '5'
            retlw    '6'
            retlw    'x'

        #ifdef    Inverted

    Send        movwf    OutByte
            bsf    b_RS232Out
            call    BitDelay
            bsf    STATUS,C
    SendLoop    rrf    OutByte,F
            movfw    OutByte
            btfsc    STATUS,Z
            goto    EndTransmit
            bsf    b_RS232Out
            btfsc    STATUS,C
            bcf    b_RS232Out
            call    BitDelay
            bcf    STATUS,C
            goto    SendLoop
    EndTransmit    bcf    b_RS232Out
            call    BitDelay
            call    BitDelay
            retlw    0

        #else

    Send        movwf    OutByte
            bcf    b_RS232Out
            call    BitDelay
            bsf    STATUS,C
    SendLoop    rrf    OutByte,F
            movfw    OutByte
            btfsc    STATUS,Z
            goto    EndTransmit
            bcf    b_RS232Out
            btfsc    STATUS,C
            bsf    b_RS232Out
            call    BitDelay
            bcf    STATUS,C
            goto    SendLoop
    EndTransmit    bsf    b_RS232Out
            call    BitDelay
            call    BitDelay
            retlw    0

        #endif


    ; at 1 meg instructions and 2400 baud
    ; delay is 1,000,000/2400 = 416 cc
    ; the calling loop and the call/return = 13
    ; therefore delay needs to be 403 cycles

    ; to change to 9600 baud
    ; delay needs to be 104 - 13 = 91
    ; which would be (3*30)-1 = 89 + 2 = 91

    BitDelay    movlw    30    ;134    change back to 134 for 2400 baud
            movwf    Temp
    DelayLoop    decfsz    Temp,F;        (3*134)-1 = 401 + 2 = 403
            goto    DelayLoop
            retlw    0

    Wait100th    clrf    Count
            movlw    13
            movwf    Temp
    Delay100L    decfsz    Count,F;    (256*3)-1 = 767
            goto    Delay100L
            decfsz    Temp,F;        (13*(767+3))-1 = 10009 + 3 = 10012
            goto    Delay100L
            retlw    0

            END
     
    A video.

    [video=youtube;Q2xrVV9w-yE]

    Note, this will not work with newer pic chips as they don't have the WPU on I/O 3.

    <edit>Somethings I forgot,
    The connections from left to right are GND, RS232 out and 5V.
    The output is TTL and so can be connected straight to a pic RX pin without a MAX232 chip.
    If you want an inverted signal to connect to a PC then uncomment the #define inverted line in the source.
    You can change the repeat delay and repeat rate in the source.
    </edit>

    Mike.

    Attached Files: