Monday 20 July 2015

pic - Implementing an I2C buffer in C


I'm implementing a read-only I2C slave on a PIC18F4620. I have made a -working- ISR handler for the MSSP module:


unsigned char dataFromMaster;

unsigned char SSPISR(void) {
unsigned char temp = SSPSTAT & 0x2d;
if ((temp ^ 0x09) == 0x00) {
// State 1: write operation, last byte was address
ReadI2C();
return 1;

} else if ((temp ^ 0x29) == 0x00) {
// State 2: write operation, last byte was data
dataFromMaster = ReadI2C();
return 2;
} else if (((temp & 0x2c) ^ 0x0c) == 0x00) {
// State 3: read operation, last byte was address
WriteI2C(0x00);
return 3;
} else if (!SSPCON1bits.CKP) {
// State 4: read operation, last byte was data

WriteI2C(0x00);
return 4;
} else {
// State 5: slave logic reset by NACK from master
return 5;
}
}

This is just a port to C from a part of the ASM code in appendix B of AN734.


In my main loop, I'm checking if there is new data, like this:



void main(void) {
if (dataFromMaster != 0x00) {
doSomething(dataFromMaster);
dataFromMaster = 0x00;
}
}

This gives a problem when the master sends bytes very fast, and new data comes in before the main loop gets to doSomething. I therefore want to implement a buffer where data from the master is stored. I need a 16-char null-terminated array (the null won't be used as a command for the slave). The ISR has to write new data to that array, and the main loop should read it from the array in the order it was received, and clear the array.


I have no idea how to implement this. Do you?



Answer




From the pseudocode of fm_andreas' answer, I made a working C18 code:


#define bufferSize 0x20
static volatile unsigned char buffer[bufferSize] = {0}; // This is the actual buffer
static volatile unsigned char readPointer = 0; // The pointer to read data
static volatile unsigned char writePointer = 0; // The pointer to write data
static volatile unsigned bufferOverflow = 0; // Indicates a buffer overflow

// In the ISR...
if (buffer[writePointer] == 0x00) { // If there is no data at the pointer
buffer[writePointer] = SSPBUF; // Put the data in the buffer

writePointer = (writePointer+1)%bufferSize; // Increase the pointer, reset if >32
} else { // If there is data...
bufferOverflow = 1; // Set the overflow flag
}

// In the main loop...
while (1) {
// Do some other stuff
if (readPointer != writePointer) { // If there is a new byte
putc(buffer[readPointer], stdout); // Do something with the data

buffer[readPointer] = 0x00; // Reset the data
readPointer = (readPointer+1)%bufferSize; // Increase the pointer, reset if >32
}
}

The beauty of this code is that it's a ring buffer:


enter image description here


It is therefore less likely to overflow when large data bulks are sent at once. This is discussed in the comments on Nick Alexeev's answer.


No comments:

Post a Comment

arduino - Can I use TI's cc2541 BLE as micro controller to perform operations/ processing instead of ATmega328P AU to save cost?

I am using arduino pro mini (which contains Atmega328p AU ) along with cc2541(HM-10) to process and transfer data over BLE to smartphone. I...