STM32F4: Cómo utilizar el ADC con DMA

Para este post, continuando con los post de esta placa, veremos los primeros pasos a dar para comenzar a obtener datos del ADC utilizando el DMA.

Pero la primera pregunta que debemos contestar es ¿qué es el DMA? El DMA (Direct Memory Access) es una forma de leer o escribir en memoria sin utilizar la CPU de la que disponen ciertos elementos como el ADC. Esto significa que no estamos perdiendo tiempo de CPU en procesar las muestras, por lo que éstas se procesan en background y mientras podemos estar haciendo otra cosa. Además, esto permite que las muestras de una señal no se pierdan por tiempo de procesado de la CPU.

CONFIGURACIÓN INICIAL

Abrimos CubeMX y habilitamos el ADC como en pasados tutoriales, pero esta vez habilitamos la opción de DMA Continuous Requests.

Configuración ADC

En ese momento, en la pestaña de DMA Settings tenemos que añadir un stream que tenga las características que se muestran en la imagen y después comprobar que en NVIC Settings de éste está habilitado. Es importante prestar atención al tipo de buffer a utilizar. Para esta primera prueba utilizaremos el normal, y después pasaremos al circular, puesto que es el que nos permite tener una captación de datos continua y renovada.

Ajustes para usar el DMA con el ADC

Tenemos que mantener el timer interno funcionando como en el tutorial anterior, ya que así garantizamos que estamos tomando las muestras correctamente, pero también se puede utilizar el ADC con DMA sin timer.

LECTURA Y REPRESENTACIÓN DE LA SEÑAL

Empezamos arrancando el timer y el ADC, pero para este último, utilizando las funciones que están relacionadas también con el DMA (como HAL_ADC_Start_DMA). Esta última función tiene como uno de sus argumentos el tamaño de los datos que vamos a leer.

Tras esto, haciendo uso de las funciones relacionadas con el DMA y el ADC, podemos controlar cuando ha terminado la lectura para, en ese momento, guardar los datos. De hecho, podemos controlar tanto la transacción completa como la mitad de la misma. Se deja la implementación al lector.

Finalmente, representamos estos datos para saber si lo estamos haciendo bien, (considerando que la configuración del timer es la misma que en el ejemplo anterior y la señal de entrada también). Manteniendo esta configuración y cambiando el buffer del DMA a circular en adc.c (hdma_adc1.Init.Mode = DMA_CIRCULAR;), lo que conseguimos es que el array donde se actualizan los datos esté en continuo cambio, pero hasta que no se detecta el fin de la transacción, no se guarda. La representación sería la misma en los dos casos (normal y circular).

Representación de los datos procesados por el DMA del ADC

 

Nota: la imagen de la portada se ha obtenido de CocoaStream Technologies.

STM32F4: Configurar el PWM con Timers

Un timer no es más que un contador. Es como un reloj que se usa para medir eventos temporales. Se configura a través de unos registros especiales y se puede elegir, entre otras cosas, su modo de funcionamiento.

En el ejemplo de hoy vamos a trabajar con la placa de desarrollo 32F411-DISCOVERY y nuestro objetivo va a ser aprender a configurar el TIM4 en modo PWM para controlar la intensidad de luz del led Naranja que viene integrado en la placa.

Esta placa está basada en el microcontrolador STM32F411 el cual dispone de hasta 11 Timers, de los cuales 6 pueden ser de 16 bits, y 2 de 32 bits, cada uno con hasta 4 canales de IC/OC/PWM o contador de pulsos. Además de 2 timers watchdog y un Systick Timer.

A través del software CubeMX activaremos el PWM Generation CH2 del TIM4 en la pestaña Pinout. Además de que asociaremos el pin PD13 la función alternativa de TIM4_CH2 (Channe2 PWM Generation CH2).

timers1

Con esto habremos conseguido dos cosas: por un lado configurar el canal 2 del Timer4 en modo PWM, y por otro lado asociar la salida del PWM al pin PD13, que es donde se encuentra el led naranja.

