Proton BASIC Compiler - Reading a PS2 keyboard


  • Pic® Basic


  • Reading a PS2 keyboard

    I had a need to read alphanumeric data into a project. I looked into reading the serial data from an old PS2 keyboard and it didn't look too difficult.
    This article details the Proton code I wrote to do the job.

    There are some things to consider:
    • the ps2 keyboard sends three characters for every keypress
    • one when you first press the key, not ASCII but a "key number"
    • a value $0F when you let go of the key
    • the first code again to show which key has just been released
    This means that you have to decide how to handle this. I went for the simple option: when I see the keyboard value 15 ($0F), I ignore it and the next character also.

    See https://www.avrfreaks.net/sites/defa...2 Keyboard.pdf for some interesting stuff including the actual connections to the DIN or mini Din plug.
    (Re-reading the above link I see that the "break" value when a key is released is $F0 and not $0F as I supposed. This is odd because my code works OK, a little experimentation will be called for. Ah, I had the data bit numbers reversed, so my bottom five bits are in fact the top five bits. Rocket science...)

    I added pull-ups to the clock and data lines as the keyboard doesn't have any. I finished up with quite a low value of pull-up resistor otherwise the signal was too slow to be read properly. The data rate is between 10kHz and 16kHz.

    This is a cut-down version of my code showing the keyboard stuff. It should work OK as it stands. Any problems PM me.....

    Code:
       
    
       Device=18F26K22
       Xtal = 64                                     ;clock freq 64MHz
         
    Config_Start
       FOSC = INTIO67                                ;Internal oscillator block, port function on RA6 and RA7
       PLLCFG = On                                   ;Oscillator multiplied by 4
       PRICLKEN = On                                 ;Primary clock is always enabled
       FCMEN = OFF                                   ;Fail-Safe Clock Monitor disabled
       IESO = OFF                                    ;Oscillator Switchover mode disabled
       PWRTEN = OFF                                  ;Power up timer disabled
       BOREN = OFF                                   ;Brown-out Reset disabled in hardware and software
       BORV = 285                                    ;VBOR set to 2.85 V nominal
       WDTEN = off                                   ;Watch dog timer disabled
       WDTPS = 2048                                  ;
       PBADEN = OFF                                  ;Segments<5:0> pins are configured as digital I/O on Reset
       HFOFST = OFF                                  ;HFINTOSC output and ready status are delayed by the oscillator stable status
       MCLRE = EXTMCLR                               ;MCLR pin enabled, RE3 input pin disabled
       STVREN = On                                   ;Stack full/underflow will cause Reset
       LVP = OFF                                     ;Single-Supply ICSP disabled
       XINST = OFF                                   ;Instruction set extension and Indexed  mode disabled (Legacy mode)
       Debug = OFF                                   ;Disabled
       Cp0 = OFF                                     ;Block 0 (000800-001FFFh) not code-protected
    Config_End
    
    
         On_Interrupt int        
    
    
         Symbol ps2_bit  PORTB.1                     ;data from keyboard, keyboard clock goes to portB.0
                                                     ;so PORTB.0 will cause an interrupt on every cycle of the keyboard's clock
                                                     ;but the kbd clock only starts and runs when there is a character to send     
    ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    
        Symbol PEIE      INTCON.6                    ;peripheral int enable
        Symbol GIE         INTCON.7                   ;Global Int Enable
        Symbol INT0IF    INTCON.1                    ;INT0 is keyboard clock
        Symbol INT0IE    INTCON.4                    ;keyboard int int enable
    
    
        Dim    ptr         As Word                   ;used to access the table that decodes the keyboard char
        Dim    bitcount    As Byte                   ;the bit number being dealt with at moment
        Dim    vector      As Byte                   ;takes us to a different place for every interrupt from keyboard clock
        Dim    ps2_char    As Byte 
        Dim    kb_ip_ptr   As Byte                   ;put-in pointer to buffer
        Dim    kb_op_ptr   As Byte                   ;take-off ..     ..  ..
        Dim    kb_buff_cnt As Byte                   ;number of chars in buffer
        Dim    temp        As Byte
        Dim    temp1       As Byte                   
        Dim    tmpchr      As Byte
        Dim    kb_char     As Byte
        Dim    ignore_nxt  As Bit                    ;the PS2 keyboard sends a 15 followed by the value for the key being released so ignore the NEXT char from the keyboard if set
        Dim    kb_buff[256] As Byte
    
    
    ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                
    
    
    ;  The characters we need from the PS2 keyboard char set. Don't forget that upper/lower case is determined by the SHIFT keys only
    ;   .. any key always generates the same code, the posn of the SHIFT key determines what value to use
    
    ; You will probably need more codes, just find out what the keyboard generates and put it in this table, I only needed A-Z 0-9 ENT etc
    
        Dim ps2_table As Code =  $38, "A", $4C, "B", $84, "C", $C4, "D", $24, "E", $D4, "F", $2C, "G", $CC, "H", $C2, "I", $DC, "J",_
                                 $42, "K", $D2, "L", $5C, "M", $8C, "N", $22, "O", $B2, "P", $A8, "Q", $B4, "R", $D8, "S", $34, "T",_
                                 $3C, "U", $54, "V", $B8, "W", $44, "X", $AC, "Y", $58, "Z", $A2, "0", $68, "1", $78, "2", $64, "3",_
                                 $A4, "4", $74, "5", $6C, "6", $BC, "7", $7C, "8", $62, "9", $5A, $0D, $66, $FF, $94, " ", $0F, $0F,_
                                 82 , "?", $0E, "0", $96, "1", $4E, "2", $5E, "3", $D6, "4", $2E, "6", $36, "7", $AE, "8", $BE, "9",_
                                 $07, $0D                             
        end_ps2_table:
                                                                                                                                                                                                                                                                                                                      
    ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    
                OSCCON =  110000                                         ;16MHz internal oscillator block   
                Nop
                OSCCON2 = 001100
                Nop                               
                Set OSCTUNE.6                                                ;enable PLL so clock now 64MHz                   
                
                Clear
                
                INTCON  = 000000                                         ;don't enable INT0 (B0) yet  
                INTCON2 = 000000                                         ;falling edge triggers int0
                INTCON3 = 000000
    
    
                ANSELA =  000000                                         ;           
                ANSELB =  000000                                         ;beware - pic starts life with all analogue channels set to analogue
                ANSELC =  000000
                
                TRISA =   000000                                         ;
                TRISB =   000011                                         ;B.0 is clock from kbd   B.1 is data from kbd
                TRISC =   000000                                         ;
                                                                            ;
                INT0IF = 0
                INT0IE = 1                                                  ;allow ints from keyboard
                PEIE = 1                 
                INT0IF = 0
    
    
                kb_buff_cnt = 0                                             ;nothing in buffer
                kb_ip_ptr = 0                                               ;buffer ptr at start of buffer
                
                GIE = 1                                                     ;here we go
    
    
    ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    
                While                                                       ;see if anything has been typed and send to rs232 port if so
                   If kb_buff_cnt > 0 Then
                      GoSub get_kb_char
                      HRSOut kb_char
                   EndIf   
                Wend
    
    
    get_kb_char:kb_char = kb_buff[kb_op_ptr]                                ;get next keyboard char from buffer
                Inc kb_op_ptr
                Dec kb_buff_cnt
                Return                  
     
    ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    
    int:    Context Save                                                    ;an interrupt has happened, data valid on FALLING edge of clock
                    If    INT0IF = 1 Then                                   
                          INT0IF = 0
                          Select vector                                     ;follows a path through the bits of the char
                            
                             Case 0                                         ;this is the start bit
                                ps2_char = 0                                ;gets built up in here
                                bitcount = 8                                ;a number of bits to collect                           
                                vector = 1                                  ;ready for first data bit
                            
                             Case 1                                         ;shift char along
                                ps2_char = ps2_char << 1
                                ps2_char.0 = ps2_bit                        ;add new bit to byte
                                Dec bitcount                                ;done all bits?
                                If bitcount = 0 Then                        ;if so then
                                     If ignore_nxt = 1 Then
                                        ignore_nxt = 0
                                        Else  
                                        If ps2_char = 15 Then
                                           ignore_nxt = 1
                                           Else
                                           temp1 = ps2_char
                                           For ptr = 0 To (end_ps2_table - ps2_table) Step 2
                                             temp = CRead8 ps2_table[ptr]                                     
                                             If temp = ps2_char Then
                                                  ps2_char = CRead8 ps2_table[ptr+1] ;get the ASCII char for that keyboard code
                                                  kb_buff[kb_ip_ptr] = ps2_char      ;and store in our cyclic (256) buffer
                                                  Inc kb_ip_ptr                              ;automatically wraps at 256
                                                  Inc kb_buff_cnt 
                                                  Break                                         ;got the char and stored it, now leave here
                                             EndIf
                                           Next
                                        EndIf   
                                     EndIf     
                                     vector =  2                            ;for the parity bit which will be next                            
                                EndIf                                       
                            
                             Case 2                                          ;this is the parity bit, the stop bit is next
                               vector = 3 
                            
                             Case 3                                         ;stop bit
                               vector = 0                                    ;all finished now, get ready for a new char from kbd
                          EndSelect
                    EndIf
                                                
    
    
            Context Restore
            
                 
    ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  • Recent Activity

    keytapper-79800

    Negetive Numbers with minus sign

    Thread Starter: amod

    Hi, Iam making a load indicator with HX711.My Problem is with negetive numbers and minus sign.Iam using 4 digit seven segment display.fourth display...

    keytapper Today, 06:40 Go to last post
    keytapper-79800

    CWrite and ISR

    Thread Starter: joesaliba

    I am struggling to make work CWrite and Interrupt together. If I comment out: - On_Interrupt Isr ' Where to go on an Interrupt ...

    keytapper Today, 05:44 Go to last post
    towlerg-21522

    Sleep and how to use it

    Thread Starter: Tim

    Hi, I need to save power in a device (16f1823) so thought, sleep a lot. But I never really used the command before. Reading the data sheet I...

    towlerg Today, 14:16 Go to last post