Interfaz del teclado a PIC16F877A

0

Estoy conectando un teclado 3X4 a PIC16f877a uC en PORTD como:

En la primera mitad del programa C, puse los primeros cuatro pines en Entrada (De LSB-RD0) y otros en Salida y almacené el valor en una variable y en la otra mitad, puse los últimos cuatro pines en Entrada y otros Para enviar y almacenar su valor en otra variable, he llamado a esta función en un bucle continuo para obtener una tecla presionada. El código es el siguiente:

int getKey(void)
{
    int toRet = 0;
    int firstHalf = 0, secHalf = 0; 
    TRISD = 15, PORTD = 240;
    firstHalf = PORTD;
    TRISD = 240, PORTD = 15;
    secHalf = PORTD;
    switch (-((firstHalf + secHalf)-255))
    {
        case 20: toRet = 7;   break; //1
        case 36: toRet = 8;   break; //2
        case 68: toRet = 9;   break; //3
        case 18: toRet = 4;   break; //4
        case 34: toRet = 5;   break; //5
        case 66: toRet = 6;   break; //6
        case 17: toRet = 1;   break; //7
        case 33: toRet = 2;   break; //8
        case 65: toRet = 3;   break; //9
        case 40: toRet = 0;   break; //0
        case 24: toRet = 10;  break; //*
        case 72: toRet = 11;  break; //#
        default: toRet = 255; break; //null
    }
    return toRet;
}

El código funciona bien en el simulador pero no en el hardware, incluso no genera ninguna tecla aleatoria presionada. No estoy usando ninguna resistencia de subida o bajada porque creo que puede perturbar el mecanismo de entrada. ¿Qué debo hacer para resolver este problema?

EDITAR: Después de la respuesta de Abdullah Kahraman, intenté agregar resistencias de 10k ohm en todas las patillas del teclado, pero el problema sigue siendo el mismo. También en la simulación (simulador de Proteus ISIS), el código funciona bien con una advertencia Logic contention detected on net en la entrada y salida de ambos pines (+ 5V desde uC) en ese momento. No estoy usando ninguna resistencia de tracción en la simulación.

    
pregunta Farid-ur-Rahman

3 respuestas

1

La declaración de cambio parece demasiado complicada, y al menos para mí no fue inmediatamente obvio lo que estaba haciendo. Me gustaría ir con una implementación más sencilla.

Como dijo Abdullah, los retrasos después de establecer los registros TRISD y PORTD pueden ser útiles, así como el rebote de la entrada.

Finalmente, obtendría un o-scope y / o un multímetro para probar estos pines para asegurarme de que todo esté conectado correctamente, alternar según lo esperado y asegurarme de que el teclado se comporte como se espera.

EDIT:

Noté que hay disparadores Schmitt internos en las entradas PORTD que están habilitadas de forma predeterminada. Esto inyectará algo de latencia, aunque no estoy seguro de cuánto, como no dice la hoja de datos.

EDIT2:

Se agregaron demoras difíciles antes del muestreo y la eliminación de la carga capacitiva

int getKey(void)
{
    // Like Abdullah said, these can be bytes
    // Also, you can save image space by not initializing these
    unsigned char toRet;
    unsigned char firstHalf, secHalf;

    // Clear lines (attempt to get rid of capacitive charge)
    TRISD     = 0xff; // Hop off
    PORTD     = 0x00; // Prepare to drive low
    TRISD     = 0x00; // Drive all lines
    __delay_us(10000);// Wait arbitrary about of time
    TRISD     = 0xff; // Stop driving while we change values

    // Drive first half
    PORTD     = 0xf0;
    TRISD     = 0x0f; // Hex makes things easier to read
    __delay_us(10000);
    firstHalf = (PORTD & 0x0f) >> 0; // Mask only the stuff we care about
    TRISD     = 0xff;

    // Clear lines
    PORTD     = 0x00;
    TRISD     = 0x00;
    __delay_us(10000);
    TRISD     = 0xff;

    // Drive second half
    PORTD     = 0x0f;
    TRISD     = 0xf0;
    __delay_us(10000);
    secHalf   = (PORTD & 0xf0) >> 4; // Mask only the stuff we care about
                                     // and shift the nibble we care about down
    TRISD     = 0xff;

    // Simple selection logic
    // Keep in mind that if multiple keys are pressed, this function will return "null"
    switch (firstHalf) {
        case 0x1:
            switch (secHalf) {
                case 0x1: toRet =   7; break; //1
                case 0x2: toRet =   8; break; //2
                case 0x4: toRet =   9; break; //3
                default:  toRet = 255; break; //null
            }
            break;
        case 0x2:
            switch (secHalf) {
                case 0x1: toRet =   4; break; //4
                case 0x2: toRet =   5; break; //5
                case 0x4: toRet =   6; break; //6
                default:  toRet = 255; break; //null
            }
            break;
        case 0x4:
            switch (secHalf) {
                case 0x1: toRet =   1; break; //7
                case 0x2: toRet =   2; break; //8
                case 0x4: toRet =   3; break; //9
                default:  toRet = 255; break; //null
            }
            break;
        case 0x8:
            switch (secHalf) {
                case 0x1: toRet =  10; break; //*
                case 0x2: toRet =   0; break; //9
                case 0x4: toRet =  11; break; //#
                default:  toRet = 255; break; //null
            }
            break;
        default:          toRet = 255; break; //null
    }
    return toRet;
}
    
