Estoy con una aplicacion de un dataloger por puerto serie usando un LPC11u67 (cortex M0+). Las librerías de NXP (LPCOpen) para esta familia posee una implementacion de un RingBuffer en la uart. La definicion del RingBuffer es la siguiente:
typedef struct {
void *data;
int count;
int itemSz;
uint32_t head;
uint32_t tail;
} RINGBUFF_T;
como se ve data es un puntero a void, por que el dato almacenado en el ringbuffer puede ser de cualquier tipo.
esta es la funcion de inicialiacion del ringbuffer
/* Initialize ring buffer */
int RingBuffer_Init(RINGBUFF_T *RingBuff, void *buffer, int itemSize, int count)
{
RingBuff->data = buffer;
RingBuff->count = count;
RingBuff->itemSz = itemSize;
RingBuff->head = RingBuff->tail = 0;
return 1;
}
En mi programa tengo esto:
/* Transmit and receive ring buffers */
STATIC RINGBUFF_T txring, rxring;
/* Ring buffer size */
#define UART_RB_SIZE 1024
/* Transmit and receive buffers */
static uint8_t rxbuff[UART_RB_SIZE];
static uint8_t txbuff[UART_RB_SIZE];
Con esto creo 2 buffer circulares, el que me interesa es rxring.
La idea es que en la recepcion de cada caracter en la UART insertemos el caracter en buffer circular.
Entonces la ISR de la UART es así:
void USART1_4_IRQHandler(void)
{
/* Want to handle any errors? Do it here. */
/* Use default ring buffer handler. Override this with your own
code if you need more capability. */
Chip_UARTN_IRQRBHandler(LPC_USART1, &rxring, &txring);
if(RingBuffer_GetCount(&rxring) == WR_BUFF_SIZE){
isRBHalfFull = 1;
RingBuffer_PopMult(&rxring, wr_buf, WR_BUFF_SIZE);
}else{
isRBHalfFull = 0;
}
}
Acá esty haciendo algo que no devo, pero no es ese el problema. Como ven se llama a Chip_UARTN_IRQRBHandler y se le pasa como parámatro la uart y los las direcciones de los buffer circulares.
Esa IRQHandler hace lo siguiente:
/* UART receive/transmit interrupt handler for ring buffers */
void Chip_UARTN_IRQRBHandler(LPC_USARTN_T *pUART, RINGBUFF_T *pRXRB, RINGBUFF_T *pTXRB)
{
/* Handle transmit interrupt if enabled */
if ((Chip_UARTN_GetStatus(pUART) & UARTN_STAT_TXRDY) != 0) {
Chip_UARTN_TXIntHandlerRB(pUART, pTXRB);
/* Disable transmit interrupt if the ring buffer is empty */
if (RingBuffer_IsEmpty(pTXRB)) {
Chip_UARTN_IntDisable(pUART, UARTN_INTEN_TXRDY);
}
}
/* Handle receive interrupt */
Chip_UARTN_RXIntHandlerRB(pUART, pRXRB);
}
donde lo realmente importante es que se llama a Chip_UARTN_RXIntHandlerRB(pUART, pRXRB);
Ese Handler es el que se encarga de poner el caracyer en el RB:
/* UART receive-only interrupt handler for ring buffers */
void Chip_UARTN_RXIntHandlerRB(LPC_USARTN_T *pUART, RINGBUFF_T *pRB)
{
/* New data will be ignored if data not popped in time */
while ((Chip_UARTN_GetStatus(pUART) & UARTN_STAT_RXRDY) != 0) {
uint8_t ch = Chip_UARTN_ReadByte(pUART);
RingBuffer_Insert(pRB, &ch);
}
}
Por si a alguie le interesa la implementacion de RingBuffer_Insert es la siguiente:
/* Insert a single item into Ring Buffer */
int RingBuffer_Insert(RINGBUFF_T *RingBuff, const void *data)
{
uint8_t *ptr = RingBuff->data;
/* We cannot insert when queue is full */
if (RingBuffer_IsFull(RingBuff))
return 0;
ptr += RB_INDH(RingBuff) * RingBuff->itemSz;
memcpy(ptr
, data
, RingBuff
->itemSz
); RingBuff->head++;
return 1;
}
Bueno, espero ese proceso se entienda. Si yo envío una gran secuencia de datos a este dataloger, cada caracter recivido es ingresado al buffer circuilar.
Luego lo que yo estoy haciendo (y está mal donde lo hago) es esperar a tener WR_BUFF_SIZE (512 en este caso) en el RB, cuando esto sucede saco WR_BUFF_SIZE datos del RB y lospongo en un buffer de escritura para una sdCard....
Bueno, el tema es que esto funciona casi perfecto. Supongamos envío 1124 bytes. Se reciven 512 en el RB y cuando esto sucede se pone en 1 una bandera y se sacan esos bytes del RB y se los pone en otro buffer. Mientras seguimos reciviendo se escriben en la SD esos 512 bytes. Se reciven 512 bytes más y pasa lo mismo. pero luego se reciben los últimos 100 bytes y ahí me queda el problema, esos 100 bytes quedarán en el RB hasta que lleguen 412 bytes más y si estos no llegan quedarán atrapados ahí.
Entonces acá biene el tema que quiero hacer. Dentro de la ISR de la uart, despues de poner el dato en el RB me gustaría obtener dicho caracter para verificar y compararlo contra algún caracter de escape.
El problema es que no sé como desreferenciar el último caracter, partiendo de la estructura del RB que tengo...
Hay una funcion llamada RingBuffer_Pop la cual saca 1 dato del RB, pero lo que hace es realmente sacarlo, yo solo quiero leerlo:
/* Pop single item from Ring Buffer */
int RingBuffer_Pop(RINGBUFF_T *RingBuff, void *data)
{
uint8_t *ptr = RingBuff->data;
/* We cannot pop when queue is empty */
if (RingBuffer_IsEmpty(RingBuff))
return 0;
ptr += RB_INDT(RingBuff) * RingBuff->itemSz;
memcpy(data
, ptr
, RingBuff
->itemSz
); RingBuff->tail++;
return 1;
}
Hace uso de esta macro:
#define RB_INDH(rb) ((rb)->head & ((rb)->count - 1))
#define RB_INDT(rb) ((rb)->tail & ((rb)->count - 1))
Podría tomar parte de ese código, pero el tema es que no lo entiendo y realmente me gustaría entender como funciona esa desreferencia...
Saludos y gracias!