Teclado Capacitivo

From Qi-Hardware
Jump to: navigation, search
Logo

Desarrollado por:

  1. Andrés Mauricio Asprilla
  2. Juan Sebastian Buendia
  3. Jairo Alberto Monsalve

Contents

[edit] Calificación

Porcentaje Presentación 20% Informe 30% Funcionamiento 50%
1 Entrega 10% 3 3 3
2 Entrega 30% 2 2 2
3 Entrega 30% 4 4 5


4 Entrega 30% 5 5 5

[edit] Proyecto de Teclado Capacitivo

En el curso de sistemas embebidos, se trabajará con la tarjeta SAKC (o ahora llamada SIE). Esta es una tarjeta muy versátil capaz de ejecutar instrucciones multimedia y gráficas de manera muy ágil y además de soportar el kernel de Linux. Por esa razón su funcionalidad hace que sea una tarjeta adecuada para el aprendizaje de los temas del curso de sistemas embebidos, además de poseer bastantes funcionalidades y herramientas como tener interconectado al procesador JZ4725 a una FPGA tener memorias RAM y NANDFLASH. Esto hace que sea un sistema muy completo y suficiente para poder emprender el proyecto que se va a presentar en este documento.

[edit] Descripción del proyecto

El proyecto que se desea hacer, es utilizar dicha herramienta (SIE), para poder elaborar un sistema de teclado capacitivo. Este accesorio tendrá funcionalidades extra para el sistema y también servirá para trabajar con Software, el cual le dará funcionalidades extra al desarrollo Hardware que pensamos hacer para llevar a cabo nuestro proyecto.

Lo que se pretende con el teclado es interconectarla con el sistema SIE para así poder que el procesador tenga acceso a él como un periférico de entrada más disponible para su uso y además poder desarrollar una aplicación utilizando Qt para poder controlarlo y así poder demostrar su correcto funcionamiento.

Esto hará que SIE tenga funcionalidades extra y dichas funcionalidades puedan ser aprovechadas por otros proyectos para que sean más fáciles de manipular por usuarios finales. Se va a utilizar el circuito integrado QT60486 de la firma Atmel y su datasheet se puede encontrar en esta página: http://www.atmel.com/dyn/resources/prod_documents/qt60486_801.pdf. En esta se encuentra el montaje básico, con el cual se implementa el esquemático y el circuito impreso que se encuentra más abajo. El circuito integrado anterior maneja 48 teclas capacitivas y con éllas se implementará un teclado completo para la SIE con las teclas numéricas, alfabéticas y símbolos básicos.

[edit] Herramientas a utilizar

[edit] Herramientas Hardware

  1. Tarjeta SIE
  2. Circuito Integrado QT60486 de Atmel http://www.atmel.com/dyn/resources/prod_documents/qt60486_801.pdf con encapsulado TQFP de 44 pines de montaje superficial

(futuramente se profundizará más en este tema).