respondido por el user35740
0

Probablemente tenga un problema de lectura, modificación y escritura. Puedes buscar en Google y encontrar mucha información al respecto. Por favor, haz esto antes de leer mi respuesta.

Para saber qué está pasando aquí, mi conjetura es la siguiente:

TRISD = 15, PORTD = 240;
firstHalf = PORTD;
TRISD = 240, PORTD = 15;
secHalf = PORTD;

En el código anterior, establece algunos pines como entrada y les envía HIGHs con algunos pines que se configuran como salida. Sin embargo, cuando estás haciendo esto, eres muy rápido. Supongamos que estamos haciendo esto solo para un pin utilizando el siguiente pseudo código.

1. TRISD0 = 1;  // D0 is input.
2. TRISD1 = 0;  // D1 is output.
3. PORTD1 = 1;  // Set D1 HIGH. D1 is connected to D0 via a switch.
4. firstHalf = PORTD0; // Read the port D0 to firstHalf variable.

El tiempo pasa entre la línea 3 y la línea 4, donde puede configurar y luego leer puede ser muy corto, dependiendo de la frecuencia de reloj de su CPU. Entonces, tal vez, estás leyendo el pin antes de que se ponga ALTO. Esto podría ser cierto si el tiempo que transcurre entre estas dos líneas es de aproximadamente 500 ns y tiene cables largos que tienen demasiada capacidad parásita que retrasa la línea.

La solución es agregar un retraso que sea suficiente para que funcione y luego configurar 5 veces este retraso para que esté en el lado seguro, que es el siguiente:

TRISD = 15, PORTD = 240;
__delay_us(5);
firstHalf = PORTD;
TRISD = 240, PORTD = 15;
__delay_us(5);
secHalf = PORTD;

Lea las respuestas de esta pregunta .

Pondría resistencias desplegables en los 7 pines del puerto (RD0 a RD6). Eso no debería interferir con su código.

Tema desactivado: ¿Por qué utiliza números enteros cuando trabaja con un microcontrolador de 8 bits? No necesita 16 bits para las variables toRet , firstHalf y secHalf . También el valor que su función devolverá es máximo de 8 bits. Yo usaría variables de tipo "carácter sin signo" para ellos.

Editar:

No entendí tu declaración dentro de switch , así que la reorganicé y ahora funciona en el mundo real pero no en la simulación. Además, he puesto desplegables para los pines RD0-RD6. Sin embargo, este código no tiene debouncing. Tendrá que hacer el debouncing llamándolo varias veces y verificando si el resultado ha cambiado, o de otra manera; Compruébalo hasta que no cambie. Aquí está tu nuevo código:

unsigned char getKey(void)
{
unsigned char toRet = 0;
unsigned char firstHalf = 0, secHalf = 0;

TRISD = 15, PORTD = 240;
__delay_us(5);
firstHalf = PORTD;

TRISD = 240, PORTD = 15;
__delay_us(5);
secHalf = PORTD;
switch ((unsigned int)(firstHalf + secHalf)-255)
{
    case 19: toRet = 7;   break; //1
    case 51: toRet = 8;   break; //2
    case 67: toRet = 9;   break; //3
    case 18: toRet = 4;   break; //4
    case 50: toRet = 5;   break; //5
    case 66: toRet = 6;   break; //6
    case 30: toRet = 1;   break; //7
    case 62: toRet = 2;   break; //8
    case 78: toRet = 3;   break; //9
    case 40: toRet = 0;   break; //0
    case 24: toRet = 10;  break; //*
    case 72: toRet = 11;  break; //#
    default: toRet = 255; break; //null
}
return toRet;
}
    
respondido por el abdullah kahraman
0

Puede ser que el compilador no sepa que PORTD está en el banco 0 y TRISD está en el banco 1. En este dispositivo, los registros de funciones especiales están organizados en bancos, y PORTD y TRISD (que configura los bits como entrada o salida ) están en la misma dirección, 0x08, en el banco 0 y el banco 1, respectivamente.

Hay bits en el registro de ESTADO (en 0x03, asignados a todos los bancos) que seleccionan el banco. Estos son bits 5 y amp; 6, referido como RP0 y RP1. El compilador puede esperar que el programador manipule estos bits, o puede hacerlo por sí mismo.

Es bastante fácil de verificar examinando la lista de resultados de ensamblaje. Entre las asignaciones a TRISD y PORTD, debe haber una instrucción que borre el bit RP0 en el registro de ESTADO. Si no lo hay, el programador debe agregar código para manejar el cambio de banco.

    
respondido por el user28910

Lea otras preguntas en las etiquetas