pub:silab0

Desarrollo SW con STM32L4

Plataformas de evaluación HW para STM32L4

Placa de desarrollo STM32 Nucleo-32 NUCLEO-L432KC con MCU STM32L432KC MCU

  • Depurador/programador en PCB ST-LINK/V2-1 con conector SWD
  • Alimentación:
    • USB VBUS o fuente externa (3.3 V, 5 V, 7 - 12 V)
    • Punto de acceso a gestión de alimentación
  • Recursos limitados en PCB:
    • LED de usuario (LD3)
    • Botón pulsador de RESET
  • Extensiones de conectividad:
    • Arduino Uno Revision 3
    • STMicroelectronics Morpho (acceso a completo a todos los pines E/S)
  • 1x puerto USB con capacidad de re-enumeración USB y 3x interfaces USB soportados:
    • VCP (Virtual Com port)
    • Almacenamiento masivo (USB Disk drive) para programación por arrastre
    • Depuración (Debug port)

  • ARM®32-bit Cortex®-M4 CPU
  • 80 MHz frecuencia máxima de CPU
  • VDD de 1.65 V a 3.6 V
  • 256 KB Flash
  • 64 KB SRAM
  • 4x temporizadores (Timers) de propósito general
  • 2x SPI/I2S
  • 2x I2C
  • 2x USART
  • 1x 12-bit ADC con 10 canales
  • 20x GPIO con capacidad de interrupción externa
  • RTC
  • TRNG (generador de aleatorios por HW)

Placa de desarrollo STM32 Discovery kit 32L476GDISCOVERY con MCU STM32L476VG

  • Depurador/programador en PCB ST-LINK/V2-1 con conector SWD
  • Alimentación (4x opciones):
    • ST-LINK/V2-1
    • Conector USB FS
    • Externa 5 V
    • Batería CR2032
  • 2x LEDs de usuario: LD4 (rojo), LD5 (verde)
  • 2x botón pulsador: USER y RESET
  • 1x puerto USB OTG FS con conector micro-AB, capacidad de re-enumeración USB y 3x interfaces USB:
    • VCP (Virtual Com port)
    • Almacenamiento masivo (USB Disk drive) para programación por arrastre
    • Depuración (Debug port)
  • 1x Joystick de 4x direcciones y selección central
  • SAI Audio DAC, estéreo, con conector Jack de salida
  • Micrófono digital MEMS
  • Acelerómetro y magnetómetro MEMS
  • Giróscopo MEMS
  • 128-Mbit memoria Quad-SPI Flash
  • Amperímetro de corriente del MCU con 4 rangos y auto calibración

  • ARM®32-bit Cortex®-M4 CPU con FPU y acelerador ART
  • 80 MHz frecuencia máxima de CPU
  • VDD de 1.71V a 3.6 V
  • 1 MB Flash
  • 128 KB SRAM
  • 114x GPIOs con capacidad de interrupción externa
  • 3x 12-bit ADCs con 16 canales
  • 2x 12-bit DAC
  • 3x USART
  • 2x UART
  • 1x LPUART
  • 2x SAI
  • 3x I2C
  • 3x SPI
  • 1x Quad SPI
  • 7x temporizadores (Timers) de propósito general, 2x básicos y 2x avanzados
  • 2x temporizadores de bajo consumo (low-power)
  • 2x temporizadores tipo Watchdog
  • 1x CAN
  • USB OTG FS
  • SDMMC
  • SWPMI
  • LCD COM x SEG
  • RTC
  • TRNG (generador de aleatorios por HW)
  • 21x sensores capacitivos
  • 2x comparadores analógicos
  • 2x amplificadores operacionales

Modelo de desarrollo SW para STM32

El modelo de desarrollo propuesto para programar los MCUs de 32 bits de STMicroelectronics, en este caso de la serie STM32L4, se basa en un conjunto de librerías escritas en lenguaje C, con mayor o menor nivel de abstracción del HW soportado por la MCU o plataforma PCB elegidas.

Se dispone en este caso del entorno de desarrollo integrado (IDE) basado en Eclipse™ Atollic TrueStudio for STM32 donde se importan proyectos SW previamente generados con la herramienta de configuración STM32CubeMX, creada por el fabricante STMicroelectronics para sus distintas series de MCUs y PCBs asociadas, para la inicialización de periféricos, configuración del sistema e inclusión de las librerías de alto nivel.

