TODOPIC

Otros Microcontroladores / Dispositivos programables => Microcontroladores ARM => Mensaje iniciado por: elgarbe en 07 de Febrero de 2016, 21:31:15

Título: Desreferenciando caracteres en una struct...
Publicado por: elgarbe en 07 de Febrero de 2016, 21:31:15
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:

Código: C
  1. typedef struct {
  2.         void *data;
  3.         int count;
  4.         int itemSz;
  5.         uint32_t head;
  6.         uint32_t tail;
  7. } 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

Código: C
  1. /* Initialize ring buffer */
  2. int RingBuffer_Init(RINGBUFF_T *RingBuff, void *buffer, int itemSize, int count)
  3. {
  4.         RingBuff->data = buffer;
  5.         RingBuff->count = count;
  6.         RingBuff->itemSz = itemSize;
  7.         RingBuff->head = RingBuff->tail = 0;
  8.  
  9.         return 1;
  10. }

En mi programa tengo esto:

Código: C
  1. /* Transmit and receive ring buffers */
  2. STATIC RINGBUFF_T txring, rxring;
  3.  
  4. /* Ring buffer size */
  5. #define UART_RB_SIZE 1024
  6.  
  7. /* Transmit and receive buffers */
  8. static uint8_t rxbuff[UART_RB_SIZE];
  9. 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í:

Código: C
  1. void USART1_4_IRQHandler(void)
  2. {
  3.         /* Want to handle any errors? Do it here. */
  4.  
  5.  
  6.         /* Use default ring buffer handler. Override this with your own
  7.            code if you need more capability. */
  8.         Chip_UARTN_IRQRBHandler(LPC_USART1, &rxring, &txring);
  9.  
  10.                 if(RingBuffer_GetCount(&rxring) == WR_BUFF_SIZE){
  11.                         isRBHalfFull = 1;
  12.                         RingBuffer_PopMult(&rxring, wr_buf, WR_BUFF_SIZE);
  13.                 }else{
  14.                         isRBHalfFull = 0;
  15.                 }
  16.  
  17. }

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:

Código: C
  1. /* UART receive/transmit interrupt handler for ring buffers */
  2. void Chip_UARTN_IRQRBHandler(LPC_USARTN_T *pUART, RINGBUFF_T *pRXRB, RINGBUFF_T *pTXRB)
  3. {
  4.         /* Handle transmit interrupt if enabled */
  5.         if ((Chip_UARTN_GetStatus(pUART) & UARTN_STAT_TXRDY) != 0) {
  6.                 Chip_UARTN_TXIntHandlerRB(pUART, pTXRB);
  7.  
  8.                 /* Disable transmit interrupt if the ring buffer is empty */
  9.                 if (RingBuffer_IsEmpty(pTXRB)) {
  10.                         Chip_UARTN_IntDisable(pUART, UARTN_INTEN_TXRDY);
  11.                 }
  12.         }
  13.  
  14.         /* Handle receive interrupt */
  15.         Chip_UARTN_RXIntHandlerRB(pUART, pRXRB);
  16. }

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:

Código: C
  1. /* UART receive-only interrupt handler for ring buffers */
  2. void Chip_UARTN_RXIntHandlerRB(LPC_USARTN_T *pUART, RINGBUFF_T *pRB)
  3. {
  4.         /* New data will be ignored if data not popped in time */
  5.         while ((Chip_UARTN_GetStatus(pUART) & UARTN_STAT_RXRDY) != 0) {
  6.                 uint8_t ch = Chip_UARTN_ReadByte(pUART);
  7.                 RingBuffer_Insert(pRB, &ch);
  8.         }
  9. }

Por si a alguie le interesa la implementacion de RingBuffer_Insert es la siguiente:

Código: C
  1. /* Insert a single item into Ring Buffer */
  2. int RingBuffer_Insert(RINGBUFF_T *RingBuff, const void *data)
  3. {
  4.         uint8_t *ptr = RingBuff->data;
  5.  
  6.         /* We cannot insert when queue is full */
  7.         if (RingBuffer_IsFull(RingBuff))
  8.                 return 0;
  9.  
  10.         ptr += RB_INDH(RingBuff) * RingBuff->itemSz;
  11.         memcpy(ptr, data, RingBuff->itemSz);
  12.         RingBuff->head++;
  13.  
  14.         return 1;
  15. }

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:

Código: C
  1. /* Pop single item from Ring Buffer */
  2. int RingBuffer_Pop(RINGBUFF_T *RingBuff, void *data)
  3. {
  4.         uint8_t *ptr = RingBuff->data;
  5.  
  6.         /* We cannot pop when queue is empty */
  7.         if (RingBuffer_IsEmpty(RingBuff))
  8.                 return 0;
  9.  
  10.         ptr += RB_INDT(RingBuff) * RingBuff->itemSz;
  11.         memcpy(data, ptr, RingBuff->itemSz);
  12.         RingBuff->tail++;
  13.  
  14.         return 1;
  15. }

Hace uso de esta macro:
Código: C
  1. #define RB_INDH(rb)                ((rb)->head & ((rb)->count - 1))
  2. #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!
Título: Re:Desreferenciando caracteres en una struc...
Publicado por: KILLERJC en 07 de Febrero de 2016, 22:48:35
Citar
Hay una funcion llamada RingBuffer_Pop la cual saca 1 dato del RB, pero lo que hace es realmente sacarlo, yo solo quiero leerlo:
Pero ademas lo que lo saca lo hace desde el tail

Y no es mejor leer siempre lo que llega ? o Pensas revisar cada tanto para ver si no llega mas nada y ahi verificar si esta el ultimo char indicando el final?
Y si llegan mas cosas y ese char que te indica el fin queda en el medio ?

Supongamos que la hiciste vos, y yo creo que estas intentado buscar una funcion que lea lo ultimo ingresado.

El ultimo valor esta dado por (head-1), por que se pre-incrementa al ingresar el dato apuntando a la direccion siguiente.
El tema es que es un void. Y habria que castearlo a un uint8_t.

Código: C
  1. static uint8_t RingBuffer_ReadByte( const RINGBUFF_T *RingBuff)
  2. {
  3.         uint8_t  ui8Temp;
  4.  
  5.         //
  6.         //  Reviso que el puntero no sea Nulo, es decir que todavia no se inicializo
  7.         //
  8.  
  9.         if(RingBuffer->data == NULL )
  10.         {
  11.                 DebugMsg("Usaste mal la funcion y no inicializaste el buffer");
  12.                 return 0;
  13.         }
  14.  
  15.         //
  16.         // Reviso que el buffer no este vacio, si esta vacio no deberia ser capas de leer nada
  17.         // Deberias asegurarte antes... esto es una comprobacion tardia
  18.         //
  19.  
  20.  
  21.         if(RingBuffer_GetCount(RingBuff)==0)
  22.         {
  23.                 DebugMsg("Estas tratando de leer algo vacio..");
  24.                 return 0;
  25.         }
  26.  
  27.  
  28.         //
  29.         // Necesito wrapear el puntero ? En caso que de toda la vuelta.
  30.         //
  31.  
  32.  
  33.         if(Ringbuff->head==0)
  34.         {
  35.                 // Buffer tiene algo, y head es 0, por lo tanto quiere decir que el buffer se paso
  36.                 // Y debo ver el ultimo valor.
  37.                 ui8Temp = ((uint8_t *)Ringbuff->data)[Ringbuff->StructSize];
  38.         }
  39.         else
  40.         {
  41.                 // Buffer posee algo y es posible usarlo, no se necesita preocuparse por el wrap
  42.                 // Ya que head no es 0
  43.                 ui8Temp = ((uint8_t *)Ringbuff->data)[(Ringbuff->head)-1];
  44.         }
  45.  
  46.  
  47.         return ui8Temp;
  48. }

Realmente no se si estoy haciendo algo malo o bueno pero es lo unico que se me ocurre en estos momentos. Y como que se vuelve un poquito jodido probarlo.

EDIT:

Cambie el codigo. Espero que funcione. El acceso a la estructura a traves de un pointer tiene la mas alta prioridad, luego como los [] tiene mayor orden de precedencia que el cast, debo ponerle otros parentesis ya que () y [] son asociativos de izquierda a derecha, primero se ejecuta el (). Y esa es la explicacion de como se llego a eso :P