Tras esto pasamos a la pestaña Configuration y ahí configuramos el control del TIM4. Dentro de la pestaña de Parameter Settings hay 3 parámetros numéricos que debemos entender para configurarlos correctamente.

Prescaler (PSC – 16 bits value).

Con este valor podemos fijar la frecuencia del reloj asociado al Timer dividiendo la frecuencia del reloj de sistema.

Counter Period (AutoReload Register – 16 bits value).

Será el valor en el cual nuestro Timer saltará y nos avisará de alguna forma, ya sea reiniciándose o lanzando una interrupcion, etc.

Pulse (16 bits value).

Con este valor podemos fijar el ciclo de trabajo de nuestro PWM.

  • FreqTimer = FreqClock / (Prescaler + 1)
  • Prescaler = (FreqClock / FreqTimer) – 1
  • FreqPWM = FreqTimer / (CounterPeriod + 1)
  • Period = (FreqTimer / FreqPWM) – 1
  • Pulse = ((Period + 1) * DutyCicle) / 100 – 1
Queremos llevar a cabo dos ejemplos, por un lado queremos ver parpadear el led a varios ciclos de trabajo diferentes, y por otro lado queremos configurar un dimmer del led.

A grandes rasgos, lo que hacemos con esta técnica es fijar el periodo de la señal utilizando la frecuencia del PWM, y utilizar la frecuencia del timer para poder modular el ancho del pulso. Siendo esta frecuencia del timer una división de la frecuencia de reloj de nuestro microcontrolador.

Ejemplo 1. Parpadeo del led.

Para el parpadeo del led queremos un periodo de trabajo lento para que nos de tiempo a ver esa conmutación entre encendido y apagado del led. Es por ello que hemos fijado la Frecuencia del PWM a 1Hz. Luego, para hacer el resto de operaciones más fáciles hemos fijado la FreqTimer a 10 Khz. Con estos datos y usando las ecuaciones anteriores obtenemos:

  • APB2 timer clocks = 96 Mhz
  • Objetivo de PWM Freq = 1 Hz
  • Prescaler = 9599
  • Period = 9999

Para poder ver el cambio de parpadeo del led usaremos estos tres valores de Pulse (% Duty cicle).

  • Pulse: 99 (1%), 4999 (50%), 9899 (99%)

Ejemplo 2. Dimmer del led.

En este caso queremos que el periodo de trabajo sera muy pequeño para que las transiciones sean fluidas para el ojo humano. Es por ello que en este ejemplo hemos fijado la Frecuencia del PWM a 10 Khz. Luego, para hacer el resto de operaciones más fáciles hemos fijado la FreqTimer a 2 Mhz. Con estos datos y usando las ecuaciones anteriores obtenemos:

timers2

  • APB2 timer clocks = 96 Mhz
  • Objetivo de PWM Freq = 10 Khz
  • Prescaler = 47
  • Period = 199

Para poder ver un dimmer en el led recorreremos todos los valores posibles de Pulse (% Duty cicle).

  • Pulse: 0 (0%)-199 (100%)

Ejecución de los ejemplos

Una vez hemos generado la plantilla de código con CubeMX es momento de continuar en Eclipse.

Ahora debemos tener en cuenta que para que el PWM se ponga en funcionamiento tenemos que llamar a la función

Y para poder hacer cambios en el valor de Pulse desde Eclipse debemos llamar a la macro

STM32F4: Primeros pasos con el entorno de desarrollo

En el tutorial que vamos a llevar a cabo hoy veremos como instalar todas las herramientas de desarrollo para poner en marcha la placa de desarrollo 32F411-DISCOVERY. Una vez tengamos todas las herramientas instaladas procederemos a compilar y ejecutar un programa que haga parpadear un led.

Unos de los entornos de desarrollo integrado (IDE) que nosotros usamos en el B105 es eclipse. Para trabajar y poder compilar de forma cruzada para los STM32 existe una versión de eclipse que contiene todos los plugins y librerias necesarias para trabajar con dispositivos de STM llamada System Workbench for STM32 (SW4STM32). Es una herramienta gratuita que debéis descargar e instalar en vuestro ordenador. Para poder descargarla tendréis que registraros en la web. Es posible, que si no lo tenéis instalado todavía, tengáis que instalar Java en vuestro ordenador.

