PicManía by Redraven
 

Inicio . Mapa . Presentación . Electrónica Básica . Experimentos . Proyectos . CCS C . C30 .MPASM . Invitados . Eagle
Datasheets . IC´s . USB . Conceptos . Trucos e Ideas . Recursos . Enlaces . Noticias . Histórico . Cajón de Sastre . Visitas

Google
 
Web PicManía

 

PROYECTOS : TECLADO PS/2

 

 

 

Conectando un Teclado PS/2 a la RRBOARD1 (Previo al Emulacion Teclado PS/2)

 
Descripción y funcionamiento a "alto nivel":
 
  •    Un teclado PS/2 de 104 teclas para PC es un dispositivo razonablemente inteligente. Esto quiere decir que él mismo se encarga de la parte hardware del rastreo de teclas, de evitar los insufribles rebotes y de transmitirnos, mediante una trama perfectamente establecida, de los resultados de nuestras manipulaciones sobre sus teclas.

  •    El teclado PS/2 tiene un mapa de teclas a las que asigna un código, de uno o dos bytes, para cada una de ellas y que son los códigos que nos va a transmitir para indicarnos que se está pulsando una tecla determinada. A estos códigos les vamos a llamar códigos de rastreo de teclado. Más abajo os muestro una imagen de un teclado estándar donde se muestran los códigos de rastreo de todas y cada una de las teclas:


 


 

  •    El teclado PS/2 envía el código de rastreo asociado a una tecla al ser ésta pulsada, tantas veces como sea necesario si se mantiene pulsada con una cadencia tal como indique su tiempo de repetición, que es programable, y el mismo código de rastreo con el prefijo del byte F0h al ser soltada, también conocido como Break Code.

  •    Por ejemplo: Para conseguir la letra G Mayúscula debemos pulsar la tecla Shift y mientras la mantenemos pulsada, pulsar la tecla G, soltar la tecla G y soltar la tecla Shift. Esa secuencia de pulsaciones nos va a hacer que el teclado transmita la siguiente secuencia de bytes:

    Pulsar Shift -> 12h,
    Pulsar "G"
    -> 34h,
    Soltar "G"
    -> F0h 34h
    y soltar Shift
    -> F0h 12h

    o escribiendo solo los bytes que vamos a recibir:

    1h, 34h, F0h, 34h, F0h, 12h

    Ahí queda eso.

  • Nuestro cometido va a ser recibir e interpretar esta secuencia de bytes y actuar en consecuencia.
  • El teclado PS/2 también admite comandos. Dispone de funciones build-in que podemos disparar mediante el envío del comando correspondiente. Abajo muestro una tabla con los comandos disponibles que podemos enviarle a nuestro teclado. (Nosotros, en este proyecto no vamos a utilizarlos, pero ahí quedan por si alguno de mis amables visitantes desea ampliar la funcionalidad del mismo).

 

 
Conexionado eléctrico:
 
  •    El teclado PS/2 se conecta mediante cuatro hilos. Dos de ellos son para alimentación Vcc a 5V y GND, y otros dos para las señales Data y Clock. El pinout de los conectores Mini-DIN PS/2 tanto hembra, los del PC, como machos, los del Teclado, es tal como se muestra en la imagen inferior:


 

  •    En el Teclado PS/2 las señales Data y Clock con de "colector abierto". Esto quiere decir que para establecer un comunicación eléctricamente correcta debemos nosotros suministrar voltaje para el nivel lógico alto, y es él el encargado de dar los correspondiente niveles lógicos bajos, tirando nuestra señal a GND cuando así sea necesario. Este tema se soluciona conectando dos resistencias de 10K entre dichas líneas y Vcc por lo que siempre tendrán nivel lógico alto, salvo cuando el teclado disponga lo contrario y lo tire a GND para dar lo correspondientes niveles lógicos bajos. Esto es lo que se llama conectar unas resistencias Pull-Up.

  • El esquema de mas abajo muestra como conectar las resistencias Pull-Up entre el teclado y el PIC:
     


 

  • Nota: Generalmente nuestros PIC's y en concreto el yo utilizo para este proyecto, el PIC 16F628A, tiene todo el puerto B con la capacidad de conectar, mediante la correspondiente configuración, una batería de resistencias Pull-Up a todos sus pines. Esto hace innecesario la conexión de las resistencias externas tal como se presenta en el esquema anterior, pero lo indico porque podríamos tener que utilizar cualquier otro puerto que no disponga de esta característica, y en ese caso sí que deberíamos conectarlas para su correcto funcionamiento.
     

 