EDIT2:
El codigo posee una proteccion de wrap, como no se de donde tomar el tamaño maximo del buffer ( que no sea el #define asi es mas general para cualquier array ) pense crear un StructSize. Aunque la estructura solo necesitaria: puntero al buffer, ReadIndex, WriteIndex, Size. Y con eso calculas todo, a no ser que quieras ahorrarte un poco de calculo y poner el Count, que seria un (WriteIndex - ReadIndex)

Y una cosa mas..
no entiendo como en tu

RingBuffer_Insert()

No wrapeas el indice ( head ), ya que lo unico que haces es incrementarlo. Pero no veo en ningun lado que se fije si llego al maximo y que lo lleve a 0 para comenzar por el otro lado. Como realmente un Ring Buffer haria. Si jamas lo vas a llenar y siempre terminas vaciandolo y borrando todo ( poniendo head/tail a 0 ) entonces no hace falta el codigo del wrap que puse. ni la comprobacion esa. Pero dejaria de ser un Ring Buffer como tal, y un buffer mas :P
Título: Re:Desreferenciando caracteres en una struc...
Publicado por: elgarbe en 08 de Febrero de 2016, 00:16:37
Y no posee una funciona esa libreria de buffer circular para tomar el ultimo valor?

no  :?

Y no es mejor leer siempre lo que llega ?

Lo que pasa es que este juego de librerías (las LPCOpen) ya traen todo para funcionar así con la UART y el ringbuffer. Si quiero recibir un caracter por la uart, verificar que es y luego meterlo al RB tendría que mirar un poco las funciones... es una opción válida y la voy a analizar. Por lo que vi esta todo medio pensado para usar el RB, pero seguro que de alguna forma me lo puedo saltear en la ISR de la uart.

o Pensas revisar cada tanto para ver si no llega mas nada y ahi verificar si esta el ultimo char indicando el final?

Tengo varias formas de hacerlo. Una forma es con un timer y una especie de timeout. Si no recivo nada por un tiempo, saco los datos que quedaron en el RB y los mando a la SD card y cierro el archivo. Acá no necesito leer el último caracter.


Y si llegan mas cosas y ese char que te indica el fin queda en el medio ?

Si implemento lo del caracter especial como escape (fin de datos) entonces es porque estoy bien seguro que lo que viene despues no sirve o es parte de otro archivo. Aún no me decido cual de las dos opciones tomar....

La aplicacion actual es como datalog de un controlador de vuelo de un UAV. Este está continuamente sacando la telemetría por un puerto serie. Entonces cuando terminas de volar la controladora sigue mandando datos que no son útiles... acá normalmente no hay que hacer nada. Apagar los equipos y listo, si quedaron datos en el RB no hay problema porque no sirven. Pero estoy tratando de pensarlo para que quede un poco más standar y reutilizable. Entonces, por ejemplo, hay un proyecto llamado openlog que al rezivir 3 CTRL+Z sale de modo log y pasa a modo comando y ahí podes  listar archivos, etc. La idea es incluir algo de eso...

Supongamos que la hiciste vos, y yo creo que estas intentado buscar una funcion que lea lo ultimo ingresado. Estoy intentando comprender como hacer, por que es bastante simple si es que es un pointer a uint8_t

El ultimo valor esta dado por (head-1), por que se pre-incrementa al ingresar el dato apuntando a la direccion siguiente. Para un ptr de 8 bits se me ocurre que deberia ser asi:

Código: C
  1. static uint8_t RingBuffer_ReadByte( const RINGBUFF_T *RingBuff)
  2. {
  3.      uint8_t  ui8Temp;
  4.  
  5.      // Revisar que exista el buffer (no NULL), revisar que NO este vacio aca. Y recien ahi proceder
  6.  
  7.      ui8Temp = Ringbuff->data[(Ringbuff->head)-1];
  8.  
  9.      return ui8Temp;
  10. }

El tema es que es un void. Y habria que castearlo a un uint8_t.

Código: C
  1. static uint8_t RingBuffer_ReadByte( const RINGBUFF_T *RingBuff)
  2. {
  3.      uint8_t  ui8Temp;
  4.  
  5.      // Revisar que exista el buffer (no NULL), revisar que NO este vacio aca.
  6.  
  7.      ui8Temp = ((uint8_t *)Ringbuff->data)[(Ringbuff->head)-1];
  8.  
  9.      return ui8Temp;
  10. }

Realmente no se si estoy haciendo algo malo o bueno pero es lo unico que se me ocurre en estos momentos. Y como que se vuelve un poquito jodido probarlo.

EDIT:

Cambie el codigo. Espero que funcione. El acceso a la estructura a traves de un pointer tiene la mas alta prioridad, luego como los [] tiene mayor orden de precedencia que el cast, debo ponerle otros parentesis ya que () y [] son asociativos de izquierda a derecha, primero se ejecuta el (). Y esa es la explicacion de como se llego a eso :P

Pobando....
Título: Re:Desreferenciando caracteres en una struc...
Publicado por: KILLERJC en 08 de Febrero de 2016, 00:18:47
Te aviso que cambie el codigo hace rato :P. Lo edite xD bastante
Título: Re:Desreferenciando caracteres en una struc...
Publicado por: elgarbe en 08 de Febrero de 2016, 00:25:59
Y una cosa mas..
no entiendo como en tu

RingBuffer_Insert()

No wrapeas el indice ( head ), ya que lo unico que haces es incrementarlo. Pero no veo en ningun lado que se fije si llego al maximo y que lo lleve a 0 para comenzar por el otro lado. Como realmente un Ring Buffer haria. Si jamas lo vas a llenar y siempre terminas vaciandolo y borrando todo ( poniendo head/tail a 0 ) entonces no hace falta el codigo del wrap que puse. ni la comprobacion esa. Pero dejaria de ser un Ring Buffer como tal, y un buffer mas :P

No es mío, es de NXP  :P
Por lo que vi van incrementando el head y el tail siempre y la cantidad de datos actuales sale como la diferencia * size. He leido que es una forma más de trabajar con los RB:
http://www.simplyembedded.org/tutorials/interrupt-free-ring-buffer/

Título: Re:Desreferenciando caracteres en una struc...
Publicado por: KILLERJC en 08 de Febrero de 2016, 04:07:13
No es mío, es de NXP  :P
Por lo que vi van incrementando el head y el tail siempre y la cantidad de datos actuales sale como la diferencia * size. He leido que es una forma más de trabajar con los RB:
http://www.simplyembedded.org/tutorials/interrupt-free-ring-buffer/

Interesante es la primera ves que lo veo implementado asi, muy buena esa.. Asi que para poder leerlo deberia ser asi (sin necesidad de wrappers):

Código: C
  1. ui8Temp = ((uint8_t *)Ringbuff->data)[((Ringbuff->head)-1) & (RingBuff->count - 1)];

De esa forma se resta 1 al head indicando la posicion anterior, y luego se hace la AND con el count-1. Asi formatearlo a lo que seria el indice.

Todo esto suponiendo que:

- count, sea la cantidad de lugares ( UART_RB_SIZE ) de la estructura, es decir lo inicializaste de esta forma:

RingBuffer_Init(&rxring, rxbuff, 1, UART_RB_SIZE);

Y una cosa que no esta dicho ahi, y que la libreria esa no verifica (y el articulo si) es que debe ser multiplo de 2 ( 128, 256, etc), y no puede ser un valor arbitrario que con el otro metodo si. Por que de ser un valor arbitrario count -1 no quedarian todos los bits que importan en 1 y asi "filtrar" el indice.
Título: Re:Desreferenciando caracteres en una struc...
Publicado por: elgarbe en 08 de Febrero de 2016, 10:19:20


Código: C
  1. ui8Temp = ((uint8_t *)Ringbuff->data)[((Ringbuff->head)-1) & (RingBuff->count - 1)];

De esa forma se resta 1 al head indicando la posicion anterior, y luego se hace la AND con el count-1. Asi formatearlo a lo que seria el indice.

Excelente, ahí obtuve el último caracter con ese código!
En mis pruebas le estaba errando en el casteo a uint8_t *. yo ponía (uint8_t *)(Ringbuff->data)[....].... no termino de entender como funcionan los paréntesis en cada caso...

Saludos!
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: elgarbe en 08 de Febrero de 2016, 13:28:24
He tenido que cambiar los -> por .
no sé x q al compilador dejó de gustarle el -> y quiere . y no tengo tiepo de investigarlo  :D

Quedó funcionando así:

Código: C
  1. void USART1_4_IRQHandler(void)
  2. {
  3.         uint8_t key;
  4.         uint16_t lastpos;
  5.         /* Want to handle any errors? Do it here. */
  6.         /* Use default ring buffer handler. Override this with your own
  7.            code if you need more capability. */
  8.         Chip_UARTN_IRQRBHandler(LPC_USART1, &rxring, &txring);
  9.  
  10.         lastpos = ((rxring.head)-1) & (rxring.count - 1);
  11.         key = ((uint8_t *)rxring.data)[lastpos];
  12.         if(key == 27)
  13.         {
  14.                 isRBHalfFull = 2;
  15.         }else
  16.         {
  17.                 if(RingBuffer_GetCount(&rxring) == WR_BUFF_SIZE){
  18.                         isRBHalfFull = 1;
  19. //                      RingBuffer_PopMult(&rxring, wr_buf, WR_BUFF_SIZE);
  20.                 }else
  21.                 {
  22.                         isRBHalfFull = 0;
  23.                 }
  24.         }
  25. }

ese "lastpos" lo uso para debug, despues lo voy a sacar.
Entonces simplemete veo si el último caracter es el 27 (tecla escape) pongo la bandera en 2. Si reciví WR_BUFF_SIZE (tengo que cambiar algunos nombres agregando SD como prefijo para que se entienda que son de la sd) pongo la bandera en 1. El else que pone la bandera en 0 creo que lo puedo sacar sin problemas.

En el main tengo:

Código: C
  1. // Monto la SD card
  2.         fs_mount();
  3.         // Abro el archivo de logueo y veo el contenido de la primer línea
  4.         fs_ReadLog();
  5.         // Creo el archivo para loguear
  6.         fs_OpenFileWrite();
  7.  
  8.         while(1)
  9.         {
  10.                 while(!isRBHalfFull);   // Me quedo esperando a que la bandera sea distinta de 0
  11.  
  12.                 fs_WriteFile(isRBHalfFull);
  13.                 isRBHalfFull = 0;
  14. //              if(isRBHalfFull==1)
  15. //              {
  16. //                      // La bandera es 1, significa que tengo el buffer tiene WR_BUFF_SIZE bytes en él
  17. //                      // Los escrivo en la sd Card
  18. //                      fs_WriteFile(1);
  19. //                      // Reseteo bandera
  20. //                      isRBHalfFull = 0;
  21. //              }else if (isRBHalfFull==2)
  22. //              {
  23. //                      // La bandera está en 2, significa que se ha encontrado el caracter ESC (27)
  24. //                      // Los escrivo en la sd Card
  25. //                      fs_WriteFile(2);
  26. //                      // Reseteo bandera
  27. //                      isRBHalfFull = 0;
  28. //              }
  29.         }

y finalmente la función de escritura:

Código: C
  1. FRESULT fs_WriteFile(uint8_t f_TipoEscritura)
  2. {
  3.         FRESULT rc;                             // Result code
  4.         UINT bw;                                // bytes escritos
  5.         uint16_t count;
  6.  
  7.  
  8.         if(f_TipoEscritura == 1)
  9.         {
  10.                 count = WR_BUFF_SIZE;
  11.  
  12.         }else if(f_TipoEscritura == 2)
  13.         {
  14.                 // Tengo que obtener los bytes que quedaron en el buffer y escribirlos en el archivo
  15.                 count = RingBuffer_GetCount(&rxring);
  16.         }
  17.  
  18.         // Extraigo count bytes y los pongo en wr_buf
  19.         RingBuffer_PopMult(&rxring, wr_buf, count);
  20.  
  21. //      printf("Write a data\n");
  22.  
  23. //      systick_start;
  24.         // Intento escribir los datos que hay en wr_buf. bw guarda la cantidad de bytes escritos
  25.         rc = f_write(&Fil, wr_buf, count, &bw);
  26. //      systick_stop;
  27.         // En este array guardo los tiempos de escritura de cada llamado
  28. //      wrotes[idx++]=time;
  29.         // Verifico que se hallan escrito tantos bytes como WR_BUFF_SIZE.
  30.         // No sé si es muy útil esta verificacion. Quizás sirva para detectar la última escritura
  31.         // que puede no ser de WR_BUFF_SIZE bytes y cerrar el archivo. Pero si es justo de WR_BUFF_SIZE bytes?
  32.         if(bw == WR_BUFF_SIZE)
  33.         {
  34.                 // Me aseguro de que se almacene en la SD todos los datos.
  35.                 rc = f_sync(&Fil);
  36. //              printf("%d bytes escritos\n", bw);
  37.                 return rc;
  38.         }else
  39.         {
  40.                 // escribí menos bytes que WR_BUFF_SIZE. Deve ser que son los ultimos bytes que había en el RB
  41.                 rc = f_close(&Fil);
  42.                 return 0;
  43.         }
  44. }

Bien, eso está funcionando de 10. Abro un terminal, me conecto al puerto serie, le arrojo un archivo de texto en la terminal y del otro lado se graba en la sd card. Cuando se termina de transmitir apreto ESC y con eso grabo los ultimos bytes.

Bien, ahora me aparece un nuevo problema... pierdo todos los <LF> (Cuando está presedido de un CR) del archivo original  :P

 - Tienes que ingresar para ver archivos adjuntos -

Le desconfío al encoding del FatFS... Voy a investigar un poco...

Saludos y gracias KILLER.
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: KILLERJC en 08 de Febrero de 2016, 14:03:08
Extraño que pierdas los LF.. Es raro por que es un valor comun como cualquiera :P.

Yo estuve tratando de hacer la funcion para que pueda ser usada con cualquier valor, ya que la libreria acepta un void, uno puede esperar valores float,byte,int, long, double , etc.
Y me quedaron 2 codigos que realmente no logro comprender demasiado del memcpy y es lo que pienso que esta mal, para mi memcpy deberia copiar 1 por 1 los elementos, hago la aritmetica en 8 bits  e itmSz. Pero luego no me queda claro si es que por ejemplo 4 elementos de un uin32_t pueden terminar en retorno todos juntos, y no espera que retorno sea un "array". Todavia no encontre nada respecto a eso.. asi que estoy buscando

Código: C
  1. static uint8_t RingBufferPeekLast( const RINGBUFF_T *RingBuff, void *retorno)
  2. {
  3.         uint32_t  ui32Offset
  4.         ui32Offset = ((RingBuff->head - 1) & (RingBuff->count - 1)) * RingBuff->itmSz;
  5.         memcpy(retorno,&(((uint8_t *)RingBuff->buff)[offset]),RingBuff->itmSz);
  6. }

Usando el codigo de lectura que pasaste (Igual un ptr a un uint8_t, como en la lectura, pero que no se si funcionara para otros tipos.):

Código: C
  1. static uint8_t RingBufferPeekLast( const RINGBUFF_T *RingBuff, void *retorno)
  2. {
  3.         uint8_t  *ptr = RingBuff->buff;
  4.         ptr += (RB_INDH(RingBuff)-1) * RingBuff->itmSz;
  5.         memcpy(retorno,ptr,RingBuff->itmSz);
  6. }

EDIT:

Citar
no sé x q al compilador dejó de gustarle el -> y quiere . y no tengo tiepo de investigarlo

Si te referis a esto:

key = ((uint8_t *)rxring.data)[lastpos];

es obvio el por que. ya que normalmente en las funciones que estuvimos haciendo arriba era un puntero a la estructura: RINGBUFF_T *RingBuff

Mientras que aca estas accediendo a la estructura. Y un elemento de la misma es data, Para poner un ejemplo:

RINGBUFF_T *RingBuff = &rxring;
key = ((uint8_t *)RingBuff->data)[lastpos];

Citar
Le desconfío al encoding del FatFS... Voy a investigar un poco...

Es raro por que no se preocupa por eso. Por que se complicaria la vida con eso? Directamente manda todos los bytes en fila... Lo unico que deberia preocuparse es por el '\0' en una string.


Y una ultima cosa que me esta carcomiendo la cabeza.
Imagino que esto es todo sin optimizaciones activas, ya que veo que no hay ningun "volatile" dando vueltas, especialmente en el buffer de lectura/escritura
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: elgarbe en 08 de Febrero de 2016, 14:37:06
Recién estaba en el baño y me llegó un toque de inspiración. Tal ves el programa terminal este cambiando cr+lf por sólo lf... Ahora salí unas horas, dsp lo investigo
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: elgarbe en 08 de Febrero de 2016, 18:21:28
Citar
no sé x q al compilador dejó de gustarle el -> y quiere . y no tengo tiepo de investigarlo

Si te referis a esto:

key = ((uint8_t *)rxring.data)[lastpos];

es obvio el por que. ya que normalmente en las funciones que estuvimos haciendo arriba era un puntero a la estructura: RINGBUFF_T *RingBuff

Mientras que aca estas accediendo a la estructura. Y un elemento de la misma es data, Para poner un ejemplo:

RINGBUFF_T *RingBuff = &rxring;
key = ((uint8_t *)RingBuff->data)[lastpos];

jaja, tenes razón!!!

Citar
Le desconfío al encoding del FatFS... Voy a investigar un poco...

Es raro por que no se preocupa por eso. Por que se complicaria la vida con eso? Directamente manda todos los bytes en fila... Lo unico que deberia preocuparse es por el '\0' en una string.

Si, no era por el encoding. Era por el terminal que enviaba el final de línea con un CR. Ahí lo configure para que envíe CR+LF y consigo exactamente el mismo archivo en la sd card. En realidad tiene 1 byte más porque no he sacado aún el último caracter (el ESC) del buffer antes de grabarlo.
 - Tienes que ingresar para ver archivos adjuntos -

Y una ultima cosa que me esta carcomiendo la cabeza.
Imagino que esto es todo sin optimizaciones activas, ya que veo que no hay ningun "volatile" dando vueltas, especialmente en el buffer de lectura/escritura

Si, no he habilitado las optimizaciones y no he verificado los VOLATILES. Ahora que está funcionando voy a ir retocando esas cosas.
Las optimizaciones nunca las uso... será cuestion de empezar a usarlas... o no...

Saludos y gracias!
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: KILLERJC en 08 de Febrero de 2016, 18:34:43
Citar
Si, no he habilitado las optimizaciones y no he verificado los VOLATILES. Ahora que está funcionando voy a ir retocando esas cosas.
Las optimizaciones nunca las uso... será cuestion de empezar a usarlas... o no...

No se que decirte.. algunos nunca usan, por ahi lei que es una mala costumbre ya que permite el mal "codeo" de la aplicacion, no usando las cosas como deben ser. Pero lo mas apreciable es la reduccion de lo ocupado en la flash y la velocidad. Que algunos por internet aseguran que han llegado al 40% de reduccion optimizando por velocidad ( que es lo que siempre se deberia hacer y no por espacio).. Ya que la flash no es algo demasiado restringente.

En fin cuestiones de gusto, solo lo vi y lo dije..

Respecto a :

Excelente, ahí obtuve el último caracter con ese código!
En mis pruebas le estaba errando en el casteo a uint8_t *. yo ponía (uint8_t *)(Ringbuff->data)[....].... no termino de entender como funcionan los paréntesis en cada caso...

Lo explique en el primer post:

Citar
Cambie el codigo. Espero que funcione. El acceso a la estructura a traves de un pointer tiene la mas alta prioridad ( -> ), luego como los [] tiene mayor orden de precedencia que el cast, debo ponerle otros parentesis ya que () y [] son asociativos de izquierda a derecha, primero se ejecuta el (). Y esa es la explicacion de como se llego a eso :P

http://en.cppreference.com/w/c/language/operator_precedence

Y algo mas grafico:
http://www.c4learn.com/c-programming/c-reading-complex-pointer-expression/

Pero por si acaso lo que estabas haciendo es
(uint8_t *)(Ringbuff->data)[....]

Prioridades 1ero lo de ( Ringbuff->data) , luego [], finalmente el cast. Es decir esos parentesis simplemente estaban de mas. No cumplian ninguna funcion. y el cast actuaba sobre el elemento devuelto.
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: elgarbe en 08 de Febrero de 2016, 21:52:49
Y una ultima cosa que me esta carcomiendo la cabeza.
Imagino que esto es todo sin optimizaciones activas, ya que veo que no hay ningun "volatile" dando vueltas, especialmente en el buffer de lectura/escritura

acabo de poner volatile a esto:

Código: C
  1. static volatile RINGBUFF_T txring, rxring;

ya que esas estructuras se pueden modificar en la interrupcion.
El compilador me arroja unos cuantos warning sobre esto:

Código: C
  1. warning: passing argument 1 of 'RingBuffer_Init' discards 'volatile' qualifier from pointer target type
  2.   RingBuffer_Init(&rxring, rxbuff, 1, UART_RB_SIZE);

sabes a que se debe y si tengo que darle importancia?

Saludos
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: BrunoF en 09 de Febrero de 2016, 02:00:02
Llegué tarde a la charla, pero creo poder responderte esto último.

Lo que te está diciendo es precisamente lo que va a suceder: que dentro de la función RingBuffer_init(...), actualmente, el primer argumento (&rxring) no va a ser tratado como volatile, y tal vez deberías revisarlo si querés que sea volatile también dentro de la función.

La solución es agregar también el modificador volatile al primer parámetro en la declaración de la función RingBuffer_Init(...).

P.D. En mis implementaciones de ring buffers, siempre usé ese método de calcular el tamaño por diferencia entre las posiciones iniciales y finales de la cola, pero tiene un problema si no se agregan controles adicionales: cuando el buffer se llena, la posición final iguala a la inicial y por ende podemos creer que el buffer está vacío cuando en realidad está lleno...
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: KILLERJC en 09 de Febrero de 2016, 05:38:32
Exacto. si vas por usar volatile, los punteros tambien tienen que ser declarados como punteros A volatiles.

volatile type *ptr

P.D. En mis implementaciones de ring buffers, siempre usé ese método de calcular el tamaño por diferencia entre las posiciones iniciales y finales de la cola, pero tiene un problema si no se agregan controles adicionales: cuando el buffer se llena, la posición final iguala a la inicial y por ende podemos creer que el buffer está vacío cuando en realidad está lleno...

Si y yo siempre lo habia visto asi al RingBuffer, la verdad es que es la primera ves que lo veo implemetando de esta forma. La diferencia es que de la forma que decis el tamaño del buffer no esta limitado por nada, mientras que la forma que posee elgarbe si o si obliga a ser multiplo de 2. Creo que es la unica diferencia que note ( ademas de la estructura que posee variables mas.
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: elgarbe en 09 de Febrero de 2016, 11:24:33
y mi pregunta es, es necesario que sean volátiles las estructuras del RB?

Ahora yo tengo esto:

Código: C
  1. typedef struct {
  2.         void *data;
  3.         int count;
  4.         int itemSz;
  5.         uint32_t head;
  6.         uint32_t tail;
  7. } RINGBUFF_T;
  8.  
  9. /* Transmit and receive ring buffers */
  10. static RINGBUFF_T txring, rxring;
  11.  
  12. // Buffers para los datos del RB
  13. static uint8_t rxbuff[UART_RB_SIZE];
  14. // No estoy usando la transmision de datos. Configuro igual el RB de transmision.
  15. static uint8_t txbuff[UART_RB_SIZE];
  16.  
  17. int RingBuffer_Init(RINGBUFF_T *RingBuff, void *buffer, int itemSize, int count)
  18. {
  19.         RingBuff->data = buffer;
  20.         RingBuff->count = count;
  21.         RingBuff->itemSz = itemSize;
  22.         RingBuff->head = RingBuff->tail = 0;
  23.  
  24.         return 1;
  25. }
  26.  
  27.         RingBuffer_Init(&rxring, rxbuff, 1, UART_RB_SIZE);
  28.         RingBuffer_Init(&txring, txbuff, 1, UART_RB_SIZE);
  29.  
  30.  
  31. void USART1_4_IRQHandler(void)
  32. {
  33.         uint8_t key;
  34.         uint16_t lastpos;
  35.         Chip_UARTN_IRQRBHandler(LPC_USART1, &rxring, &txring);
  36.  
  37.         lastpos = ((rxring.head)-1) & (rxring.count - 1);
  38.         key = ((uint8_t *)rxring.data)[lastpos];
  39.         if(key == 27)
  40.         {
  41.                 isRBHalfFull = 2;
  42.         }else
  43.         {
  44.                 if(RingBuffer_GetCount(&rxring) == SD_WR_BUFF_SIZE){
  45.                         isRBHalfFull = 1;
  46.                 }else
  47.                 {
  48.                         isRBHalfFull = 0;
  49.                 }
  50.         }
  51. }

Puse todo el código junto para que se entienda, aunque en los sources no estan juntos.

Mi duda es que cosa puede hacer el compilador (con la optimizacion habilitada) que tenga un mal funcionamiento?
Por que estoy pasando siempre a las funciones del RB la direccion del RingBuffer, dicha direccion no cambia nunca, entonces no veo que el compilador pueda hacer macana. No sé como funciona el almacenaje de los miembros de la estructura... Quizá si el compilador almacena en un registro el miembro "count" que no es volatile y en algúna parte peligrosa del codigo yo cambiara ese miembro puede ser que haya problemas. Pero el unico miembro que cambia es "data" que al ser un puntero no volatile, no sé que pueda pasar....

Distinto es si declaro un puntero a una variable y, por ejemplo, dentro de una interrupcion hago que ese puntero apunte a otra cosa...
No tengo bien claro este tema, pero así lo entiendo yo...
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: KILLERJC en 09 de Febrero de 2016, 13:34:49
Es simple elgarbe, activa las optimizaciones y si funciona esta todo bien :P

Citar
Mi duda es que cosa puede hacer el compilador (con la optimizacion habilitada) que tenga un mal funcionamiento?
Por que estoy pasando siempre a las funciones del RB la direccion del RingBuffer, dicha direccion no cambia nunca, entonces no veo que el compilador pueda hacer macana. No sé como funciona el almacenaje de los miembros de la estructura... Quizá si el compilador almacena en un registro el miembro "count" que no es volatile y en algúna parte peligrosa del codigo yo cambiara ese miembro puede ser que haya problemas. Pero el unico miembro que cambia es "data" que al ser un puntero no volatile, no sé que pueda pasar....

Lo que cambia es: head y tail. Nada mas,  *data se mantiene constante pero el cotenido no.
El compilador creo que debe tomar "funcion a funcion" y ver si por ejemplo no se modifica head o no se modifica tail, entonces pensara que no tiene sentido leer una y otra ves esos valores (ya que jamas son modificados). Entonces tal ves inserte codigo para calcularlo una sola ves o insertar un valor constante. Y ahi es donde entra el problema.

Supongamos una funcion en tu main preguntando por isRBHalfFull dentro de un while, y que nada modifique ese valor por fuera del mismo.
Si no es declarado volatile va a suponer que jamas cambia y nunca va a entrar.

Como decia, si queres probar si funciona con optimizaciones, activalo, grabalo y fijate si funciona. Si no funciona es por que el volatile es necesario en alguna parte :P

Código: C
  1. Distinto es si declaro un puntero a una variable y, por ejemplo, dentro de una interrupcion hago que ese puntero apunte a otra cosa...
  2. No tengo bien claro este tema, pero así lo entiendo yo..

Si es un puntero declarado a una variable comun, es lo mismo que antes. Posiblemente considere : Puntero constante, valor apuntado constante
Si es un puntero declarado a una variable volatile, entonces va a acceder siempre a la variable, considerando : Puntero constante, valor apuntado volatile.
Si es un puntero volatile apuntando a una variable volatile, Seria todo volatile hasta el puntero. ( volatile type * volatile ptr )
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: BrunoF en 09 de Febrero de 2016, 19:31:12
El tema de los riesgos y si es útil o no, depende mucho de las características propias del uC en cuestión, y de cómo las explote el compilador.

Básicamente el compilador puede recurrir a dos formas de optimización (puede haber más pero se me ocurren estas dos básicas) que pueden hacer la diferencia con una variable volatile o no:
a) copia del valor de una variable a otra shadow en RAM. Es decir que en lugar de usar el registro original, el compilador genera una copia y usa la copia para realizar las operaciones temporalmente, sin refrescar el valor la variable original en cada aparición;
b) copia del valor de una variable a un registro de trabajo interno del uC. Obviamente en microcontroladores con muchos registros de trabajo es más común ver este tipo de código/optimizaciones.

