Quería comentarles que ya tengo funcionando (faltan algunos detalles, pero como prueba de concepto, ya funciona) la conexión del teclado al módem. Para ello, pedí prestado el código hecho por XP8100 y que RedPic modificó. Traduje el código para ser compilado en SDCC y para que funcione en un pic16f877a.
Aquí un esquema del cableado entre el teclado, el 16f877a y el UART del modem
Y aquí el código (recordar que es solo un esbozo):
//-----------------------------------------------------------------------------
// 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
// #Readaptado para 16F877A y SDCC por uniqdom
//
// NOTA: De momento este codigo es un esbozo. Falta arreglar unas cuantas cosas.
// Pero como prueba de concepto, funciona.
//
//-----------------------------------------------------------------------------
//
// 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'
//-----------------------------------------------------------------------------
/* Define processor and include header file. */
#define __16f677a
#include"/usr/local/share/sdcc/non-free/include/pic14/pic16f877a.h"
//#include"/usr/share/sdcc/non-free/include/pic/pic16f877a.h"
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
// If KHZ is not specified by the makefile, assume it to be 4 MHZ
#ifndef KHZ
#define KHZ 20000
#endif
// These are fixed. The 16f628a can only use these as transmit and recieve.
#define TX_PORT 2
#define RX_PORT 1
#define TX_BIT (1<<TX_PORT)
#define RX_BIT (1<<RX_PORT)
// Twiddle these as you like BUT remember that not all values work right!
// See the datasheet for what values can work with what clock frequencies.
#define BAUD 115200
#define BAUD_HI 1
// This section calculates the proper value for SPBRG from the given
// values of BAUD and BAUD_HI. Derived from Microchip's datasheet.
#if (BAUD_HI == 1)
#define BAUD_FACTOR (16L*BAUD)
#else
#define BAUD_FACTOR (64L*BAUD)
#endif
#define SPBRG_VALUE (unsigned char)(((KHZ*1000L)-BAUD_FACTOR)/BAUD_FACTOR)
//-----------------------------------------------------------------------------
// Definiciones globales
//-----------------------------------------------------------------------------
unsigned char edge, bitcount;
char got_interrupt;
char interrupt_count;
char status_b3;
//-------- 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);
void delay_ms(long ms)
{
long i;
while (ms--)
for(i=0;i!=330;i++)
;
}
//-----------------------------------------------------------------------------
// Rutina de gestión de interrupciones
//-----------------------------------------------------------------------------
static void int_ext_isr(void) __interrupt 0 {
unsigned char datos;
//-------- Los bit 3 a 10 se consideran datos. Paridad, start y stop
//-------- son ignorados
if(bitcount < 11 && bitcount > 2){
datos = (datos >> 1);
//status_b3 = input(PIN_B3);
status_b3= RB3;
if((status_b3) == 1){
datos = datos | 0x80;
}
}
//-------- Todos los bits se han recibido
if(--bitcount == 0){
decode(datos);
datos = 0;
bitcount = 11;
got_interrupt = TRUE;
}
got_interrupt = TRUE;
interrupt_count++;
//disable_interrupts(INT_EXT);
INTE = 0;
}
//-----------------------------------------------------------------------------
// Main
//-----------------------------------------------------------------------------
void main(void)
{
TRISB|=TX_BIT|RX_BIT; // These need to be 1 for USART to work
//SPBRG=SPBRG_VALUE; // Baud Rate register, calculated by macro
SPBRG=10; //el valor calculado por la macro da 9. Realicé los cálculos y en verdad da 9.850.
//en el lenguaje C, al realizar la operación división, el resultado es truncado.
//por lo tanto da 9. La cosa es que cuando realicé las pruebas, solo llegaban datos
//erróneos. Yo lo aproximé a 10 y funcionó (aunque algunos caracteres se los comía).
BRGH=BAUD_HI;
SYNC=0; // Disable Synchronous/Enable Asynchronous
SPEN=1; // Enable serial port
TXEN=1; // Enable transmission mode
delay_ms(100);
init_kb();
//B0 y B3 son configurados como entradas
TRISB = 0x09; // 0x09 = 00001001
//-------- Activa pullups sobre todos los pins del puerto B.
OPTION_REG=0x7F;
//-------- Espera a que se activen.
//delay_us(5);
delay_ms(100);
//--------
//TODO: esta linea creo q no es necesaria
//output_low(PIN_B5);
//-------- 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);
INTEDG=0;
//-------- 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.
INTCON = 0; /* clear interrupt flag bits */
//-------- Configuramos las interrupciones
INTE = 1; /* external interrupt enabled in pin B0 */
GIE = 1; /* global interrupt enable */
//-------- 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;
INTF = 0; // ahi solo borramos el flag de interrupcion externa.
//con INTCON=0 podriamos borrar todos los flags...
//pero en verdad no lo necesitamos ya que no estamos
// usando las otras interrupciones.
//-------- Reactivar interrupciones
//enable_interrupts(INT_EXT);
INTE = 1;
} // --- 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);
//-------- 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");
TXREG='\n'; // Add a character to the output buffer
while(!TXIF);
TXREG='\r'; // Add a character to the output buffer
while(!TXIF);
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]);
TXREG=unshifted[i][1]; // Add a character to the output buffer
while(!TXIF);
}
}
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]);
TXREG=shifted[i][1]; // Add a character to the output buffer
while(!TXIF);
}
}
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
}
}
Quiero comentarles que hice pruebas entre mi PC, la interfaz y el teclado, a funcionando a 9600bps y no hubo problemas.
El módem funciona a 115200bps, así que modifique el código para que puedan establecer la comunicación a dicha velocidad. La cosa es que a veces pulso alguna tecla y esta no se alcanza a leer.
leyendo en el datasheet del 16f877a (pagina 115) dice que para velocidad alta:
BRGH = 1 (High Speed), el baud rate está dado por:
Baud Rate = FOSC/(16 (X + 1)), donde X = value in SPBRG (0 to 255)
por lo tanto si quiero que el Baud Rate sea de 115200, despejando X:
X=9.850...
ahí en el código hay una macro que saca el calculo automáticamente
pero da 9 porque en lenguaje C, al realizarse la operación de división se trunca la parte fraccionaria.
Si hago funcionar el pic con SPBRG=9 (aproximado hacia abajo), solo muestra un caracter erroneo por cada tecla pulsada.
Luego forcé el valor a 10. es decir SPBRG=10 (aproximado hacia arriba) con lo cual de 10 letras yo diría que 7 llegan bien y las otras no son leídas. ¿Algún tip para mejorar la comunicación?
Sé que este pic es demasiado grande para el propósito. Por lo mismo lo tengo en una protoboard y no he hecho una PCB. Estoy en búsqueda de alguno mas pequeño. Si saben de alguno que tenga UART y interrupción externa me avisan.
Ahora avanzaré en el generador de imágenes NTSC.
Como el sistema en el módem es un Linux, y la interacción es mediante un terminal lo ideal sería generar vídeo en modo 80x25 lineas.
De generación de imágenes no se nada, así que comenzaré a leer.