STM32F4: Depuración con OpenOCD de programas con múltiples hebras

Dentro de la serie de publicaciones relacionadas con la placa de desarrollo STM32F411-DISCOVERY, hoy os traemos un breve tutorial para activar la opción de soporte de RTOS del depurador OpenOCD, que es el que viene por defecto con el entorno de desarrollo SW4STM32. Esta opción facilita enormemente la depuración de programas en los que existen múltiples hebras corriendo en paralelo, ya que en cada paso nos proporciona información del estado de cada una de ellas. Esta técnica se conoce como thread awareness. Los sistemas operativos soportados por OpenOCD a día de hoy son: FreeRTOS, Linux, ChibiOS, embKernel, eCos, ThreadX, mqx y uCOS-III.

Para activar esta opción, que por defecto viene deshabilitada, debemos seguir los siguientes pasos:

1.- Editar el fichero stm32f4x.cfg, o el correspondiente a nuestro microcontrolador de la serie STM32, añadiendo la opción -rtos auto en la siguiente línea:

Este fichero se encuentra en la siguiente ruta:

 

2.- En Debug Configurations, en la pestaña de Startup, hay que cambiar la función donde se pone el breakpoint inicial al lanzar la depuración. Por defecto, esta función es main(), pero para que esta opción funcione correctamente, necesitamos que la función en la que pare sea posterior a la inicialización y puesta en marcha del sistema operativo, es decir, debe ser una función dentro de una hebra.

Figura 1. Captura de Debug Configurations. En este caso el punto de parada se pone en la función InitTask_main, que es la primera función de la primera hebra que se lanza.
Figura 1. Captura de Debug Configurations. En este caso el punto de parada se pone en la función InitTask_main, que es la primera función de la primera hebra que se lanza.

 

3.- Por último, al menos para el caso concreto de FreeRTOS, hay que añadir la siguiente línea en el fichero en el que se halle la función en la que hemos fijado el punto de parada anterior. En concreto, hay que colocarlo en las declaraciones globales, de forma que pueda ser leída por el depurador. De lo contrario, fallará al intentar parar la ejecución.

Esto se debe a que, en versiones anteriores de FreeRTOS, la variable uxTopUsedPriority contenía el máximo nivel de prioridad de las tareas, necesario al adquirir la información de las mismas. Esta variable cambió de nombre, pero los desarrolladores de OpenOCD aún no lo han corregido.

 

Una vez hayamos hecho todo lo anterior, al lanzar la depuración debemos poder ver todas nuestras hebras en la ventana de Debug.

Imagen 2. Captura de la ventana de Debug con el soporte de RTOS de OpenOCD activado.
Imagen 2. Captura de la ventana de Debug con el soporte de RTOS de OpenOCD activado.

 

Puede ser que, en ocasiones, sea necesario parar la ejecución antes de que se llegue a la función fijada en el punto 2, o pararla en cualquier momento antes del inicio del planificador del sistema operativo. En estos casos, será necesario revertir el argumento del paso 1 para desactivar el thread awareness, ya que si no lo hacemos OpenOCD fallará al intentar leer las hebras.

STM32F4: Consideraciones para el uso de la memoria flash (Parte I)

Siguiendo la serie de tutoriales y guías sobre el uso de la placa de desarrollo STM32F411-DISCOVERY, hoy hablaremos de la escritura, lectura y manejo de la memoria flash del microcontrolador (MCU). Como ya sabréis, el STM32F411VET6 cuenta con 128 Kbytes de memoria SRAM y 512 Kbytes de flash. La principal diferencia entre estos dos tipos de memoria es que la flash, al contrario que la SRAM, es no volátil, es decir, no pierde la información que almacena al desactivar la alimentación.  Esta característica hace que, en general:

  • La memoria SRAM se destine para el almacenamiento y manipulación de datos parciales, variables locales, cambios de contexto, etc.
  • La memoria flash se utilice parte para la memoria de programa, donde se almacena el código del programa a ejecutar por el procesador, y parte para el almacenamiento de datos de configuración o de los procesos que se controlan y se quieren conservar tras una desactivación temporal del sistema.

En este artículo nos centraremos en este último tipo de memoria, aportando algunas claves importantes para su uso, así como una serie de consideraciones y buenas prácticas a tener en cuenta para evitarnos algunos problemas frecuentes.