No creo que suceda nada si no la ponés como volatile por lo que mencionás. Si no cambiás el valor del puntero nunca, poco puede hacer para mandarse macana con esa variable.

Sinceramente, el volatile es una forma de garantizar cierto refresco de la variable pero de ninguna forma garantiza la sincronización o coherencia en todo momento del valor de un registro.

Imaginemos un código sencillo:

volatile uint8_t activarBomba = 0;

while(1)
{
    activarBomba = 0;
    if(activarBomba)
        Explota();
}
...

y una interrupción configurada, habilitada y frecuentemente invocada que haga:
{
    activarBomba = 1;
}

explotará eventualmente? o no?
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: KILLERJC en 09 de Febrero de 2016, 19:35:30
Segun el ejemplo que diste, para mi de forma optimizada no, de forma no optimizada puede ser.
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: BrunoF en 09 de Febrero de 2016, 19:47:23
Podrías desarrollar un poco más tu opinión y justificar las diferencias en ambos casos?
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: KILLERJC en 09 de Febrero de 2016, 20:16:07
Empiezo que me olvide de ver el volatile :P de la variable jeje

Ahora que veo el volatile expreso mi opinion nuevamente y ademas me extiendo con el por que lo pienso asi.

Sin optimizacion:

Posiblemente explosion:
Con volatile estas obligando al compilador que tome nuevamente la variable desde donde se necesita ( no "cachearlo"). Y de no hacerlo estaria incumpliendo la misma.