eclpse

 

Ahora procedemos a descargar e instalar el software STM32CubeMX que no es más que un generador de código de inicialización para los microcontroladores y plataformas de desarrollo de ST. Con este software podremos crear el código inicial con las capas de abstracción hardware con las que queremos inicializar nuestra plataforma.

cubmxAl instalar SW4STM32 y CubeMX se nos deberían instalar los drivers correspondientes para poder usar el programador integrado en la placa Discovery para el STM-Link v2 o v2.1.

Proyecto Toggle Led

Abrimos CubeMX y creamos un nuevo proyecto. En la pestaña Board Selector elegimos nuestra placa STM32F411E-DISCO.

cubemx1

 

A través de este software se pueden activar y desactivar los periféricos y los middlewares disponibles en el microcontrolador STM32F411. Además se pueden asociar los periféricos y funciones a los pines, o asignar funciones alternativas a un pin tales como: GPIO de salida, entrada, de interrupción, analógico, de PWM, etc.

Antes de configurar nada del microcontrolador vamos a configurar el programa para que nos genere el código con la estructura correcta. Para ellos entraremos en Project Settings, pondremos un nombre a nuestro proyecto en Project Name, por ejemplo Toggle Led. En Toolchain/IDE seleccionaremos SW4STM32 y desactivamos el Generate Under Root.

Bajo la pestaña Code Generator seleccionamos Copy only the necessary library files, Generate peripheral initialization as a pair of ‘.c/.h’, Keep user code when re-generating y Delete previously generated files when not re-generated. Aceptamos la configuración.

Ahora nos fijaremos en el pin PD12, que está asociado al led verde. Comprobamos que está en modo GPIO_Output. Y ahora simplemente confiamos que el resto de configuraciones por defecto son correctas.

Generamos el código pulsando sobre Project -> Generate Code. El programa comenzará a generar todo el código y al finalizar, en el cuadro emergente pulsaremos sobre abrir proyecto. Esto nos debe abrir e incluir el proyecto en SW4STM32 (Eclipse).

Podemos ver como tenemos una estructura de directorios y ficheros que nos ha generado CubeMX con todos los drivers y librerias necesarias para nuestro sencillo proyecto. Si abrimos el main.c podemos ver que hay muchas líneas que dicen USER CODE BEGIN/END.

eclipse2

 

Es muy importante que todo el código que escribamos lo hagamos dentro del bloque BEGIN/END. De lo contrario, cuando volvamos a generar código CubeMX lo sobreescribirá y no lo conservará.

En la inicialización vemos que se hace la llamada a MX_GPIO_Init(), con eclipse si mantenemos pulsado la tecla Ctrl mientras hacemos click sobre las función, nos abrirá su definición. Si la leemos un poco podemos ver una sección donde se define la funcionalidad del LD4_Pin asociado al puerto GPIOD y pin GPIO_PIN_12 que en nuestro caso es el GPIO asociado al Led Verde.

Si ahora accedemos a la definición de HAL_GPIO_Init() nos abrirá el fichero stm32f4xx_hal_gpio.c donde podemos ver todas las funciones de la HAL que nos ha generado CubeMX, entre ellas las de Read, Write y Toggle de un GPIO.

En nuestro caso, dentro del while(1) del main() haremos uso de HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12) para hacer parpadear el led. Para que podamos visualizarlo tendremos que poner un HAL_Delay(500).

Solo nos falta compilar, y debuguear en Ac6 STM32 C/C++ Application. Si dado el caso os preguntan que interfaz deseas utilizar (JTAG o SWD), debéis seleccionar SWD para la correcta comunicación con la placa de desarrollo. Y con eso podremos ver parpadear nuestro led verde cada 0.5 segundos en nuestra plataforma de desarrollo.