Este blog está dedicado a mis experiencias, proyectos, dificultades y demás en todo lo relacionado a la electrónica y la programación en general sobre GNU/Linux

ARM Cortex-M, Electrónica, STM32

Actualización crítica del Serial-libOpenCM3

¡IMPORTANTE!

Como habrían visto en mi blog, codifique una lib para utilizar el puerto Serie (UART) con la BluePill con libOpenCM3. Pero debo hacer un mea culpa, ya que debido a un pequeño error la comunicación se congelaba después de transmitir/recibir 65536 datos. Por ello, deben sí o sí utilizar la versión v0.4.3 (la actual) en adelante.

El error

Para entender el error, debemos analizar la implementación del buffer circular y la arquitectura de 32bits. Mi lib, para manejar los buffers, utiliza indices declarados como enteros sin signo de 16bits. Como analizamos en un post de AVR, los buffers circulares tienen 2 índices, uno de entrada [tail](por donde se insertan los datos), y otro de salida [head]. La condición para que empiece la transmisión es que la resta entre el de entrada y salida sea 1, lo cual codifiqué de la siguiente manera:

if((index_in - index_out) == 1) { strart_tx(); }

Lo cual no supone problemas, ya que cuando estamos en el desborde (0 – 65535), el resultado es 1… claro, siempre y cuando el 0 y el 65535 sean enteros sin signo de 16bits. ¿Por qué? Muy fácil, para entenderlo tenemos que recordar como restan las computadoras (en CA2), es decir que la resta es la suma del Complemento a 2:

  3  -   4  =>   3  + (-4) =  -1
0011 - 0100 => 0011 + 1100 = 1111

Entonces, ¿qué pasa cuando estamos en el mayor número entero natural? Veamos:

  0  -  15  =>   0  +   1  =   1
0000 - 1111 => 0000 + 0001 = 0001

Esto ocurre porque el 15 en binario natural equivale al -1 en CA2, así que mi código como estaba más arriba no debería de fallar pero, pero, pero… El problema es que el STM32F103 es un ARM Cortex-M de 32 bits, por lo tanto las operaciones son en 32bits y la comparación con [resultado] == 1 este es de 32bits y el resultado también, o sea, que la operación de dos variables de 16bits estaban siendo promocionadas a 32bits, por lo tanto, el resultado estaba lejos de ser 1.

La solución

La solución es muy sencilla, se podría declarar una variable auxiliar, como:

uint16_t res = index_in - index_out;
if(res == 1) { strart_tx(); }

Pero, gastar una variable de más no sería una solución apropiada. Por ello, lo mejor, es «exigirle» al compilador de que el resultado sea un entero sin signo de 16bits. Con un simple casteo, santo remedio:

if ((uint16_t)(index_in - index_out) == 1) { start_tx(); }

Conclusión

Este pequeño (e imperceptible) error, nos recuerda que debemos ser cuidadosos con las operaciones que hacemos y testear bien las funcionalidades en los límites. En C tenemos el control absoluto de lo que ocurre, y con este casteo es aún más portable a otras arquitecturas (8, 16, 64, etc.)

Pueden ver el repo en gitlab o la lib en PlatformIO.

En un futuro próximo voy a extender la lib para poder utilizarla con cualquier STM32F1xx y luego a los otros STM32Fxxx.

Dejar una respuesta