Protocolo de comunicación PS/2:
 
  •  El teclado PS/2 se comunica mediante un Protocolo Serie Síncrono. Utiliza, por lo tanto, una señal de Clock que índica cuando están disponibles los correspondientes bits en la señal de Data.
  • En reposo la señal de Clock está a nivel alto; a cada pulso a nivel bajo corresponde un pulso a nivel alto o bajo en la señal de Data, que se traducen respectivamente como bits 0 ó 1  del dato a transmitir.

  • La trama completa se compone de 11 bits. Siendo el primero un bit de Start, a continuación los 8 bits del Dato a transmitir enviándose primero el LSB (ó bit menos significativo), el décimo es el de paridad (usa la Impar, u Odd en Inglés) y por último un bit de ACK o Stop.
  • Abajo puede verse un cronograma de esta trama de comunicación PS/2 Teclado (Keyboard) -> PC (host):


 

  • Este protocolo de comunicación es bidireccional. El teclado PS/2 admite también comandos enviados desde el PC con el mismo formato que estamos estudiando, tal como adelantamos en el primer apartado de este proyecto. Y tal como decíamos allí, nosotros no vamos a implementar el envío de comandos al teclado, pero creo conveniente discutir también su procedimiento de envío, a nivel de protocolo por si alguien desea implementarlos en su propio trabajo.

  • Para poder habilitar la comunicación inversa, del PC (host) al Teclado PS/2 (Keyboard) debemos primero indicárselo así al Teclado mediante la señal de Clock. Para ello debemos poner a nivel bajo el Clock durante unos 160 uS, y la señal de Data a bajo unos 35 uS después de haber lanzado la del Clock. A partir de ahí debemos esperar la señal del Clock generada por el Teclado. Esto nos indicará que el teclado está dispuesto para recibir nuestro comando. Detectamos dicha señal como primer pulso de Clock, y a partir del siguiente podemos comenzar a enviarle nuestro byte.
  • Le enviaremos entonces los ocho bits de nuestro comando, cada uno de ellos cuando el correspondiente pulso en bajo del Clock del teclado así nos lo indique, empezando por el LSB, a continuación el bit de paridad impar (El numero de unos en los datos mas el de paridad deber ser impar o sea 1 si el numero de unos es par y cero si el total de unos es impar)

  • Y entonces debemos esperar el ACK del teclado, que debe venir tras dos pulsos de reloj, en nivel bajo, indicándonos de este modo que el teclado ha recibido correctamente nuestro comando. en caso contrario debemos volver a repetir nuestra secuencia de envío.

  • Mas abajo se muestra un cronograma de la transmisión PC (host) -> Teclado PS/2 (Keyboard)
 
Y con esto creo que ya estamos en disposición de implementar la lectura de nuestro teclado con el PIC.
 
 
Implementación en la RRBOARD1:
 
  •  La RRBOARD1 calza un PIC 16F628, y de este micro vamos a hacer uso de un par de características que nos vienen como anillo al dedo: 

  •  Por un lado lo que ya hemos visto en el apartado de la conexión eléctrica, y que consiste en activar las resistencias pull-up internas del PIC para evitar así el tener que cablearlas externamente entre las señales Clock & Data y Vcc.

  • La otra característica que vamos a utilizar es la de la interrupción por disparo externo, INT_EXT,  que tenemos disponible en el Pin RB0 del puerto B. Con esta interrupción vamos a detectar la señal del Clock del teclado, que nos indica periódicamente cuando tenemos disponible el siguiente bit a leer, hasta completar nuestra comunicación.

  • Para ello he modificado el conector de 10 pines de que dispone la RRBOARD1 publicando los pines RB0 y RB3 que serán los encargados de las señales de Clock y Data respectivamente. He cortado un cable alargador de Teclado PS/2, que dispone de los correspondientes conectores macho y hembra PS/2 en sus extremos, y he soldado los hilos correspondientes al conector hembra a los hilos Vcc, GND, RB0 y RB3 del cable plano del conector de 10 hilos de la RRBOARD1. Abajo puede verse una imagen de esta conexión:


 

Y con esto ya tenemos listo todo el Hardware necesario para poder "leer" el teclado PS/2 desde la RRBOARD1. A continuación vamos a ver el Software que vamos a construir.
 
Software de lectura:
 
