Search code examples
pici2cslave

Writing SSPBUF from variable in I2C slave protocol in PIC18


I am writing an I2C slave routine for PIC18F25K80 and I am stuck on a weird problem.

This is my routine:

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

if (PIR1bits.SSPIF != 1) {
    //This is not I2C interruption;
    PIE1bits.SSPIE = 1; // Enable Master Synchronous Serial Port Interrupt
    return;
}

//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 
        address = SSPBUF; //Clear BF
        while(BF); //Wait until completion
        if (SSPSTATbits.R_NOT_W) {
            SSPCON1bits.WCOL = 0;
            unsigned char a = 0x01;
            SSPBUF = a;//0x01 works //Deliver first byte
            asm("nop");
        }
    } 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
            asm("nop");
        } else {
            //WRITE
            debug(2, ON);
        }
    }
    transmitted = TRUE;
    SSPCON1bits.CKP = 1;
    PIR1bits.SSPIF = 0;

    PIE1bits.SSPIE = 1; // Enable Master Synchronous Serial Port Interrupt
}
}

It works like a charm if I set constant values on SSPBUF. For example, if you do:

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

I get the two bytes on the master. I can even see the wave forms of the bytes being transmitted on the oscilloscope. Quite fun!

But when I try to set SSPBUF using a variable like:

unsigned char a = 0x01;
SSPBUF = a;

I get zero on the master.

It is driving me crazy.

Some hypothesis I've discarded:

  1. Watchdog timer is messing up interrupting in the middle of the protocol: It is not. It is disabled and the problem happens in both SSPBUF assignments
  2. I need to wait until BF goes low to continue: I don't. AFAIK, you setup the SSPBUF, clear SSPIF, set CKP and return from interruption to take care of life in 4Mhz while the hardware send data in few Khz. It will interrupt you again when it finishes.

It makes no sense to me. How good it is if you cannot define an arbitrary value using a variable?

Please gurus out there, enlighten this poor programmer.

Thanks in advance.


Solution

  • It has something to do with how the compiler generates the code and some undocumented/unknown PIC restriction around SSPBUF (it is an special register anyway).

    I found out that it works when the compiler uses movwf and does not work when the compiler uses movff.

    I moved the question to another forum because I realized the audience there is more adequate.

    You will find more details here: https://electronics.stackexchange.com/questions/251763/writing-sspbuf-from-variable-in-i2c-slave-protocol-in-pic18/251771#251771