Problema en código de teclado + LCD en XC8

#1
Hola,
Tengo un problema al montar mi circuito con Proteus ya que en una PIC SCHOOL si que me funciona.
El pic es pic16f886 y mediante un teclado matricial 4x4 quiero que salgan las teclas pulsadas.
El problema es que yo creo que lo tengo mal conectado.

Código:
#ifndef LCD_H

#define	LCD_H


#ifdef 	__cplusplus
extern "C" 
{

#endif

////////////// cuerpo del header///////////////////////


#define DispRS    RA3

#define DispRW    RA2

#define DispE     RA1
#define LCD_DAT_PORT PORTB

// Register selection
#define CMD_REG         0x00    // Command register
#define DATA_REG    0x01    // Data register
#define LCD_WRITE    0x00    // Writes to LCD
#define LCD_READ    0x01    // Reads from LCD


// LCD specific defines
#define LCD_CLR    0x01    // Clears display
#define LCD_HOME    0x02    // Returns home


// Entry mode set
#define LCD_DEC        0x04    // Decrements DDRAM address
#define LCD_SHIFT_R    0x05    // Shifts display to the right
#define LCD_INC        0x06    // Increments DDRAM address cursor from left to right?
#define LCD_SHIFT_L    0x07    // Shifts display to the left


// Display ON/OFF
#define LCD_DISPOFF    0x08    // Display off
#define LCD_DISPON    0x0C    // Display on
#define LCD_CRS_OFF    LCD_DISPON | 0x00    // Cursor does not display
#define LCD_CRS_ON    LCD_DISPON | 0x02    // Cursor displays
#define LCD_BL_OFF    LCD_DISPON | 0x00    // Cursor does not blink
#define LCD_BL_ON    LCD_CRS_ON | 0x01    // Cursor blinks



// Cursor and Display Shift
#define LCD_SH_C_L    0x10    // Shifts the cursor position to the left (Address Counter is decremented by 1)
#define LCD_SH_C_R    0x14    // Shifts the cursor position to the right (Address Counter is incremented by 1)
#define LCD_SH_D_L    0x18    // Shifts the entire display to the left. The cursor follows the display shift
#define LCD_SH_D_R    0x1C    // Shifts the entire display to the right. The cursor follows the display shift


// Function Set
#define LCD_FN_SET    0x28                // Function set. Data 4-bit
#define LCD_1L_5x8    LCD_FN_SET | 0x00    // Displays 1 line. Character font: 5x 8 dots
#define LCD_1L_5x10    LCD_FN_SET | 0x04    // Displays 1 line. Character font: 5x10 dots
#define LCD_2L_5x8    LCD_FN_SET | 0x08    // Displays 2 line. Character font: 5x 8 dots


// LCD defines for calling lcd_xy with line number, position and cursor status
#define LCD_LN1        0x80 //0x00    // DDRAM address of the 1st line
#define LCD_LN2        0xC0//0x40    // DDRAM address of the 2nd line


#define LCD_CG_SET    0x40    // Sets CG RAM address
#define LCD_DD_SET    0x80    // Sets DDRAM address
#define LCD_BUSY        0x80    // Busy flag


#define LCDNCHARS    16        // Define number of chars per line


// Function prototypes
void lcd_init(void);        // LCD initialization
void lcd_send_cmd(unsigned char data);    // Writes instruction to LCD
void lcd_send_dat(unsigned char data);    // Writes data to LCD



//////////////////////////////////////////////////////
#ifdef	__cplusplus
}
#endif

#endif	/* LCD_H */
Código:
//FICHERO AUTOGENERADO DE BITS DE CONFIGURACIÓN DEL MICROPROCESADOR
//#include <xc.h>

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

// CONFIG1
#pragma config FOSC = INTRC_NOCLKOUT// Oscillator Selection bits (INTOSC oscillator: CLKOUT function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = OFF       // RE3/MCLR pin function select bit (RE3/MCLR pin function is MCLR)
#pragma config CP = OFF         // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = OFF       // Brown Out Reset Selection bits (BOR enabled)
#pragma config IESO = OFF       // Internal External Switchover bit (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)
#pragma config LVP = OFF        // Low Voltage Programming Enable bit (RB3 pin has digital I/O, HV on MCLR must be used for programming)

