6 липня 2017 р.

Підключення PS/2 keyboard до мікроконтролеру PIC 16F690

Створюючи маленьку охорону систему, спробував підключення різних пристроїв до мікроконтролера. І публікую тут як нотатку для себе.
У тому числі вирішив використати стару PS/2 клавіатуру. Протокол зв'язку гарно описаний
у публікаціях: 

Реалізація

На основі цих даних сформував власний код обробки сигналу що йде від клавіатури.
Через переривання програми за спадом сигналу (INT/RA2) реалізована обробка сигналу CLOCK, сигнал DATA зчитується безпосередньо у підпрограмі з обробки переривання.
Формати пакету даних при передачі від клавіатури PS/2.


 
#define TMR0InitValue 6;    //4Mhz cpu clock, Timer0 start value

volatile int32_t t0_millis = 0;     /**< Milli-Second Counter.*/
volatile uint8_t pc2kbd_answer=0x0, pc2kbd_count=0;
bit pc2kbd_ready;
bit pc2kbd_parity;
bit pc2kbd_start;
bit pc2kbd_data;
int32_t pc2kbd_timer; 
 
void interrupt isr(void) {
    // Timer0 Interrupt - Freq = 1000.00 Hz - Period = 0.001000 seconds
    if (INTCONbits.T0IF == 1) // timer 0 interrupt flag
    {
        INTCONbits.T0IF = 0; // clear the flag
        //INTCONbits.T0IE = 1; // reenable the interrupt
        TMR0 = TMR0InitValue; // reset the timer preset count
        t0_millis++;
    }else if (INTCONbits.INTF == 1) {
        INTCONbits.INTF = 0;
        pc2kbd_data = PC2Keyboard_DATA; //ASAP rember vlaue of data bit .
        pc2kbd_count++;
        if (pc2kbd_count == 1) {
            if ((t0_millis - pc2kbd_timer) > 4) {
                //wait real start bit after previos packet of data max ~80ms. 
                //next package of data when repated key is ~86ms
                //I use 4ms
                pc2kbd_start = (pc2kbd_data == 0); //stat bit always must be 0
                pc2kbd_timer = t0_millis; //remember time of last 1 bit
                if (pc2kbd_start) {
                    pc2kbd_answer = 0; // initial value for scaned code
                    pc2kbd_parity = 1; // inital value for odd parity 
                } else {
                    //start bit is wrong, reset counter
                    pc2kbd_count = 0;
                }
            } else {
                //time of start bit is wrong, reset counter
                pc2kbd_count = 0;
            }
        } else if (pc2kbd_count > 1 && pc2kbd_count < 10) {
            if (pc2kbd_data == 1) {
                // data bit is high
                pc2kbd_answer = pc2kbd_answer | 0b10000000; // if input bit is high   
                pc2kbd_parity ^= 1; // calc odd parity by XOR with 1
            }
            if (pc2kbd_count < 9) {
                pc2kbd_answer = pc2kbd_answer >> 1; // need to shift 7times,not to loose last bit
            }
        } else if (pc2kbd_count == 10) {
            //check claculated odd parity with real 10 bit 
            pc2kbd_parity = (pc2kbd_parity == pc2kbd_data);
        } else if (pc2kbd_count == 11) {
            //ready check if start bit state, parity state and real stop bit data is set
            pc2kbd_ready = pc2kbd_start && pc2kbd_parity && pc2kbd_data;
            if (!pc2kbd_ready) {
                pc2kbd_answer = 0;
            }
            //reset counter
            pc2kbd_count = 0;
        }
    }
}
 

Проблеми

Головною проблемою для мене стало те що з'являлося сміття у значеннях, від того що обробка не знає як визначити початок формування пакету. У мене чомусь зажди після пройнятого пакету з 11 біт, надалі йшли не вірні данні.
Коли ввів віконний таймер при аналізі стартового біту, все стало гаразд.

Таймер стартового біту

Клавіатура може повторювати пакети при  натисканні клавіші, але не частіше чим 86мс. Тому раніше ніж 86мс даних не повинно бути. Так як довжина усього пакета менше ніж 1 мс, тому я використав таймер на 4 мс, що є менше 86 і більше 1мс. Що гарантує що це повинен бути стартовий біт. Якщо ні, то чекаємо іншого вікна.

Аналіз коду

Далі у головному коді програми робимо аналіз коду (pc2kbd_answer) отриманого з переривання коли бачимо сигнал готовості (pc2kbd_ready) процедурою PC2Keboard_Process();
.


void PC2Keboard_Process() {
    if (pc2kbd_ready == 1) {
        char kbd_char = 0x0;
        switch (pc2kbd_answer) {
            case 0x70:
                kbd_char = '0';
                break;
            case 0x69:
                kbd_char = '1';
                break;
            case 0x72:
                kbd_char = '2';
                break;
            case 0x7A:
                kbd_char = '3';
                break;
            case 0x6B:
                kbd_char = '4';
                break;
            case 0x73:
                kbd_char = '5';
                break;
            case 0x74:
                kbd_char = '6';
                break;
            case 0x6C:
                kbd_char = '7';
                break;
            case 0x75:
                kbd_char = '8';
                break;
            case 0x7D:
                kbd_char = '9';
                break;
        }
       if (kbd_char) {
#ifdef useDebugRS232    
            UART_Write(kbd_char);
#endif
        }
        pc2kbd_answer = 0;
        pc2kbd_ready = 0;
    }
}
 
void PC2Keyboard_Init(void) {
    //PC2 CLOCK - (INT)RA2/INT
    //INTEDG
    CM2CON0 = 0;
    ANSELbits.ANS2 = 0; //0 = Digital I/O. Pin is assigned to port or special function.
    OPTION_REGbits.INTEDG = 0; //falling egde
    INTCONbits.INTE = 1; //enable inetrupt by INT 
    INTCONbits.INTF = 0;
}  

Ініціалізація 

Переривання реалізовано в адаптації зо конкретного мікроконтролеру PIC 16F690. У процедурі
PC2Keyboard_Init();
Повний код, присутній на GitHub.


Немає коментарів:


Коли забув ти рідну мову, біднієш духом ти щодня...
When you forgot your native language you would become a poor at spirit every day ...

Д.Білоус / D.Bilous
Рабів до раю не пускають. Будь вільним!

ipv6 ready