Es decir, si uno asegura que activarBomba = 0; y deja una copia en un registro interno, ( stack no ), cuando haga el if() y nuevamente se pida el valor del mismo estariamos violando ese "volatile".

Entonces supongamos que por alguna casualidad ocurre la interrupcion. En un micro que se basa en el read-modify-write como es ARM y que seria el mas "propenso" a que ocurra y no en un PIC que lo hace todo en una instruccion (me refiero a los momentos de interrupciones).
Si la interrupcion ocurre antes del if ( de la instruccion de read de memoria es la unica opcion ) habria una explosion. Ya que no tenes ningun "seguro" de que no pueda ser cambiado el valor entre esas 2 instrucciones. Al ejecutarse el if vuelve a tomar el valor de RAM ( obligado por el volatile ). Cualquier otro momento que ocurra la interrupcion no va a poder ocurrir una explosion.

Con optimizacion:
Nuevamente el volatile hace de las suyas, haciendo que cualquier optimizacion sobre la variable se descarte y por ende funcionaria al igual que lo anterior
Asi que tambien es una posible explosion. Ya que no hay nada que asegure que el valor de esa varaible no sea cambiada.

-------------------------------------

Distinto hubiera sido sin volatile. Que es lo primero que lei equivocamente.

Sin volatile funcionaria igual que lo anterior sin optimizacion.

