Escribiendo SSPBUF de una variable en el protocolo I2C slave en PIC18

3

Estoy migrando esta pregunta de StackOverflow a este foro porque creo que es más adecuada.

Estoy escribiendo una rutina de esclavo I2C para PIC18F25K80 y estoy atrapado en un problema extraño.

Esta es mi rutina: (EDITADO CON LA VERSIÓN MÁS ACTUAL BASADA EN LOS COMENTARIOS, AÚN MISMO PROBLEMA)

void interrupt interruption_handler() {
INTCON1bits.GIE = 0; // Disable Master Synchronous Serial Port Interrupt

if (PIR1bits.ADIF == 1) {
    //This is a A/D interruption
    PIR1bits.ADIF = 0;     
    INTCON1bits.GIE = 1; // Enable Master Synchronous Serial Port Interrupt
    return;
} else
if (PIR1bits.SSPIF == 1) {
    //This is a I2C interruption
    PIR1bits.SSPIF = 0;
    //Treat overflow
    if ((SSPCON1bits.SSPOV) || (SSPCON1bits.WCOL)) {
        dummy = SSPBUF; // Read the previous value to clear the buffer
        SSPCON1bits.SSPOV = 0; // Clear the overflow flag
        SSPCON1bits.WCOL = 0; // Clear the collision bit
        SSPCON1bits.CKP = 1;
        board_state = BOARD_STATE_ERROR;
    } else {
        if (!SSPSTATbits.D_NOT_A) {
            //Slave address
            debug(0, ON);
            //Read address (A/D number)
            address = SSPBUF; //Clear BF
            while (BF); //Wait until completion
            if (SSPSTATbits.R_NOT_W) {
                SSPCON1bits.WCOL = 0;
                unsigned char a = 0x01;
                SSPBUF =  a; //0x01; //a+1; //Deliver first byte
            }
        } else {
            if (SSPSTATbits.BF) {
                dummy = SSPBUF; // Clear BF (just in case)
                while (BF);
            }
            if (SSPSTATbits.R_NOT_W) {
                //Multi-byte read
                debug(1, ON);
                SSPCON1bits.WCOL = 0;
                SSPBUF = 0x02; //Deliver second byte
            } else {
                //WRITE
                debug(2, ON);
            }
        }
        transmitted = TRUE;
        SSPCON1bits.CKP = 1;
        PIR1bits.SSPIF = 0; //Clear again just in case

        INTCON1bits.GIE = 1; // Enable Master Synchronous Serial Port Interrupt
    }
} else
    PIR1 = 0x00; //Just in case
}

Funciona como un encanto si configuro valores constantes en SSPBUF. Por ejemplo, si lo hace:

SSPBUF = 0x01;
(...)
SSPBUF = 0x02;

Obtengo los dos bytes en el master. Incluso puedo ver las formas de onda de los bytes que se transmiten en el osciloscopio. Muy divertido!

Pero cuando intento establecer SSPBUF usando una variable como:

unsigned char a = 0x01;
SSPBUF = a;

Tengo cero en el maestro.

Me está volviendo loco.

Algunas hipótesis que he descartado:

  1. El temporizador de vigilancia está desordenando la interrupción en medio del protocolo: no lo está. Está deshabilitado y el problema ocurre en ambas asignaciones SSPBUF
  2. Necesito esperar hasta que BF baje para continuar: no lo hago. AFAIK, usted configura el SSPBUF, borra el SSPIF, configura el CKP y regresa de la interrupción para cuidar la vida en 4Mhz mientras que el hardware envía datos en pocos Khz. Te interrumpirá de nuevo cuando termine.

No tiene sentido para mí. ¿Qué tan bueno es si no puede definir un valor arbitrario usando una variable?

Por favor, gurús, ilumina a este pobre programador.

Gracias de antemano.

    
pregunta Chocksmith

2 respuestas

2

Desactive el conjunto de instrucciones extendidas en la configuración de los bits de configuración de sus códigos, por ejemplo:

#pragma config XINST = OFF   // Extended Instruction Set (Disabled)

Hace todo tipo de problemas extraños, solo recuerda apagarlo desde el principio.

Las cosas relacionadas con I2C también se ponen en un tizzy a veces si no dejas un pequeño retraso entre la inicialización de I2C, las funciones de llamada, etc.

    
respondido por el Ken
1

He encontrado una solución. Es absolutamente alucinante. Si cambio:

SSPBUF = a;

a

SSPBUF = a+1;

funciona. Los datos + 1 van al servidor I2C. Supongo que tiene algo que ver con el código del ensamblador generado.

Analicemos las opciones:

Opción 1: asignar una constante (funciona)

El código

SSPBUF = 0x23;

resultados en:

1548                           ;naplaca.c: 320: SSPBUF = 0x2;
1549  00008A  0E02                  movlw   2
1550  00008C  D00F                  goto    L3
(...)
1661  0000BA                     L3:
1662  0000BA  6EC9                  movwf   4041,c  ;volatile

Opción 2: asignar una variable (no funciona)

El código:

unsigned char a = 0x2;
SSPBUF = a;//0x01; //Deliver first byte

resultados en:

1544                           ;naplaca.c: 318: unsigned char a = 0x2;
1545  000086  0E02                  movlw   2
1546  000088  6E11                  movwf   interruption_handler@a,c
1547                           
1548                           ;naplaca.c: 319: SSPBUF = a;
1549  00008A  C011  FFC9            movff   interruption_handler@a,4041 ;volatile

Opción 3: asignar el resultado de una expresión (funciona pero envía datos +1)

El código:

unsigned char a = 0x2;
SSPBUF = a+1;//0x01; //Deliver first byte

resultados en:

1544                           ;naplaca.c: 318: unsigned char a = 0x2;
1545  000086  0E02                  movlw   2
1546  000088  6E11                  movwf   interruption_handler@a,c
1547                           
1548                           ;naplaca.c: 319: SSPBUF = a+1;
1549  00008A  2811                  incf    interruption_handler@a,w,c
1550  00008C  D00F                  goto    L3
1551  00
(...)
1661  0000BA                     L3:
1662  0000BA  6EC9                  movwf   4041,c  ;volatile   

Intenté sumar 0, pero el compilador lo optimiza y lo hace igual a la opción 2. Lo mismo sucede con un + 1-1 y un + 2-1-1.

Por lo tanto, la solución hasta ahora es enviar datos + 1.

Opción 4 (variable volátil - no funciona) - editar

El código:

volatile unsigned char a = 0x09;
SSPBUF =  a;// byte1_0 + 1; //0x01; //Deliver first byte

resultados en:

1634                           ;naplaca.c: 350: volatile unsigned char a = 0x09;
1635  000098  0E09                  movlw   9
1636  00009A  6E11                  movwf   interruption_handler@a,c    ;volatile
1637                           
1638                           ;naplaca.c: 351: SSPBUF = a;
1639  00009C  C011  FFC9            movff   interruption_handler@a,4041 ;volatile
1640                  

Conclusión

Parece que funciona cuando el compilador asigna un valor a SSPBUF con movwf. Cuando usa mover, no funciona.

Estoy publicando esto como una respuesta, pero no lo marcaré como la respuesta correcta. Espero que alguien traiga algo de luz sobre este problema.

    
respondido por el Chocksmith

Lea otras preguntas en las etiquetas