// CONFIG2
#pragma config BOR4V = BOR40V   // Brown-out Reset Selection bit (Brown-out Reset set to 4.0V)
#pragma config WRT = OFF        // Flash Program Memory Self Write Enable bits (Write protection off)
Os dejo los codigos que uso para esto.
La conexión la e realizado de la misma manera que la realizo en la protoboard y no me funciona.
No e utilizado nunca el Proteus y nose si me faltara algo
Muchas gracias
 

Adjuntos

#2
Hola,
Mi programa basicamente consiste en que cuando pulse una tecla, esta tecla aparezca en el LCD.
Como no quiero que este todo el rato testeando si a cambiado algun bit del puerto B, quiero hacerlo con interrupciones pero no me funciona.
Pongo aqui el código haber si podeis hechar una mano
Muchas gracias

Código:
#include <stdio.h> 
#include <stdlib.h>
#include <xc.h>
#include "lcd.h"
#include <pic16f886.h>


/* Dos definiciones para determinar un retardo */

#define _XTAL_FREQ 4000000 // necesario para macros __delay_ms()

#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
typedef unsigned char uint8_t;

unsigned char c;
char const KEYS[4][4] = {{'1','2','3','A'},
                         {'4','5','6','B'},
                         {'7','8','9','C'},
                         {'E','0','F','D'}};

void lcd_init()
{
    DispE = 0;
        DispRS = 0;
        __delay_ms(30);

        lcd_send_cmd(0x03);
        lcd_send_cmd(0x03);
        lcd_send_cmd(0x03);

        lcd_send_cmd(0x02);
        lcd_send_cmd(LCD_FN_SET);
        lcd_send_cmd(LCD_DISPOFF);
        lcd_send_cmd(LCD_CLR);
        lcd_send_cmd(LCD_DISPON);
}

void pulseEnable()
{
    DispE = 0;
    __delay_us(1);
    DispE = 1;
    __delay_us(1);
    DispE = 0;
    __delay_us(100);
}

void lcd_send_dat(unsigned char c)
{
        DispE = 0;
        DispRS = 1; //1 para mandar dato
        LCD_DAT_PORT = c >> 4; //ALTO

    pulseEnable();
        __delay_us(200); //5ms para comando, 200us para dato
            LCD_DAT_PORT = c;//( c & 0x0F ); //BAJO
    pulseEnable();
        __delay_us(200);
}

void lcd_send_cmd(unsigned char c)
{
        DispE = 0;
        DispRS = 0; //0 para mandar comando
        LCD_DAT_PORT = c >> 4; //ALTO
    pulseEnable();
        __delay_ms(5); //5ms para comando, 200us para dato
            LCD_DAT_PORT = c;//( c & 0x0F ); //BAJO
    pulseEnable();
        __delay_ms(5);
}

void writeString(const char *msg)
{
    while(*msg!='\0')
        lcd_send_dat(*msg++);
}

char teclado(void){
        unsigned char i,j;
	
	i=0;
	for(j=0;j<4;j++){
		if(PORTBbits.RB4==0){break;}
		i++;
		if(PORTBbits.RB5==0){break;}
		i++;
		if(PORTBbits.RB6==0){break;}
		i++;
		if(PORTBbits.RB7==0){break;}
		i++;
		PORTB<<=1;	//Trasladamos el 0 a siguiente Pin.
	}
	return(KEYS[i][j]);
            }