Pero con optimizacion el compilador como no hay ninguna modificacion a esa variable directamente quitaria el if y su codigo.
El por que tomar esa decision?
No se modifica nunca la variable, es decir la variable tiene el mismo valor sea donde sea la funcion y no se espera que cambie por otro lado, asi que podria usar una version cacheada. Procedemos a quitarla. Solucionado en tiempo de compilacion.


Igual estoy asumiendo que el volatile OBLIGA al compilador a leer la memoria, cuando veo que cada compilador puede implementar el volatile como le guste.

EDIT: Creo saber a donde queres llegar con esto, y mas si es aplicado al ejemplo de elgarbe.
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: BrunoF en 09 de Febrero de 2016, 20:57:17
Comparto tu última opinión, que al estar el modificador volatile en la variable, el riesgo de que se ejecute alguna vez el Explota(), independientemente del nivel de optimización, es prácticamente inminente.

Habría que hacer algunas pruebas para confirmar si efectivamente el compilador con las optimizaciones habilitadas suprimiría el código y solucionaría el problema de cuajo.
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: KILLERJC en 09 de Febrero de 2016, 23:09:28
Siguiendo el tema, te agradezco Bruno por hacerme ver lo que se me escapaba. Como puse en el EDIT, creo saber adonde querias llegar con esto.

El tema principal esta en que por mas que uno lo ponga como volatil (optimizacion/sin) y ya que en la situacion real de RingBuffer, existe una pequeña posibilidad de que exista un error.

Y que las funciones como las tiene actualmente elgarbe no cubren ( incluso puesto como volatiles)

Si las funciones de ringbuffer son usadas fuera de la interrupcion tambien. Hay que proteger las variables head y tail contra una posible corrupcion de datos de la misma. El caso paso a paso que ocurra esto seria:

Programa normal - lee del registro , por lo cual toma el valor de tail y lo pone en un registro interno (para modificarlo y guardalo)
Interrupcion: Entra aqui y se procede a guardar/sacar algunos valores los cuales modifican head y tail, guardandolos y saliendo.
Programa normal: (Reestablecido el stack)Modifica y guarda la variable tail. Sobreescribiendo lo anterior.

Finalmente tenes la variable tail corrupta. Y no hay volatile que te salve. Asi que las funciones de lectura/escritura, especialmente en el momento de modificar los indices head y tail, deben ser protegidos contra esto, y la unica forma de que esto ocurra es creando una zona critica ( libre de interrupciones ).

Aunque es una ventana "pequeña" estamos hablando de 2 instrucciones. Esto puede ocurrir y es lo importante (Con o sin optimizacion, volatil o no).
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: BrunoF en 10 de Febrero de 2016, 00:29:11
Efectivamente, KILLER, hemos desembocado en el quid de la cuestión.

Y otra vez más, estás en lo cierto viendo que la única solución potable y sencilla parecería ser deshabilitar las interrupciones.