Es importante conocer la estructura y utilidad las librerías que pueden utilizarse en el desarrollo SW para dichos MCUs, destacan:

  • Hardware Abstraction Layer (HAL): Capa de abstracción que contiene drivers para todos los periféricos disponibles para la serie del MCU en uso.
  • Low-Layer APIs (LL): Capa de abstracción ligera y rápida orientada a expertos, más cercana al HW, y que puede utilizarse en combinación con HAL con pocas restricciones.
  • Board Support Package (BSP): Es una capa al mismo nivel de abstracción que la HAL, y que se compone de controladores específicos para la PCB de desarrollo escogida. En la mayoría de casos contiene controladores (drivers) para memorias externas en PCB (como SDRAM o Flash QSPI), soporte para pantallas LCD, pantallas táctiles, sensores inerciales (acelerómetros, giróscopos,…),…
  • Middlewares: Paquetes de SW que proporcionan aún mayor abstracción sobre el HW. Entre ellos, destacan el protocolo TCP/IP (LwIP), un sistema operativo de tiempo real (FreeRTOS), un sistema de archivos FAT (FatFS), soporte de múltiples clases USB y una librería de gráficos (STemWin).
  • CMSIS (Cortex Microcontroller Software Interface Standard): Dentro de la categoría de utilidades s es una HAL, independiente del fabricante de MCU, creada por ARM™ para las series de procesadores Cortex®-M y Cortex®-A, que define interfaces genéricas para herramientas SW y facilita el desarrollo rápido de aplicaciones en distintos ámbitos como DSP, NN, RTOS y un gran número de periféricos.

Herramientas de desarrollo SW

Soporte y recursos

Tutorial básico con NUCLEO-L432KC

Crearemos un ejemplo básico con el objetivo de hacer parpadear el LED de usuario LD3 (verde) y probar la E/S básica asíncrona serie con una cadena generada a partir del estado del LED y el temporizador del sistema (SysTick).

Desde ventana principal acceder a la selección de placas STBoard e iniciar un nuevo proyecto (Start Project) para la PCB NUCLEO-L432KC:

Aparecerá una ventana emergente para la inicialización de periféricos a su modo por defecto y contestaremos No para hacer una configuración manual de los mismos:

Se presenta a continuación la vista de pines del MCU, para la configuración de los mismos y los periféricos internos al SoC asociados:

  • Los pines sombreados en verde indican que ya están conectados al HW en PCB, como el pin PB3 conectado al led LD3.
  • Los pines de sistema (SYS) están sombreados en amarillo/verde-amarillo
  • El HW opcional, como cristales y USART se sombrean en naranja, indicando que son conexiones en PCB pero no tiene HW conectado por defecto.

Accedemos en la configuración por categorías de la izquierda a System Core (SYS) para establecer la interfaz de depuración a Serial Wire, lo cual fijara como conectados (verde) los pines PA13 (SWDIO) y PA14 (SWCLK). Dejaremos con los valores por defecto el resto de apartados, fijándonos en que aparezca establecido como temporizador base del sistema el SysTick.

En System Core (RCC) activamos los relojes externos de alta velocidad (HSE), pin PA0, y el de baja velocidad (LSE), pines PC14 y PC15, asociado a un cristal de 32.768 KHz (X1) en estas PCBs.

Finalmente accedemos a la categoría de conectividad, Conectivity (USART2), para configurar el puerto serie genérico 2 que permite operar en modo virtual a través del conector USB, asociado a pines PA1 (VCP_RX) y PA2 (VCP_TX), configurando en modo asíncrono sin control de flujo, velocidad de 9600 Bits/s, formato de datos de 8 bits con 1 bit de Stop y sin paridad (9600-8-N-1). El resto de parámetros se dejará con valores por defecto.

Una configuración de pines adicional puede realizarse desde la vista de sistema, si bien lo único que se necesita confirmar es el GPIO asociado al LED en PB3 en modo Output Push Pull.

La figura siguiente refleja los cambios de configuración indicados en la vista de pines y de sistema:

Cambiamos a la pestaña de configuración de relojes (Clock Configuration) y fijamos la máxima velocidad posible para el reloj principal de alta velocidad (HCLK) = 80 MHz para que recalcule la ruta de encaminamiento de señales más adecuada. Aunque hemos conectado los relojes externos en el apartado anterior, HSE y LSE, no se puede obtener a partir de ellos la velocidad máxima, por lo que asocia la fuente MSI (MultiSpeed Internal RC oscillator) a través de un PLL multiplicando x40 y dividiendo /2. Puede observarse también que, tanto el núcleo como memoria en AHB, temporizadores y periféricos en APB1 y APB2, toman por defecto esa máxima frecuencia, que para ciertas aplicaciones será necesario reasignar.

