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:
- 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
- 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.
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\$volatile unsigned char a = 0x01;
\$\endgroup\$