Ahora, si elegimos una arquitectura, como por ejemplo en este caso el ARM Cortex M0+:
a) podríamos encontrar otra solución segura sin recurrir a deshabilitar las interrupciones en el código crítico?
b) realmente hacer:

- deshabilitar interrupcion/es;
- modificar variable compartida;
- rehabilitar interrupcion/es;

alcanza para garantizar la no corrupción de la variable?

Ya empezamos a rozar la filosofía en cualquier momento... :D :D
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: KILLERJC en 10 de Febrero de 2016, 01:08:23
Y ahora nuevamente me hiciste pensar xD.. Aunque el que deberia pensar es elgarbe, pero me gusta esto como va encaminado :P..

Yo estuve viendo la arquitectura de los ARM mas que nada para meterme con ASM. Pero ya casi no me acordaba.

Respecto a la arquitectura:
Lo primero que me acorde fue del: Load-Exclusive / Store-Exclusive , pero estaba seguro que en el ARMv7-m existia, pero en el ARMv6-m no existe.

Asi que me juego por DMB/DSB. ( Aunque no me queda muy en claro su funcionamiento y que gracias a eso tal ves estoy tirando esto al azar)

Y es lo unico que se me ocurre aparte de:
-  desactivar las interrupciones
-  modificar las prioridades
-  O usar el SVC que al poseeria una mayor prioridad.

( Estos ultimos 2 ni se me ocurriria hacerlos, solo lo planteo como una posibilidad ).

Y ninguno sabria como implmentarlos sin irme por ASM. Y tampoco se si las opciones que di son en realidad lo que estas buscando. De todas formas yo me jugaba por LDREX/STREX aunque me la complicaste con el M0+

Citar
- deshabilitar interrupcion/es;
- modificar variable compartida;
- rehabilitar interrupcion/es;

alcanza para garantizar la no corrupción de la variable?

Si, pero con una salvedad, es que estemos hablando de interrupcion y programa principal. Y no estemos hablando de diferentes task como si estuviera un OS (en el que se vean involucradas otras fuentes a la corrupcion).
Como creo que no posee un OS entonces garantizaria la corrupcion de la variable, pero si preguntaste es por que hay alguna trampa ..  :D , como una excepcion pero que descarte en lo que dije antes, al quitar otras fuentes.

Respecto al software:
Crear un mutex / semaforo para indicar que no puede acceder a los datos todavia. Pero esto me deja en un problema. Si uno activa el mutex y luego se entrar a la interrupcion ( el cual leeria el mutex ) como es posible que la interrupcion se vuelva a ejecutar luego de que salga de la misma y se reestablesca el mutex.
Tal ves tener una indicador (desde la interrupcion al ver activo el mutex) que se disparo la interrupcion y con eso en el programa principal finalizado el mutex poner un pending a la interrupcion en cuestion.

Mismo impacto que deshabilitar las interrupciones. Pero esta ves extensible a otro task/thread


PD: Me siento caminando en un campo minado..
PD2: Es mas feo cuando sos al que solamente le llegan preguntas y esperan que responda bien jeje. Pero igual lo que me importa es aprender.
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: elgarbe en 10 de Febrero de 2016, 06:55:47
A ver si puedo dar vuelta la cosa.

En mi programa principal, yo solo saco datos usando esta funcion (la cual solo modifica TAIL):

Código: C
  1. /* Pop multiple items from Ring buffer */
  2. int RingBuffer_PopMult(RINGBUFF_T *RingBuff, void *data, int num)
  3. {
  4.         uint8_t *ptr = RingBuff->data;
  5.         int cnt1, cnt2;
  6.  
  7.         /* We cannot insert when queue is empty */
  8.         if (RingBuffer_IsEmpty(RingBuff))
  9.                 return 0;
  10.  
  11.         /* Calculate the segment lengths */
  12.         cnt1 = cnt2 = RingBuffer_GetCount(RingBuff);
  13.         if (RB_INDT(RingBuff) + cnt1 >= RingBuff->count)
  14.                 cnt1 = RingBuff->count - RB_INDT(RingBuff);
  15.         cnt2 -= cnt1;
  16.  
  17.         cnt1 = MIN(cnt1, num);
  18.         num -= cnt1;
  19.  
  20.         cnt2 = MIN(cnt2, num);
  21.         num -= cnt2;
  22.  
  23.         /* Write segment 1 */
  24.         ptr += RB_INDT(RingBuff) * RingBuff->itemSz;
  25.         memcpy(data, ptr, cnt1 * RingBuff->itemSz);
  26.         RingBuff->tail += cnt1;
  27.  
  28.         /* Write segment 2 */
  29.         ptr = (uint8_t *) RingBuff->data + RB_INDT(RingBuff) * RingBuff->itemSz;
  30.         data = (uint8_t *) data + cnt1 * RingBuff->itemSz;
  31.         memcpy(data, ptr, cnt2 * RingBuff->itemSz);
  32.         RingBuff->tail += cnt2;
  33.  
  34.         return cnt1 + cnt2;
  35. }

y en mi interrupcion, despues de seguir varios llamados a ISRs llego a esta funcion que mete cosas en el RB (la cual solo modifica HEAD):

Código: C
  1. /* Insert a single item into Ring Buffer */
  2. int RingBuffer_Insert(RINGBUFF_T *RingBuff, const void *data)
  3. {
  4.         uint8_t *ptr = RingBuff->data;
  5.  
  6.         /* We cannot insert when queue is full */
  7.         if (RingBuffer_IsFull(RingBuff))
  8.                 return 0;
  9.  
  10.         ptr += RB_INDH(RingBuff) * RingBuff->itemSz;
  11.         memcpy(ptr, data, RingBuff->itemSz);
  12.         RingBuff->head++;
  13.  
  14.         return 1;
  15. }

Como ven una modifica la tail y la otra modifica la head. Ninguna funcion modifica las dos cosas.
Entonces, mientras en el main solo haga uso de Insertar ó Quitar cosas al/del RB y en la ISR haga la accion contraria, no veo que haya problema.
Creo que los problemas con los RB es cuando se mezclan las cosas, o sea, tanto en el main como en la ISR quiero meter cosas y/o quiero quitar cosas.

Saludos!
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: KILLERJC en 10 de Febrero de 2016, 13:21:31
Podrias pasar que hace estas funciones:

Código: [Seleccionar]
RB_INDH(RingBuff)
Código: [Seleccionar]
RB_INDT(RingBuff)
Y como decis. Si usas uno y otro no deberia haber ningun problema. A no ser que uses tambien alguna otra como la de un flush del buffer.
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: BrunoF en 10 de Febrero de 2016, 14:17:05
Tengo pendiente responderte KILLER, perdoná. No creas que es un examen! Yo planteo las preguntas para todos! No tengo necesariamente las respuestas! Es un tema de debate, no de profesor/alumno!

y qué pasa con el miembro count de la struct del RB? acaso no lo modificás desde fuera y dentro de la interr si modificás el tail y el head?
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: KILLERJC en 10 de Febrero de 2016, 17:21:48
Tengo pendiente responderte KILLER, perdoná. No creas que es un examen! Yo planteo las preguntas para todos! No tengo necesariamente las respuestas! Es un tema de debate, no de profesor/alumno!

y qué pasa con el miembro count de la struct del RB? acaso no lo modificás desde fuera y dentro de la interr si modificás el tail y el head?

No bruno los unicos modificables son head y tail nomas. Todo lo demas se inicia y quedaria como constante.
Por la forma que usa head y tail. Tiene que saber si o si: la cantidad de elementos + tamaño de cada uno. Explico el procedimiento

Como veras head y tail no se wrapean, lo unico que puede pasar es un integer overflow. es decir siempre estan sumando. Entonces como hace para obtener el indice ?

Suponete que tenes 3 elementos y que tu buffer es de 512 elementos, y que usaste muy mucho ya el array entonces

head es: 0xFFFF.549
tail es: 0xFFFF.546
count: 0x0200

Entonces para sacar el indice , lo que hace es restar primero head y tail y haciendo una AND con count - 1. quedando:

head - tail = 0x3
count - 1 = 0x1FF

Resultado = 0x3
Que luego afecta al puntero multiplicandose por el tamaño del item ( imtSz )

Es decir de todos los elementos de la estructura, los unicos que varian son head/tail
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: elgarbe en 10 de Febrero de 2016, 18:18:41
Si bien creo que todos conocen la forma de trabajar de RB que implementó NXP, acá repito un buen link sobre el tema:

http://www.simplyembedded.org/tutorials/interrupt-free-ring-buffer/

tambien otro sobre interrupt-safe RB:

http://www.downtowndougbrown.com/2013/01/microcontrollers-interrupt-safe-ring-buffers/