Pasando a la pestaña gestión del proyecto (Project Manager) realizamos la configuración del proyecto para generar el código y exportar a un espacio de trabajo de la herramienta Atollic TrueSTUDIO™ basada en Eclipse™:

  • Asignar un nombre al proyecto y la ubicación del espacio de trabajo, confirmando la ubicación final para las herramientas de compilación.
  • Seleccionar como conjunto de herramientas (Toolchain/IDE) TrueSTUDIO.
  • La ubicación del FW suele estar asociada a la más reciente instalada por CubeMX, la primera vez que se use la serie correspondiente de MCU, o en siguientes actualizaciones.

La sección de generación de código se configurará de acuerdo a la figura siguiente para:

  • Copiar al proyecto sólo los archivos necesarios del FW, referenciarlos solamente mediante un enlace al repositorio de librerías, o incluir la librería completa.
  • Generar o no archivos fuente/cabecera por cada periférico.
  • Mantener las secciones delimitadas de código de usuario (/* USER CODE x */) en distintos cambios de HW.
  • Eliminar ficheros generados en iteraciones anteriores.

Tras invocar la generación de código con GENERATE CODE obtenemos acceso a los archivos generados en la carpeta del proyecto, podemos examinarlos directamente con un editor de texto, si bien los trataremos desde el IDE en el paso siguiente.

La herramienta CubeMX puede cerrarse ahora, o dejar abierta si pretendemos realizar posteriores modificaciones en la configuración de HW, en cualquier caso ésta se almacenará en un archivo con el nombre del proyecto y extensión .ioc dentro de la su carpeta.

Como en otros IDE basados en el entorno ECLIPSE, se opera bajo un modelo basado en proyectos dentro de espacios de trabajo (Workspaces), sólo puede estar activo un espacio de trabajo para acceder sólo a los proyectos incluidos en él, por lo que al iniciar el entorno se fija/cambia (File→Switch Workspace) al espacio de trabajo en curso que corresponde a una carpeta del sistema de archivos, que incluya a su vez la carpeta del proyecto generado por CubeMX, y procedemos a la importación (File→Import→General→Existing Projects into Workspace):

Una vez importado correctamente el proyecto, el entorno nos presenta varias vistas (view) con varias características del mismo, el conjunto de vistas simultaneas constituyen una perspectiva (perspective), asociada normalmente a una tarea de desarrollo, trabajaremos principalmente en dos de ellas:

  • Edición (C/C++ Code Editing): vistas de edición, esquema (Outline) y explorador de clases (Class Browser)
  • Depuración (Debugging): vistas de hilos en ejecución (threads) , puntos de ruptura (Breakpoints) y registros (CPU Registers)

La figura siguiente muestra la perspectiva de edición, que aparece por defecto, actualizada tras el proceso de importación. La vista jerárquica del proyecto (Project Explorer) muestra los archivos contenidos en mismo:

  • Binaries: archivos ejecutables (extensión .elf) del proyecto generados por el compilador con determinadas opciones de optimización y depuración.
  • Includes: archivos cabecera asociados a librerías incluidas en el proyecto, derivadas del propio compilador y la configuración de MCU desde CubeMX.
  • Drivers: archivos fuente y cabecera de controladores de dispositivos
    • CMSIS (Cortex Microcontroller Software Interface Standard): estándar de soporte y librerías (DSP, NN, …) para dispositivos ARM Cortex
    • STM32L4xx_HAL_Driver: controladores de dispositivo HAL (Hardware Abstraction Layer), capa SW de abstracción de HW para la serie STM32L34
  • Inc, Src: archivos cabecera (.h) y fuente C (.c) de la aplicación
  • startup: código fuente ensamblador (.s) de cargador de inicio de registros y tabla de vectores de interrupción
  • Debug, Release: código objeto (.o) compilado de los fuentes (.c) para ejecutables de depuración y final respectivamente
  • XXX_FLASH.ld: archivo de script para enlazado (linker script) donde se indican secciones y tamaños de memoria asociados a código, datos, pila, …
  • syscalls.c: código fuente que implementa rutinas básicas de entrada/salida a modo de llamadas del sistema

El código generado por CubeMX es sólo un esqueleto que debe ser completado por nuestra parte. El objetivo en este caso es hacer parpadear el led verde (configurado en PB3 como GPIO de salida), por lo que debemos invocar repetidamente a la función que conmute (toggle) su estado con un cierto retardo, p.ej. 500 ms, que haga visible ese parpadeo. Además, generaremos con la misma frecuencia, un mensaje por el puerto serie con el estado en curso.

Las funciones necesarias para el objetivo derivan de las librerías HAL asociadas a cada periférico con un nombre relativo a la función y un prefijo con el patrón HAL_PPP_Function (PPP es el nombre corto del periférico, p.ej. GPIO). Es de utilidad el asistente de contenido propio de la herramienta Eclipse que se invoca mediante la combinación de teclas Ctl+SPACE a partir del los primeros caracteres escritos en el editor.