int main(int argc, char** argv) {
  
    ANSEL = 0X00 ;      //Deshabilitar ADC: Puertos digitales
    ANSELH = 0X00 ;     //Deshabilitar ADC: Puertos digitales
    TRISA = 0xf1;
    PORTA = 0x00;
    TRISB = 0xF0;
    PORTB = 0x00;

   INTCON = 0X88;

//Inicializar
        lcd_init();

    while(1)
    {
        
            lcd_send_cmd(LCD_LN1);      //1ª Línea
            lcd_send_cmd(LCD_HOME);     // Cursor a la posición 0
            lcd_send_cmd(LCD_CLR);      //Borra display
            

                if(INTCONbits.RBIF==1){
                c=teclado();
		lcd_send_dat(c);	// Visualizamos tecla pulsada.
		while(PORTBbits.RB4==0 || PORTBbits.RB5==0 || PORTBbits.RB6==0 || PORTBbits.RB7==0){} //Esperamos a que se suelte.-
		PORTB=0x00;
		INTCONbits.RBIF = 0; //Borramos bandera
	}


       
    }
    return (EXIT_SUCCESS);
}
 
#3
Hola.
No veo el bloque de interrupción... es necesario declarar cuando se activa interrupciones, de lo contrario al compilar el código, ocupa el espacio del vector de interrupción... va a suceder un bucle innecesario y también provoca que la pila se llene, perdiendo direcciones de retorno y/o datos en el proceso...

void interrupt isr(void)
{
...
// Aquí y fuera NO...
INTCONbits.RBIF = 0;
}

