Experimentando con mTouch: lectura de sensores capacitivos

14 diciembre, 2009

En este proyecto experimentamos con la tecnología mTouch de Microchip, que incorporan algunos de sus micros y que permite leer sensores capacitivos.


Los sensores capacitivos nos ofrecen la posibilidad de diseñar interfaces de usuario muy versátiles gracias a que son capaces de detectar la pulsación de un dedo a través de un material que los recubra, como puedes comprobar con el metacrilato y el papel que he usado en el vídeo más abajo.

Además, usando esta técnica se consiguen paneles perfectamente herméticos válidos para la intemperie ya que la humedad no puede pasar a través de los sensores.

La familia de micros de Microchip que incorpora esta tecnología es esta: http://www.microchip.com/en_us/technology/mtouch/

En mi caso, elegí un 16LF727 para mi prototipo:

 

Como se puede comprobar, la PCB está realizada en una única cara, y sólo tiene dos puentes. Esto facilita su rápida fabricación y montaje.

Para poder diseñarla a una sola cara, tuve que colocar los Touchpads y los leds en los pines que «geográficamente» más me interesaban, y esto no coincide con el orden de los mismos en el esquemático. Por ello el esquema ha quedado algo desordenado:

 

Aquí puedes ver un vídeo de su funcionamiento:

 

Y este es el código fuente, escrito en CCS:

pruebas mtouch.c

/*
mTouch con 16LF727
http://www.micropic.es/          Nocturno 2009
*/


#include <16LF727.h>
#include <pruebas mtouch.h>

#define ANTIRREBOTES  1
#define SENSIBILIDAD 5

unsigned char indice;
unsigned long lectura[8];     // lectura de cada sensor
unsigned long promedio[8];     // promedio leído de cada sensor
unsigned long umbral;      // umbral que debe superar para que se considere pulsado
unsigned long promedio_alto;    // variable temporal de promedio alto
unsigned long promedio_bajo;    // variable temporal de promedio bajo

typedef struct{       // Estructura de información de los sensores
unsigned char rebotes;
unsigned char Pulsado:1;
unsigned char SePulso:1;
unsigned char Soltado:1;
} TIPO_SENSOR;

TIPO_SENSOR Sensor[8];

/*************************************************************************************
INICIALIZACIÓN
*************************************************************************************/

void inicializacion() {
char i;

setup_oscillator(OSC_16MHZ);
for (indice=0; indice<8; indice++){ // Inicialización de variables
promedio[indice] = 0;
lectura[indice] = 0;
}

/* Utilizaremos estos 8 sensores:
CPS4: RB4
CPS5:  RB5
CPS6: RA4
CPS7: RA5
CPS8: RD0
CPS9: RD1
CPS10: RD2
CPS11: RD3
*/


ANSELA = 0b00110000;
set_tris_a (0b11110111);

ANSELB = 0b00110000;
set_tris_b (0b01110001);

ANSELD = 0b00001111;
set_tris_d (0b11111111);

set_tris_c (0b11101001);

ANSELE = 0b00000000;
set_tris_e (0b00000000);

setup_timer_2(T2_DIV_BY_16,0xB4,15);// Timer2 activo, prescaler 1:16
setup_timer_1(T1_GATE_INVERTED);  // TMR1 activo, asociado a capacitivo, sin prescaler
T1GCON = 0b11100010;     // Timer1 gate activo, y asociado al Timer2

CPSCON0 = 0b10001100;     // Activamos los sensores capacitivos
CPSCON1 = 0x04;      // Empezamos por el sensor nº 4, el primero de los que usamos
indice = 0;

TMR1GIF = 0;       // Borramos el flag de interrupción
TMR1GIE = 1;       // Activamos la interrupción del Timer1 Gate
enable_interrupts(GLOBAL);   // Activamos la gestión de interrupciones

for(i=0; i<8; i++) Sensor[i] = 0; // Inicialización de variables

output_low(LED0);     // Apagamos los leds
output_low(LED1);
output_low(LED2);
output_low(LED3);
output_low(LED4);
output_low(LED5);
output_low(LED6);
output_low(LED7);
}

/*************************************************************************************
RUTINA DE INTERRUPCIÓN
*************************************************************************************/