En primer lugar trataremos el parpadeo del led verde (PB3), e incluiremos en main.c el siguiente código en la sección de usuario 3 correspondiente al cuerpo de un bucle infinito:

Antes de escribir el resto de código para la salida serie, vamos a probar a compilar y ejecutar este código. Utilizamos la opción de construir (Project→Build All), la combinación de teclas (Ctrl+B) o haciendo clic en el icono del martillo:

Build Project

En la vista de consola obtendremos el resultado de la compilación: posibles errores y/o avisos, tamaños de código (text), datos (data), pila (bss) y del archivo ejecutable (.elf) generado, también en formato Intel (.hex) para la programación/depuración en PCB.

Finalmente, completamos el código haciendo uso de UART2 para transmitir una cadena que incluye el mensaje “Hola mundo!” junto con el estado del led en modo texto (ON/OFF) y el tiempo de sistema en curso, haciendo uso de las correspondientes funciones HAL. Nótese que se han incorporado las cabeceras necesarias para trabajar con cadenas (string.h) y entrada/salida estándar (stdio.h) en la sección de usuario correspondiente.

A continuación, volvemos a repetir la construcción del proyecto y corregir algún posible error/aviso de compilación, antes de pasar a la depuración/ejecución en PCB.

Ejecutaremos ya el código generado en modo depuración, para ello invocamos (Run→Debug), la tecla (F11) o haciendo clic en el icono de la cucaracha:

Start Debug Session

Esto nos lleva al cambio a la perspectiva de depuración (Debug), como puede observarse en la siguiente figura, desde donde podemos controlar la ejecución en PCB en modo continuo, paso a paso, establecer puntos de ruptura y observar los cambios en memoria y registros:

Inicialmente la ejecución se interrumpe en la primera sentencia de la rutina main() mediante un punto de ruptura preestablecido. Mediante una barra de botones en la parte superior, o la combinación de teclas asociada, podemos controlar el modo de ejecución.

Presuponiendo que el código es correcto pasamos directamente a la ejecución directa (Run) y deberemos observar el parpadeo del LED verde de usuario. Para comprobar la salida serie podemos hace uso de un programa de emulación de terminal como PuTTy configurando previamente el número puerto serie mapeado en nuestro máquina de desarrollo PC, así como los parámetros de comunicación establecidos (9600-8-N-1), y visualizar la salida como se refleja en las figuras siguientes.

Resultado de la ejecución (desde PuTTY):

De forma alternativa podemos activar una pestaña de terminal en el propio entorno de Eclipse desde (Window→Show View→Terminal) como puede verse a continuación, si bien no en todos los sistemas operativos se consigue abrir el puerto asignado de la máquina debido a que Eclipse está hecho sobre máquina virtual Java y necesitan asignar determinados permisos.

Para volver a la perspectiva de edición anterior, una vez verificada la funcionalidad de la aplicación, es importante terminar totalmente la ejecución de los hilos de depuración, con el fin de poder realizar otras iteraciones individuales y no concurrentes con las anteriores, que podría generar errores en la ejecución de la herramienta y de verificación de funcionamiento. Para ello es necesario parar/terminar (Stop/Terminate) con el botón rojo y matar todos los procesos en curso (Remove All Terminated Launches) con el botón de doble X desde la consola:

Incorporar al proyecto anterior la funcionalidad adicional de comprobar el estado de un pin GPIO en modo entrada, p.ej. el pin PA12 (D2 en PCB), aprovechando que es contiguo a un pin de tierra (GND), para poder establecer un puente mediante un Jumper y hacer cambiar el valor de entrada.

El valor leído del pin de entrada servirá para cambiar la velocidad de parpadeo (500 ms / 250 ms) y refresco de la salida en terminal, donde también debe mostrarse el valor del pin en el mensaje.

Tutorial básico con DISCO-L476VG

Crearemos un ejemplo básico con el objetivo de hacer parpadear alternativamente los LEDs de usuario LD4 (rojo) y LD5 (verde) en base a la interrupción generada por un temporizador básico, así como probar la entrada/salida básica, a través de un puerto USB OTG (On-The-Go) en modo dispositivo de comunicaciones (CDC), con una cadena generada a partir del estado de los sensores MEMS inerciales incluidos en el PCB (L3GD20 y LSM303C) conectados a través de SPI.

Desde ventana principal acceder a la selección de placas STBoard e iniciar un nuevo proyecto (Start Project) para la DISCO-L476VG:

Aparecerá una ventana emergente para la inicialización de periféricos a su modo por defecto y contestaremos No para hacer una configuración manual de los mismos:

Se presenta a continuación la vista de pines del MCU, para la configuración de los mismos y los periféricos internos al SoC asociados:

  • Los pines sombreados en verde indican que ya están conectados al HW en PCB, como el pin PB2 conectado al LED rojo LD4 (LD_R) y el pin PE8 al LED verde LD5 (LD_G).
  • Los pines de sistema (SYS) están sombreados en amarillo/verde-amarillo
  • El HW opcional, como cristales y USART se sombrean en naranja, indicando que son conexiones en PCB pero no tiene HW conectado por defecto.

Accedemos en la configuración por categorías de la izquierda a System Core (SYS) para establecer la interfaz de depuración a Serial Wire, lo cual fijara como conectados (verde) los pines PA13 (SWDIO) y PA14 (SWCLK). Dejaremos con los valores por defecto el resto de apartados, fijándonos en que aparezca establecido como temporizador base del sistema el SysTick.

En System Core (RCC) activamos los relojes externos de alta velocidad (HSE), pin PH0, y el de baja velocidad (LSE), pines PC14 y PC15, asociado a un cristal de 32.768 KHz (X1) en estas PCBs.

Por otro lado, será necesario configurar un temporizador básico, categoría (Timers), tomaremos para el ejemplo el TIM6, un temporizador de tipo básico, de 16 bits, con un Prescaler (PSC) de 16 bits también, que permite configurar la carga a cero del registro cuando se alcanza un determinado valor (AutoReload); utilizando tanto el PSC como esta característica, conseguiremos temporizar 0.1 segundos, que es el objetivo. Del mismo modo, cada vez que se produce la recarga del temporizador, se produce un evento de actualización que puede generar interrupciones; usaremos esta capacidad para obtener la generación de interrupciones cada 100 milisegundos.

Como puede observarse en la figura siguiente, pasamos su estado del TIM6 a modo activado (Activated), se fija el valor del PSC a 8000 y el del registro de auto-recarga con 1000, y se activa la interrupción global en la pestaña de NVIC Settings . Teniendo en cuenta que ajustaremos la frecuencia a la que operará el temporizador (según la configuración de la distribución de señales de reloj) a la máxima en este MCU de 80 MHz, se consigue reducirlo a 10 kHz, al dividirlo por 8000, si contamos hasta 1000 y luego producimos una interrupción, el tiempo desde la activación o desbordamiento (overflow) hasta que se produzca la interrupción es de 100 ms, por lo que cumplimos el objetivo marcado.

Finalmente accedemos a la categoría de conectividad, para activar el periférico USB_OTG_FS, que se encuentra implementado en la placa de evaluación mediante un conector micro-USB. Para ello, seleccionamos en el periférico que aparece por defecto en modo desactivado (Disabled) y cambiamos al modo dispositivo únicamente (Device_Only) de entre los posibles:

  • Modo Device: en este modo el dispositivo se encuentra esperando a que el Host inicie todas las comunicaciones, es el caso de un dispositivo de almacenamiento masivo (MSC), un dispositivo interfaz humana (HID, tipo ratón, teclado, joystick…), un dispositivo de comunicaciones (CDC)…
  • Modo Host: el periférico USB implementado será el encargado de iniciar todas las transacciones de información, en este caso se puede entender como un anfitrión de diferentes tipos de dispositivos, que se corresponden con los comentados en modo Device.
  • Modo OTG/Dual: el dispositivo encargado de conmutar entre ambas funcionalidades es el propio MCU, como en el caso de algunos terminales Android, capaces de actuar como Host para un dispositivo de almacenamiento masivo y como Device de cara al otra máquina como un PC.

Una vez activado el periférico, aparecerá activada una capa de abstracción SW en la categoría Middleware denominada USB_DEVICE donde seleccionaremos la Clase USB de tipo CDC (Comunication Device Class) para fijar el modo de puerto virtual de comunicaciones (Virtual Port Com):

Con esta configuración creamos un puerto serie que permite operar en modo virtual a través del conector USB), sin necesidad de más configuración, que opera en modo asíncrono sin control de flujo, la velocidad en este caso se ajustará a la fijada por el terminal del equipo DTE, que se recomienda de 115200 Bits/s, formato de datos de 8 bits con 1 bit de Stop y sin paridad (115200-8-N-1).

Volviendo a la categoría de conectividad necesitamos dar soporte a los sensores de movimiento en 9-ejes, que están en PCB conectados por SPI al MCU, y no tienen soporte directo desde el código generado por CubeMX, ni por HAL ni Middleware. Es por ello que tendremos que incorporar las librerías de tipo BSP facilitadas por el fabricante (STM) para esta PCB en la fase posterior a la generación con CubeMX, es decir, desde el IDE TrueSTUDIO; si bien, para facilitar la integración del BSP, y sabiendo que el tipo de conectividad de los sensores es SPI, incorporamos un periférico de este tipo en CubeMX, en este caso el SPI2 en modo maestro como se muestra en la figura:

Una configuración de pines adicional puede realizarse desde la vista de sistema, si bien lo único que se necesita confirmar es el GPIO asociado a los LEDs en PB3 y PE8 para que operen en modo Output Push Pull, conectados en Pull-up y en velocidad Very High.

La figura siguiente refleja los cambios de configuración indicados en la vista de pines y de sistema:

Cambiamos a la pestaña de configuración de relojes y fijamos la máxima velocidad posible para el reloj principal de alta velocidad (HCLK) = 80 MHz para que recalcule la ruta de encaminamiento de señales más adecuada. Aunque hemos conectado los relojes externos en el apartado anterior, HSE y LSE, no se puede obtener a partir de ellos la velocidad máxima, por lo que asocia la fuente MSI (MultiSpeed Internal RC oscillator) a través de un PLL multiplicando x40 y dividiendo /2. Puede observarse también que, tanto el núcleo como memoria en AHB, temporizadores y periféricos en APB1 y APB2, toman por defecto esa máxima frecuencia, que para ciertas aplicaciones será necesario reasignar.

Pasando a la pestaña Project Manager realizamos la configuración del proyecto para generar el código y exportar a un espacio de trabajo de la herramienta de Atollic TrueSTUDIO™:

  • Asignar un nombre al proyecto y la ubicación del espacio de trabajo, confirmando la ubicación final para las herramientas de compilación.
  • Seleccionar TrueSTUDIO como IDE.
  • La ubicación del FW suele estar asociada a la más reciente instalada por CubeMX, la primera vez que se use la serie correspondiente de MCU, o en siguientes actualizaciones.

La sección de generación de código se configurará de acuerdo a la figura siguiente para:

  • Copiar al proyecto sólo los archivos necesarios del FW, referenciarlos solamente mediante un enlace al repositorio de librerías, o incluir la librería completa.
  • Generar o no archivos fuente/cabecera por cada periférico.
  • Mantener las secciones delimitadas de código de usuario (/* USER CODE x */) en distintos cambios de HW.
  • Eliminar ficheros generados en iteraciones anteriores.

Tras invocar la generación de código con GENERATE CODE obtenemos acceso a los archivos generados en la carpeta del proyecto, podemos examinarlos directamente con un editor de texto, si bien los trataremos desde el IDE en el paso siguiente.

La herramienta CubeMX puede cerrarse ahora, o dejar abierta si pretendemos realizar posteriores modificaciones en la configuración de HW, en cualquier caso ésta se almacenará en un archivo con el nombre del proyecto y extensión .ioc dentro de la su carpeta.

Como en otros IDE basados en el entorno ECLIPSE, se opera bajo un modelo basado en proyectos dentro de espacios de trabajo (Workspaces), sólo puede estar activo un espacio de trabajo para acceder sólo a los proyectos incluidos en él, por lo que al iniciar el entorno se fija/cambia (File→Switch Workspace) al espacio de trabajo en curso que corresponde a una carpeta del sistema de archivos, que incluya a su vez la carpeta del proyecto generado por CubeMX, y procedemos a la importación (File→Import→General→Existing Projects into Workspace):

Una vez importado correctamente el proyecto, el entorno nos presenta varias vistas (view) con varias características del mismo, el conjunto de vistas simultaneas constituyen una perspectiva (perspective), asociada normalmente a una tarea de desarrollo, trabajaremos principalmente en dos de ellas:

  • Edición (C/C++ Code Editing): vistas de edición, esquema (Outline) y explorador de clases (Class Browser)
  • Depuración (Debugging): vistas de hilos en ejecución (threads) , puntos de ruptura (Breakpoints) y registros (CPU Registers)