Tambíen, mientraz un flag esté activo, siempre va a saltar al vector de interrupción... y las líneas debajo de if(INTCONbits.RBIF==1){... dificilmente se van a ejecutar.

Ten en cuenta que la interrupción por cambio de estado en el puerto B es eso, cada vez que cambia de estado ya sea de 0 a 1 o de 1 a 0.

Es necesario rehacer la idea... modifica el código...
Saludos.
 
#4
Gracias, pero lo que yo quiero es que no este muestreando siempre y eso se hace con la interrupcion. Pero claro nose que es lo que declarar a la interrupción para que no muestree, nose si me entiendes.
Llevo varios días con esto y probando varias cosas y me estoy volviendo un poco loco.
 
#5
por lo que entiendo... quieres leer el teclado solo cuando alguna tecla esté presionada y no siempre?
Eso se soluciona con interrupción, solo que de una u otra forma se tiene que verificar algo para saber si hay o no una tecla presionada... Por ejemplo, describo como funcionaría en el código:

Código:
#include <xc.h>

struct{
    unsigned char Key;
    unsigned IsPress:1;
    unsigned :7;
}STA = {0, 0};

void interrupt scanKey(void){
    if(INTCONbits.RBIF){
        //-- Lee el teclado
        //-- Recorre cada fila/columna para saber que tecla está presionada
        //-- Guarda en STA.Key
        //-- STA.IsPress = 1 -> Si hay una tecla activa

        PORTB = 0;  // Inhabilita el teclado
        INTCONbits.RBIF = 0;
    }
}

void main(void) {
    OPTION_REGbits.nRBPU = 0;   // PullUp Interno para 4 pines de entrada
    TRISB = 0x0F;               // 4 bits parte baja -> entradas
    INTCON = 0x88;              // Activa interrupciones

    //-- Importante:
    //-- Se mantiene inactivo al puerto del teclado
    PORTB = 0; // Con cero, los 4 bits de salida son cero
    //-- Con eso se asegura que cuando precione cualquier tecla, siempre se envíe
    //-- un cero a los pines de entrada, se cual sea...
    //-- y cuando se produzca la interrupción, recien ahí se escanea el teclado
    //-- esto se aprovecha por la lentitud de los cambios de estado al pulsar una tecla.

    while(1){
        if(STA.IsPress){    // Cuando hay una tecla
           STA.IsPress = 0;
           // Lee STA.Key -> Para saber la tecla presionada

           //--> Posible lugar para el antidebounce, ya que no se recomienda
           // poner en la interrupción
        }

        // Resto del código....
        // ....
    }
}
Es una idea... falta poner en práctica y asegurar un par de cosas...
Saludos
 
#6
El problema sigue estando en que continuamente muestrea las filas, así que el problema es ese, en proteus esos pines no hacen otra cosa que cambiar de bajo a alto.
Y solo e conseguido que durante el tiempo que pulso una tecla en la primera columna de la izquierda salga el número 4 en todas las teclas.
Yo tengo el puerto B configurado de esta manera
TRISB = 0xF0;
Si le configuro de la otra forma no me funciona
 
#7
Hola,
Ahora mismo el problema que tengo es que me escribe solo en el LCD y siempre un 1 o un 4 al accionar la tecla 7.
Dejo el código sin las interrupciones haber si alguien me puede echar una mano, gracias

Código:
#include <stdio.h> 
#include <stdlib.h>
#include <xc.h>
#include "lcd.h"
#include <pic16f886.h>


/* Dos definiciones para determinar un retardo */

#define _XTAL_FREQ 4000000 // necesario para macros __delay_ms()

#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
typedef unsigned char uint8_t;

unsigned char tecla;
char const KEYS[4][4] = {{'1','2','3','A'},
                         {'4','5','6','B'},
                         {'7','8','9','C'},
                         {'E','0','F','D'}};

void lcd_init()
{
    DispE = 0;
        DispRS = 0;
        __delay_ms(30);

        lcd_send_cmd(0x03);
        lcd_send_cmd(0x03);
        lcd_send_cmd(0x03);

        lcd_send_cmd(0x02);
        lcd_send_cmd(LCD_FN_SET);
        lcd_send_cmd(LCD_DISPOFF);
        lcd_send_cmd(LCD_CLR);
        lcd_send_cmd(LCD_DISPON);
}

void pulseEnable()
{
    DispE = 0;
    __delay_us(1);
    DispE = 1;
    __delay_us(1);
    DispE = 0;
    __delay_us(100);
}

void lcd_send_dat(unsigned char c)
{
        DispE = 0;
        DispRS = 1; //1 para mandar dato
        LCD_DAT_PORT = c >> 4; //ALTO

    pulseEnable();
        __delay_us(200); //5ms para comando, 200us para dato
            LCD_DAT_PORT = c;//( c & 0x0F ); //BAJO
    pulseEnable();
        __delay_us(200);
}

void lcd_send_cmd(unsigned char c)
{
        DispE = 0;
        DispRS = 0; //0 para mandar comando
        LCD_DAT_PORT = c >> 4; //ALTO
    pulseEnable();
        __delay_ms(5); //5ms para comando, 200us para dato
            LCD_DAT_PORT = c;//( c & 0x0F ); //BAJO
    pulseEnable();
        __delay_ms(5);
}

void writeString(const char *msg)
{
    while(*msg!='\0')
        lcd_send_dat(*msg++);
}

char teclado(void){
        unsigned char i,j;
	
	i=0;
	for(j=0;j<4;j++){
		if(PORTBbits.RB4==0){
                __delay_ms(20);
                break;}
		i++;
		if(PORTBbits.RB5==0){
                __delay_ms(20);
                break;}
		i++;
		if(PORTBbits.RB6==0){
                __delay_ms(20);
                break;}
		i++;
		if(PORTBbits.RB7==0){
                __delay_ms(20);
                break;}
		i++;
		PORTB<<=1;	//Trasladamos el 0 a siguiente Pin.
	}
	return(KEYS[i][j]);
            }

int main(int argc, char** argv) {
  
    ANSEL = 0X00 ;      //Deshabilitar ADC: Puertos digitales
    ANSELH = 0X00 ;     //Deshabilitar ADC: Puertos digitales
    TRISA = 0xf1;
    PORTA = 0x00;
    TRISB = 0xF0;
    PORTB = 0x00;

   INTCON = 0X00;

//Inicializar
        lcd_init();
        lcd_send_cmd(LCD_LN1);
    while(PORTBbits.RB4 ==0 && PORTBbits.RB0 ==0 )
    {
        
                 //1ª Línea
           
            
            writeString("X=");
            

               if(1 ){
                   
                
                
                tecla=teclado();
		lcd_send_dat(tecla);
                
                __delay_ms(20);
		
		
	}


       
    }
    return (EXIT_SUCCESS);
}
 
Arriba