Registro RCREG de PIC no adquiere valor según Proteus.

¡Saludos! Tengo una duda y quisiera saber si alguno podría ayudarme a resolver. Espero poder ser claro con mi duda.

No tiene mucho que inicié mi carrera en Ing. Electrónica y ya me han atrapado muchos temas y ramas de ésta . Una de ellas es la programación de embebidos; pero apenas me estoy iniciando y hay cosas que parece que aun no entiendo del todo bien.

Hace unas semanas se me ocurrió la idea de hacer una interfaz entre un control de NES y el PIC 16F887, me leí el funcionamiento del control de Nintendo (The NES Controller Handler) y medio base mi código al que habían hecho para un Arduino (NES Controller Interface with an Arduino UNO) y de un video de Youtube.

Pero a la hora de poner todo en marcha me doy cuenta que no entiendo del todo bien cómo funcionan varios registros, y entre ellos está el RCREG. Hasta donde yo entendí es el registro en el que se guardan los datos recibidos por el pin de recepción serial, y yo con esto en mente realicé mi código.

NOTA: Uso MPLAB X y el lenguaje C con el compilador XC8.
NOTA2: El integrado que esta dentro del control de NES es un shift register 4021.


C:
#pragma config FOSC = INTRC_NOCLKOUT// Oscillator Selection bits (INTOSCIO oscillator: I/O 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 = ON       // 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 = ON       // Brown Out Reset Selection bits (BOR enabled)
#pragma config IESO = ON        // Internal External Switchover bit (Internal/External Switchover mode is enabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)
#pragma config LVP = ON         // Low Voltage Programming Enable bit (RB3/PGM pin has PGM function, low voltage programming enabled)
// 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)

#include <xc.h>
#define _XTAL_FREQ 8000000

void main(void) {
    ANSEL=ANSELH=0x00;
    TRISA=0x00;
    TRISC=1;
    PORTC=0x00;
    PORTA=0x00;
    //PORTD=0;
    //TRISD=0;
    /////////////////
    OSCCON=0b01111110; //Pongo la frecuencia del oscilador en 8MHz
    /////////////////
    //INTCONbits.GIE=1;  //Las interrupciones las quité mejor porque iba a manejar RCREG de manera
    //INTCONbits.PEIE=1; //indirecta
    //PIR1bits.RCIF=0;
    //PIE1bits.RCIE=1;
    ////////////////
    RCSTAbits.SPEN=1;
    RCSTAbits.CREN=1;
    TXSTAbits.SYNC=0;
    ///////////////
    BAUDCTLbits.BRG16=0;
    TXSTAbits.BRGH=0;
    SPBRG=12; //según el datasheet este es el valor para el baudrate de 9600 si tengo 8MHz
    //RCREG=0b00000000;
    while(1){
        //char data;
        //char resultado;
        //data=RCREG;   //creo que esto no es necesario
        int c=0,a=1,resultado=0;
        
       __delay_us(12);
        PORTAbits.RA0=1;
        __delay_us(12);
        PORTAbits.RA0=0;
        __delay_us(6);
        for(c=0;c<8;c++){
        if(!RCREG) {resultado+=a;} //Con esto trato de leer cambios en RCREG por cada pulso de relog
        a<<=1;                       //Si hay un botón o varios presionados, según la página que leí
        PORTAbits.RA1=1;         //cada uno de los 8 pulsos de relog leera uno a uno los 8 botones del control
       __delay_us(6);            //de esta manera la variable resultado tendría un valor correspondiente a lo que se haya presionado
        PORTAbits.RA1=0;    //el problema aquí es que presione o no algo, siempre entra en el if, por lo que la variable resultado termina con un valor de 255
        __delay_us(6);
        }
        
        if(resultado==1)  //después de los 8 pulsos aquí pienso poner varios if's de prueba
        {                 //según el botón que se presionó será el valor que tendrá resultado.
            PORTAbits.RA3=1;
            __delay_ms(2000);
            PORTAbits.RA3=0;
            __delay_ms(2000);
        }
        
        if(resultado==!1) //uso esto para ver si al no presionar nada el pin RA4 se activa(pero no sucede)
        {
          PORTAbits.RA4=1;
           __delay_ms(5000);
          PORTAbits.RA4=0;
        }
  return;
}    }
El problema viene cuando este código pasado a Proteus no hace lo que debería hacer, y yo deduzco dos cosas:
1)Es posible que la sintaxis y la lógica que estoy usando es incorrecta
2) no entiendo el funcionamiento del registro RCREG.

