ARM: Digital I/O

Como en todos los microcontroladores, es importante saber encender y apagar un LED, éste no puede ser menos. Además incluiré el sensado del botón de usuario.

Para ello solo utilizaré lo que la placa de la STM32F4 Discovery lleva integrado, 4 LEDs y un botón.

¿Cómo funcionan los periféricos de un ARM?

Con la intención de ser lo más eficientes posible, los núcleos ARM tienen la característica especial frente a cualquier otro tipo de microcontrolador de que los periféricos se deben activar uno por uno.

Es decir, al inicio del programa un periférico, tal como las entradas/salidas digitales, están desconectadas de la alimentación y de la señal interna de reloj.

Para poder utilizar los puertos digitales hay que inicializarlos con una serie de instrucciones

Las librerías GPIO y RCC

Son unas librerías que vienen en la carpeta del Firmware de la tarjeta, que ya se vio en el segundo capítulo. Deberían estar ya incluidas en el Include Path de Keil. Recordad que para hacer un nuevo proyecto, es conveniente copiar la plantilla y renombrar la carpeta, de forma que no se pierde la configuración.

Hoy voy a enseñaros una verdadera base de datos de las librerías que contiene el Firmware, se encuentra aquí:

http://www.disca.upv.es/aperles/arm_cortex_m3/curset/STM32F4xx_DSP_StdPeriph_Lib_V1.0.1/html/index.html

Si pincháis en Modules aparecerá una lista de todos los posibles periféricos de la placa, es genial. En éste caso vamos a centrarnos en la GPIO.

La libería GPIO

Rebuscando un poco encontramos el apartado “Initialization and configuration”, de él, lo que nos interesa es poder inicializar unos cuantos pines. Para ello podemos utilizar la función GPIO_Init, cuyos parámetros son un puerto (GPIOA, GPIOB, GPIOC, …) y una estructura de datos que tenemos que definir, llamada GPIO_InitStruct.

GPIO_InitStruct es un tipo de variable personalizado para llevar unos parámetros de configuración dentro de sí. Éste tipo de variable se definió como GPIO_InitTypeDef. Si buscamos dentro de éste tipo encontramos los distintos parámetros que contiene:

  • GPIO_Mode: el modo de operación de los pines que se va a configurar, puede ser IN, OUT, AN (analog) o AF (alternate)
  • GPIO_OType: en caso de configurar output, el tipo: PP (pushpull) o OD (open drain)
  • GPIO_Pin: el número de pin que se va a configurar, del 0 al 15 en puertos de 16 pines disponibles.
  • GPIO_PuPd: configura la conexión a resistencias internas de pullup/pulldown: NOPULL (ninguno), UP (pullup), DOWN (pulldown)
  • GPIO_Speed: velocidad de reloj: 2MHz, 25MHz, 50MHz, 100MHz.

Una vez configurado todo ésto, llevamos a cabo la función GPIO_Init(GPIOX, GPIO_InitStruct), pero nos falta otra cosa:

Librería RCC

Ya he comentado antes que para configurar un periférico hay que dar señal de reloj a éste abriendo el bus que lo comunica con el resto del micro, y ésto se hace mediante la librería RCC. Volviendo a nuestra guía de funciones, vemos que hay un apartado especial llamado “Peripheral clocks configuration functions”.

Como hay mucha función extraña, diré que la que nos interesa es la RCC_AHB1PeriphClockCmd. Vale, hay AHB1, AHB2, y AHB3, no sé en qué se distinguen pero por ahora usaremos la AHB1.

En éste caso es tan simple como decirle el puerto al que queremos conectar y ponerle ENABLE.

Los pines que nos interesan

Muy importante, antes de ponerse a programar a lo loco, es saber que los pines que tenemos que configurar son los siguientes:

  • Los LEDs se encuentran en el puerto D entre los pines 12 y 15
  • El botón de usuario se encuentra en el puerto A en el pin 0

Ya podemos construir una función para cada cosa
void InicializarLeds(){
GPIO_InitTypeDef GPIO_InitStructure; //Definimos una variable con el tipo personalizado
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); //Damos señal de reloj al puerto D
//Y configuramos todos los parámetros de la variable
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15|GPIO_Pin_14|GPIO_Pin_13|GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
//Cuando ya está, metemos esta variable en la función de inicialización
GPIO_Init(GPIOD, &GPIO_InitStructure);
}

void InicializarBoton(){
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}

Ahora habrá que leer y escribir los puertos

Nos volvemos a referir a la librería GPIO, y encontramos:

  • GPIO_ReadInputDataBit: que tiene como parámetros el puerto y el pin
  • GPIO_SetBits: pone en alto los bits que le indiques del puerto que le indiques
  • GPIO_ResetBits: pone en bajo los bits que le indiques del puerto que le indiques
  • GPIO_WriteBit: hace lo mismo que las dos anteriores pero en una sola función, puedes indicar el estado que quieres

Delays: algo malo tenía que haber

La función delay no existe en ARM, así que lo que se hace es poner una variable en un bucle for a contar. A mí me ha funcionado con un tiempo decente como para ver el LED parpadear con 5 millones de ciclos.

void retardo(uint32_t n){
uint32_t t;
for(t=0; t<n; t++);
}

n es el número de ciclos. Haced un #include <stdint.h> al principio del archivo main para que el tipo uint32_t funcione.

Y con todo ésto ya me despido, supongo que seréis capaces de ordenar y preparar un programa en cuyo main se configuran los puertos usando las funciones y que luego haga cualquier chorrada con las luces.

Nota: no olvidéis pulsar el botón de reset una vez se haya cargado el programa para que funcione

Leave a comment