[edit] Herramientas Software

  1. Qt (http://qt.nokia.com)
  2. Xilinx ISE WebPack
  3. Herramienta KiCAD (para el desarrollo de PCB's).

(futuramente se profundizará más en este tema).

[edit] Esquematico y PCB

A continuación se ilustra el diagrama esquemático (propenso a cambios) para la implementar del teclado capacitivo a la tarjeta SIE usando el integrado QT-60486 con encapsulado TQFP de 44 pines. Este circuito, es capaz de sostener una matriz de 48 teclas.

Cap.ogv
Video de funcionamiento

[edit] Diagrama de bloques de la comunicación procesador-Periférico

A continuación se mostrará el diagrama de bloques que se usará como guía para establecer la comunicación entre el procesador XBURST, la FPGA y el chip, en el se encuentra cada bloque referente al HARDWARE del proyecto:

Diagrama de bloques de la comunicación del proyecto entre el procesador, FPGA y teclado capacitivo


El sistema se propone funcionar de la siguiente manera: cuando se va a escribir en el periférico, se envía la señal de CS2 desde el procesador XBURST, activando el funcionamiento de la escritura desactivando el buffer Tri-state (dejándolo en alta impedancia), lo cual hace que el bus data solo se envíe al módulo de sincronismo de la señal, y se decodifique la dirección o las direcciones pertenecientes al teclado, las cuales se decodifican desde el address decoder que activa el CHIP SELECT de la UART, con esto se hace que cuando se escriba en el periférico (se envíe la órden al teclado de detectar todas las teclas), el teclado reciba la órden que se envía desde la UART que indica que se tiene que iniciar la detección de todas las teclas (órden 0x07). Con esto se ha escrito en el periférico teclado. En la siguiente figura se muestra de una manera gráfica y más comprensible dicho ciclo de escritura:

Diagrama de bloques de la escritura desde el procesador al teclado capacitivo


Ahora bien, cuando se piensa(n) leer la(s) tecla(s) pulsada(s), lo que tiene que hacer el Hardware es: solicitar una interrupción, luego de solicitar dicha interrupción al PIC, este tiene que enviar la información del dispositivo que está pidiendo dicha interrupción (en este caso el teclado), luego de eso se activa el CS de la UART el cual lee por el RX el código de la tecla, se convierte en un valor numérico el cual a su vez es convertido en el SCAN CODE necesario por el Driver de Linux y esa información es enviada al bus de datos del procesador, el cual recibe el scan code y se lo entrega al Driver del teclado en espacio de Kernel, el cual interpreta dicho scan code y entrega la letra pulsada en la consola o en la aplicación de software la cual va a recibir la tecla pulsada. En la siguiente figura se muestra el proceso más detallado:

Diagrama RTL de la parte HW del proyecto


[edit] Diagrama RTL de la comunicación del procesador con el periférico

Al implementar la UART como periférico, se utilizó el diagrama de bloques anteriormente elaborado y la estructura propuesta aquí. Lo cual el software de Xilinx nos genera el siguiente diagrama RTL:

Diagrama de bloques de la lectura del teclado capacitivo desde el procesador XBURST


[edit] Hardware

El componente hardware del proyecto del teclado capacitivo consiste básicamente en:

  1. Comunicación UART (universal asynchronous receiver-transmitter)
  2. Codificación de la información recibida del QT60486 para que sea recibida por el procesador y sea aceptada por el driver.

[edit] UART

Los parámetros aceptados para la comunicación UART por el QT son

  1. velocidad de transmisión: 9600-115200 baudios
  2. bits de dato: 8
  3. bits de iniciación: 1
  4. bits de parada: 1
  5. bits de paridad: 0

[edit] Señales

Para la comunicación UART con el integrado QT, intervienen 3 señales (y el reset):

  1. DRDY: Es la señal que habilita la comunicación entre el integrado y la entidad de adquisición de datos. Siempre que esta señal este en alto se posibilita ya sea la recepción de una instrucción por parte del QT o la transmisión de un datos desde el QT. Cada vez que está en alto vuelve a estar en bajo (<1ms) ya sea para recepción o transmisión. Para instrucciones como sleep y carga del sistema el tiempo en bajo suele tardar aproximadamente 20 ms. Puede decirse que esta señal coordina los diferentes procesos en la comunicación. Esta señal es bidireccional debido a que tanto el anfitrión (host) como el QT pueden ponerla baja(lógica wire_and) (Esta señal a lo hora del proyecto es recomendable mas no indispensable, para este caso no se uso).
  1. Rx (QT): Esta es una señal únicamente de entrada la cual se encarga de mandar las instrucciones desde el anfitrión (host) al integrado.
  1. Tx (QT): Señal de salida desde el integrado por la cual se transmiten los datos del teclado capacitivo.

Al mandar una señal del host al QT, este tiende a responder, mandando la información al host, aproximadamente 1 ms después. Al transmitir información (instrucciones o datos) los bits pueden estar separados entre 10 us y no mas de 100 ms. Regularmente el retardo bit a bit tiende a estar en el orden de micro segundos. Es importante recalcar que para la implementación de este proyecto, solo se enviaban comandos y se establecían retardos mas no se esperaba la respuesta del integrado (En espacio de usuario). Es decir, la única información que se recibia vía uart del QT-60486 era cada tecla oprimida.

[edit] Comandos de control (instrucciones)

Para la operación correcta del QT existen varias instrucciones para hacer posible la comunicación entre host y el integrado. Es importante que al ejecutar un comando del anfitrión al QT-60486, este puede recibir una respuesta en uno o mas BYTES. Existen comandos de carga, reset, verificación de estado, verificación de errores y petición de datos. Existen 22 comandos en total los cuales son descritos y referenciados de manera resumida en las paginas 18 y 19 de la siguiente hoja de datos del QT-60486:(­http://www.atmel.com/dyn/resources/prod_documents/qt60486_801.pdf)

[edit] Problemas que se deben solucionar al implementar el proyecto

Al enfrentarnos al problema, es necesario definir los problemas que tenemos que afrontar para poder que el proyecto pueda funcionar de manera satisfactoria. En un principio identificamos las siguientes problemáticas e ilustraremos las formas en que deseamos solucionarlas:


  • Es necesario que Linux reconozca el driver del puerto serie que se realizará en la FPGA (como tty1, o tty2). Esto se hace realizando dicho controlador basado en un chip comercial, en este caso se ajustará el comportamiento de dicho controlador al chip ST16552 para que este pueda trabajar bajo Linux y basado en eso realizar el driver adecuado, de esa forma se realizarán pruebas para observar el funcionamiento de dicho controlador a la velocidad requerida.
  • El otro problema es sobre las teclas capacitivas que se pretenden desarrollar, estas teclas son realizadas con algunos parámetros que se pueden observar aquí. Lo que se pretende en el diseño es que el sistema sea modular, es decir que se le puedan conectar y desconectar diferentes prototipos de teclado, esto es con el fin de poder hacer pruebas para escoger el mejor comportamiento deseado y las teclas más apropiadas a nuestro diseño. Se pretende hacer varios circuitos impresos y hacer pruebas con distintos tipos de teclas para escoger el mejor funcionamiento y también poder observar cuáles son las más apropiadas en el caso de necesitar realizar el prototipo de carcasa.

[edit] Diagrama de estados de la UART software-qt60486

A continuación se ilustra el diagrama de estados el cual indica el procedimiento que se efectúo en software (espacio de usuario) para accionar y configurar el integrado usado en el teclado capacitivo (qt60486):

Diagrama de estados comunicación FPGA- QT60486


.

En la hoja de datos del integrado ((­http://www.atmel.com/dyn/resources/prod_documents/qt60486_801.pdf)) hay un diagrama de estados mas completo que toma en cuenta mas variables y referencias para un diseño mas robusto. Sin embargo, se implemento el anterior el cual facilito muchas tareas y funciona de manera correcta.

[edit] Nociones básicas del driver de Linux para el teclado

Básicamente, las señales que envía y recibe el teclado, dependen del comando solicitado. Los comandos que se entregan son los siguientes (entre paréntesis se indica cuales fueron usadas en el proyecto):

  • 0x00: Null command.
  • 0x01: Enter setups mode. (usado)
  • 0x03: Call all.
  • 0x04: Force Reset. (usado)
  • 0x05: General Status
  • 0x06: Report First key (usado)
  • 0x07: Report all keys
  • 0x08: Signals for all
  • 0x09: References for all
  • 0x0a: Deltas For all
  • 0x0b: Error flags for all
  • 0x0e: EEPROM CRC (usado)
  • 0x0F: Reportar ultima instrucción (usado)
  • 0x16: Sleep

Por lo tanto, al querer tener un buen funcionamiento del chip, debemos realizar los siguientes pasos según la máquina de estados mostrada anteriormente:

  1. Por medio de la instrucción 0x01 se le indica al chip que se enviaran 247 bytes de configuración para ajustar retardos, sensibilidad bajo diferentes circunstancias de cada tecla.
  2. Al reportar la tecla oprimida, se tiene que procesar la tecla oprimida (en este caso, primero reportar la interrupción y reportar una entrada usando la función input_report_key() declarada en el archivo include/linux/input.h).
  3. Luego de esto, se tiene que tener un retardo de 10ms aproximadamente para poder enviar el reporte de otra tecla oprimida.

La idea con el driver, es proporcionar en el espacio de usuario la anterior secuencia para que el chip funcione adecuadamente, además poder indicarle al kernel de Linux que este teclado es un dispositivo de entrada. Para poder escribir el driver, se necesita definir el dispositivo como un archivo en el directorio /dev con sus diferentes rutinas (abrir, cerrar, leer, escribir, etc). Por lo tanto, se necesitan identificar las diferentes rutinas referentes a los respectivos comandos enviados.

[edit] Espacio de usuario

[edit] Prueba de funcionamiento y configuración de la tarjeta del teclado capacitivo (Espacio de usuario)

Una vez hecho la baquela y soldado todos los componentes de montaje superficial, se hace necesario efectuar cierto tipo de pruebas al integrado para verificar su funcionamiento y verificar las señales tanto que recibe como devuelve. Basando se en el diagrama de estados anteriormente ilustrado, se realizo un código en C en espacio de usuario de tal forma que se pudiera comprobar el funcionamiento de la placa diseñada y configurar el chip. Este programa, debía inicializar la uart (fpga) mandando las direcciones correspondientes y después mandar los registros necesarios para configurarla a la velocidad deseada (9600). Una vez hecho esto, se empezaría con el envío de los registros de configuración del chip. Finalmente, se establece la recepción de las datos y la codificación de estos para que se puedan representar en consola como caracteres al presionar las distintas teclas del teclado capacitivo. El programa se puede descargar de acá. Dicha prueba, nos permite que al teclear cualquier tecla (o secuencia de teclas) del teclado capacitivo y posteriormente oprimir el "enter" (del teclado capacitivo) de este se muestre por pantalla el arreglo tecleado:

Prueba de transmisión del integrado y funcionamiento de la placa

[edit] Demonio en espacio de usuario

El integrado de Atmel posee un problema: requiere que se le envié un registro de reportar tecla cada 20 ms y este responde con un valor por defecto si no se oprime nada o con una valor para la tecla detectada (es obvio que los 8 bits son enviados vía serial a la UART en la FPGA). Debido a esta complicación y a que un driver en espacio de kernel no es en tiempo real (en espacio de usuario si), es necesario hacer un programa en espacio de usuario que se ejecute en "background" (demonio). Este programa lo que hace es inicializar la uart con ciertos registros (LCR, IER, DIVMSB y DIVLSB), y enviar los registros del diagrama de estados ilustrado anteriormente. Es importante decir que después de mandar la instrucción 0x01, se envían 247 registros de configuración de las teclas. Para mayor información e ilustración de la configuración y de el significado y manera de enviar cada registro, es mejor leer el datasheet la sección de setup (-http://www.atmel.com/dyn/resources/prod_documents/qt60486_801.pdf). Finalmente, una vez configurado el teclado, se forza un reset y se entra al ciclo infinito en el cual se manda la instrucción de reportar tecla (0x06). Cabe recalcar que este programa solo envía mas no recibe. La lectura la realiza el driver del teclado en espacio de kernel.

El código, comentado, de este programa es (basado en el código de la prueba de funcionamiento):

#include <stdio.h>
#include <unistd.h>
 
#include "jz47xx_gpio.h"
#include "jz47xx_mmap.h"
 
#define CS2_PORT JZ_GPIO_PORT_B
#define CS2_PIN  26 //pin del chip select 2
#define RHR 0x00 /* Receive Holding Register */
#define THR 0x00 /* Receive Holding Register */
#define IER 0x01 /* Interrupt Enable Register */
#define FCR 0x02 /* FIFO control Register       */
#define ISR 0x02 /* Interrupt Status Register */
#define LCR 0x03 /* Line control register    */
#define MCR 0x04 /* Modem Control Register         */
#define LSR 0x05 /* Line Status Register      */
#define MSR 0x06 /* Modem Status Register          */
#define SCR 0x07 /* Scratch pad Register        */
/* This two offsets are used for defining the baud rate   */
#define       DIVLSB                    0x00 /* Divisor LSB latch address */
#define       DIVMSB                    0x01 /* Divisor MSB Latch address */
 
 
int main () {
 
  JZ_PIO *pio;
  char *virt_addr, get_tecla;
  int i;
  pio = jz_gpio_map (CS2_PORT);//mapeo del CS2
  jz_gpio_as_func (pio, CS2_PIN, 0);//habilitar el CS2
 
virt_addr = (int *) jz_mmap (0x13010000) + 0x18;//configurador de memoria externa
 if (*virt_addr != 0xFFF7700)
    {                           // 0 WS, 8 bits
      *virt_addr = 0xFFF7700;
    //  printf ("Configuring CS2 8 bits \n");
    }
  else
    printf ("CS2, already configured\n");
 
  virt_addr = (JZ_REG *) jz_mmap (0x15000000);//direccion fisica de la fpga
 
  virt_addr[LCR]=0x80;//registros de configuracion de UART
  virt_addr[DIVMSB]=0x00;
  virt_addr[DIVLSB]=0x0C;
  virt_addr[LCR]=0x03;
  virt_addr[IER]= 0x01;
 
		virt_addr[THR]=0x0f;//inicio de estados
		usleep(20000);
		virt_addr[THR]=0x0E;
		usleep(20000);
		virt_addr[THR]=0x01;
		usleep(20000);
		virt_addr[THR]=0x01;
		usleep(20000);
		for (i=0;i<48;i++)//registros de configuracion
			{virt_addr[THR]=0x26;//registro que domina la sensibilidad
			usleep(20000);}
		for (i=0;i<48;i++)
			{virt_addr[THR]=0x4A;
			usleep(20000);}
		for (i=0;i<48;i++)
			{virt_addr[THR]=0x52;
			usleep(20000);}
		for (i=0;i<48;i++)
			{virt_addr[THR]=0x14;
			usleep(20000);}
		for (i=0;i<48;i++)
			{virt_addr[THR]=0xA6;
			usleep(20000);}
		virt_addr[THR]=0x11;
		usleep(20000);
		virt_addr[THR]=0x00;
		usleep(20000);
		virt_addr[THR]=0x64;
		usleep(20000);
		virt_addr[THR]=0x00;
		usleep(20000);
		virt_addr[THR]=0x6c;
		usleep(20000);
		virt_addr[THR]=0x00;
		usleep(20000);
		virt_addr[THR]=0x00;//fin de registros de configuracion
		usleep(20000);
		virt_addr[THR]=0x04;
		usleep(20000);
		virt_addr[THR]=0x04;
		usleep(20000);
		virt_addr[THR]=0x0f;
		usleep(20000);
		virt_addr[THR]=0x0e;//fin de estados finitos
 
 
 
			while (1){
 
				//recepcion de los registros enviados por el integrado
 
				virt_addr[THR]=0x06;
				in=virt_addr[THR];
				usleep(60000);}

[edit] Drivers

[edit] Drivers a utilizar y modificar

Para la implementacion del proyecto, es necesario el diseño de el driver del teclado (basado en el atakbd.c).

Sin embargo, debido a que en un principio se pensó que era necesario efectuar el driver de la uart para después invocarlo desde el driver del teclado también se implemento el driver de la uart (8250_fpga.c).

[edit] Driver Teclado

Como fue documentado anteriormente, el driver del teclado fue hecho con base en el driver de Atari de un teclado llamado atakbd.c. A este driver se le hicieron ciertas modificaciones para poder implementarlo. El driver se puede dividir en 3 partes:

  1. Keycode y decodificadores de la información enviada por el integrado: En el arreglo del keycode simplemente se definen las teclas a utilizar en el driver, es decir, con estas teclas o su combinación obtendremos todos los caracteres deseado para el proyecto. Por otro lado, las funciones que contiene los decodificadores simplemente asignan a una variable global (salida) la tecla del keycode según lo oprimido en el teclado capacitivo físico (hay dos decodificadores: uno para cuando la tecla FN esta desactiva y otro para cuando FN esta activa). En otras palabras, estas funciones analiza la tecla o las secuencias de teclas (si FN esta activa) y devuelve el keycode para ser procesado posteriormente.
  2. Atención a interrupciones y acciones a tomar: Por medio de la función de irq_handler() se atienden las interrupciones y se reciben los datos mandados por el integrado por medio de la función inb(). Además, por medio de esta función se controla la secuencia de recibir teclas y se lee como tal los datos para ser representados por pantalla. Es decir, acá esta toda la lógica la cual controla la activación de las teclas FN, ctrl, alt y shift. Cabe aclarar que este teclado solo reporta una tecla a la vez (por medio de la función input_report_key()) por lo que la combinación de teclas simultaneas no existe. Por este motivo se hace necesario que al realizar una combinación de tecla primero se oprima la tecla función y después la tecla (ej: en vez de shift+a=A oprimimos shift soltamos y después oprimimos a y veremos por pantalla A ). Finalmente es importante decir que aparte de la manipulación de estas teclas, también existe una lógica la cual controla que al oprimir por largo tiempo una tecla solo la imprima o lea una vez.
  3. Inicialización y configuración del teclado: Este proceso se realiza en la función atakbd_init(). Acá, se asignan variables relevantes como función y valor de IRQ, ubicación de memoria para el dispositivo, dirección física, tipo de bus e indica que hay un nuevo dispositivo de entrada. Esta es la parte que hace que el kernel reconozca al teclado.

El código comentado del driver del teclado es:

/*Atmel QT-60486 drivers
 *Sistemas Embebidos
 *UNAL
 *Authors: amasprillav,jsbuendiag,jamonsalveh
 *Year: 2010-II*/
 
#include <linux/module.h>       /* Needed by all modules */
#include <linux/kernel.h>       /* Needed for KERN_INFO */
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/interrupt.h>  /* We want an interrupt */
#include <linux/irq.h>        /* We want an interrupt */
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <asm/delay.h>
#include <asm/uaccess.h>  
#include <asm/io.h>
#include <linux/gpio.h>
#include <asm/mach-jz4740/gpio.h>
#include <linux/input.h>
#define FPGA_IRQ_PIN          JZ_GPIO_PORTC(15) //pin del irq
#define FPGA_BASE 	      0xB5000000 //direccion virtual base de la FPGA
 
MODULE_AUTHOR("amasprillav,jsbuendiag,jamonsalveho");
MODULE_DESCRIPTION("Atmel QT60486 Capacitive Keyboard Driver");
MODULE_LICENSE("GPL");
 
static unsigned int fn=0, anterior;//variables globales para indicar si la tecla fn ha sido oprimida 
//y para controlar que no se escriba una tecla mas de una vez respectivamente
 
//arreglo que posee el keycode de las teclas posibles
static unsigned int cap_keycode[] = {
	KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0,
	KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P,
	KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, KEY_BACKSPACE,
	KEY_ESC, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, KEY_EQUAL, KEY_ENTER,
	KEY_TAB, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_COMMA, KEY_DOT, KEY_BACKSLASH, KEY_SLASH, KEY_APOSTROPHE, KEY_UP, KEY_SEMICOLON,
	KEY_LEFTSHIFT, KEY_RIGHTALT, KEY_LEFTALT, KEY_SPACE, KEY_RIGHTCTRL, KEY_LEFT, KEY_DOWN, KEY_RIGHT, KEY_MINUS, KEY_RIGHTALT
};
 
unsigned int irq;//variable global del numero del irq asignado
 
//funcion del codificador normal
static unsigned int keydecode(unsigned char valor){
	unsigned int salida;//variable la cual tiene el valor del keycode para ser usada en las demas funciones
	//codificador para cuando se oprime una tecla normalmente
	switch(valor){
		case 150:salida=KEY_Q;break;
		case 200:salida=KEY_W;break;
		case 170:salida=KEY_E;break;
		case 244:salida=KEY_R;break;
		case 22:salida=KEY_T;break;
		case 72:salida=KEY_Y;break;
		case 41:salida=KEY_U;break;
		case 119:salida=KEY_I;break;
		case 149:salida=KEY_O;break;
		case 203:salida=KEY_P;break;
		case 116:salida=KEY_A;break;
		case 169:salida=KEY_S;break;
		case 104:salida=KEY_D;break;
		case 54:salida=KEY_F;break;
		case 212:salida=KEY_G;break;
		case 138:salida=KEY_H;break;
		case 235:salida=KEY_J;break;
		case 181:salida=KEY_K;break;
		case 87:salida=KEY_L;break;
		case 9:salida=KEY_BACKSPACE;break;
		case 42:salida=KEY_ESC;break;
		case 247:salida=KEY_Z;break;
		case 55:salida=KEY_X;break;
		case 105:salida=KEY_C;break;
		case 139:salida=KEY_V;break;
		case 213:salida=KEY_B;break;
		case 180:salida=KEY_N;break;
		case 234:salida=KEY_M;break;
		case 8:salida=KEY_EQUAL;break;
		case 86:salida=KEY_ENTER;break;
		case 75:salida=KEY_TAB;break;
		case 21:salida=KEY_LEFTBRACE;break;
		case 245:salida=KEY_RIGHTBRACE;break;
		case 171:salida=KEY_COMMA;break;
		case 73:salida=KEY_DOT;break;
		case 23:salida=KEY_BACKSLASH;break;
		case 118:salida=KEY_SLASH;break;
		case 40:salida=KEY_APOSTROPHE;break;
		case 202:salida=KEY_UP;break;
		case 137:salida=KEY_LEFTSHIFT;break;
		case 215:salida=KEY_RIGHTALT;break;
		case 53:salida=KEY_LEFTALT;break;
		case 107:salida=KEY_SPACE;break;
		case 10:salida=KEY_RIGHTCTRL;break;
		case 84:salida=KEY_LEFT;break;
		case 182:salida=KEY_DOWN;break;
		case 232:salida=KEY_RIGHT;break;
		default: salida=0;break;
		}
 
 
 
 
 
return salida;
} 
 
 
//funcion del codificador FN
static unsigned int keydecodefn(unsigned char valor){
	unsigned int salida; //variable la cual tiene el valor del keycode para ser usada en las demas funciones
	//codificador para cuando se oprime una tecla una vez se ha oprimida la tecla FN
	switch(valor){
		case 150:salida=KEY_1;break;
		case 200:salida=KEY_2;break;
		case 170:salida=KEY_3;break;
		case 244:salida=KEY_4;break;
		case 22:salida=KEY_5;break;
		case 72:salida=KEY_6;break;
		case 41:salida=KEY_7;break;
		case 119:salida=KEY_8;break;
		case 149:salida=KEY_9;break;
		case 203:salida=KEY_P;break;
		case 116:salida=KEY_7;break;
		case 169:salida=KEY_8;break;
		case 104:salida=KEY_9;break;
		case 54:salida=KEY_0;break;
		case 212:salida=KEY_MINUS;break;
		case 138:salida=KEY_H;break;
		case 235:salida=KEY_4;break;
		case 181:salida=KEY_5;break;
		case 87:salida=KEY_6;break;
		case 9:salida=KEY_BACKSPACE;break;
		case 42:salida=KEY_ESC;break;
		case 247:salida=KEY_Z;break;
		case 55:salida=KEY_X;break;
		case 105:salida=KEY_C;break;
		case 139:salida=KEY_V;break;
		case 213:salida=KEY_B;break;
		case 180:salida=KEY_1;break;
		case 234:salida=KEY_2;break;
		case 8:salida=KEY_3;break;
		case 86:salida=KEY_ENTER;break;
		case 75:salida=KEY_TAB;break;
		case 21:salida=KEY_LEFTBRACE;break;
		case 245:salida=KEY_RIGHTBRACE;break;
		case 171:salida=KEY_COMMA;break;
		case 73:salida=KEY_DOT;break;
		case 23:salida=KEY_BACKSLASH;break;
		case 118:salida=KEY_0;break;
		case 40:salida=KEY_MINUS;break;
		case 202:salida=KEY_UP;break;
		case 137:salida=KEY_LEFTSHIFT;break;
		case 215:salida=KEY_RIGHTALT;break;
		case 53:salida=KEY_LEFTALT;break;
		case 107:salida=KEY_SPACE;break;
		case 10:salida=KEY_RIGHTCTRL;break;
		case 84:salida=KEY_LEFT;break;
		case 182:salida=KEY_DOWN;break;
		case 232:salida=KEY_RIGHT;break;
		default: salida=0;break;
		}
 
 
return salida;
} 
 
 
 
 
static struct input_dev *cap_keyboard;//estructura que define el dispositivo de entrada
 
static irqreturn_t irq_handler(int irq, void *dev_id){  //funcion que atiende las interrupciones y define las acciones a tomar
 
	  if(inb(FPGA_BASE)==anterior){	//condicional para deshabilitar la multiple lectura de una tecla    
		return IRQ_HANDLED;
	  }
 
	  else if(inb(FPGA_BASE)==85){	//condicional para evitar que se escriba algo del valor mandado por defecto del integrado
	  	anterior=inb(FPGA_BASE);	    
		return IRQ_HANDLED;
	  }
 
 
	else if (inb(FPGA_BASE)==148){	//condicional para activacion de la tecla FN
 
		input_sync(cap_keyboard);
		 anterior=inb(FPGA_BASE);		
		fn=1;
	   	return IRQ_HANDLED;}
 
	//condicional para evaluar la activacion de las teclas alt, alt gr, ctrl y shift
	else if ((inb(FPGA_BASE)==137) || (inb(FPGA_BASE)==215) || (inb(FPGA_BASE)==10) || (inb(FPGA_BASE)==53)){ 
		input_report_key(cap_keyboard,keydecode(inb(FPGA_BASE)),1);//funcion que detecta la tecla de la dirección base y la pone en 1 o 0 logico
		input_sync(cap_keyboard); //sincronizar el buffer tty con la ultima tecla oprimida
		anterior=inb(FPGA_BASE); //asignacion a la variable global de la ultima tecla oprimida
	    return IRQ_HANDLED; //retorno de interrupcion controlada
	  }
 
	else if(fn!=1) { //condicional para detectar la tecla ya sea en modo normal o con combinacion de ctrl, alt o shift		
		input_report_key(cap_keyboard,keydecode(inb(FPGA_BASE)),1);
	    	input_report_key(cap_keyboard,KEY_LEFTSHIFT,0);
	    	input_report_key(cap_keyboard,KEY_RIGHTALT,0);
	    	input_report_key(cap_keyboard,KEY_LEFTALT,0);
	    	input_report_key(cap_keyboard,KEY_RIGHTCTRL,0);
		input_report_key(cap_keyboard,keydecode(inb(FPGA_BASE)),0);
		input_sync(cap_keyboard);
		anterior=inb(FPGA_BASE);
	   	return IRQ_HANDLED;}
 
	//condicional que indica que hacer en el caso de haber oprimido FN y otra letra. La mayoria son numeros
	else if ((inb(FPGA_BASE)==41) ||(inb(FPGA_BASE)==119) ||(inb(FPGA_BASE)==149) ||(inb(FPGA_BASE)==235) ||(inb(FPGA_BASE)==181) ||(inb(FPGA_BASE)==876) ||(inb(FPGA_BASE)==180) ||(inb(FPGA_BASE)==234) ||(inb(FPGA_BASE)==8) ||(inb(FPGA_BASE)==118) ||(inb(FPGA_BASE)==40)){
 
		input_report_key(cap_keyboard,KEY_LEFTSHIFT,0);
	    	input_report_key(cap_keyboard,KEY_RIGHTALT,0);
	    	input_report_key(cap_keyboard,KEY_RIGHTCTRL,0);
	    	input_report_key(cap_keyboard,KEY_LEFTALT,0);
		input_report_key(cap_keyboard,keydecodefn(inb(FPGA_BASE)),1);
		input_report_key(cap_keyboard,keydecodefn(inb(FPGA_BASE)),0);
		input_sync(cap_keyboard);
		fn=0;
		anterior=inb(FPGA_BASE);
	   	return IRQ_HANDLED;}
 
	//condicional que indica que hacer en el caso de haber oprimido FN y otra letra. Estas son teclas que requieren de shift para ilustrarlas
	else 
	 	{input_report_key(cap_keyboard,KEY_LEFTSHIFT,1);
	    	input_report_key(cap_keyboard,KEY_RIGHTALT,0);
	    	input_report_key(cap_keyboard,KEY_RIGHTCTRL,0);
	    	input_report_key(cap_keyboard,KEY_LEFTALT,0);
		input_report_key(cap_keyboard,keydecodefn(inb(FPGA_BASE)),1);
		input_report_key(cap_keyboard,keydecodefn(inb(FPGA_BASE)),0);
	 	input_report_key(cap_keyboard,KEY_LEFTSHIFT,0);
		input_sync(cap_keyboard);
		fn=0;
		 anterior=inb(FPGA_BASE);
	   	return IRQ_HANDLED;}
 
	  return IRQ_HANDLED;
}
 
static int __init atakbd_init(void)//funcion de inicializacion del teclado
{
	int i, error,res;
 
 
	cap_keyboard = input_allocate_device(); // ubica memoria para el dispositivo de entrada
	if (!cap_keyboard)
		return -ENOMEM;
 
	cap_keyboard->name = "CAP Keyboard"; //asignacion de datos del teclado
	cap_keyboard->phys = "capkey/input0"; //direccion fisica
	cap_keyboard->id.bustype = BUS_HOST;//tipo de bus
	cap_keyboard->id.vendor = 0x0010;
	cap_keyboard->id.product = 0x0001;
	cap_keyboard->id.version = 0x001;
 
	cap_keyboard->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
	cap_keyboard->keycode = cap_keycode;//asignacion del arreglo del keycode
	cap_keyboard->keycodesize = sizeof(unsigned char);
	cap_keyboard->keycodemax = ARRAY_SIZE(cap_keycode);
 
	for (i = 0; i <= 60; i++) { //funcion que ubica las 48 teclas (keycode) en memoria
		set_bit(cap_keycode[i], cap_keyboard->keybit);
	}
 
	error = input_register_device(cap_keyboard);//indica al sistema el nuevo dispositivo de entrada
	if (error) {
		input_free_device(cap_keyboard);
		return error;
	}
 
        irq = gpio_to_irq(FPGA_IRQ_PIN);//asigna el numero del irq al pin fisico del procesador
 
        res = request_irq(irq, irq_handler, IRQF_DISABLED | IRQF_TRIGGER_RISING, "cap_keyboard", NULL); // asignar el valor del irq y la funcion que lo utiliza
	return 0;
}
 
static void __exit atakbd_exit(void)
{
	input_unregister_device(cap_keyboard);//libera los recursos utilizados por el dispositivo
	free_irq(irq, NULL);
}
 
module_init(atakbd_init);
module_exit(atakbd_exit);

[edit] 8250_fpga.c

Para la implementación de este driver, en un principio se intentó modificar el archivo setup.c, el cual tenía unas rutinas específicas para inicializar la UART que trae por defecto el Xburst. Esto no fué posible, debido a las grandes diferencias existentes entre las dos UARTs a pesar de ser del mismo tipo (16550). Por esa razón se implementó el driver de dicho dispositivo utilizando ejemplos existentes en diferentes libros [5],[6] y de algunos portales con foros en Internet (pronto se describirán cuáles fueron) en los cuales mencionaron algunos problemas al elaborar los drivers y sus soluciones. A continuación se mostrarán los pasos que se utilizaron para implementar dicho driver y los resultados del mismo:

  1. Pasos para implementar el controlador:
  • En un principio, a todo driver de Linux, hay que analizarlo como si se tratase de un archivo. Ese archivo tiene unas operaciones las cuales tienen que ser tomadas en cuenta (en este caso no son operaciones sencillas como abrir, cerrar, leer y escribir). Esas operaciones ya están definidas en el estándar de un driver serial de Linux y van a una estructura definida (llamada struct uart_ops en la biblioteca include/linux/serial_core.h). Esa estructura está definida como sigue:
struct uart_ops {
  unsigned int    (*tx_empty)(struct uart_port *);
  void   (*set_mctrl)(struct uart_port *,
                      unsigned int mctrl);
  unsigned int    (*get_mctrl)(struct uart_port *);
  void   (*stop_tx)(struct uart_port *,
                    unsigned int tty_stop);
  void   (*start_tx)(struct uart_port *,
                     unsigned int tty_start);
  void   (*send_xchar)(struct uart_port *, char ch);
  void   (*stop_rx)(struct uart_port *);
  void   (*enable_ms)(struct uart_port *);
  void   (*break_ctl)(struct uart_port *, int ctl);
  int    (*startup)(struct uart_port *);
  void   (*shutdown)(struct uart_port *);
  void   (*change_speed)(struct uart_port *,
                         unsigned int cflag,
                         unsigned int iflag,
                         unsigned int quot);
  void        (*pm)(struct uart_port *,
                    unsigned int state,
                unsigned int oldstate);
  int        (*set_wake)(struct uart_port *,
                         unsigned int state);
  /*
   * Return a string describing the port type
   */
  const char *(*type)(struct uart_port *);
  /*
   * Release IO and memory resources used by
   * the port. This includes iounmap if necessary.
   */
  void   (*release_port)(struct uart_port *);
  /*
   * Request IO and memory resources used by the
   * port.  This includes iomapping the port if
   * necessary.
   */
  int    (*request_port)(struct uart_port *);
  void   (*config_port)(struct uart_port *, int);
  int    (*verify_port)(struct uart_port *,
                        struct serial_struct *);
  int    (*ioctl)(struct uart_port *, unsigned int,
                  unsigned long);
};

En el caso anterior, utilizamos algunas de éllas, como por ejemplo:

static struct uart_ops fpga_uart_ops = {
	.tx_empty	= fpga_tx_empty,
	.set_mctrl	= fpga_set_mctrl,
	.get_mctrl	= fpga_get_mctrl,
	.stop_tx	= fpga_stop_tx,
	.start_tx	= fpga_start_tx,
	.stop_rx	= fpga_stop_rx,
	.enable_ms	= fpga_enable_ms,
	.break_ctl	= fpga_break_ctl,
	.startup	= fpga_startup,
	.shutdown	= fpga_shutdown,
	.set_termios	= fpga_set_termios,
	.type		= fpga_type,
	.release_port	= fpga_release_port,
	.request_port	= fpga_request_port,
	.config_port	= fpga_config_port,
	.verify_port	= fpga_verify_port,
};

las anteriores eran funciones que se definen en el código fuente, más adelante se hablará en detalle de cada una de éllas.

  • Como ya se sabe también, todo driver de Linux contiene un número mayor y un número menor. Por lo cual los números mayores y menores se escogen como 240 y 0 respectivamente. Eso también es definido en una estructura ya estándar llamada 'static struct uart_driver', la cual está definida como sigue:
static struct uart_driver fpga_driver = {
	.owner		= THIS_MODULE,
	.driver_name	= driver_name,
	.dev_name	= tty_dev_name,
	.major		= 240,
	.minor		= 0,
	.nr		= 1,
};

esta nos permite utilizar la función uart_register_driver y también la función uart_register_one_port. En la estructura anterior se define cuál es el propietario del driver, cómo se llama el driver y cómo va a ser localizado en /dev (.driver_name y .dev_name), los números mayor y menor y el número de UARTs que vamos a implementar en el sistema (en este caso es sólamente 1).

  • Ahora, teniendo esas dos estructuras hay que definir cómo es que se comporta el puerto debido a que todos los puertos seriales no son iguales (son de distintas familias y distintos fabricantes), por lo que se tiene que definir otra estructura que se llama uart_port. En este caso la definimos también global pero en la rutina __init le introducimos los valores que nosotros queremos que tenga.
  • Por ese lado todo estaría bien, solo faltaría escribir las operaciones de la UART y además hacer las rutinas de inicialización (__init) y salida del módulo (__exit), las cuales se definen a continuación:
static int __init fpga_init (void){
	int ret;
	unsigned int irq;
	//registrar el driver
	ret = uart_register_driver(&fpga_driver);
	if (ret) {
		pr_err("%s: No se pudo registrar la UART!\n", driver_name);
		return ret;
	}
	//asignar el pin de IRQ al que está conectado a la FPGA (IRQ_F en el esquemático)
 	irq = gpio_to_irq(FPGA_IRQ_PIN);
	//insertar los valores del puerto
	port.membase=(u8*)FPGA_BASE;
	port.line = 0;
	port.ops = &fpga_uart_ops;
	port.flags = ASYNC_BOOT_AUTOCONF;
	port.type = PORT_16550A;
	port.irq = irq;
	ret = uart_add_one_port(&fpga_driver, &port);
	if (ret) {
		pr_err("%s: No se pudo agregar el puerto ttyFPGA0!\n", driver_name);
		uart_remove_one_port(&fpga_driver, &port);
		uart_unregister_driver(&fpga_driver);
		return ret;
	}
 
	printk ("%s: Modulo cargado...\n", driver_name);
	return 0;		
}

En esta rutina lo que se hace es: en un principio se registra el driver de la UART (uart_register_driver(&fpga_driver)), el cual le dice al sistema operativo que el dispositivo que se está controlando es una UART (es uno de los procesos más importantes en esta rutina, debido a que acá es donde se le dice al sistema que existe una UART que se llama 'ttyFPGA'). Esta función si retorna un valor distinto de cero, se saca un error que dice que no se pudo agregar el puerto y se ejecuta el goto que retorna el número del error y el programa finaliza. Luego de esto, se ingresan los valores del puerto como son el irq, las operaciones del driver, el tipo de puerto y los flags que permiten trabajar la UART como si fuera una de tipo 16550. También, se registra el puerto asociado a ese driver (con la función uart_add_one_port(&fpga_driver,&port)) y si todo resulta bien el módulo se carga satisfactoriamente.

En la rutina __exit, básicamente lo que se hace es liberar el puerto y liberar el recurso que ocupa el uart_driver.

Las operaciones que utilizamos para poder trabajar con la UART son:

static int fpga_startup(struct uart_port *port){
 
	int res;
  	res = request_irq(port->irq, vuart_rxint, IRQF_DISABLED | IRQF_TRIGGER_FALLING, "ttyFPGA",(void*)port);
	if(res){
		printk("No se pudo registrar el IRQ!\n");
		return res;
	}
	//Envío los registros de inicialización para la velocidad de transmisión y recepción, también el formato de datos...
	outb(0x80,FPGA_BASE+LCR);
	//acá lo coloco a 9600 baudios porque es la velocidad de transmisión (esto es temporal).
	outb(0x00,FPGA_BASE+DIVMSB);
	outb(0x0c,FPGA_BASE+DIVLSB);
	//registro para habilitar las interrupciones...
	outb(0x03,FPGA_BASE+LCR);
	outb(0xCF,FPGA_BASE+FCR);
	outb(0x0B,FPGA_BASE+MCR);
	outb(0x01,FPGA_BASE+IER);
	return 0;
}

La anterior función lo que hace básicamente es registrar la irq que se asignó en __init, por lo cual en el log del kernel entrega lo siguiente: [ 2103.500000] ttyFPGA0 at I/O 0x0 (irq = 135) is a ttyfpga. Lo que quiere decir que el módulo fué cargado exitosamente.

Luego de esta función, se ingresa a la siguiente que es:

static void fpga_start_tx(struct uart_port *port){
 
	while(1){
		fpga_putc(port,port->state->xmit.buf[port->state->xmit.tail]);
		printk("enviado: %c\n",port->state->xmit.buf[port->state->xmit.tail]);
		//Ajustar la cola de la UART Al buffer
		port->state->xmit.tail=(port->state->xmit.tail+1)&(UART_XMIT_SIZE-1);
		port->icount.tx++;
		if(uart_circ_empty(&port->state->xmit))break;
	}
 
}

La cual es la que transmite los datos que se escriben y se almacenan en el búfer de transmisión, pero como éste es una estructura de datos organizada en forma de memoria dinámica, se tiene que correr el apuntador de la estructura del último elemento para poder almacenar un siguiente elemento en el último que se ingresó. es lo que se hace en las útlimas tres líneas del código anteriormente descrito.


Otra función importante que utilizamos es:

static irqreturn_t vuart_rxint(int irq,void *dev_id){	
		printk("tecla recibida: %c\n",inb(FPGA_BASE));	
		tty_insert_flip_char(port.state->port.tty, inb(FPGA_BASE), TTY_NORMAL);
		tty_flip_buffer_push(port.state->port.tty);
		return IRQ_HANDLED;
}

La cual es la que reacciona cuando se presenta una interrupción al momento de recibir un dato. Esta lo que hace es colocar en la pila que se genera con el buffer de datos el último caracter que se escribió.

Otra función importante que se utilizó es:

static void fpga_config_port(struct uart_port *port, int flags){
	if (flags & UART_CONFIG_TYPE)
               port->type = PORT_16550A;
}

Esta estructura se coloca como un comodín, es simplemente para poder establecer ciertos flags para poder decirle al sistema que es una UART 16550A, esto sólamente tiene la utilidad de indicar al sistema que existe una UART y que se inicien las operaciones anteriormente descritas, no influye en el funcionamiento debido a que las UARTs que vienen por defecto no van a influir en el funcionamiento del sistema.

Otra estructura importante es la que desconecta la UART y hace que deje de funcionar cuando no se necesita.

static void fpga_shutdown(struct uart_port *port){free_irq(port->irq, (void*)port);}

Esta función solo hace que se libere el IRQ establecido en la rutina __init, no tiene ningún otro efecto.

Las estructuras de get_termios y set_termios, no se utilizan debido a que sólamente necesitamos transmitir una sola velocidad. La configuración del baud rate se realizó en la función fpga_startup.

[edit] Comprobación del funcionamiento del driver

Para comprobar que el driver si funciona efectivamente, configuramos la FPGA con el bitstream que contiene la UART como periférico (uart_peripheral.bit, de la cual se hablará más adelante de cómo es implementada), se habilita el CS2 para poder establecer la escritura en el periférico y además se carga el módulo generado (con el comando insmod 8250_fpga.ko).

Para habilitar el CS2, se utiliza el siguiente código en C (extraído del jz_init_sram):

#include "jz47xx_gpio.h"
#include "jz47xx_mmap.h"
 
#define CS2_PORT JZ_GPIO_PORT_B
#define CS2_PIN  26
 
int main (){
  JZ_PIO *pio;
  // Set GPIOB26 as part of External Memory Controller
  pio = jz_gpio_map (CS2_PORT);
  jz_gpio_as_func (pio, CS2_PIN, 0);
 
  return 0;
}

Luego de habilitar el CS2 e instalar el módulo con insmod, se realiza la prueba utilizando el comando echo (echo prueba121212>/dev/ttyFPGA0) y se abre otra sesión SSH en la cual se abre el minicom configurado hacia el puerto /dev/ttyS0 (UART de la tarjeta SIE). En el minicom se observan las letras transmitidas. En la siguiente figura se muestra dicho comportamiento:

Prueba de transmisión


Ahora para probar la recepción, se puentean los pines de TxD y RxD de la FPGA conectados al MAX232 de la placa SIE. Luego se abre el Minicom para observar lo que transmite y recibe el dispositivo, se observa en dicha ventana que se transmitieron y recibieron los caracteres de inicialización del módem enviados a la UART, también se observa que al transmitir cualquier letra se recibe la misma tecla una sola vez, lo cual hace que se concluya que el driver está instalado correctamente.

Prueba de recepción


[edit] Esquema del teclado

A continuación se muestra una propuesta del teclado, para poder cubrir las teclas principales fue necesario utilizar FN que permite utilizar las segundas teclas, SHIFT referenciado en azul y ALT GR en rojo.

Esquema del teclado


[edit] Editor de texto en Qt

Para probar el teclado, fue necesario realizar un programa, en este caso un editor de texto, desarrollado en Qt Creator que es un entorno de desarrollo integrado ideal para hacer aplicaciones, ya que usa bibliotecas Qt que son multiplataforma. A continuación se muestra una imagen del programa con el editor de texto ya hecho, que realmente es bastante sencillo y básico ya que esta no era la finalidad del proyecto.


Editor de texto básico realizado en Qt Creator


.

Es importante aclarar como compilar este programa de tal manera que se pueda ejecutar en la sie, una vez terminada la aplicación, se debe ubicar en el directorio del proyecto y ejecutar el comando make distclean con el fin de eliminar los ejecutables y otros archivos innecesarios, posteriormente en el mismo directorio se copia el archivo compile-mips que se encuentra en los archivos fuentes de la sie en el ejemplo del osciloscopio, se edita de acuerdo a los directorios del pc igual que el qmake.conf. Finalmente desde la consola en el directorio del proyecto se ejecuta ./compile-mips y ya esta listo el ejecutable para subir a la sie.

[edit] Análisis económico del proyecto

Para este análisis de costos se evaluó la realización del prototipo y la realización de 100 unidades del producto.

Realización del prototipo:

Evaluación de las horas de trabajo:

  • 12 horas de trabajo a la semana
  • 3 integrantes
  • 15 semanas

Total: 540 horas de trabajo

Costo por hora laboral: $25.000 Valor de la mano de obra: $13.500.000 total de los 3 integrantes.

  • Valor de la mano de obra por cada integrante: $4.500.000 en total ($1.125.000 al mes).
  • 2 tarjetas SIE
  • PCB en Microensamble: $160.000
  • Tarjeta Hija: $15.000
  • Componentes: $160.000
  • Gastos varios (servicios públicos): $40.000

Total: $14.275.000


Realización de 100 unidades:

  • PCB(x100 Unidades): $401.62 USD
  • Envío del PCB +Impuestos: $150 USD
  • chips ATMEL(x100 Unidades): $940 USD
  • Componentes del PCB(x100 Unidades): $550 USD
  • Soldadura y ubicación de componentes (x100 unidades): $300 dólares

Costo Total de fabricación=$2342 USD ($23.42 USD C/U) Precio de venta=$30 USD C/U.

Si se evalúa el valor del dólar a hoy (27/11/2010), cada teclado cuesta: $57200.7 pesos

[edit] Referencias y Bibliografía utilizada

  1. Hoja de datos del fabricante del chip
  2. Referencia para el diseño de condensadores interdigitales de ATMEL
  3. Tutorial de KiCAD
  4. Instrucciones para la instalación del Toolchain para la tarjeta SIE
  5. Sreekrishnan Venkateswaran, Essential Linux device drivers, Prentice Hall, Boston, 2008
  6. Jonathan Corbet, Alessandro Rubini, and Greg Kroah-Hartman, Linux Device Drivers, Third Edition, Link, chapter 18.
  7. Linux Journal, The Serial Driver Layer,Dec 01, 2002 By Greg Kroah-Hartman
Personal tools
Namespaces
Variants
Actions
Navigation
interactive
Toolbox
Print/export