Lo primero que es importante tener claro es cómo se organiza la memoria flash de nuestro microcontrolador. Esto suele variar bastante de unos a otros. En general, la memoria se divide en bloques o bancos, que a su vez se pueden subdividir en sectores. La siguiente unidad de división de la memoria serían las páginas y, por último, las palabras, que se componen de un determinado número de bytes. En general, la palabra suele ser la unidad mínima de lectura/escritura en flash aunque, en algunos casos, es posible hacerlo a nivel de byte, y en otros se opera en double-word, es decir, de dos en dos palabras simultáneamente.

Para saber la organización en particular de un microcontrolador, lo mejor es acudir al manual de referencia del mismo. A continuación, podéis consultar tres ejemplos distintos del propio STMicroelectronics, para que podáis comprobar de primera mano cómo en cada uno la organización es de una forma distinta:

Este último ejemplo, el STM32F411VE, se corresponde con el MCU de nuestra placa Discovery, y será el caso concreto que utilizaremos en este artículo. En la siguiente imagen se muestra la información principal de nuestra memoria.

flashMemory
Figura 1. Captura de la sección “Embedded Flash memory in STM32F411xC/E”

En la tabla anterior podemos observar que los 512 Kbytes de memoria flash que tiene el MCU según se especifica en sus características, son en realidad un gran bloque llamado Main memory.  A parte, vemos que existen otros tres bloques reservados para usos específicos y que, por lo tanto, no contabilizan como memoria flash.

Lo primero que debemos decidir es qué parte de la memoria principal queremos reservarnos para nuestra disposición. En este caso, utilizaremos el Sector 7, al que nos referiremos como USER_FLASH. Una vez tenemos esto claro, es muy importante asegurarnos de que ese sector no se va a utilizar para nada más, de modo que ni nosotros machaquemos información de otra utilidad, ni otra utilidad sobrescriba nuestra información. Para garantizar esto, es necesario editar el linker script de nuestro proyecto, que ha sido generado automáticamente por CubeMX, y se encuentra en la ruta:

Este fichero es el encargado de dar las instrucciones al linker del uso y tamaño de cada área de memoria y, por lo tanto, debe editarse con sumo cuidado. Se escapa del alcance de esta publicación explicarlo en detalle, por lo que únicamente daremos las indicaciones básicas de las modificaciones necesarias. Para profundizar en su comprensión, os dejamos algo de documentación.

En primer lugar, en la sección MEMORY del fichero STM32F411VETx_FLASH.ld, que es donde se especifican las áreas de memoria, debemos reducir FLASH en los 128 Kbytes correspondientes al Sector 7, y añadir USER_FLASH indicando sus permisos, dirección de origen y tamaño. Con ello, esta sección pasa de esta configuración:

A esta otra:

En segundo lugar, hay que editar el apartado SECTIONS del mismo fichero, que es donde se definen las distintas secciones de memoria, ya sean para guardar código o datos. En concreto, vamos crear una nueva para guardar nuestros datos no volátiles en el área de memoria USER_FLASH. De este modo, desde nuestro código de programa, podremos declararnos variables de cualquier tipo que se almacenen en dicha sección y, por lo tanto, sean no volátiles. Para ello, al principio de este apartado, antes de la sección .isr_vector, añadimos el siguiente código:

Con estos pasos, ya tendríamos nuestra área de memoria flash delimitada de una manera segura y preparada para utilizarse desde nuestro código.

Para probar que todo está correctamente, vamos a proceder a leer y escribir un dato en memoria flash. Para ello, partiremos del proyecto desarrollado en el tutorial previo STM32F4: Interrupción externa. En concreto, lo que haremos es aprovechar la interrupción producida al pulsar el botón de usuario para conmutar la frecuencia de parpadeo del LED verde entre dos valores, que definiremos en main.c dentro de USER CODE BEGIN Includes.

Sin embargo, lo que haremos es guardar la variable que contiene el valor del delay que se esté utilizando en cada instante en la memoria flash. De este modo, en cualquier momento el sistema puede apagarse o resetearse y, al volver a funcionar, mantiene la última frecuencia de conmutación que tuvo operativa. Para ello, en main.c, dentro de USER CODE BEGIN PV, nos declaramos la variable globar current_delay del siguiente modo:

Lo más llamativo de esta declaración es el atributo:

Esta parte es justo la que le indica al linker que la variable current_delay de tipo uint32_t debe guardarse en la sección .user_data_flash, en vez de donde se guardan las variables por defecto. De este modo, el valor de la variable es no volátil. Hay que tener en cuenta que si bien esta variable se puede leer como cualquier otra, para escribir en ella, al tratarse de memoria flash, hay que seguir un procedimiento especial que comentaremos más adelante. Además, también hay que destacar que el atributo __section__ no se puede emplear para variables locales.

