Electronic – Writing SSPBUF from variable in I2C slave protocol in PIC18

i2cpicpickitxc8

I am migrating this question from StackOverflow to this forum because I believe it is more adequate.

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

This is my routine: (EDITED WITH THE LATEST VERSION BASED ON THE COMMENTS – STILL SAME PROBLEM)

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
}

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.

Best Answer

Turn the extended instruction set off in your codes config bit settings ie:

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

It makes all sorts of weird problems, just remember to turn it off from the start.

I2C stuff also gets itself in a tizzy sometimes if you don't leave a small delay between initializing I2C, calling functions etc.