Antes que nada deciros que este programa no lo he hecho yo. Como podéis ver en los créditos iniciales está realizado por XP8100. Yo solo me he limitado a adaptarlo a mi hardware específico, y a monitorizar tanto los códigos de rastreo de teclado involucrados como el carácter decodificado correspondiente.

 

 
     
 
//-----------------------------------------------------------------------------
// Title: keyboard_pc_to_rs232.c
// Description: Interfase entre un teclado convencional tipo AT y un puerto RS232C
// Date: Abr-2005
// Ver.Rev.: V01
// Author: XP8100 (xp8100@gmail.com)
//
// #Based on the AN AVR313: Interfacing the PC AT Keyboard from ATMEL#
// #Adaptado para 16F628A por Redraven
//
//-----------------------------------------------------------------------------
//
// init_kb() Inicializa rutina y contadores
//
// decode (char) Decodifica la pulsación realizada, convirtiendola a un caracter de la tabla
//
// int_ext_isr Rutina de gestión de interrupciones. Captura los diferentes bit's
//
//-----------------------------------------------------------------------------
// RB0 - Señal de reloj
// RB3 - Tren de impulsos (11 bit) Start+10101010+Paridad+Stop
//-----------------------------------------------------------------------------
//
// Commment : Permite conectar un teclado convencional de PC a un entorno
// gestionado por un PIC 16F877.
// El actual sistema se define como un primer prototipo, en el que no se realizan
// acciones concretas asociadas a teclas establecidas.
// Tampoco se actua sobre los indicadores luminosos del teclado, repetición de teclas, ...
//
//
//
// THIS DOCUMENT IS PROVIDED TO THE USER 'AS IS'
//-----------------------------------------------------------------------------
#include "16F628a.h"
#fuses HS,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOLVP
#use Delay(Clock=20000000)
#use rs232(baud=19200, xmit=PIN_B2, rcv=PIN_B1)
#zero_ram
//-----------------------------------------------------------------------------
// Definiciones globales
//-----------------------------------------------------------------------------
unsigned char edge, bitcount;
char got_interrupt;
char interrupt_count;
char status_b3;
#bit INTF_BIT = 0x0B.1 // INTCON BIT 1 = INTF RB0/INT External Interrupt Flag Bit
//-------- Tabla de caracteres correspondientes a la pulsación de la tecla
//-------- en modalidad normal (sin pulsar SHIFT)
unsigned char const unshifted[68][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
};
//-------- Tabla de caracteres correspondientes a la pulsación de la tecla
//-------- en modalidad desplazamiento (pulsando SHIFT)
unsigned char const shifted[68][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
};
//-----------------------------------------------------------------------------
// Definición de protipos
//-----------------------------------------------------------------------------
void init_kb(void);
void decode(unsigned char sc);
//-----------------------------------------------------------------------------
// Rutina de gestión de interrupciones
//-----------------------------------------------------------------------------
#int_ext
void int_ext_isr(void){
  unsigned char data;
  //-------- Los bit 3 a 10 se considerran datos. Paridad, start y stop
  //-------- son ignorados
  if(bitcount < 11 && bitcount > 2){
    data = (data >> 1);
    status_b3 = input(PIN_B3);
    if((status_b3) == 1){
      data = data | 0x80;
   }
  }
  //-------- Todos los bits se han recibido
  if(--bitcount == 0){
    decode(data);
    data = 0;
    bitcount = 11;
    got_interrupt = TRUE;
  }
  got_interrupt = TRUE;
  interrupt_count++;
  disable_interrupts(INT_EXT);
}
//-----------------------------------------------------------------------------
// Main
//-----------------------------------------------------------------------------
void main(void){
  delay_ms(100);
  init_kb();
  //-------- Los pins indicados (B0 y B3) son configurados como entradas.
  output_float(PIN_B0);
  output_float(PIN_B3);
  //-------- Activa pullups sobre todos los pins del puerto B.
  port_b_pullups(TRUE);
  //-------- Espera a que se activen.
  delay_us(5);
  //-------- Inicializa las variables usadas por la rutina de interrupción
  //-------- antes de activar las interrupciones
  interrupt_count = 0;
  got_interrupt = FALSE;
  status_b3 = 0;
  //-------- Desde que se activó el modo PULLUPS del puerto B, el estado
  //-------- normal del pin B0 es ALTO. La gestión de la interrupción externa
  //-------- se gestiona cuando se produce un cambio de nivel ALTO a BAJO.
  ext_int_edge(H_TO_L);
  //-------- Asegurarse de que el el bit de flag de la interrupción externa
  //-------- es borrado antes de activar la gestión de dicha interrupción
  //-------- externa.
  INTF_BIT = 0;
  enable_interrupts(INT_EXT);
  enable_interrupts(GLOBAL);
  //-------- Bucle principal.
  //-------- Chequear si se produce alguna interrupción (got_interrupt). Si es así, contar, borrar
  //-------- el flag y esperar 50 ms, reactivando la gestión de las interrupciones
  while(1){
    //-------- Chequear si se produce alguna interrupción (got_interrupt).
    if(got_interrupt == TRUE){
    //-------- Borrar el flag global que se inicio en la rutina de servicio
    //-------- de interrupciones externas.
    got_interrupt = FALSE;
    //-------- Esperar 50 ms para evitar rebotes en los contactos de las teclas.
    //delay_ms(50);
    //-------- Borrar cualquier interrupción producida durante el periodo de espera.
    INTF_BIT = 0;
    //-------- Reactivar interrupciones
    enable_interrupts(INT_EXT);
    } // --- End If ---
  } // --- End While ---
} // --- End MAIN ---
//-----------------------------------------------------------------------------
// Funciones
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Inicialización de teclado.
//-----------------------------------------------------------------------------
void init_kb(void){
  //-------- Longitud de la trama para cada pulsación y mensaje de bienvenida
  bitcount = 11;
  printf("\n\rPC AT Keyboard Interface Ver 1.0 by XP8100" );
  printf("\n\rAdpapted for 16F628A by Redpic" );
  printf("\n\rDecoder and Monitoring for 16F628A connected ...\n\r\n\r" );
}
//-----------------------------------------------------------------------------
// Decodificación de pulsaciones
//-----------------------------------------------------------------------------
void decode(unsigned char sc){
  static unsigned char is_up=0, shift = 0, mode = 0;
  unsigned char i;

  printf("[%X]",sc); // Monitor de código de rastreo

  //-------- El último dato recibido fue el identificador de Up-Key
  if (!is_up){
    switch (sc){
      //-------- Identificador de Up-Key
      case 0xF0 :
        is_up = 1;
        break;
      //-------- SHIFT Izquierdo
      case 0x12 :
        shift = 1;
        break;
      //-------- SHIFT Derecho
      case 0x59 :
        shift = 1;
        break;
      //-------- ENTER
      case 0x5A :
        shift = 0;
        printf("\n\r" );
        break;
      //-------- Si no es ninguno de los identificadores especiales, procesar
      //-------- pulsación, localizando caracter en tabla de caracteres.
      default:
        //-------- Pulsación normal
        if(!shift){
          for(i = 0; unshifted[ i ][ 0 ]!=sc && unshifted[ i ][ 0 ]; i++);
          if (unshifted[ i ][ 0 ] == sc){
            printf("<%c>", unshifted[ i ][ 1 ]);
          }
        }
       else
       //-------- Pulsación + SHIFT presionado
       {
         for(i = 0; shifted[ i ][ 0 ]!=sc && shifted[ i ][ 0 ]; i++);
         if (shifted[ i ][ 0 ] == sc){
           printf("<%c>", shifted[ i ][ 1 ]);
         }
       }
       break;
     } // --- End Switch
   }
   else
   {
      //-------- No se permiten 2 0xF0 en una fila
      is_up = 0;
      switch (sc){
        //-------- SHIFT Izquierdo
        case 0x12 :
          shift = 0;
          break;
        //-------- SHIFT Derecho
        case 0x59 :
          shift = 0;
          break;
      } // --- End Switch
    }
}
 
 
 