Por otra parte, si recordáis, en el tutorial de la interrupción externa se comentó que es recomendable minimizar el número de operaciones a realizar dentro del callback de la interrupción. Por ello, para este caso utilizaremos un flag que declararemos e inicializaremos en main.c, también dentro de USER CODE BEGIN PV, como:

Modificar el valor de este flag será lo único que haremos dentro del callback de la interrupción, que quedará del siguiente modo. Recordamos que este callback se encuentra en el fichero gpio.c.

Es importante destacar que para poder emplear la variable swap_flag en este otro fichero, hay que declararla en gpio.c, dentro de  USER CODE BEGIN 0, como extern.

Para este ejemplo, todo lo demás lo programamos dentro de la función main(). Queda en la mano del lector reorganizar este código en las funciones que considere oportunas para lograr un código de mayor calidad. Del mismo modo, queda pendiente el tratamiento de errores. Para el ejemplo, la función quedaría así:

Como asumimos ciertos conocimientos de programación por parte del lector, en este punto únicamente comentaremos ciertos aspectos relevantes:

  • Antes de escribir en memoria, hay que borrarla mediante la función HAL_FLASHEx_Erase(&EraseInitStruct, &PAGError), definida en el fichero stm32f4xx_hal_flash_ex.c. En la Sección 3.5.4 del Reference Manual RM0383 Rev 2 podéis encontrar más información al respecto.
  • La estructura EraseInitStruct se utiliza para configurar la operación de borrado de la memoria. En este caso, utilizamos siempre la misma configuración y por eso sólo se rellena una vez, pero esto no tiene por qué ser así. Podéis leer más sobre sus distintos campos en la cabecera de su definición, FLASH_EraseInitTypeDef, en el fichero stm32f4xx_hal_flash_ex.h.
  • Antes de realizar cualquier operación de borrado o escritura de la flash, es necesario desbloquearla con la función HAL_FLASH_Unlock(). Del mismo modo, al concluir dichas operaciones, hay que volver a bloquearla con la función HAL_FLASH_Lock().
  • Para escribir en memoria flash utilizamos la función HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (uint32_t)&current_delay, (uint32_t)data), definida en el fichero stm32f4xx_hal_flash.c.
    • El primer argumento que le pasamos es una macro definida en stm32f4xx_hal_flash.h que indica que haremos una escritura de una palabra, es decir, un dato de 32 bits. En ese fichero podéis ver qué otras opciones permite este microcontrolador.
    • El segundo argumento que le pasamos es la dirección de la variable donde queremos guardar el dato.
    • El tercer argumento que le pasamos es el dato a guardar que, en este caso, debe coincidir con el tamaño de palabra.

Por último, para poder confirmar que la escritura se ha realizado correctamente, durante su ejecución en Debug, podemos monitorizar las distintas posiciones de memoria desde la pestaña Memory del entorno Eclipse. En este caso vemos como la variable current_delay se encuentra al principio de la sección .user_data_flash, en la dirección 0x0806 0000. 

memory
Figura 2. Herramienta de Eclipse para monitorizar la memoria del MCU.

Además, como esta memoria es no volátil, podría suceder, dependiendo de la configuración del Debugger, que al reprogramar el MCU no se borrase la flash de usuario. Para hacerlo manualmente, podemos usar la herramienta Erase chip del siguiente modo.

La herramienta Erase chip permite borrar toda la memoria del MCU por completo.
Figura 3. La herramienta Erase chip permite borrar toda la memoria del MCU por completo.

Hasta aquí la publicación de hoy. Esperemos que os haya parecido interesante y os animéis a profundizar en las distintas líneas que se quedan abiertas. En la siguiente parte, comentaremos más aspectos importantes a tener en cuenta al trabajar con la memoria flash, por ejemplo, el almacenamiento de múltiples datos en ella.

Aprende Electrónica con la placa de desarrollo STM32F411-DISCOVERY

“Me lo contaron y lo olvidé; lo ví y lo entendí; lo hice y lo aprendí.”

Confucio

En el B105 tenemos la convicción de que la mejor manera para aprender algo es haciéndolo. Además, actualmente existen en el mercado multitud de placas de desarrollo muy económicas, por lo que ya no hay escusa para no ponerse manos a la obra.

Por ello, arrancamos esta serie de publicaciones con el objetivo de guiar el aprendizaje del uso de sistemas empotrados. Para ello, hemos escogido la placa de desarrollo STM32F411-DISCOVERY de STMicroelectronics, que tiene un precio inferior a 15€ y el software necesario para desarrollar con ella es gratuito. Aun así, aunque el método variará, los conceptos o fundamentos que esperamos transmitiros con estas publicaciones son aplicables a cualquier plataforma basada en un microcontrolador.

