Implementando un búfer I2C en C

4

Estoy implementando un I 2 esclavo de solo lectura en un PIC18F4620 . He hecho un controlador ISR de trabajo para el módulo MSSP:

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;
    }
}

Esto es solo un puerto a C desde una parte del código ASM en el apéndice B de AN734 .

En mi bucle principal, estoy comprobando si hay datos nuevos, como este:

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

Esto genera un problema cuando el maestro envía bytes muy rápido y entran nuevos datos antes de que el bucle principal llegue a doSomething . Por lo tanto, quiero implementar un búfer donde se almacenan los datos del maestro. Necesito una matriz terminada en nulo de 16 caracteres (el null no se usará como comando para el esclavo). El ISR tiene que escribir nuevos datos en esa matriz, y el bucle principal debe leerlos de la matriz en el orden en que se recibió, y borrar la matriz.

No tengo idea de cómo implementar esto. ¿Usted?

    
pregunta Keelan

4 respuestas

0

Desde el pseudocódigo de respuesta de fm_andreas , hice un código C18 funcional:

#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
    }
}

La belleza de este código es que es un anillo de almacenamiento :

Porlotanto,esmenosprobablequesedesbordecuandoseenvíangrandesvolúmenesdedatosalavez.Estosediscuteenloscomentariosen la respuesta de Nick Alexeev .

    
respondido por el Keelan
2

No tengo experiencia con el PIC, pero el problema parece suficientemente genérico. Me gustaría crear una matriz simple con dos punteros independientes en la matriz: un puntero de lectura y un puntero de escritura. Cada vez que recibe un byte, incrementa el puntero de escritura y escribe en la nueva posición; en su bucle principal puede verificar si el puntero de lectura y el puntero de escritura son iguales. Si no es así, simplemente lea y procese desde el búfer y aumente el puntero de lectura para cada byte hasta que estén.

Luego, puede restablecer los punteros al principio de la matriz, o dejar que "fluyan" al principio haciendo esencialmente un búfer circular. Esto es más fácil si el tamaño de la matriz es un factor de 2, ya que puede simplemente enmascarar ambos punteros después de sus incrementos.

Algunos ejemplos (pseudo) código:

volatile unsigned int readPointer= 0;
volatile unsigned int writePointer=0;
volatile char theBuffer[32];
...
//in your ISR
writePointer = (writePointer+1) & 0x1F;
theBuffer[writePointer] = ReadI2C(); // assuming this is the easiest way to do it
                                     // I would probably just read the register directly
...
//in main
while (readPointer != writePointer) {
  readPointer = (readPointer+1) & 0x1F;
  nextByte = theBuffer[readPointer];
  // do whatever necessary with nextByte
}
    
respondido por el fm_andreas
1

Si desea hacer esto correctamente, la mejor solución es implementar algún tipo de anillo de almacenamiento .
Pero ten en cuenta que la implementación tiene que ser "segura para interrupciones". Esto es necesario, porque mientras los contenidos del búfer se procesan en el bucle principal, ¡pueden llegar datos adicionales en cualquier momento en su SPI ISR! Por lo tanto, es posible que deba considerar el uso de volatile y las operaciones "ATÓMICAS" si no está familiarizado con ellas.

    
respondido por el Rev1.0
0

@ fm-andreas me ha vencido en esto. Iba a proponer lo mismo: buffer de ráfaga con posiciones de lectura y escritura. (Esto no es un búfer de anillo. Es más simple. No se envuelve). Un búfer de ráfaga puede almacenar una ráfaga de datos. El sistema debe diseñarse de manera que haya suficiente tiempo entre las ráfagas para procesar los datos.

Aquí está mi versión (pseudo-código):

const unsigned char g_BUFF_LEN = 16;
unsigned char   g_dataBuff[BUFF_LEN];       // buffer
unsigned char   g_g_iWriteOffset, g_g_iReadOffset;      // write and read offsets
unsigned char   g_iFlags;


void main()
{
    resetBuffer();      // initialize the burst buffer

    // process the contents buffer
    while (1)
    {
        // other code that lives in the main loop

        while (g_iWriteOffset > g_iReadOffset)      // inner loop for processing the received data
        {
            doSomething(g_dataBuff[g_iReadOffset]);     

            // disable the receiveISR.  If the ISR occurs in this block, it can corrupr the buffer.
            ++g_iReadOffset;        // advance the read offset
            if (g_iReadOffset == g_iWriteOffset)    // is there remaining unprocessed data in the buffer?
            {
                resetBuffer();
            }
            // re-enable the receive ISR
        }
    }
}


void resetBuffer()
{
    g_iWriteOffset = 0;
    g_iReadOffset = 0;
}


void receiveISR()
{
    /*  Receive the byte.
        Specific code for keeping the hardware happy goes here.
        Camil, you've already posted it in the O.P.  I'll save some time and not repeat it.  */

    g_dataBuff[g_iWriteOffset] = newByte;

    ++g_iWriteOffset;       // advance the write offset
    if (g_iWriteOffset >= g_BUFF_LEN)   // have we got a buffer overflow?
    {
        g_iFlags |= COMM_BUFF_OVERFLOW;
        /*  Handling of errors (such as this overflow) is an interesting topic.
            However, is depends on the nature of the instrument.
            It's somewhat outside the sope of the question. */
    }
}

P.S.
En una nota relacionada, mira en los buffers de ping-pong. Esto es útil cuando tiene paquetes de varios bytes (o comandos), y necesita recibir todo el paquete antes de que pueda comenzar a procesarlo.

2x buffers idénticos (o más de 2x). ISR está llenando un búfer hasta que detecta el final del comando. Mientras tanto, el bucle main () procesa el otro búfer. Si el ISR ha recibido el siguiente comando completo y main () se realiza con el procesamiento del comando anterior, entonces los punteros del búfer se intercambian.

    
respondido por el Nick Alexeev

Lea otras preguntas en las etiquetas