Además, según Proteus, RCREG jamas adquiere un valor, no importa si yo inicializo RCREG como =0, en Proteus se lee como "\0".

Proteus.png

RCREG.png

¿Qué estoy haciendo mal? Así fueran 100 correcciones diferentes cada una de ellas me servirían de retroalimentación de una forma estupenda para seguir aprendiendo.
¿Alguna sugerencia?
¿Alguien ya hizo interfaz con un PIC usando este control?

Sí, noté que tengo un error en el return que está dentro del while y en TRISC que en vez de poner todos como entradas puse un 1 decimal. Pero además de eso el código en Proteus no funciona y RCREG sigue sin tener cambios aún con los datos enviados por el 4021.
 
Última edición por un moderador:
Yo veo que el problema está en el bucle de 8 vueltas leyendo el registro RCREG.

Fíjate que, después de leerlo y guardar el resultado, mandas un pulso por RA1, con dos esperas de 6 µs.
Si sumamos todos esos tiempos... y que el microcontrolador está trabajando a 8 Mhz, tenemos que ese bucle se ejecuta muy rápido. Y esa es la posible razón por la que siempre resultado acaba valiendo 255.

El trabajo que estás haciendo de activar cada bit de resultado... es lo que realmente debería hacer ya el registro RCREG, así que no habría que hacer nada más que leerlo (siempre y cuando la llegada de bits esté bien sincronizada). Yo lo que haría sería volcar el contenido de RCREG al puerto B para ver qué ha leído.

Otra cosa... según la documentación, al poner SPEN a 1, se configura el pin de RX como entrada... pero el simulador de MPLAB X dice que no, que está como salida... y RX está como salida porque unas líneas más arriba tienes pustoTRISC a 1. Si comento esa línea, ya aparece como de entrada. No sé... un poco raro que la documentación diga una cosa y el simulador haga otra.

Tienes un error de sintaxis en el segundo if de resultado. El operador de desigualdad es !=
 
Última edición:
Gracias por tu respuesta. He seguido tus sugerencias, pero sigue sin dar el resultado deseado.

Cambié a un oscilador externo de 3.68MHz, y modifiqué ligeramente el código.

C:
#include <xc.h>
#include "configuracion.h"
#define _XTAL_FREQ 3686400
#define LATCH PORTAbits.RA0
#define CLK PORTAbits.RA1
void main(void) {
    ANSEL=ANSELH=0x00;
    TRISA=0b10000000;
    TRISC=0b10111111;
    PORTC=0x00;
    PORTA=0x00;
    PORTB=0x00;
    TRISB=0x00;
    /////////////////
    //OSCCON=0b01111110;
    //OSCCONbits.IRCF=0b111;
    //OSCCONbits.SCS=0;
    /////////////////
    RCSTAbits.SPEN=1;
    RCSTAbits.CREN=1;
    TXSTAbits.SYNC=0;
    ///////////////
    BAUDCTLbits.BRG16=0;
    TXSTAbits.BRGH=0;
    SPBRG=5;
    //RCREG=0x00;
    while(1){
        int c=0;    //,a=1,resultado=0;
     //  __delay_us(12);
        LATCH=1;
        __delay_us(48); //agregue más retraso, si lo aumento más ya no cambia RCREG
        LATCH=0;
        __delay_us(24);
        for(c=0;c<8;c++){
        //if(!DATA) {resultado+=a;}
        //a<<=1;
        CLK=1;
       __delay_us(24);
        CLK=0;
        __delay_us(24);
        }
        
        PORTB=RCREG; 
    }
    return;
}
Proteus2.png

No importa que botón presione, RB5 siempre se pone en 1, incluso aunque use los 8MHz internos que estaba usando antes. ¿Qué podría estar haciendo mal? ¿El cableado en el 4021 es correcto?
 