Sin alargarnos mucho más, os dejamos una lista de tutoriales y ejemplos, que irá creciendo con el tiempo, en la que tendréis los enlaces a las distintas publicaciones de esta serie.

  1. STM32F4: Primeros pasos con el entorno de desarrollo
  2. STM32F4: Interrupción externa
  3. STM32F4: Configurar el PWM con timers
  4. STM32F4: Consideraciones para el uso de la memoria flash (Parte I)
  5. STM32F4: Depuración con OpenOCD de programas con múltiples hebras
  6. STM32F4: Configuración y uso básico del ADC
  7. STM32F4: Configuración del ADC con un timer
  8. STM32F4: Cómo utilizar el ADC con DMA

¡Suerte y ánimo!

The Twelve of B105: B105 Radar

Termina febrero y eso significa una nueva entrega de…

theTwelveOfB105

Esta vez nos hemos centrado en la tecnología radar, con la que llevamos ya varios años trabajando. En la actualidad, dentro del proyecto All-in-One, hemos desarrollado varios prototipos de sensores de bajo coste basados en radar.

Nuestro primer prototipo fue la placa RALPH, que nos permitió validar el diseño y desarrollar el grueso del software de procesado para la detección de la velocidad de los vehículos y su conteo. Incluso tuvimos estudiantes que se atrevieron a meterle mano a esta plataforma y mejorar algunos aspectos de la misma.

En una segunda aproximación, se llevó a cabo un esfuerzo importante para optimizar RALPH al máximo en tamaño, coste y utilización de recursos. Así fue como vio la luz ISHTAR, nuestro nodo sensor radar que, por cierto, utiliza YetiOS como soporte para todo el software. Esta plataforma ha sido probada y validada múltiples veces en entornos reales.

YetiRadar

 

 

Nuevamente nos despedimos con la esperanza de que nuestros twelve os entusiasmen tanto como a nosotros, y que os permitan conocer mejor todo lo que hacemos por si os interesa uniros y participar de ello. Pincha aquí si quieres ver otros meses.

Si encuentras interesante toda esta información, no dudes en seguirnos en las redes sociales para mantenerte informado.

¡Hasta el mes que viene!

The Twelve of B105: Road Safety

¡Hoy, casi en la bocina, os traemos una nueva entrega de…

theTwelveOfB105

Este mes de enero hemos querido hacer un repaso de toda nuestra actividad en materia de Seguridad Vial. Desde hace ya varios años, existe en el labo una línea de proyectos de innovación en esta temática. Algunos ya terminados y otro en marcha actualmente, todos ellos persiguen la prevención de accidentes y, en caso de que estos sucedan, poder acelerar la respuesta de los servicios de emergencia. 

A continuación, os dejamos una lista de varios ejemplos de proyectos en los que hemos diseñado y desarrollado soluciones en este área temática:

  • CARRETERAS. En este proyecto desarrollamos una serie de herramientas de soporte para las entidades encargadas del mantenimiento de las carreteras. Estas herramientas les permitían de una manera ágil e intuitiva registrar eventos en la carretera, guardar sus coordenadas, capturar evidencias gráficas, etc. A lo largo del proyecto, hicimos varios días de  pruebas en terreno para pulir nuestro sistema y llegar a una versión final de calidad. También fue muy interesante poder visitar las instalaciones de estas entidades.
  • DEPERITA. Aquí trabajamos en un sistema de perimetración virtual de zonas en las que se están llevando a cabo labores de mantenimiento de modo que, en caso de producirse una intrusión en el área restringida, se alarmase inmediatamente a los trabajadores para evitar un posible accidente.
  • SIMBIOSYS. Una de las partes más interesantes de este proyecto fue trabajar en la detección de fatiga en conductores, con el objetivo de anticipar situaciones de peligro. Para ello, se trabajó en una aproximación basada en la obtención de imágenes del conductor y su procesado en tiempo real, y otra mediante electroencefalograma.

Además de proyectos de innovación, en el B105 también hemos llevado a cabo investigación en el ámbito de las Redes Vehiculares o VANETs, especialmente en aspectos como la fiabilidad y la seguridad, y en el ámbito de la visión por computador aplicada a la conducción.

unnamed-1024x576

Esperamos que nuestros twelve os entusiasmen tanto como a nosotros, y que os permitan conocer mejor todo lo que hacemos por si os interesa uniros y participar de ello. Pincha aquí si quieres ver otros meses.

Si encuentras interesante toda esta información, no dudes en seguirnos en las redes sociales para mantenerte informado.

¡Hasta el mes que viene!