Podrias pasar que hace estas funciones:
Código: [Seleccionar]
RB_INDH(RingBuff)
Código: [Seleccionar]
RB_INDT(RingBuff)Y como decis. Si usas uno y otro no deberia haber ningun problema. A no ser que uses tambien alguna otra como la de un flush del buffer.

en ring_buffer.c

Código: C
  1. #define RB_INDH(rb)                ((rb)->head & ((rb)->count - 1))
  2. #define RB_INDT(rb)                ((rb)->tail & ((rb)->count - 1))

En ring_buffer.h:

Código: C
  1. /**
  2.  * @def         RB_VHEAD(rb)
  3.  * volatile typecasted head index
  4.  */
  5. #define RB_VHEAD(rb)              (*(volatile uint32_t *) &(rb)->head)
  6.  
  7. /**
  8.  * @def         RB_VTAIL(rb)
  9.  * volatile typecasted tail index
  10.  */
  11. #define RB_VTAIL(rb)              (*(volatile uint32_t *) &(rb)->tail)
  12.  
  13. /**
  14.  * @brief       Initialize ring buffer
  15.  * @param       RingBuff        : Pointer to ring buffer to initialize
  16.  * @param       buffer          : Pointer to buffer to associate with RingBuff
  17.  * @param       itemSize        : Size of each buffer item size
  18.  * @param       count           : Size of ring buffer
  19.  * @note        Memory pointed by @a buffer must have correct alignment of
  20.  *                      @a itemSize, and @a count must be a power of 2 and must at
  21.  *                      least be 2 or greater.
  22.  * @return      Nothing
  23.  */
  24. int RingBuffer_Init(RINGBUFF_T *RingBuff, void *buffer, int itemSize, int count);

" * @note   Memory pointed by @a buffer must have correct alignment of
 *          @a itemSize, and @a count must be a power of 2 and must at
 *          least be 2 or greater.
 * @return   Nothing"

No estoy en casa así que este codigo lo saque de internet y creo que es de otro chip, pero la implementacion es transparente al chip, por lo que seguro el código es el mismo.

Saludos!
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: KILLERJC en 13 de Febrero de 2016, 00:20:32
Lamento elgarbe por desvirtuarte el tema y espero que Bruno le guste el ASM xD

Jugando ahora si con las compilaciones..

Bruno, Cree el codigo que me dijiste, en realidad hice esto en CCS (TI tiene su propio compilador), NO ES GCC pero es lo que mas tenia a mano. Y es para un ARMv7-M ( M4F )
Una cosa mas.. puede que los resultados no sean exactos en la parte con optimizacion, especialmente en la parte de volatile, por que la funcion que cree Explota() en si no sirve para nada. Y que tal ves eso es lo que hace que quite esta funcion. Mas adelante voy a crear otra funcion Explota() y veremos la diferencia.

Código: C
  1. #include <stdint.h>
  2.  
  3. void Explota(void);
  4.  
  5. volatile uint8_t activarBomba = 0;
  6.  
  7. void main(void) {
  8.  
  9.         while(1)
  10.         {
  11.             activarBomba = 0;
  12.             if(activarBomba)
  13.             {
  14.                 Explota();
  15.             }
  16.  
  17.         }
  18. }
  19.  
  20. void Explota(void)
  21. {
  22.         int i = 3;
  23.         int j = 1;
  24.         i += j;
  25.         j += i;
  26. }
  27.  
  28. static void IntDefaultHandler(void)
  29. {
  30.         activarBomba = 1;
  31. }

--------------------------------------
Luego de creado el C, compilado con distintas versiones y optimizaciones , procedi a hacerle un dissasembly con el objdump, a la derecha estan los comentarios intentando traducirlo a "C", obtuve lo siguiente:

Sin optimizaciones (CON volatile): (Si quieren omitan estos 2 que son largos y procedan al resultado)

