2
\$\begingroup\$

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.

\$\endgroup\$
  • 1
    \$\begingroup\$ Not a solution, but an unrelated hint: When verifying that the interrupt was caused by I2C (if (PIR1bits.SSPIF != 1)), you don't clear any interrupt flags. If the interrupt was, in fact, caused by some other interrupt source, then the ISR will be called again immediately, forever... \$\endgroup\$ – bitsmack Aug 10 '16 at 23:41
  • \$\begingroup\$ Good point. This is a hidden bug. It is not interfering now because on my setup there is no other interruption possible. I will fix it. Tks! Let me know if you have any other comment that help me to sleep at night. I've been working on this for days. \$\endgroup\$ – Chocksmith Aug 10 '16 at 23:44
  • \$\begingroup\$ I don't see anything obvious, but I hope that someone else will! You should certainly be able to use a variable to feed SSPBUF. As you say, it wouldn't be very useful otherwise. \$\endgroup\$ – bitsmack Aug 10 '16 at 23:57
  • \$\begingroup\$ Try volatile unsigned char a = 0x01; \$\endgroup\$ – rdtsc Aug 11 '16 at 18:47
  • \$\begingroup\$ rdtsc, I did. Does not work. I've edited the answer and included option 4. I still do not understand and do not consider my workaround as a definitive solution. \$\endgroup\$ – Chocksmith Aug 12 '16 at 11:26
1
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ The Extended Instruction Set isn't even supported by the compiler the OP's using, so it's disabled by default.. \$\endgroup\$ – m.Alin Jan 23 '17 at 22:41
  • \$\begingroup\$ Hi M, hey it's not supported by mine either. If you leave the above line out of your config then sometimes it defaults to ON & leads you on a merry chase around the walls after miss behaving variables. Last time for me was 18f45k80 after writing a small test code for i2c which turned out a wasted 1/2 day on the above issue. Maybe will help someone... \$\endgroup\$ – Ken Jan 23 '17 at 23:34
  • \$\begingroup\$ It solved the problem! \$\endgroup\$ – Chocksmith Jan 24 '17 at 1:14
1
\$\begingroup\$

I found a workaround. It is absolutely mind blowing. If I change:

SSPBUF = a;

to

SSPBUF = a+1;

it works. The data+1 goes to the I2C server. I guess it has something to do with the generated assembler code.

Let's analyze the options:

Option 1 - assigning a constant (it works)

The code

SSPBUF = 0x23;

results in:

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

Option 2 - assigning a variable (does not work)

The code:

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

results in:

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

Option 3 - assigning the result of an expression (works but send data +1)

The code:

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

results in:

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   

I tried to sum 0, but the compiler optimizes it and make it equals to option 2. The same happens for a+1-1 and a+2-1-1.

Thus the workaround so far is to send data+1.

Option 4 (volatile variable - does not work) -- edit

The code:

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

results in:

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                  

Conclusion

It seems to work when the compiler assign value to SSPBUF with movwf. When it uses move, it does not work.

I am posting this as an answer but I will not mark it as the right answer. I hope someone bring some light to this problem.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.