Descargar Fuentes

 

 

  •    Y ahora como siempre una imagen que vale mas que mil palabras. En este caso muestro el monitor RS232 conectado al PIC y su contenido es lo que hemos pulsado en el Teclado PS/2 conectado al PIC con la secuencia de [a][s][d][Intro][f][g]:

 

 Recursos:
En Español  Ratón PS/2 controlado por un dsPIC por Nocturno.
En Español  .pdf Teclado AT-PS2 Interfaz y funciones v1.2 (PDF)
En Inglés  TECHNICAL SPECIFICATION FOR G83-6000 (PDF)
En Inglés  AT Keyboard Interface V1.04
En Inglés  The PS/2 Keyboard Interface
En Inglés  DIY hardware keylogger
En Inglés  As to connect pic to the connector mini din (ps/2) in CCS C Forum

 Ampliación: Con la RRBOARD2

 

Para el proyecto KBDEMUL voy a necesitar "algo" que reciba PS/2 y que no sea mi PC ya que puede ser una pequeña locura intentar depurar un "emisor PS/2" utilizando la misma entrada donde tengo conectado el teclado con el que estoy trabajando ...

Así que la solución lógica es utilizar otro PC, pero como no tengo otro no tengo mas remedio que tirar de la RRBOARD2 haciendo las mismas funciones que en este hacía la RRBOARD1. Así mi "emisor" le hablará a la RRBOAR2 y ésta transmitirá lo que reciba vía RS232 al PC con lo que podré depurar sin problemas ...

Aquí os pego lo que realizado para conectarle el Teclado PS/2 a la RRBOARD2 que calza un 18F4550:

Primero el Adaptador PS/2 a Cable Plano 10 pines ...
 


 

Después la conexión entre el Keyboard PS/2 y la RRBOARD2 ...
 


 

Y por último un ejemplo de conexión con el monitor serie del PC:
 

 

Esta página se modificó el 27/12/2008


Esta página usa la letra Ñ

Nota pública importante sobre las consultas al Webmaster de PicManía.


Sugerencias a Picmanía... (que serán leídas pero seguramente no podrán ser contestadas)

Esta página pertenece al grupo de páginas de Diego RedRaven