A ver... hay que seguir el protocolo serie que el EUSART espera: un bit de inicio, 8 (o 9) bits de datos, y uno o más bits de parada. Solo cuando se cumple esto, se activa el bit RCIF de PIR1, indicando que hay un carácter en el búfer de espera. Solo entonces se podrá leer del registro RCREG (12.1 en la hoja de datos).

Te voy a poner aquí unos enlaces con la inicialización correcta de EUSART, y un enlace a una página de ejemplos. El verdadero problema es "imitar" un protocolo serie con solo botones... me parece extremadamente complicado. Si está funcionando a 9600 bps, cada cambio debería durar 104 µs. Muy complicado. Pero... en el Proteus podrías poner un circuito más que sí genere esa señal serie, por ejemplo con otro PIC 16F887, que es uno de los ejemplos que te enlazo.

Módulo EUSART del PIC16F887 - Esta es una presentación de la que solo interesa las últimas pantallas donde muestra el orden de la inicialización del EUSART tanto en transmisión como en recepción. Pero... no hay que hacer caso de todos los pasos...
USART PIC Comunicación serial - Estas son las bases de la comunicación serie en esta familia de PIC.
USART PIC ejemplos - Aquí están los ejemplos. Son muy sencillos de replicar en Proteus. A partir de ahí, los puedes ampliar a tu proyecto, pero... tienes que saber qué protocolo usa el controlador de la NES.

Una cosa interesante de este PIC es que tiene autodetección de velocidad. Puede ser interesante en algunos casos (12.3.1 de la hoja de datos).

Si... quieres seguir probando de forma manual (con los botones), entonces yo lo que haría sería trabajar en modo síncrono, en que el formato de los datos son 8 bits normales.
 
Solo cuando se cumple esto, se activa el bit RCIF de PIR1, indicando que hay un carácter en el búfer de espera. Solo entonces se podrá leer del registro RCREG
Esto era justamente el problema. Yo pensaba que podía leer en tiempo real los cambios en RCREG mientras se estaba en el ciclo for. Lo de la bandera nunca lo activé porque el control de NES tiene por default todo los bits en alto. Cada botón presionado haría un 0 en su respectiva posición, así que imaginé que apenas entre voltaje al control se mandaría la interrupción.

Anoche me pasé leyendo los enlaces que compartiste y había llegado a la conclusión de que para que funcionara habría que cambiar todo el código y la lógica. Pero entonces se me ocurrió la idea de que aunque no puedo estar leyendo RCREG, tal vez sí podía leer cuando el pin tenga cambios, por ejemplo, pensé que si había 01000000 o 00000100 en el pin esto sería igual a 1. No estaba seguro de que esto fuera así y menos por el poco tiempo del ciclo, pero probé y funcionó!

Al final el código quedó así:
C:
#include <xc.h>
#include "configuracion.h"
#define _XTAL_FREQ 8000000
#define LATCH PORTAbits.RA0
#define CLK PORTAbits.RA1
void main(void) {
    ANSEL=ANSELH=0x00;
    TRISA=0x00;
    TRISC=0xFF;
    PORTC=0x00;
    PORTA=0x00;
    PORTB=0x00;
    TRISB=0x00;
    /////////////////
    OSCCON=0b01111110;
    /////////////////
    RCSTAbits.SPEN=1;
    RCSTAbits.CREN=1;
    TXSTAbits.SYNC=0;
    ///////////////
    BAUDCTLbits.BRG16=0;
    TXSTAbits.BRGH=0;
    SPBRG=12;
    ////////////////
    while(1){
        int c=0,a=1,resultado=0;
        LATCH=1;
        __delay_us(12);
        LATCH=0;
        __delay_us(6);
        for(c=0;c<8;c++){
       if(!PORTCbits.RC7) {resultado+=a;}
        a<<=1;
        CLK=0;
        __delay_us(6);
        CLK=1;
        __delay_us(6);
        }
        __delay_us(100);
        
        PORTB=resultado; 
    }
    return;
}
ProyectoNESControllerPIC.png

Gracias por las sugerencias y por las correcciones, me ayudaron para entender mejor el funcionamiento del pic y a realizar este proyectillo que tenía en mente. Ahora sólo falta probarlo en físico. :D!

Bueno, por si alguien tiene un control de NES y quiere hacer la interfaz con un PIC ya tiene aquí una idea de cómo podría hacerse.
 

Arriba