La figura siguiente muestra la perspectiva de edición, que aparece por defecto, actualizada tras el proceso de importación. La vista jerárquica del proyecto (Project Explorer) muestra los archivos contenidos en mismo:

  • Binaries: archivos ejecutables (extensión .elf) del proyecto generados por el compilador con determinadas opciones de optimización y depuración.
  • Includes: archivos cabecera (.h) asociados a librerías incluidas en el proyecto, derivadas del propio compilador y la configuración de MCU desde CubeMX.
  • Drivers: archivos fuente y cabecera de controladores de dispositivos
    • CMSIS (Cortex Microcontroller Software Interface Standard): estándar de soporte y librerías (DSP, NN, …) para dispositivos ARM Cortex
    • STM32L4xx_HAL_Driver: controladores de dispositivo HAL (Hardware Abstraction Layer), capa SW de abstracción de HW para la serie STM32L34
    • BSP (Board Support Package): controladores de dispositivos específicos del PCB al mismo nivel de abstracción que la HAL, como los sensores inerciales que incluiremos en una sección posterior.
  • Middlewares: controladores SW de dispositivos, protocolos de interfaz para otros dispositivos, sistemas operativos, de archivos, etc. de alto nivel de abstracción, en este caso se incluye la clase USB CDC para la comunicación serie.
  • Inc, Src: archivos cabecera (.h) y fuente C (.c) de la aplicación
  • startup: código fuente ensamblador (.s) de cargador de inicio de registros y tabla de vectores de interrupción
  • Debug, Release: código objeto (.o) compilado de los fuentes (.c) para ejecutables de depuración y final respectivamente
  • XXX_FLASH.ld: archivo de script para enlazado (linker script) donde se indican secciones y tamaños de memoria asociados a código, datos, pila, …
  • syscalls.c: código fuente que implementa rutinas básicas de entrada/salida a modo de llamadas del sistema

El código generado por CubeMX es sólo un esqueleto que debe ser completado por nuestra parte. El objetivo en este caso es hacer parpadear alternativamente los LEDs de usuario rojo y verde (configurados en PB2 y PE8 como GPIOs de salida rápida Pull-up), con una frecuencia marcada por la expiración del temporizador ya fijada en 100 ms, y además generar un mensaje, por la salida serie vía USB, con el tiempo en curso.

Dado que el temporizador provoca una interrupción, tendremos que codificar la rutina de servicio de interrupción (ISR) pertinente que va cambiar el estado de los LEDs. La generación del mensaje se hará en la rutina principal para evitar la sobrecarga de la ISR con el código CDC, a través de una variable global que se pone a 1 en la ISR y se cambia a 0 en la función principal tras la transmisión del mensaje.

Las funciones necesarias para el objetivo derivan de las librerías HAL asociadas a cada periférico con un nombre relativo a la función y un prefijo con el patrón HAL_PPP_Function (PPP es el nombre corto del periférico, p.ej. GPIO). Es de utilidad el asistente de contenido propio de la herramienta Eclipse que se invoca mediante la combinación de teclas Ctl+SPACE a partir del los primeros caracteres escritos en el editor.

En primer lugar trataremos la puesta del valor inicial en los LEDs rojo(PB2) y verde (PE8) y la inicialización del temporizador, e incluiremos en main.c el siguiente código en la sección de código usuario 2 correspondiente a la parte de inicialización de periféricos:

Declaramos una variable global de comunicación entre la ISR y la principal, en la sección de usuario de variables privadas, p.ej.:

Para el uso de la clase USB CDC necesitamos invocar a una rutina del Middleware correspondiente, para ello incluimos el archivo de cabecera correspondiente en la sección de cabeceras privadas de usuario:

Ahora ya podemos codificar el cuerpo completo de la rutina principal para el objetivo propuesto: comprobar el estado de la variable para escribir el mensaje con el tiempo en curso, haciendo uso de llamada HAL para obtener el tiempo de sistema, y del Middleware CDC para transmitir el mensaje en la sección de código de usuario 3:

Se ha completado el código componiendo la cadena a transmitir una cadena que incluye el mensaje “Hola mundo!” junto con el tiempo de sistema en curso, haciendo uso de las correspondientes funciones HAL y CDC. Nótese que se deben incorpor las cabeceras necesarias para trabajar con cadenas (string.h) y entrada/salida estándar (stdio.h) en la sección de usuario correspondiente.

A continuación codificamos la rutina ISR que cambia el estado de los LEDs y de la variable, para ello hacemos uso de la retro-llamada a la HAL de periodo expirado del temporizador, en la sección de código de usuario 4:

Antes de escribir el resto de código, ya podemos probar a compilar y ejecutar este código para una verificación parcial. Utilizamos la opción de construir (Project→Build All), la combinación de teclas (Ctrl+B) o haciendo clic en el icono del martillo:

Build Project

En la vista de consola obtendremos el resultado de la compilación: posibles errores y/o avisos, tamaños de código (text), datos (data), pila (bss) y del archivo ejecutable (.elf) generado, también en formato Intel (.hex) para la programación/depuración en PCB.

Una desventaja de STM32CubeMX es que no proporciona opciones de inicialización de algunos periféricos incluidos en PCB, ni tampoco la opción de incluir los paquetes de soporte correspondientes. En el caso que nos ocupa, eso sucede con los sensores MEMS inerciales del PCB DISCO-L476: magnetómetro, acelerómetro y giróscopo, los dos primeros incluidos en el circuito LSM303C y el tercero en el circuito L3GD20. Será necesario por tanto, incluir los archivos correspondientes de la BSP proporcionados en el FW; para ello, primero debemos localizar la BSP, que se encuentra en el siguiente directorio relativo a la carpeta de instalación de CubeMX:

