0
votes

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.

2
What is the definition for SSPBUF? What happens if you do SSPBUF = (unsigned char)0x02? Also, what optimization level are you using? Perhaps the asm("nop") ends up in the wrong place when you use the variable? - David Wohlferd
I tested now. If I do SSBUF = (unsigned char) 0x02 I get 02 on the server, as expected. If I do SSBUF = (unsigned char) a; I get zero... The asm("nop") was just to help me find things on the assembler. I have already removed. No effect. - Chocksmith
About the optimization level... I am using the default of the free version of the MPLAB. I have never changed anything about this. I will give it a try but I do not believe that will change anything because I've already inspected the generated assembler code and it seems OK. - Chocksmith
I spent some time poking around the optimization parameters and nothing changed... - Chocksmith
The only optimization parameters worth experimenting with here are -O0, and -O<NotZero>. Nothing else really occurs to me here. My next step would be to examine the output (-S if you are using gcc). - David Wohlferd

2 Answers

0
votes

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

0
votes
  1. Try move declaration : "unsigned char a = 0x01;" to the beginning of the function or try define it as volatile global variable.
  2. take into accunte that SSPBUF is both read and write buffer.check if there are conditions that may cause I2C module to reset this buffer.