Código: ASM
  1. 00000498 <main>:                                ; void main(void) {
  2.  498:   b508            push    {r3, lr}        ;
  3.  49a:   ea4f 0808       mov.w   r8, r8          ;
  4.  49e:   46c0            nop                     ;
  5.  4a0:   f240 2114       movw    r1, #532        ; while(1) {
  6.  4a4:   2000            movs    r0, #0          ;
  7.  4a6:   f2c2 0100       movt    r1, #8192       ;  
  8.  4aa:   7008            strb    r0, [r1, #0]    ; activaBomba = 0;
  9.  4ac:   f240 2014       movw    r0, #532        ;
  10.  4b0:   f2c2 0000       movt    r0, #8192       ;
  11.  4b4:   7800            ldrb    r0, [r0, #0]    ;  <-- lee de memoria
  12.  4b6:   2800            cmp     r0, #0          ; if(activaBomba)
  13.  4b8:   d0f2            beq.n   4a0 <main+0x8; activaBomba es 0 salta a 0x4A0
  14.  4ba:   f000 f801       bl      4c0 <Explota>   ; activaBomba es 1 salta a 0x4C0 ( Explota )
  15.  4be:   e7ef            b.n     4a0 <main+0x8>  ; } Fin del while(1)
  16.  
  17. 000004c0 <Explota>:                             ; void Explota(void) {
  18.  4c0:   f1ad 0d08       sub.w   sp, sp, #8      ; // Espacio para las variables
  19.  4c4:   2003            movs    r0, #3          ; i = 3;
  20.  4c6:   9000            str     r0, [sp, #0]    ;
  21.  4c8:   2001            movs    r0, #1          ; j = 1;
  22.  4ca:   9001            str     r0, [sp, #4]    ;
  23.  4cc:   9900            ldr     r1, [sp, #0]    ;
  24.  4ce:   9801            ldr     r0, [sp, #4]    ;
  25.  4d0:   1840            adds    r0, r0, r1      ; j += i;
  26.  4d2:   9000            str     r0, [sp, #0]    ;
  27.  4d4:   9901            ldr     r1, [sp, #4]    ;
  28.  4d6:   9800            ldr     r0, [sp, #0]    ;
  29.  4d8:   1840            adds    r0, r0, r1      ; i += j ;
  30.  4da:   9001            str     r0, [sp, #4]    ;
  31.  4dc:   b002            add     sp, #8          ; // Retorno el Stack Pointer al lugar de antes
  32.  4de:   4770            bx      lr              ; } // Fin Explota();
  33.  
  34. 00000540 <IntDefaultHandler>:
  35.  540:   f240 2114       movw    r1, #532        ; Interrupcion() {
  36.  544:   2001            movs    r0, #1          ;
  37.  546:   f2c2 0100       movt    r1, #8192       ;
  38.  54a:   7008            strb    r0, [r1, #0]    ; activaBomba = 1;
  39.  54c:   4770            bx      lr              ; }

--------------------------------------------

Sin optimizaciones ( SIN volatile )

Código: ASM
  1. 00000498 <main>:                                ; void main(void) {
  2.  498:   b508            push    {r3, lr}
  3.  49a:   ea4f 0808       mov.w   r8, r8
  4.  49e:   46c0            nop                     ;
  5.  4a0:   f240 2114       movw    r1, #532        ; while(1) {
  6.  4a4:   2000            movs    r0, #0          ;
  7.  4a6:   f2c2 0100       movt    r1, #8192       ;
  8.  4aa:   7008            strb    r0, [r1, #0]    ; activaBomba = 0;
  9.  4ac:   f240 2014       movw    r0, #532        ;
  10.  4b0:   f2c2 0000       movt    r0, #8192       ;
  11.  4b4:   7800            ldrb    r0, [r0, #0]    ;  <-- lee de memoria
  12.  4b6:   2800            cmp     r0, #0          ; if (activaBomba) {
  13.  4b8:   d0f2            beq.n   4a0 <main+0x8; Igual a 0: Comienza el while de nuevo
  14.  4ba:   f000 f801       bl      4c0 <Explota>   ; Igual a 1: Explota
  15.  4be:   e7ef            b.n     4a0 <main+0x8>  ; } Fin del while } Fin de main
  16.  
  17. 000004c0 <Explota>:                             ; void Explota(void) {
  18.  4c0:   f1ad 0d08       sub.w   sp, sp, #8      ;
  19.  4c4:   2003            movs    r0, #3
  20.  4c6:   9000            str     r0, [sp, #0]    ; i = 3;
  21.  4c8:   2001            movs    r0, #1
  22.  4ca:   9001            str     r0, [sp, #4]    ; j = 1
  23.  4cc:   9900            ldr     r1, [sp, #0]
  24.  4ce:   9801            ldr     r0, [sp, #4]
  25.  4d0:   1840            adds    r0, r0, r1      ; j += i;
  26.  4d2:   9000            str     r0, [sp, #0]
  27.  4d4:   9901            ldr     r1, [sp, #4]
  28.  4d6:   9800            ldr     r0, [sp, #0]
  29.  4d8:   1840            adds    r0, r0, r1      ; i += j;
  30.  4da:   9001            str     r0, [sp, #4]
  31.  4dc:   b002            add     sp, #8
  32.  4de:   4770            bx      lr              ; } Fin Explota()
  33.  
  34. 00000540 <IntDefaultHandler>:                   ; void Interrupt() {
  35.  540:   f240 2114       movw    r1, #532        ;
  36.  544:   2001            movs    r0, #1
  37.  546:   f2c2 0100       movt    r1, #8192       ; 0x2000
  38.  54a:   7008            strb    r0, [r1, #0]    ; activaBomba = 1;
  39.  54c:   4770            bx      lr              ; }


Resultado: Hasta aqui se puede ver que SIN o CON volatile , si uno NO usa las optimizaciones es lo mismo. Y el codigo definitivamente es HORRIBLE.

----------------------------------------------

Optimizacion maxima y optimizado a velocidad , SIN volatile

Código: ASM
  1. 000004e0 <IntDefaultHandler>:                   ; void Interrupt() {
  2.  4e0:   f240 2114       movw    r1, #532        ;
  3.  4e4:   2001            movs    r0, #1
  4.  4e6:   f2c2 0100       movt    r1, #8192       ;
  5.  4ea:   7008            strb    r0, [r1, #0]    ; activaBomba = 1
  6.  4ec:   4770            bx      lr
  7.  4ee:   46c0            nop                     ; }
  8.  
  9. 00000520 <main>:                                ; void main() {
  10.  520:   e7fe            b.n     520 <main>      ;  while(1); } // Fin de main

Resultado: Esta fue la mas reveladora. el main, esta formado por un while(true) nomas. Se observa ademas que Explota() desaparecio, ni siquiera existe. Ya que lo unico que hacia era poner una crear 2 varaibles dentro del scope de la funcion sumarlas y nada mas, es decir no se usaban para nada. Y el optimizador procedio a eliminarlas.

---------------------------------------------

Optimizacion maxima y optimizado a velocidad , CON volatile

Código: ASM
  1. 000004e0 <IntDefaultHandler>:                   ; void Interrupt(void) {
  2.  4e0:   f240 2114       movw    r1, #532        ;
  3.  4e4:   2001            movs    r0, #1
  4.  4e6:   f2c2 0100       movt    r1, #8192       ;
  5.  4ea:   7008            strb    r0, [r1, #0]    ; activaBomba = 1
  6.  4ec:   4770            bx      lr
  7.  4ee:   46c0            nop                     ; }
  8.  
  9. 00000510 <main>:                                ; void main(void) {
  10.  510:   f240 2114       movw    r1, #532        ; 0x214
  11.  514:   2200            movs    r2, #0
  12.  516:   f2c2 0100       movt    r1, #8192       ; 0x2000
  13.  51a:   ea4f 0808       mov.w   r8, r8
  14.  51e:   46c0            nop                     ;
  15.  520:   700a            strb    r2, [r1, #0]    ; while(1) {
  16.  522:   7808            ldrb    r0, [r1, #0]    ; activaBomba=0; R0 = ???? pero que no lleva a ningun lado
  17.  524:   e7fc            b.n     520 <main+0x10> ; }

Resultado: Con volatile cambia, Esta ves pone nuestra variable en 0, pero imagino que debido a que Explota() es una funcion sin "sentido" por lo que explique antes, lo elimino. Y por ende el if() quedaba vacio dentro, por lo cual procedio tambien procedio a eliminarlo.

EDIT: Por favor leer la respuesta de BrunoF luego de este post, explicando esta instruccion
Del ASM lo unico que no puedo identificar es que esta tomando y poniendo en R0, ya que en 0x214 hay una instruccion y no un dato:

Código: ASM
  1. 214:    0783            lsls    r3, r0, #30

De todas formas no usa R0 dentro del loop del while(1) y queda como una instruccion "extra", tal ves sea por el pipeline y no tener intruccion-salto-intruccion-salto.

------------------------------------------------------------------------

Para probar el tema de que pasaria si tenemos realmente una funcion buena, voy a cambiar Explota() por :

Código: C
  1. void Explota(void)
  2. {
  3.         activarBomba = 0;
  4. }

Asi de esta forma no lo "optimiza" (es decir elimina por completo ) y ver si procede a dejar el if() esta ves.

Luego de compilado me encuentro con lo siguiente:
- La rutina de interrupcion no cambia.
- Explota() no aparece
- Solo cambio el main()

Código: ASM
  1. 000004f8 <main>:                                ; void main(void) {
  2.  4f8:   f240 2114       movw    r1, #532        ;
  3.  4fc:   2200            movs    r2, #0
  4.  4fe:   f2c2 0100       movt    r1, #8192       ;
  5.  502:   ea4f 0808       mov.w   r8, r8
  6.  506:   46c0            nop                     ;
  7.  508:   700a            strb    r2, [r1, #0]    ; while(1) { activaBomba = 0
  8.  50a:   7808            ldrb    r0, [r1, #0]    ; <- Toma directo de memoria
  9.  50c:   2800            cmp     r0, #0          ; if(activaBomba)
  10.  50e:   d0fb            beq.n   508 <main+0x10> ; Si es 0, vuelve al while(1)
  11.  510:   700a            strb    r2, [r1, #0]    ; activaBomba = 0; // Esto de Explota();
  12.  512:   e7f9            b.n     508 <main+0x10> ; } // Fin del while } Fin del main

Resultado: El optimizador hizo de Explota() una funcion inline. Es decir puso el activarBomba=0; en la rutina de main. Pero esta ves ahora si procede a realizarse el if.
Comprobando lo que habia dicho antes :P

Activar la optimizacion te puede borrar una funcion si no posee sentido. Volatile evita la optimizacion sobre la variable y por ende termina realizando lo que debe.
Ademas hace uso de mas registros del core, sin optimizacion se ve que no sale de R0 y R1, con optimizacion llego a usar R2 ( creo que hasta R7 usa con optimizacion si no mal recuerdo y hasta R3 sin optimizacion en programas mas demandantes ).
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: BrunoF en 13 de Febrero de 2016, 01:59:46
Muy interesante KILLER...Si querés mudo esto a otro hilo para no ensuciarle más este a Leo.

Código: ASM
  1. 4e0:   f240 2114       movw    r1, #532        ;
  2. 4e4:   2001            movs    r0, #1
  3. 4e6:   f2c2 0100       movt    r1, #8192       ;

Pasa que movw carga sólo los 16 bits de la parte baja, y movt sólo los 16 bits de la parte baja de r1. Por ende el valor final del registro r1 sería: 0x20000214

Todavía te debo mis comentarios sobre las formas de lograr o no generar código seguro.
Título: Re:Desreferenciando caracteres en una struct...
Publicado por: KILLERJC en 13 de Febrero de 2016, 02:14:07
Citar
Si querés mudo esto a otro hilo para no ensuciarle más este a Leo.

Si Leo lo decide asi. Adelante. Sino seguimos, yo no tengo problemas para ir a cualquier lado.

Citar
Pasa que movw carga sólo los 16 bits de la parte baja, y movt sólo los 16 bits de la parte baja de r1. Por ende el valor final del registro r1 sería: 0x20000214

Exactamente, eso me pasa por no ponerme a ver las instrucciones. Las demas las tenia de memoria, pero esas no me acordaba exactamente que eran, hacia ya desde octubre que no tocaba el ASM de ARM ( http://www.todopic.com.ar/foros/index.php?topic=45280.0 )  , y no quise abrir el cheatsheet de las instrucciones. ahora que lo veo:

MOVT : Move Top , 16 bit parte alta
MOVW: Move Wide, 16 parte baja

Pense que iba a hacer uso de la flash para almacenar la direccion y tomarla con un offset, pero por velocidad (y tener a mano el set completo de Thumb-2 ) decidio ir por esa opcion.
Distinto hubiera sido en un M0+ que no soporta todo el set de Thumb-2 y como no puede cargar todo el literal, lo mejor seria tomarlo de la flash.

Del objdump:
Citar
20000214 g     O .data   00000001 .hidden activarBomba

Y en el micro que tengo la RAM esta mapeada de 0x2000.0000 a 0x2006.FFFF

Citar
Todavía te debo mis comentarios sobre las formas de lograr o no generar código seguro.

Cuando tengas tiempo, lo escribis. Mientras, yo espero  :D