Створюючи маленьку охорону систему, спробував підключення різних пристроїв до мікроконтролера. І публікую тут як нотатку для себе.
У тому числі вирішив використати стару PS/2 клавіатуру. Протокол зв'язку гарно описаний
у публікаціях:
Через переривання програми за спадом сигналу (INT/RA2) реалізована обробка сигналу CLOCK, сигнал DATA зчитується безпосередньо у підпрограмі з обробки переривання.
Коли ввів віконний таймер при аналізі стартового біту, все стало гаразд.
.
Повний код, присутній на GitHub.
У тому числі вирішив використати стару PS/2 клавіатуру. Протокол зв'язку гарно описаний
у публікаціях:
- Adam Chapweske, The PS/2 Mouse/Keyboard Protocol,
- AT Keyboard Interface V1.04 (alt)
- Microcontroller Projects For Beginners: PS2 KEYBOARD INTERFACING PROJECT
Реалізація
На основі цих даних сформував власний код обробки сигналу що йде від клавіатури.Через переривання програми за спадом сигналу (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.
Немає коментарів:
Дописати коментар