#INT_DEFAULT
void GestionDeInterrupciones() {
if (TMR1GIF && TMR1GIE) {   // Interrupción del Timer1 Gate
TMR1GIF = 0;      // Borramos el flag de interrupción
TMR1ON = 0;      // Paramos el timer1

// Cálculo de la media móvil de los últimos 4 ciclos
lectura[indice] = promedio_alto = get_timer1() * 4;
promedio_bajo = promedio[indice] / 4;
umbral = promedio[indice]>>SENSIBILIDAD;

// Si ha pulsado, veremos que el Timer se ha acortado, por debajo del promedio - umbral
if (promedio_alto < promedio[indice] - umbral) {
// Chequeo repetitivo para evitar rebotes
if(Sensor[indice].rebotes < ANTIRREBOTES) Sensor[indice].rebotes++;
if(!Sensor[indice].SePulso && Sensor[indice].rebotes == ANTIRREBOTES) {
Sensor[indice].Pulsado = 1;
Sensor[indice].SePulso = 1;
}
} else {
// Si no ha pulsado y estamos en ciclo de rebotes
if(Sensor[indice].rebotes > 0) Sensor[indice].rebotes--;
if(Sensor[indice].SePulso && Sensor[indice].rebotes == 0) {
Sensor[indice].SePulso = 0;
Sensor[indice].Soltado = 1;
}
// Al promedio acumulado le restamos el valor antiguo y le metemos el nuevo
promedio[indice] += promedio_alto/4 - promedio_bajo;
}
set_timer1(0);     // Iniciamos la cuenta en el Timer1
TMR1ON = 1;      // Activamos nuevamente el Timer1
indice ++;      // Pasamos al siguiente sensor
indice &= 0x07;     // La máscara nos permite contar de 0 a 7
CPSCON1 = indice+4;    // Sumamos 4 porque nuestros sensores son del 4 al 11
}
}

/*************************************************************************************
PROGRAMA PRINCIPAL
*************************************************************************************/

void main() {
unsigned char i;

inicializacion();

while (true) {       // bucle infinito
for(i=0; i<8; i++) {   // recorremos los 8 sensores
if(Sensor[i].Pulsado) {  // se ha detectado pulsación
switch(i) {    // encendemos el led que corresponda
case 0:
output_high(LED0);
break;
case 1:
output_high(LED1);
break;
case 2:
output_high(LED2);
break;
case 3:
output_high(LED3);
break;
case 4:
output_high(LED4);
break;
case 5:
output_high(LED5);
break;
case 6:
output_high(LED6);
break;
case 7:
output_high(LED7);
break;
}
Sensor[i].Pulsado = 0; // trabajo realizado, borramos el indicador
} else if(Sensor[i].Soltado) { // se ha quitado el dedo
switch(i) {    // apagamos el led que corresponda
case 0:
output_low(LED0);
break;
case 1:
output_low(LED1);
break;
case 2:
output_low(LED2);
break;
case 3:
output_low(LED3);
break;
case 4:
output_low(LED4);
break;
case 5:
output_low(LED5);
break;
case 6:
output_low(LED6);
break;
case 7:
output_low(LED7);
break;
}
Sensor[i].Soltado = 0; // trabajo realizado, borramos el indicador
}
}
}
}

pruebas mtouch.h

#FUSES INTRC_IO                 //Oscilador interno
#FUSES VCAP_A0                  //VCAP en pin A0
#FUSES NOWDT                    //No Watch Dog Timer
#FUSES NOPROTECT                //Código accesible
#FUSES PLLEN                    //PLL activado
#FUSES MCLR                     //Master Clear activado
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOPUT                    //No Power Up Timer
#use delay (clock=8000000)
// No dejamos que el compilador establezca los TRIS
#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
#use fast_io(D)
#use fast_io(E)
// Registros del micro
#byte ANSELA = 0x185
#byte ANSELB = 0x186
#byte ANSELD = 0x188
#byte ANSELE = 0x189
#byte PORTA = 0x5
#byte PORTB = 0x6
#byte PORTC = 0x7
#byte PORTD = 0x8
#byte PORTE = 0x9
#byte TRISA = 0x85
#byte TRISB = 0x86
#byte TRISC = 0x87
#byte TRISD = 0x88
#byte TRISE = 0x89
#byte T1CON = 0x010
#byte T1GCON = 0x08F
#byte CPSCON0 = 0x108
#byte CPSCON1 = 0x109
// Bits de configuración
#bit TMR1GIF = 0x0C.7 // clear gate intpt flag
#bit TMR1GIE = 0x8C.7 // enable gate intpt
#bit TMR1ON = 0x10.0
// Salidas de leds
#define LED0            PIN_B1
#define LED1            PIN_A3
#define LED2            PIN_B7
#define LED3            PIN_C1
#define LED4            PIN_C2
#define LED5            PIN_C4
#define LED6            PIN_B3
#define LED7            PIN_B2

El código fuente también está disponible para su descarga aquí: Código fuente mtouch en CCS

Espero que lo disfrutes.

Share

Etiquetas: , , ,