[carpeta_STMCubeMX-version]/Repository/STM32Cube_FW_L4_V1.13.0/Drivers/BSP

Dentro de ese directorio, encontraremos una carpeta por cada placa de evaluación y una carpeta para los componentes de las placas (acelerómetros, drivers de audio…). Debemos arrastrar y copiar (Copy files and folders) la carpeta BSP a nuestro proyecto en Eclipse, dentro de Drivers, posteriormente iremos eliminando archivos y carpetas innecesarias de otras PCBs y dispositivos hasta que queden únicamente los necesarios, como se muestra a continuación:

Tras incluir las fuentes que se muestran, será necesario marcar los caminos a los archivos de cabecera, para ello se modifican las propiedades del proyecto, haciendo clic derecho sobre el mismo en el explorador de Eclipse, y click izquierdo sobre la opción Properties y llegamos a la ventana siguiente. Basta con incluir una referencia a la carpeta del PCB concreto dentro de Drivers/BSP para cumplir las dependencia de referencias a los archivos de cabecera añadidos para los dispositivos, para ello, se pulsa sobre el botón (Add…) y se marca la ruta dentro del espacio de trabajo como puede verse en la figura:

Los últimos cambios en el SW serán en la incorporación de las nuevas cabeceras del BSP:

así como la inicialización de los periféricos en PCB en la sección de código de usuario 2, y las llamadas a las funciones de los BSPs para obtener los valores de los 3 ejes que aporta cada sensor de movimiento para incluirlos en la cadena del mensaje, en la sección de código de usuario 3. Téngase en cuenta que para este ejemplo no se ha tenido en cuenta la etapa de calibración necesaria en este tipo de sensores, simplemente se obtienen los valores en bruto. El código modificado de la rutina principal quedaría como sigue:

Ejecutaremos ya el código generado en modo depuración, para ello invocamos (Run→Debug), la tecla (F11) o haciendo clic en el icono de la cucaracha:

Start Debug Session

Esto nos lleva al cambio a la perspectiva de depuración (Debug), como puede observarse en la siguiente figura, desde donde podemos controlar la ejecución en PCB en modo continuo, paso a paso, establecer puntos de ruptura y observar los cambios en memoria y registros:

Inicialmente la ejecución se interrumpe en la primera sentencia de la rutina main() mediante un punto de ruptura preestablecido. Mediante una barra de botones en la parte superior, o la combinación de teclas asociada, podemos controlar el modo de ejecución.

Presuponiendo que el código es correcto pasamos directamente a la ejecución directa (Run) y deberemos observar el parpadeo alternativo de los LEDs rojo y verde de usuario. Para comprobar la salida serie USB, podemos hacer uso de un programa de emulación de terminal como PuTTy configurando previamente el número puerto serie mapeado en nuestro máquina de desarrollo PC, así como los parámetros de comunicación establecidos (115200-8-N-1), y visualizar la salida como se refleja en las figuras siguientes.

Resultado de la ejecución inicial sin incorporación de BSPs:

Resultado de la ejecución final con incorporación de BSPs:

De forma alternativa podemos activar una pestaña de terminal en el propio entorno de Eclipse desde (Window→Show View→Terminal) como puede verse a continuación, si bien no en todos los sistemas operativos se consigue abrir el puerto asignado de la máquina debido a que Eclipse está hecho sobre máquina virtual Java y necesitan asignar determinados permisos.

Para volver a la perspectiva de edición anterior, una vez verificada la funcionalidad de la aplicación, es importante terminar totalmente la ejecución de los hilos de depuración, con el fin de poder realizar otras iteraciones individuales y no concurrentes con las anteriores, que podría generar errores en la ejecución de la herramienta y de verificación de funcionamiento. Para ello es necesario parar/terminar (Stop/Terminate) con el botón rojo y matar todos los procesos en curso (Remove All Terminated Launches) con el botón de doble X desde la consola:

Incorporar al proyecto anterior la funcionalidad adicional de comprobar el estado del pin GPIO correspondiente al botón/pulsador central del Joystick (B2), pin PA0 (JOY_CENTER), en modo entrada externa de interrupción EXTI de mayor prioridad que el TIM6, para habilitar/deshabilitar alternativamente sólo el parpadeo de los LEDs, sin afectar a la transmisión serie de salida.

  • pub/silab0.txt
  • Última modificación: 2020/09/28 11:44
  • (editor externo)