Libro ARM Cortex M
Libro ARM Cortex M
Libro ARM Cortex M
Àngel Perles
Departament d’Informàtica de Sistemes i Computadors
Universitat Politècnica de València
13 de mayo de 2017
Presentación
iii
Ahora voy a explicar cómo usar esto. Partimos de que el aprendiz tiene conocimientos
básicos de electrónica digital, electrónica analógica y de programación en lenguaje
C. Si, además, se tienen conocimientos sobre otros microcontroladores (PIC, AVR,
8051, HC-11, etc.) entonces será fácil seguir esto (espero). Si no se cumplen estas
condiciones, mejor no sigas y empieza con la fantástica plataforma Arduino. Eso no
es para jugar, es para desarrollos serios para empresas.
Empieza a trabajar de manera lineal y, cuando no se entienda algo de programación,
acude al apartado correspondiente para ver si te lo resuelve. Si no es así, deberás
buscar ayuda fuera del libro.
Àngel Perles
iv
Reconocimientos
v
Índice general
Presentación iii
Reconocimientos v
Contenido vii
1 Los microcontroladores 1
1.1 Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Qué es un microcontrolador. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.3 Aplicaciones del microntrolador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.4 Sistemas embebidos, embarcados o empotrados. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.5 Clasificaciones típicas de los microcontroladores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5.1 Por el tamaño de palabra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5.2 Por el enfoque en la ejecución de instrucciones: CISC o RICS. . . . . . . . . . . . . . . . . . . . 6
1.5.3 Por el camino usado para los datos y las instrucciones: Von Neuman o Harvard. . . . . . . . . 6
vii
Índice general
3 Entrada/salida digital 27
3.1 Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.2 Puertos y líneas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.3 La célula de cada pin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.4 Salida digital . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.4.1 La célula en modo salida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4 Interrupciones 57
4.1 Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.2 Funcionamiento general y jerga del sistema de interrupciones . . . . . . . . . . . . . . . . . . . . . 59
4.3 Interrupciones en los ARM Cortex-M . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
4.3.1 Cosas pendientes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
viii
Índice general
5 Contadores y temporizadores 73
5.1 Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
5.2 Los timers en genérico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.3 SysTick, el contador común a los Cortex-M . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.3.1 Biblioteca HAL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
5.3.2 Midiendo el paso del tiempo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.3.3 Haciendo pausas de precisión. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.3.4 Tareas periódicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.3.5 NO MIRAR: Cómo es . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
6.5 Bibliotecas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6.6 El Cortex Microcontroller Software Interface Standard (CMSIS) . . . . . . . . . . . . . . . . . . . 95
6.7 El firmware STM32Cube HAL de St . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
6.8 Tablas “look-up” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
6.9 Tratamiento bit a bit. Máscaras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
6.9.1 Representación externa. Representación interna . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
6.9.2 Representación hexadecimal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
6.9.3 Operadores de bit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
6.9.4 Máscaras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
ix
Índice general
8 Prácticas 133
8.1 Práctica: Instalación de St-Link y volcado de ejecutable en la placa Discovery . . . . . . . . . . 134
8.1.1 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
8.1.2 Material necesario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
8.1.3 Instalación y comprobación de St-Link . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
8.1.4 Actualización de la sonda St-Link de la Discovery . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
8.1.5 Volcado de ejecutables en la Discovery . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
8.1.6 Volcado de la demo de la Discovery. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
x
Índice general
xi
Índice general
8.10 Práctica: Usando EXTI para contar vehículos en una carretera . . . . . . . . . . . . . . . . . . . 169
8.10.1 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
8.10.2 Material necesario. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
8.10.3 Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
8.10.4 Preparación inicial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
8.10.5 Modificar main() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
8.10.6 Tarea: Desarrollar la biblioteca cars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
8.10.7 Tarea: Añadir el manejador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
8.10.8 Probar la aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
8.10.9 Ampliación: Salida por la pantalla gráfica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
8.10.10 NO MIRAR: Con método “callback” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
Soluciones 187
Glosario 187
Bibliografía 189
xii
Índice general
Bibliografía 189
xiii
Índice general
xiv
Capítulo 1
Los microcontroladores
1.1 Introducción
(Al terminar este capítulo, faltaría redactar intro con resumen y objetivos). (Pendent
incorporar material llibre 8051).
1
Capítulo 1. Los microcontroladores
El procesador, también conocido como CPU (del inglés, Central Processing Unit)
es la encargada de ejecutar los programas en forma de instrucciones máquina para
procesar datos. Tanto las instrucciones como los datos estarán representados como
números digitales binarios.
La memoria principal será la encargada de almacenar el programa y los datos.
En general, convivirán distintos tipos de memoria dentro del encapsulado del mi-
crocontrolador. Por ejemplo, se tendrá memoria ROM (Read-Only Memory) para
almacenar el programa y las constantes, y memoria RAM (Random Access Memory)
para contener las variables de la aplicación, que se perderán al dejar de alimentar el
chip.
2
1.3 Aplicaciones del microntrolador
3
Capítulo 1. Los microcontroladores
4
1.5 Clasificaciones típicas de los microcontroladores
Figura 1.3: Representación del efecto de realizar una suma de 32 bits en un procesador de 8 bits (izquierda)
y de 32 bits (derecha)
A primera vista, parecería ideal que todos los microcontroladores fuesen de 64 bits;
sin embargo, un mayor tamaño de palabra requiere más líneas eléctricas, más con-
sumo energético, más silicio y más precio, así que es necesario buscar un equilibrio.
5
Capítulo 1. Los microcontroladores
1.5.3 Por el camino usado para los datos y las instrucciones: Von
Neuman o Harvard
El procesador ejecuta instrucciones máquina que están en memoria principal, y
muchas de esas instrucciones tienen que ver con el tratamiento de datos (sumar,
restar, decidir, ...).
La cuestión ahora es dónde está el programa y dónde están los datos. Un plantea-
miento es entremezclar programa y datos en una misma memoria (bueno, siendo
puristas, mismo “camino”) y se habla de una arquitectura Von Neuman (un telar,
que es la base de los computadores).
Otra aproximación habitual es separar claramente el camino para el programa y el
camino para los datos, con lo que se tendrá una arquitectura llamada Harvard.
En general, la arquitectura Harvard es más eficiente al tener dos caminos separados
que pueden funcionar simultáneamente y que permiten optimizaciones extra. La
realidad es que la arquitectura Von Neuman también tiene sus ventajas, así que los
procesadores actuales suelen tener una mezcla de las dos aproximaciones.
6
1.6 Eligiendo el microcontrolador adecuado
Elegir una familia que cubra un rango de problemas lo más amplio posible para
que el esfuerzo de aprendizaje sirva para todo el espectro de problemas.
Elegir soluciones ampliamente aceptadas: más herramientas, más ejemplos, más
comunidades.
No vincularse a un fabricante para tener margen de maniobra en caso de fallo
de suministro.
Asegurarse de la disponibilidad de kits de evaluación de bajo coste. Dada la
coyuntura, es bueno tener la posibilidad de probar antes de decidirse
Primar la facilidad de diseño/elección con respecto al precio del chip. Un chip
grande/caro puede resolver distintos proyectos y no hay que empezar de nuevo.
El coste de mano de obra es muy importante en los proyectos pequeños.
Figura 1.4: Un ecosistema equilibrado con sus plantitas, animalitos y todos felices.
7
Capítulo 1. Los microcontroladores
Figura 1.5: Evolución del reparto de microcontroladores por tamaño de palabra (Fte. ICInsigts)
8
1.6 Eligiendo el microcontrolador adecuado
La figura 1.6 representa una estimación del reparto del mercado de microcontrola-
dores por fabricante. Se observa un claro predominio del gigante Renesas como cabe
esperar. Indicar aquí que cada fabricante puede proveer distintas arquitecturas de
microcontrolador y que hay un fuerte componente geoestratégico. Así, la japonesa
Renesas es la principal proveedora del mercado asiático del segmento de automo-
ción (un coche tiene decenas de microcontroladores, por lo que es un segmento muy
competitivo).
Figura 1.6: Reparto del mercado de microcontroladores del año 2011 (Fte. Databeans)
Añadir ahora que las empresas consolidadas son rehacias a los cambios, por lo que las
oscilaciones en las ventas de un fabricante se deben más a la evolución del mercado
donde se implantan sus microcontroladores. Por ejemplo, un fabricante fuertemente
orientado al segmento del automóvil, verá reducidas sus ventas debido a crisis en
este segmento en su zona geográfica de influencia.
Por otra parte, una nueva empresa que necesite hacer uso de microcontroladores y
deba competir en un determinado mercado, tendrá la ventaja de que puede elegir
arquitectura. Una empresa ya consolidada tiene mucho más difícil hacer un cambio.
Elíjase en consecuencia sin tomar como fundamental el orden mostrado en la figura
1.6.
9
Capítulo 1. Los microcontroladores
Por otro lado están los fabricantes de microcontroladores, que pueden trabajar de
distintas maneras. Por ejemplo, los hay que solo se encargan del diseño y fabricación
del chip; otros diseñan y mandan fabricar a otros (en inglés, fabless); otros compran
la licencia y fabrican el circuito integrado ... en definitiva, todas las posibles com-
binaciones. Además, es muy habitual que un mismo fabricante produzca distintas
familias/arquitecturas.
Y, por último, el término licencia que acaba de aparecer se refiere al “permiso” para
emplear determinado diseño pagando royalties. En este mundo son habituales las
empresas de ingeniería que licencian sus diseños a otros.
10
1.6 Eligiendo el microcontrolador adecuado
11
Capítulo 1. Los microcontroladores
Tabla 1.1: Tabla resumen de las versiones de ARM Cortex-M (falta el próximo M5)
En realidad la familia Cortex-M está formada por distintas subfamilias que se adap-
tan a diferentes problemáticas. La tabla 1.1 resume las características de cada subfa-
milia. No es interesante profundizar aquí en cada variante, destacando simplemente
que los modelo M0 son menos potentes y requieren menos silicio para su fabricación
y los M4 ofrecen mucho más rendimiento a costa de mayor superficie de silicio.
Este libro trata de la familia ARM Cortex-M, que proporciona beneficios como:
12
1.7 Las herramientas
13
Capítulo 1. Los microcontroladores
1.7.3 Simuladores
En el contexto de los microcontroladores, un simulador es software que se ejecuta
en un ordenador personal e imita el funcionamiento del microcontrolador, tanto a
nivel de ejecución de aplicaciones como de periféricos.
Un simulador es adecuado en las etapas iniciales de aprendizaje o en entornos en que
se integre el microcontrolador simulado junto a otros elementos simulador (circuitos
electrónicos, señales, etc.).
Dada la relativa novedad de los microcontroladores ARM Cortex-M, los simuladores
disponibles para esta arquitectura suelen ser de los primeros modelos. Por eje,mplo,
la figura 1.9 muestra un ejemplo del entrono de desarrollo electrónico Proteus simu-
lando un circuito con un ARM Cortex-M3 de Texas Instruments.
14
1.7 Las herramientas
15
Capítulo 1. Los microcontroladores
16
Capítulo 2
2.1 Introducción
(Al terminar este capítulo, faltaría redactar intro con resumen y objetivos). (enfocar
de lo particular St a lo general M4F)
17
Capítulo 2. Arquitectura del microcontrolador St STM32F4xxx (ARM Cortex-M4F)
18
2.3 Arquitectura del microcontrolador STM32F407
19
Capítulo 2. Arquitectura del microcontrolador St STM32F4xxx (ARM Cortex-M4F)
20
2.4 Encapsulado y patillaje de un St STM32F4
21
Capítulo 2. Arquitectura del microcontrolador St STM32F4xxx (ARM Cortex-M4F)
22
2.5 Sistemas con un STM32F4
Figura 2.6: Equipo comercial desarrollado en la UPV que incorpora un microcontrolador de la gama St
STM32
La alimentación
Los primeros microcontroladores solían emplear tensiones TTL (5 volt.), conectán-
dose el terminal “VSS” a masa y el terminal “VCC” o “VDD” a +5 voltios. Hoy en
dia, los micros y los dispositivos analógicos y digitales se suelen alimentar a ten-
siones inferiores, pues menor tension implica menor consumo. En las últimas hay
que tener especial precaución con las tensiones negativas en cualquiera de los pines,
pudiéndose proteger con diodos.
Algunos uC admiten rangos de tension amplios por el tipo de tecnología empleado,
o por la inclusión de reguladores de tension internos.
23
Capítulo 2. Arquitectura del microcontrolador St STM32F4xxx (ARM Cortex-M4F)
Figura 2.7: Equipo comercial desarrollado en la UPV que incorpora un microcontrolador de la gama St
STM32 (bottom)
El reloj
Un uC es, básicamente, un autómata que evoluciona al ritmo de una señal cuadrada
o “reloj”. Para tener reloj, la mayoría de los uC modernos suelen incorporar uno o
varios osciladores internos y/o la electrónica parcial para tener parte del oscilador
externamente mediante redes tipo R-C o cristales de cuarzo-cerámicos-sintéticos.
Es habitual que coexistan varios osciladores simultáneos, por ejemplo, un oscilador
para la CPU y un oscilador para el reloj de tiempo real con un cristal de 32 kHz. O
también un oscilador de baja precisión interno (alrededor del 2 %) y uno de buena
exactitud externo con cristal de cuarzo.
24
2.6 ¿Más adelante?
El reinicio/reset
Para poner el microcontrolador en su estado de partida inicial es necesario propor-
ciona la llamada señal de reset o reinicio.
Habitualmente, esta señal se realizaba mediante un condensador y una resistencia
externa que fijaban a un determinado nivel un pin cuando se producía alimentación.
Los modernos microcontroladores no sulen necesitar nada, pues incorporan unidades
internas que monitorizan la tensión de alimentación (POR-power-on-rset) y llegan
más allá monitorizando el funcionamiento de osciladores, memoria flash, etc. para
generar la señal de reset en caso de detectar anomalías.
2.5.2 Ejemplos
2.5.4 Razonable
Oscilador externo, reset de usuario, ???. Que lo diseñen ellos.
2.6.2 Arranque
Posición inicial, ajuste de stack, salto a inii y a main. Vectores.
25
Capítulo 2. Arquitectura del microcontrolador St STM32F4xxx (ARM Cortex-M4F)
26
Capítulo 3
Entrada/salida digital
27
Capítulo 3. Entrada/salida digital
3.1 Introducción
La figura 3.1 representa la idea de “entrada” y de “salida”, donde se toma siempre
como referencia al microcontrolador.
28
3.2 Puertos y líneas
29
Capítulo 3. Entrada/salida digital
Dentro de la célula hay un driver de entrada digital, un driver de salida digital, una
posible conexión a entrada/salida analógica y una posible conexión a una función
alternativa digital.
30
3.4 Salida digital
31
Capítulo 3. Entrada/salida digital
Para conocer el estado real de las líneas, el subsistema de entrada digital también
sigue activo.
# ifndef LED_H
# define LED_H
32
3.5 Biblioteca STM32Cube para gestionar la GPIO
# endif
Supóngase ahora el LED conectado al pin PG13 según el esquemático de la figura 3.6.
Dicho LED requeriría una configuración de tipo push-pull para funcionar. Respecto
a las resistencias, no tiene sentido que estén activadas.
Para implementar esta funcionalidad, el primer paso será inicializar el hardware tal
como se ilustra en la implementación de LED_Init(). Básicamente se proporciona
primero reloj al periférico para activarlo., a continuación se prepara una estructu-
ra de datos específica para cada periférico y, finalmente, se aplica la estructura a
periférico en sí.
Una vez inicializado el periférico, se puede acceder a sus características seleccionando
las funciones del HAL o accediendo a bajo nivel al propio dispositivo. En el listado
se ilustra la manera de hacerlo a alto nivel mediante las HAL.
/* *
@file led . c
@brief Basic LED handling for libraries example
@author Angel Perles
@date 2016/02/11
*/
# include " stm32f4xx_hal . h " // cabeceras propo rciona das por St para simplificar el uso de los peri
# include " led . h "
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/* *
* @brief Preparing pin corresponding to LED green ( PG13 )
* @return none
*/
void LED_Init ( void ) {
33
Capítulo 3. Entrada/salida digital
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* *
* @brief Encender el LED
* @return none
*/
void LED_On ( void ) {
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* *
* @brief Apagar el LED
* @return none
*/
void LED_Off ( void ) {
/* ** End of file * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
Para facilitar el uso de esta estructura, las HAL incluyen definiciones que simplifican
la escritura de las aplicaciones. El siguiente listado incluye algunas de las definiciones
para compresión de la idea.
# define GPIO_PIN_0 (( uint16_t )0 x0001 ) /* Pin 0 selected */
# define GPIO_PIN_1 (( uint16_t )0 x0002 ) /* Pin 1 selected */
# define GPIO_PIN_2 (( uint16_t )0 x0004 ) /* Pin 2 selected */
# define GPIO_PIN_3 (( uint16_t )0 x0008 ) /* Pin 3 selected */
# define GPIO_PIN_4 (( uint16_t )0 x0010 ) /* Pin 4 selected */
# define GPIO_PIN_5 (( uint16_t )0 x0020 ) /* Pin 5 selected */
# define GPIO_PIN_6 (( uint16_t )0 x0040 ) /* Pin 6 selected */
# define GPIO_PIN_7 (( uint16_t )0 x0080 ) /* Pin 7 selected */
# define GPIO_PIN_8 (( uint16_t )0 x0100 ) /* Pin 8 selected */
# define GPIO_PIN_9 (( uint16_t )0 x0200 ) /* Pin 9 selected */
# define GPIO_PIN_10 (( uint16_t )0 x0400 ) /* Pin 10 selected */
# define GPIO_PIN_11 (( uint16_t )0 x0800 ) /* Pin 11 selected */
# define GPIO_PIN_12 (( uint16_t )0 x1000 ) /* Pin 12 selected */
# define GPIO_PIN_13 (( uint16_t )0 x2000 ) /* Pin 13 selected */
# define GPIO_PIN_14 (( uint16_t )0 x4000 ) /* Pin 14 selected */
# define GPIO_PIN_15 (( uint16_t )0 x8000 ) /* Pin 15 selected */
34
3.6 Entrada digital
Actividad: Modifica el módulo led.c para que gestione un LED montado según la figura
3.7.
35
Capítulo 3. Entrada/salida digital
36
3.6 Entrada digital
# ifndef BUTTON_H
# define BUTTON_H
# endif
G P I O _ I n i t T y p e D e f port ;
_ _ H A L _ R C C _ G P I O A _ C L K _ E N A B L E ();
37
Capítulo 3. Entrada/salida digital
{
if ( H A L _ G P I O _ R e a d P i n ( GPIOA , GPIO_PIN_0 ) == GPIO_PIN_SET ) {
return BUTT ON_PRE SSED ;
} else {
return B U TT ON _ RE LE A SE D ;
}
}
while (1) {
if ( button_Read () == BUTTO N_PRES SED ) {
// actions here
}
}
}
G P I O _ I n i t T y p e D e f port ;
_ _ H A L _ R C C _ G P I O A _ C L K _ E N A B L E ();
38
3.7 Funciones extendidas para la GPIO
tmp = A P _ G P I O _ R e a d I n p u t D a t a ( GPIOA );
if (( tmp & 0 x0001 ) != 0) {
return BUTT ON_PRE SSED ;
} else {
return B U TT ON _ RE LE A SE D ;
39
Capítulo 3. Entrada/salida digital
}
}
Para ello el equipo de desarrollo de alto nivel propone un archivo de cabecera que
lo abstrae y tiene el siguiente listado.
/* *
@file selector . h
@brief Header for abastracting the selector ( or tumble ??)
*/
# ifndef SELECTOR_H
# define SELECTOR_H
# endif
40
3.7 Funciones extendidas para la GPIO
G P I O _ I n i t T y p e D e f port ;
_ _ H A L _ R C C _ G P I O A _ C L K _ E N A B L E ();
T Se le c to r St at u s selector_Read ( void )
{
uint16_t data ;
T Se le c to r St at u s value ;
switch ( data ) {
case 0 x0FFE :
value = SELECTOR_OFF ;
break ;
case 0 x0FFD :
value = S E L E C T O R _ C O T T O N _ 1 ;
41
Capítulo 3. Entrada/salida digital
break ;
case 0 x0FFB :
value = S E L E C T O R _ C O T T O N _ 2 ;
break ;
case 0 x0FF7 :
value = S E L E C T O R _ S Y N T H E T I C ;
break ;
case 0 x0FEF :
value = SELEC TOR_QU ICK ;
break ;
case 0 x0FDF :
value = S E L E C T O R _ D E L I C A T E ;
break ;
case 0 x0FBF :
value = SELECTOR_WOOL ;
break ;
case 0 x0F7F :
value = SELECTOR_CARE ;
break ;
case 0 x0EFF :
value = S E L E C T O R _ D E S A G U E ;
break ;
case 0 x0DFF :
value = SELECTOR_OFF ;
break ;
case 0 x0BFF :
value = S EL E CT OR _ SP I N_ 1 ;
break ;
case 0 x07FF :
value = S EL E CT OR _ SP I N_ 2 ;
break ;
case 0 x0FFF :
value = S E L E C T O R _ A C L A R A D O ;
break ;
default :
value = S E L E C T O R _ U N D E F I N E D ;
break ;
}
return value ;
}
42
3.8 Ejemplo: un display de 7 segmentos
Figura 3.13: Identificación de segmentos de un Figura 3.14: Conexión típica en cátodo común.
display de 7 segmentos.
Símbolo G F E D C B A Hexadecimal
0 0 1 1 1 1 1 1 3Fh
1 0 0 0 0 1 1 0 06h
2 1 0 1 1 0 1 1 5Bh
3 1 0 0 1 1 1 1 4Fh
4 1 1 0 0 1 1 0 66h
5 1 1 0 1 1 0 1 6Dh
6 1 1 1 1 1 0 1 7Dh
7 0 0 0 0 1 1 1 07h
8 1 1 1 1 1 1 1 7Fh
9 1 1 0 1 1 1 1 6Fh
E 1 1 1 1 0 0 1 79h
43
Capítulo 3. Entrada/salida digital
Se propone crear un módulo con funciones para manejar este display. Por ejemplo, las
funciones listadas en el archivo de cabecera display7seg.h mostrado a continuación.
/* *
@file display7seg . h
@brief Module header for handling a 7 segments display
*/
# ifndef DISPLAY7SEG_H
# define DISPLAY7SEG_H
void d i s p l a y 7 s e g _ I n i t ( void );
void d i s p l a y 7 s e g _ S e t N u m b e r ( uint8_t value );
void d is pl a y7 se g _O ff ( void );
# endif
d i s p l a y 7 s e g _ I n i t ();
while (1) {
for ( n =0; n <= 10; n ++) {
d i s p l a y 7 s e g _ S e t N u m b e r ( n );
HAL_Delay (500);
}
}
Ahora toca implementarlo. Este display es una excusa estupenda para introduciendo
distintas mejoras en el estilo de programación y en la adaptación a un sistema
embebido con restricciones en cuanto a espacio y velocidad.
Supóngase que se ha de crear un módulo C con nombre display7seg.c que use
la siguiente configuración de la electrónica: display de cátodo común, pines PB6
hasta PB0 en modo salida, drenador abierto, push/pull desactivado y velocidad de
actualización baja.
Una primera solución de novato sería:
/* *
@file display7seg - v1 . c
@brief Handles a 7 - segment display .
*/
void d i s p l a y 7 s e g _ I n i t ( void ){
G P I O _ I n i t T y p e D e f port ;
_ _ H A L _ R C C _ G P I O B _ C L K _ E N A B L E ();
44
3.8 Ejemplo: un display de 7 segmentos
uint8_t out ;
switch ( value ){
case 0:
out = 0 x3F ;
break ;
case 1:
out = 0 x06 ;
break ;
case 2:
out = 0 x5B ;
break ;
case 3:
out = 0 x4F ;
break ;
case 4:
out = 0 x66 ;
break ;
case 5:
out = 0 x6D ;
break ;
case 6:
out = 0 x7D ;
break ;
case 7:
out = 0 x07 ;
break ;
case 8:
out = 0 x7F ;
break ;
case 9:
out = 0 x6F ;
break ;
default :
out = 0 x79 ;
break ;
}
A P _ G P I O _ W r i t e O u t p u t D a t a ( GPIOB , out );
}
void d is pl a y7 se g _O ff ( void ) {
A P _ G P I O _ W r i t e O u t p u t D a t a ( GPIOB , 0 X00 );
}
45
Capítulo 3. Entrada/salida digital
uint8_t out ;
if ( value <= 9) {
out = d i s p l a y 7 s e g _ t a b n u m [ value ];
} else {
out = 0 x79 ; // symbol E
}
A P _ G P I O _ W r i t e O u t p u t D a t a ( GPIOB , out );
}
Obsérvese el uso del atributo const para mantener los datos en la memoria de
programa.
Aunque el programa se ha mejorado, estas soluciones modificas también las líneas del
puerto que no tienen relación con display. Esto es una chapuza en toda regla, así que
se deberían emplear opciones que modifiquen solo las líneas implicadas, para ello hay
que emplear máscaras y/o las funciones que permiten modificar bits individuales.
El siguiente listado corresponde a esta mejora.
static const uint8_t d i s p l a y 7 s e g _ t a b n u m [] = {0 x3F ,0 x06 ,0 x5B ,0 x4F ,0 x66 ,0 x6D ,0 x7D ,0 x07 ,0 x7F ,0 x6F };
uint8_t out ;
if ( value <= 9) {
out = d i s p l a y 7 s e g _ t a b n u m [ value ];
} else {
out = 0 x79 ; // symbol E
}
46
3.9 Teclados matriciales
# ifndef KEYBOARD_H
# define KEYBOARD_H
# endif
47
Capítulo 3. Entrada/salida digital
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* *
* @brief Hardware configuration
* @param none
* @returns none
*/
void keyboard_init ( void ) {
GPIO_InitTypeDef g;
R C C _ A H B 1 P e r i p h C l o c k C m d ( RCC_AHB1Periph_GPIOE , ENABLE );
G PI O_ S tr u ct In i t (& g );
/* rows config */
g . GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 ;
g . GPIO_Mode = GPIO_Mode_OUT ;
g . GPIO_OType = GPIO_OType_OD ;
g . GPIO_Speed = G P IO _S p ee d_ 2 MH z ;
g . GPIO_PuPd = GPIO_PuPd_NOPULL ;
/* cols config */
g . GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 ;
g . GPIO_Mode = GPIO_Mode_IN ;
g . GPIO_PuPd = GPIO_PuPd_UP ;
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* *
* @brief Scan keyboard
48
3.9 Teclados matriciales
* @retval x
*/
retardo (100);
data = G P I O _ R e a d I n p u t D a t a ( GPIOE );
data = ( data >> 8) & 0 xF ;
col = -1;
switch ( data ) {
case 0 xE : col = 0; break ;
case 0 xD : col = 1; break ;
case 0 xB : col = 2; break ;
case 0 x7 : col = 3; break ;
}
if ( col != -1) {
break ; // exit loop
}
}
if ( col == -1) {
return 0;
} else {
return key_table [ row ][ col ];
}
}
49
Capítulo 3. Entrada/salida digital
50
3.10 Multiplexado temporal con varios display de 7 segmentos
Esto es avanzado para este tema. Hay ejemplo de tarea asociada a una interrupción
y de acceso directo al hardware. Si alguien lo necesita, se la paso.
Cabecera propuesta:
/* *
@file displaymx . h
@brief Module header for handling a multiplexed 7 seg . diplay
*/
# ifndef DISPLAYMX_H
# define DISPLAYMX_H
51
Capítulo 3. Entrada/salida digital
52
3.10 Multiplexado temporal con varios display de 7 segmentos
# endif
Implementación:
/* *
@file displaymx . h
@brief Impl ementa tion for handling a multiplexed 7 seg . display
// 0 = refresh is inactive
volatile uint8_t d i s p l a y m x _ r e f r e s h _ a c t i v e = 0;
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/* *
* @brief Initializes the hardware
* @param none
* @retval none
*/
void d isplay mx_Ini t ( void ){
53
Capítulo 3. Entrada/salida digital
_ _ H A L _ R C C _ G P I O G _ C L K _ E N A B L E ();
port . Pin = GPIO_PIN_3 | GPIO_PIN_2 ;
port . Mode = G P I O _ M O D E _ O U T P U T _ P P ;
port . Pull = GPIO_NOPULL ;
port . Speed = G P I O _ S P E E D _ F R E Q _ L O W ;
HAL_GPIO_Init ( GPIOG , & port );
// display selector
_ _ H A L _ R C C _ G P I O C _ C L K _ E N A B L E ();
port . Pin = GPIO_PIN_13 | GPIO_PIN_12 | GPIO_PIN_11 ;
port . Mode = G P I O _ M O D E _ O U T P U T _ P P ;
port . Pull = GPIO_NOPULL ;
port . Speed = G P I O _ S P E E D _ F R E Q _ L O W ;
HAL_GPIO_Init ( GPIOC , & port );
// set values for setting bits for each display , COLLINS que mal m ’ explique
d i s p l a y m x _ s e l e c t o r _ P C 1 3 _ P C 1 1 [0]=0 x30000800 ;
d i s p l a y m x _ s e l e c t o r _ P C 1 3 _ P C 1 1 [1]=0 x28001000 ;
d i s p l a y m x _ s e l e c t o r _ P C 1 3 _ P C 1 1 [2]=0 x18002000 ;
// d i s p l a y m x _ d a t a _ P E 6 _ P E 2 [0] = 0 x0000007A ;
displaymx_Off ();
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* *
* @brief Activates the display refreshing
* @param none
* @retval none
*/
void displaymx_On ( void )
{
d i s p l a y m x _ r e f r e s h _ a c t i v e = 1;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* *
* @brief Deactivates display refreshing
* @param none
* @retval none
*/
void displaymx_Off ( void )
{
d i s p l a y m x _ r e f r e s h _ a c t i v e = 0;
H A L _ G P I O _ W r i t e P i n ( GPIOC , GPIO_PIN_13 | GPIO_PIN_12 | GPIO_PIN_11 , GPIO _PIN_R ESET );
// H A L _ G P I O _ W r i t e P i n ( GPIOC , GPIO_PIN_13 , GPIO_PIN_SET );
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* *
* @brief Puts an integer number in the display
* @param value to be represented from 0 to 9999
* @retval Nada
*/
void d i s p l a y m x _ S e t N u m b e r ( int16_t value )
{
// uint16_t data ;
uint8_t i ;
54
3.10 Multiplexado temporal con varios display de 7 segmentos
// clear segments
for ( i =0; i < NUM_DISPLAYS ; i ++) {
d i s p l a y m x _ S e t S e g m e n t s (i ,0); // delete all segments
}
// test range
if (( value <0) || ( value >999) ){
return ;
}
// decompose number
for ( i =0; i < NUM_DISPLAYS ; i ++) {
d i s p l a y m x _ S e t S e g m e n t s (i , n u m b e r _ t o _ s e g m e n t s _ t a b l e [ value % 10]);
value /= 10;
if ( value == 0) {
break ; // exit for
}
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/* *
* @brief Set the state of the 7 segments of a given display
* @param num_display elected display
* @param config segment configuration bit 7 to 0 mapped to segments g to a
* @returns none
*/
void d i s p l a y m x _ S e t S e g m e n t s ( uint8_t num_display , uint8_t config )
{
uint32_t tmp , mask ;
if ( num_display >= NUM_DISPLAYS ) {
return ;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/* *
* @brief Refresh the display
* @param none
* @returns none
*
* This function tries to be as efficient as posible . This is the reason for accessing GPIO direc
* and the relative complexity of the rest of the code
*/
void d i s p l a y m x _ R e f r e s h ( void ) {
55
Capítulo 3. Entrada/salida digital
/* End of file * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
56
Capítulo 4
Interrupciones
4.1 Introducción
Objetivos
Comprender qué es una interrupción.
Ser conscientes de las ventajas y peligros de las interrupciones.
Aprender a configurar el sistema de interrupciones.
Saber crear un servicio de interrupción.
En este capítulo se introduce el concepto de interrupción y su aplicación con los
microcontroladores ARM Cortex-M.
Este importante mecanismo es común a todos los procesadores, pero es en los mi-
crocontroladores donde destaca por su agilidad de funcionamiento, lo cual es funda-
mental en la mayoría de las aplicaciones embebidas. Por tanto, es necesario conocer
su funcionamiento y su aprovechamiento para poder sacar el máximo partido a un
microcontrolador.
Antes de describir el mecanismo de interrupción, es importante introducir el con-
cepto de "técnica de sincronización", que es la manera en que la CPU sabe si un
determinado dispositivo necesita ser atendido. Son dos:
La consulta por programa o “polling”, que consiste en leer continuamente los
registros de los dispositivos para conocer su estado y actuar en consecuencia.
La interrupción, que consiste en señales que informan a la CPU de que hay que
atender algo.
La figura 4.1 muestra el organigrama de una típica aplicación de un microcontrola-
dor. En el caso a), un bucle consulta continuamente el estado de todos los posibles
57
Capítulo 4. Interrupciones
eventos a tratar. La mayoría de las veces no será necesario hacer nada. En el caso
b), los eventos se tratan sólo cuando se producen, haciéndose el tratamiento con
subprogramas independientes. Cuando se produzca el evento, se detendrá momen-
táneamente la tarea cíclica en curso y se atenderá al subprograma correspondiente.
Figura 4.1: Ejemplo de aplicación realizada con técnica de polling (a) y de interrupciones (b).
58
4.2 Funcionamiento general y jerga del sistema de interrupciones
59
Capítulo 4. Interrupciones
60
4.3 Interrupciones en los ARM Cortex-M
61
Capítulo 4. Interrupciones
Las interrupciones con número más bajo tendrán prioridad, lo que significa que si
hay dos peticiones simultáneas, se atenderá la que tenga el menor número y, cuando
esta termine, se podrá atender a la otra. El sistema de interrupciones es preemptivo,
es decir, si durante la ejecución de una interrupción llega otra de mayor prioridad,
entonces la interrupción de baja prioridad es interrumpida a su vez para poder
atender a la de mayor prioridad.
Con el fin de tener flexibilidad para reorganizar las prioridades de las interrupciones,
los ARM Cortex-M incorporan mecanismos de prioridad por grupos. Cada interrup-
ción se puede asociar a un grupo con lo que se puede lograr que interrupciones con
números altos puedan tener más prioridad que interrupciones con números más ba-
jos. Por ejemplo, el microcontrolador STM32F4xx permite 32 grupos de prioridad.
En ARM Cortex-M, una rutina de servicio no es más que una función C normal
y, con el fin de poner un poco de orden, el estándar CMSIS propone unas reglas
para la elección de los nombres de estas funciones. Se puede echar un vistazo al
archivo estándar en ensamblador startup_XXXXX.s (startup_stm32f4xx.s para los
STM32F4xx) para ver estos nombre, mostrándose a continuación un fragmento del
listado:
EXPORT WW D G_ IR Q Ha nd l er [ WEAK ]
EXPORT PV D_IRQH andler [ WEAK ]
EXPORT TAMP_STAMP_IRQHandler [ WEAK ]
EXPORT RTC_WKUP_IRQHandler [ WEAK ]
EXPORT FLASH_IRQHandler [ WEAK ]
EXPORT RC C_IRQH andler [ WEAK ]
EXPORT EXTI0_IRQHandler [ WEAK ]
...
Para escribir un manejador asociado a una interrupción, bastará con escribir una
función con el nombre idéntico al del listado. Esa función pasará a ser, automáti-
camente, el manejador de dicha interrupción. Como las aplicaciones que hacen uso
intensivo de interrupciones son complejas, es práctica habitual tener muy localizadas
las rutinas de servicio. Siguiendo las buenas prácticas de CMSIS, el archivo sugeri-
do para ello se llama XXXX_it.c (stm32f4xx_it.c para los STM32F4xx). Véase a
continuación un fragmento de este archivo:
/* *
* @brief This function handles NMI exception .
* @param None
* @retval None
*/
void NMI_Handler ( void )
{
}
/* *
* @brief This function handles Hard Fault exception .
* @param None
* @retval None
*/
void H a r d F a u l t _ H a n d l e r ( void )
{
/* Go to infinite loop when Hard Fault exception occurs */
62
4.3 Interrupciones en los ARM Cortex-M
while (1)
{
}
}
/* *
* @brief This function handles Memory Manage exception .
* @param None
* @retval None
*/
void M e m M a n a g e _ H a n d l e r ( void )
{
/* Go to infinite loop when Memory Manage exception occurs */
while (1)
{
}
}
/* *
* @brief This function handles Bus Fault exception .
* @param None
* @retval None
*/
void B u s F a u l t _ H a n d l e r ( void )
{
/* Go to infinite loop when Bus Fault exception occurs */
while (1)
{
}
}
Y usar el siguiente programa principal que causa la excepción y, por tanto, hará
lucir el LED y dejar al microcontrolador enganchado. La razón del fallo se debe a
que el programa principal termina, lo que no es razonable en un microcontrolador.
Si se descomenta el bucle while(), el fallo no se debería producir.
63
Capítulo 4. Interrupciones
Sets the priority grouping field (pre-emption priority and subpriority) using the
required unlock sequence.
void H A L _ N V I C _ S y s t e m R e s e t ( void )
return 0;
}
64
4.4 El periférico EXTI y las interrupciones
uint32_t H A L _ N V I C _ G e t P r i o r i t y G r o u p i n g ( void )
Gets the priority grouping field from the NVIC Interrupt Controller.
void H A L _ N V I C _ G e t P r i o r i t y ( IRQn_Type IRQn , uint32_t PriorityGroup ,
uint32_t * pPreemptPriority , uint32_t * pSubPriority )
Gets the priority of an interrupt.
void H A L _ N V I C _ S e t P e n d i n g I R Q ( IRQn_Type IRQn )
Sets Pending bit of an external interrupt.
uint32_t H A L _ N V I C _ G e t P e n d i n g I R Q ( IRQn_Type IRQn )
Gets Pending Interrupt (reads the pending register in the NVIC and returns the pending bit for
void H A L _ N V I C _ C l e a r P e n d i n g I R Q ( IRQn_Type IRQn )
Clears the pending bit of an external interrupt.
uint32_t H A L _ N V I C _ G e t A c t i v e ( IRQn_Type IRQn )
Gets active interrupt ( reads the active register in NVIC and returns the active bit).
4.4.1 Funcionalidad
El EXTI consiste en una serie de detectores de flancos capaces de generar eventos o
interrupciones. Estos detectores están conectados a pines de los puertos o a elementos
como la alarma del reloj de tiempo real, el evento de despertar del puerto Ethernet,
etc. En [7] se describe ampliamente este elemento.
EXTI se puede configurar para que genere evento/interrupción cuando detecta flanco
de subida, de bajada o ambos. Para su programación, las bibliotecas HAL ocultan
en parte su configuración asociándola a la programación del dispositivo objetivo.
Por ejemplo, para usarlo asociado a la GPIO, determinadas combinaciones de con-
figuración de los pines harán que el HAL configure automáticamente este periférico.
Debido a la grana cantidad de pines de GPIO (más de 160 en algunos encapsulados),
cada bit de un puerto está asociado a un EXTI particular (o línea). Por ejemplo,
todos los Px0 están asociados a EXTI0, los Px1 a EXTI1 y así sucesivamente.
65
Capítulo 4. Interrupciones
66
4.4 El periférico EXTI y las interrupciones
/* SUPER - LOOP */
while (1)
{
// forever tasks !
}
}
void E X T I 0 _ I R Q H a n d l e r ( void )
{
if ( _ _ H A L _ G P I O _ E X T I _ G E T _ I T ( GPIO_PIN_O ) != RESET ) {
_ _ H A L _ G P I O _ E X T I _ C L E A R _ I T ( GPIO_PIN_O );
// here your task , for example LED_On ();
}
}
67
Capítulo 4. Interrupciones
Tabla 4.3: Macros y funciones del HAL para EXTI y GPIO (posiblemente internas).
_ _ H A L _ G P I O _ E X T I _ G E T _ I T ( __EXTI_LINE__ )
Comprueba si en determinado flag de petición está establecido o no. Se le pasa un pin.
_ _ H A L _ G P I O _ E X T I _ C L E A R _ I T ( __EXTI_LINE__ )
Límpia un determinado flag de petición de interrupción. Se le pasa un pin..
void H A L _ G P I O _ E X T I _ I R Q H a n d l e r ( uint16_t GPIO_Pin );
Manejador predefinido del HAL de St. Se le deba pasar el pin que genera la interrupción.
weak void H A L _ G P I O _ E X T I _ C a l l b a c k ( uint16_t GPIO_Pin );
Función de callback predefinida. El usuario la reescribe.
// ...
void E X T I 0 _ I R Q H a n d l e r ( void )
{
static uint8_t estado = 0;
if ( _ _ H A L _ G P I O _ E X T I _ G E T _ I T ( GPIO_PIN_O ) != RESET ) {
_ _ H A L _ G P I O _ E X T I _ C L E A R _ I T ( GPIO_PIN_O );
if ( estado == 0) {
estado = 1;
LED_On ();
} else {
estado = 0;
LED_Off ();
}
}
}
68
4.4 El periférico EXTI y las interrupciones
void E X T I 0 _ I R Q H a n d l e r ( void )
{
if ( _ _ H A L _ G P I O _ E X T I _ G E T _ I T ( GPIO_PIN_O ) != RESET ) {
_ _ H A L _ G P I O _ E X T I _ C L E A R _ I T ( GPIO_PIN_O );
}
}
uint8_t b u t t o n _ G e t P r e s s e d ( void )
{
return _button_state ;
}
69
Capítulo 4. Interrupciones
{
// ...
while (1)
{
// check if the button was pressed in the past
if ( b u t t o n _ G e t P r e s s e d ()) {
printf ( " Se ␣ ha ␣ pulsado ␣ el ␣ botoncito \ n " );
button_Reset ();
}
void E X T I 0 _ I R Q H a n d l e r ( void )
{
// St ’s HAL proposes to call this predefined function
H A L _ G P I O _ E X T I _ I R Q H a n d l e r ( GPIO_PIN_0 );
}
70
4.4 El periférico EXTI y las interrupciones
/* *
* @brief EXTI line detection callbacks .
* @param GPIO_Pin : Specifies the pins connected EXTI line
* @retval None
*/
__weak void H A L _ G P I O _ E X T I _ C a l l b a c k ( uint16_t GPIO_Pin )
{
/* NOTE : This function Should not be modified , when the callback is needed ,
the H A L _ G P I O _ E X T I _ C a l l b a c k could be implemented in the user file
*/
}
Al final del listado se observa una función definida con __weak. Esta es otra ayuda
del HAL que pretende que la tarea a realizar se escriba con una función con dicho
nombre. Ahora queda rellenar la función de callback con la tarea encomendada.
Dicha función hay que escribirla en otra parte, no hay que tocar el HAL.
Por ejemplo, para el parpadeo del LED:
# include < led .h >
// ...
71
Capítulo 4. Interrupciones
72
Capítulo 5
Contadores y temporizadores
5.1 Introducción
La mayor parte de las aplicaciones para microcontrolador necesitan contar eventos o
generar retardos de gran precisión. Por software es posible realizar retardos de cierta
precisión y contar eventos, pero la mayor parte del potencial de la CPU se invertiría
en éste cometido, y no dejaría tiempo para realizar otras acciones o complicando el
diseño de la aplicación.
Los contadores/temporizadores o, en la jerga, “timers”, son periféricos hardware
que suplen este defecto, descargando de un trabajo poco grato al micro. Es tal la
importancia de este tipo de dispositivos que los microcontroladores más avanzados
incluyen decenas de ellos o, incluso, un coprocesador dedicado a gestionarlos.
Estos dispositivos permiten, entre otras cosas, medir anchos de pulso de señales,
generar señales digitales, contar impulsos, provocar acciones periódicas, implementar
relojes de tiempo real, generar el ritmo para comunicaciones, comparación/captura,
generación PWM (modulación por ancho de pulso) para control digital directo, etc.
Esta capítulo pretende ilustrar el uso de los timers desde el punto de vista de sus
posibles aplicaciones más que una exposición de características de un periférico da-
do. Como en capítulos previos, es indispensable utilizar referencias externas para
complementar la información sobre estos dispositivos.
Bla, bla ... (estructura)
73
Capítulo 5. Contadores y temporizadores
El bloque de generación es el origen de las señales digitales que han de ser con-
tabilizadas y que, en general, serán flancos que deberán seguir ciertas restricciones
(duración mínima a un determinado nivel, etc.). Si la señal a contabilizar procede
de un reloj/oscilador, entonces la cuenta se modifica a un ritmo conocido y se habla
de “temporizador”, siendo su propósito el de medir tiempo. Si las señales son espo-
rádicas y sin patrón temporal predefinido, entonces se habla de “contador”, pues el
propósito será contabilizar cosas.
La llegada de eventos al registro de cuenta puede habilitarse/deshabilitarse de muy
distintas formas, siendo opciones habituales el control por software o mediante se-
ñales externas. A esta parte se la denomina “control de puerta”.
Finalmente se tiene el contador propiamente dicho, cuyo valor variará en función de
los eventos que lleguen (por ejemplo, que se incrementándose o decrementándose).
Además, suelen incluirse características adicionales para comparar el valor de la
cuenta con un patrón (comparación/captura) y para realizar recargas automáticas
de la cuenta.
Por supuesto, un timer será capaz de generar señales al subsistema de interrupciones.
Para esta característica suelen ser casos habituales el desborde de un timer, el cambio
en la señal de puerta, la coincidencia con un patrón, etc.
74
5.3 SysTick, el contador común a los Cortex-M
Este periférico usa un contador descendente (resta 1 por evento) con un registro
de 24 bits contiene la cuenta actual. Cuando la cuenta está en 0 y llega un nuevo
evento, el registro de cuenta se recarga con un valor de “precarga” establecido por
programa y seguirá descontando a partir de ese valor. La figura 5.2 lo representa.
El ritmo de descuento se deriva del reloj del sistema. Por ejemplo, si se usa directa-
mente el reloj del sistema y este es de 180 MHz, el tiempo de cada tick sería:
1 1
T = =
F 180 × 106
Por tanto, para medir un lapso L de 1 ms, se tendría que usar una recarga de:
L 1 × 10−3
recarga = = 1
T 180×106
Y escrito de manera más amigable para la aritmética entera del computador sería:
180 × 106
recarga =
1 × 103
Actividad:
Actividad:
75
Capítulo 5. Contadores y temporizadores
y cuyo contenido es la frecuencia de reloj del core en Hz. Por ejemplo, si se desea
un ritmo de tick de 1 ms, bastaría con aplicar el cálculo de la siguiente manera:
L 1 × 10−3
recarga = = 1
T SystemCoreClock
SystemCoreClock
recarga =
1 × 103
L 5 × 10−3
recarga = = 1
T SystemCoreClock
SystemCoreClock
recarga =
200
H A L _ S Y S T I C K _ C o n f i g ( S ys te m Co r eC lo c k /200);
76
5.3 SysTick, el contador común a los Cortex-M
void S ys Ti c k_ Ha n dl er ( void )
{
TicksCount ++;
}
// ...
uint32_t toma_tiempo ;
toma_tiempo = TicksCount ;
// hacer un monton de cosas
toma_tiempo = TicksCount - toma_tiempo ;
# endif
77
Capítulo 5. Contadores y temporizadores
uint32_t ticks_end ;
void S ys Ti c k_ Ha n dl e r ( void )
{
TicksCount ++;
if (( TicksCount % 20)==0) {
// task every 20 ticks
}
}
void S ys Ti c k_ Ha n dl e r ( void )
{
TicksCount ++;
if (( TicksCount % 20)==0) {
78
5.3 SysTick, el contador común a los Cortex-M
t a s k _ p e p e _ a c t i v a t e d = 1;
}
}
while (1)
{
// tasks polling
if ( t a s k _ p e p e _ a c t i v a t e d )
{
// do task related work
t a s k _ p e p e _ a c t i v a t e d = 0;
}
}
Figura 5.3: Mapa de registros y valores de reset de SysTick. Dirección base E000E010h
Este periférico usa un contador descendente (resta 1 por evento) de 24 bits al que se
puede acceder a través del registro STK_VAL, que contendrá la cuenta actual. Cuando
la cuenta está en 0 y llega un nuevo evento, el registro de cuenta se recarga con el
valor del registro STK_LOAD y sigue descontando a partir de ese valor.
Mediante ciertos bits del registro STK_CTRL se puede controlar el funcionamiento y
saber el estado del contador. Por ejemplo, la puesta a 1 del bit ENABLE hace que se
recargue la cuenta y se inicie el descuento. Cuando la cuenta llegue a 0, se pondrá a
79
Capítulo 5. Contadores y temporizadores
Se puede hacer a pelo, pero es más fácil apoyarse en las bibliotecas CMSIS.
80
Capítulo 6
81
Capítulo 6. Programación en C para ARM Cortex-M
6.1 Introducción
El lenguaje C es, con diferencia el más extendido en el desarrollo para sistemas
embebidos basados en microcontrolador y, por tanto, la elección más adecuada. En
cualquier caso, el lenguaje C está repleto de problemáticas que incidirán negativa-
mente en el desarrollo si no se toman una serie de precauciones.
El objetivo de este capítulo es presentar algunas reglas, consejos y particularidades
que permiten usar C adecuadamente en un microcontrolador con garantías de éxito.
Téngase en consideración que un microcontrolador dispone de pocos recursos de
memoria en comparación con los computadores de propósito general, y que el tipo de
aplicaciones destino es bien distinto por lo que se deberá tener un cuidado exquisito
en gestionar adecuadamente los recursos.
Se parte del que el lector tiene conocimientos de C, revisándose solo ciertos aspec-
tos fundamentales y, después, yendo directamente a las aspectos concretos sobre
desarrollo embebido.
(Al terminar este capítulo, faltaría redactar intro con resumen y objetivos). (¿Hablar
sobre C++?)
82
6.3 Del código al ejecutable
83
Capítulo 6. Programación en C para ARM Cortex-M
84
6.4 Tratando con datos
85
Capítulo 6. Programación en C para ARM Cortex-M
las asignaciones.
86
6.4 Tratando con datos
Tabla 6.1: Los tipos de datos habituales para operar en coma flotante
float a ;
a = 3.0;
a = a + 2.7;
Para que el código esté bien adaptado a los cálculos con "float", de deberán emplear
calificadores de tipo. Por ejemplo, la corrección al anterior listado quedaría,
float a ;
a = 3.0 F ;
a = a + 2.7 F ;
float ep , ed , ei ;
float respuesta , tm ;
87
Capítulo 6. Programación en C para ARM Cortex-M
main ()
{
tm =50.3 F ;
respuesta = kp * ep + kd * ed * ep + ki *( ei +( ep + tm ));
Lo que se ha hecho es crear una lista de palabras para representar distintos estados
de un concepto. En el ejemplo anterior se va más allá creando un nuevo tipo de
datos que hará también más fiable la etapa de comprobación del código a la hora
de compilar.
A partir de la creación del nuevo tipo, se puede usar como se muestra a continuación:
TValveState state ;
state = VALVE_OPENED ;
...
if ( state == VALVE_CLOSED ) {
printf ( " La ␣ valvula ␣ esta ␣ cerrada .\ n " );
}
88
6.4 Tratando con datos
Al compilar este fragmento se creará una constante con el valor 3597 en la zona
de memoria de constantes y se generará código máquina en la zona de programa
para copiar el valor constante a la variable real durante la ejecución. Todo esto se
traducirá en espacio ocupado en la zona de constantes/programas, espacio en la zona
de variables y tiempo para copiar el valor a la variable.
Cuando una variable o unos datos son constantes, es decir, no van a variar en el
ciclo de vida del programa, puede ser aconsejable forzar la colocación, tanto de la
variable como su valor, en la zona de constantes. Con este planteamiento se puede
ahorrar mucho espacio en la memoria de programa/constantes.
En C, la manera de lograr que una variable no se copie al espacio de datos es usar
el atributo const. El siguiente fragmento ilustra la idea para el caso anterior:
const int32_t ratio = 3597;
89
Capítulo 6. Programación en C para ARM Cortex-M
Obsérvese que la variable i que es utilizada para la cuenta. Esa misma observación
la hará el copilador y, con mucha probabilidad, tomará la decisión de hacer un solo
acceso a memoria de datos para acceder a la variable i y guardársela en un registro
de la CPU durante la ejecución de todo el bucle. El efecto será que la aplicación,
literalmente, "volará"; y el efecto colateral es que la variable i de la memoria no se
habrá tocado hasta que termine el bucle.
En la mayoría de casos, estas optimizaciones son adecuadas, pero hay situaciones en
las que es adecuado forzar a que el acceso se haga a la verdadera variable.
La misión del atributo volatile es forzar (intentar) a que los accesos sean explícitos
a la variable. Aplicado al fragmento anterior, quedaría:
volatile uint32_t i ;
for ( i =0; i <1000000; i ++) {};
Este bucle se ejecutará ahora mucho más lentamente, pero se estará accediendo
siempre a la variable.
Uno de los intereses para utilizar volatile es cuando distintas partes de la aplica-
ción pueden acceder concurrentemente a una misma variable. Esto es típico de las
interrupciones y de las aplicaciones multitarea.
En la arquitectura ARM, otro caso es el acceso a los periféricos, cuyos registros
son ofrecidos como direcciones de memoria. Si se aplicase la optimización, entonces
podría ocurrir que no se leyesen/escribiesen realmente los registros. Con volatile
se evita el problema.
Es tal la importancia de esto, que el estándar CMSIS usa la recomendación MISRA-
C de incluir las siguientes definiciones para simplificar su uso.
Calificador E/S MISRA-C tipo ANSI C Acceso
#define __I volatile const solo lectura
#define __O volatile solo escritura
#define __IO volatile lectura/escritura
En este libro se utilizarán las construcciones ANSI C, pues introducir particula-
ridades del ámbito de automoción o aerosespacial complicarían la compresión del
texto.
(L’any que ve, ficar ejemple d’acces a periféric a pel.).
90
6.5 Bibliotecas
6.5 Bibliotecas
(Més endavant, canviar Third_Party per third_party o ThirdParty).
En sentido amplio, las bibliotecas son código fuente o compilados de terceros que
se añaden a las aplicaciones en la etapa de enlazado. Las bibliotecas proporcionan
funcionalidades adicionales a las aplicaciones sin necesidad de desarrollarlas desde
cero.
El principal interés de las bibliotecas es lograr productividad aprovechando el trabajo
de otros en forma de paquetes comerciales o código libre/abierto/etc. La idea es no
inventar la rueda si esta ya ha sido inventada por otro. Las bibliotecas también las
podemos desarrollar nosotros y reutilizar en otros proyectos.
Algunas bibliotecas se las considera estándar y suelen venir ya incorporadas en
las distribuciones de herramientas de desarrollo. Por ejemplo, en C, las funciones
printf(), sin(), rnd(), etc. suelen estar disponibles por defecto en una biblioteca
preinstalada. Es un error habitual considerar a estas funciones de biblioteca estándar
como parte del lenguaje C.
En el desarrollo de sistemas embebidos, una de las tareas tediosas es lograr que las
bibliotecas externas funcionen adecuadamente con la aplicación a desarrollar. En
muchos casos, el esfuerzo será grande por depender del estilo del desarrollador, de
las documentación disponible, de las dependencias, etc.
Al crecer la aplicación, también crecerá el número de bibliotecas externas de las que
dependa. Para poder tener acotado este problema se debe proceder a organizarlas
adecuadamente. Por ejemplo, es habitual que las bibliotecas se organicen en un
subdirectorio específico en el que cada biblioteca se almacenará en un subdirectorio
propio. El nombre de ese directorio suele ser Third_Party o similar.
Siguiendo este principio, la figura 6.7 muestra la organización propuesta para traba-
jar los contenidos del libro. En la subcarpeta Third_Party se colocarán las bibliote-
91
Capítulo 6. Programación en C para ARM Cortex-M
# ifndef RETARDO_H
# define RETARDO_H
# endif
92
6.5 Bibliotecas
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* *
* @brief Basic delay function
* @param cuenta time to delay " a ojo "
* @return none
*/
void retardo ( uint32_t cuenta )
{
volatile uint32_t cnt ;
cnt = cuenta ;
Para que las aplicaciones desarrolladas tengan acceso a esta biblioteca básica, serán
necesarias dos cosas:
Una vez realizados los pasos anteriores, se podrá hacer uso de la biblioteca. Para
ello, bastará con incluir la cabecera en el módulo que haga uso de la función como
muestra el siguiente ejemplo.
93
Capítulo 6. Programación en C para ARM Cortex-M
return 0;
}
Desarrollar un módulo con nombre maxmin que contenga dos funciones: una
con nombre max que devuelva el máximo de dos parámetros de tipo float,
y una con nombre min que devuelva el mínimo de dos parámetros float.
Sigue estos pasos:
Actividad: Crea la cabecera apropiada.
Realiza la implementación en el módulo apropiado.
Desarrolla una aplicación que demuestre que funciona.
Comprueba el funcionamiento en tu entorno de programación favorito
(Qt, BCB, DevC++, ...).
94
6.6 El Cortex Microcontroller Software Interface Standard (CMSIS)
Entre otras cosas, CMSIS propone una arquitectura a distintos niveles para simpli-
ficar la manera de resolver distintos problemas. En la figura 6.10 se representa esta
arquitectura.
El bloque CMSIS-core provee las funcionalidades para arranque del sistema (reloj,
...) hasta llegar al main(), acceso características específicas del núcleo y periféricos
básicos, una visión consistente de los registros de periféricos y servicios de interrup-
ción, etc.
El bloque CMSIS-RTOS provee una abstracción de las primitivas de los Real-time
operating systems (RTOS) para microcontroladores. Intenta facilitar la migración
entre distintos proveedores de microkernels.
El bloque CMSIS-DSP pretende proveer de funciones típicas de DSP de manera
sencilla para que sea fácil explotar este potencial sin necesidad de ser un experto.
95
Capítulo 6. Programación en C para ARM Cortex-M
96
6.7 El firmware STM32Cube HAL de St
Figura 6.11: Estructura del paquete de firmware para los microcontroladores de la serie STM32F4
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/* *
* @brief Preparing pin corresponding to LED green ( PG13 )
* @return none
*/
void LED_Init ( void ) {
97
Capítulo 6. Programación en C para ARM Cortex-M
Tabla 6.2: Archivos mínimos de un proyecto completo para STM32 que siga CMSIS.
Archivo Descripción
startup_stm32f4xx.s STM32F4xx devices startup file. Es un
archivo en ensamblador y punto de
arranque del microcontrolador. En C,
antes de llegar al main(), se pasa por
aquí.
system_stm32f4xx.h/.c CMSIS Cortex-M4F STM32F4 devices
peripheral access layer system. Son las
funciones del core de ARM común a to-
dos los fabricantes adheridos.
stm32f4x_???.h /.c Cabecera y código del driver de
un dispositivo ???. Por ejemplo,
stm32f4x_spi.h es la cabecera para el
driver del SPI.
stm32f4xx_conf.h Peripheral’s drivers configuration file.
Configura que cabeceras de periféricos
se añaden. No lo toques al principio.
stm32f4xx.h CMSIS Cortex-Mx STMFx device pe-
ripheral access. Incluyendo esta cabece-
ra en nuestros módulos, debería ser su-
ficiente para acceder a los periféricos.
stm32f4xx_it.h/.c Cabecera y plantillas para todos los
servicios de interrupción. Basta añadir
nuestro código de servicio dentro.
98
6.7 El firmware STM32Cube HAL de St
99
Capítulo 6. Programación en C para ARM Cortex-M
Destacar ahora que no sería factible calcular un valor con parte fraccionaria como
seno(3,45). Para resolverlo, bastaría con aplicar una interpolación.
La aplicación de esta técnica es muy amplia. Véase un ejemplo más cercano imagi-
nando que se desea implementar un decodificador digotal 3x8 cuya tabla de verdad
se muestra a continuación.
decimal C B A b7 b6 b5 b4 b3 b2 b1 b0 hexadecimal
0 0 0 0 0 0 0 0 0 0 0 1 01h
1 0 0 1 0 0 0 0 0 0 1 0 02h
2 0 1 0 0 0 0 0 0 1 0 0 04h
3 0 1 1 0 0 0 0 1 0 0 0 08h
4 1 0 0 0 0 0 1 0 0 0 0 10h
5 1 0 1 0 0 1 0 0 0 0 0 20h
6 1 1 0 0 1 0 0 0 0 0 0 40h
7 1 1 1 1 0 0 0 0 0 0 0 80h
100
6.9 Tratamiento bit a bit. Máscaras
101
Capítulo 6. Programación en C para ARM Cortex-M
return 0;
}
102
6.9 Tratamiento bit a bit. Máscaras
Número: 1010101011110101010111110101010
A grupos de 4: 101 0101 0111 1010 1010 1111 1010 1010
Equivalente hexadecimal: 5 5 7 A A F A A
AND OR X-OR
0 &0= 0 0 | 0= 0 0 ∧0= 0
0 &1= 0 0 | 1= 1 0 ∧1= 1
1 &0= 0 1 | 0= 1 1 ∧0= 1
1 &1= 1 1 | 1= 1 1 ∧1= 0
103
Capítulo 6. Programación en C para ARM Cortex-M
6.9.4 Máscaras
Conociendo los operadores anteriores y aplicando simples reglas repetitivas es fácil
manipular adecuadamente los números enteros a nivel de bit. Se dan a continuación
las recetas para ello.
Extraer bits
Dada una palabra binaria, una operación muy habitual es extraer unos bits de
interés y eliminar el resto (ponerlos a 0). Para lograrlo, se empleará el operador
AND aprovechándose de que b AND 0=0 y b AND 1=b.
Ejemplo: Se desean extraer los 3 bits menos significativos de una palabra binaria.
Para hacerlo, se empleará el operador AND con el valor binario 0..,01112 .
bN ... b5 b4 b3 b2 b1 b0
& 0 ... 0 0 0 1 1 1
0 ... 0 0 0 b2 b1 b0
Expresado en C sería,
resultado = dato & 0 x7 ;
Suele ser interesante combinar las extracciones de bits con las operaciones de des-
plazamiento.
Ejemplo: En los bits 5 al 2 de un número entero está contenido un dato de 4 bits.
Para extraerlo, se podrá aplicar la máscara binaria 0..,01111002 y, a continuación,
hacer un desplazamiento de 2 posiciones.
bN ... b5 b4 b3 b2 b1 b0
& 0 ... 1 1 1 1 0 0
0 ... b4 b4 b3 b2 0 0
>> 2 0 ... b5 b4 b3 b2 0 0
0 ... 0 0 b5 b4 b3 b2
Y, expresado en C sería,
resultado = ( dato & 0 x36 ) >> 2;
104
6.9 Tratamiento bit a bit. Máscaras
bN ... b5 b4 b3 b2 b1 b0
& 0 ... 0 0 0 1 1 1
0 ... 0 0 0 b2 0 0
O, también,
if (( dato & 0 x4 ) != 0) {
printf ( " Vale ␣ 1\ n " );
} else {
printf ( " Vale ␣ 0\ n " );
}
Poner bits a 0
Otra necesidad típica es la de poner determinados bits a 0. Para lograrlo, se empleará
el operador AND aprovechándose de que b AND 0=0 y b AND 1=b.
Ejemplo: Se desea poner a 0 los bits 4, 3 y 2 de una palabra binaria de 16 bits. Para
hacerlo se empleará el operador AND con el valor binario 1..,1000112 .
b15 ... b5 b4 b3 b2 b1 b0
& 1 ... 1 0 0 0 1 1
b15 ... b5 0 0 0 b1 b0
Expresado en C sería,
105
Capítulo 6. Programación en C para ARM Cortex-M
Poner bits a 1
También suele ser necesario poner determinados bits a 1. Para lograrlo, se empleará
el operador OR aprovechándose de que b OR 0=b y b OR 1=1.
Ejemplo: Se desea poner a 1 los bits 5, 2 y 1 de una palabra binaria. Para hacerlo
se empleará el operador OR y con el valor binario 0..,01001102 .
bN ... b5 b4 b3 b2 b1 b0
| 0 ... 1 0 0 1 1 0
bN ... 1 b4 b3 1 1 b0
Expresado en C sería,
resultado = dato | 0 x26 ;
Complementar bits
Otra operación de máscara muy habitual es la que permite complementar bits indi-
viduales. Para lograrlo, se emplea el operador X-OR aprovechándose de que b X-OR
0=b y b X-OR 1=/b.
Ejemplo: Se desean complementar los bits 3, 1 y 0 de una palabra binaria. Para
hacerlo, se empleará el operador X-OR con el valor binario 0..,0010112 .
bN ... b5 b4 b3 b2 b1 b0
∧ 0 ... 0 0 1 0 1 1
bN ... b5 b4 b¯3 b2 b¯1 b¯0
Expresado en C sería,
resultado = dato ^ 0 xB ;
Componer/descomponer palabras
106
Capítulo 7
Entorno de trabajo
107
Capítulo 7. Entorno de trabajo
7.1 Introducción
Este capítulo contiene las instrucciones básicas para poner a punto el puesto de
trabajo con el que desarrollar las actividades del libro.
Cualquiera pueda hacerse con todos los componentes y tener su propio puesto de
trabajo. Solo necesitará invertir en la placa de desarrollo.
108
7.2 Ordenador personal
109
Capítulo 7. Entorno de trabajo
110
7.3 Placa de evaluación STM32F429ZI Discovery kit
111
Capítulo 7. Entorno de trabajo
112
7.4 Sistema de depuración St-Link
Figura 7.2: Izquierda: sonda St link. Derecha: sonda st-link embebida en la Discovery.
113
Capítulo 7. Entorno de trabajo
114
7.4 Sistema de depuración St-Link
115
Capítulo 7. Entorno de trabajo
116
7.5 Keil MDK-ARM 5
117
Capítulo 7. Entorno de trabajo
118
7.5 Keil MDK-ARM 5
construir, pues solo trata los archivos que han sido modificados o cuyas dependencias
han variado, haciendo más rápida la construcción. Finalmente, la opción compilar
realiza la compilación del módulo que se esté editando en ese momento y, por tanto,
es útil para comprobar que no contiene errores de compilación.
119
Capítulo 7. Entorno de trabajo
http://armcortexm.blogs.upv.es/stm32f4-discovery-and-printf-redirection-to-debug-viewe
Gracias al depurador St-link, es posible hacer cosas más interesantes como controlar
la ejecución, establecer puntos de ruptura, hacer ejecución paso a paso, monitori-
zar el valor de variables, etc. Para iniciar el depurador se puede emplear el icono
correspondiente de la figura 7.12.
En la figura 7.13 se puede ver el aspecto por defecto de Keil en modo depuración.
120
7.5 Keil MDK-ARM 5
121
Capítulo 7. Entorno de trabajo
122
7.5 Keil MDK-ARM 5
123
Capítulo 7. Entorno de trabajo
124
7.6 STM32CubeMX: STM32Cube initialization code generator
125
Capítulo 7. Entorno de trabajo
126
7.7 Paquete de bibliotecas STM32CubeF4
127
Capítulo 7. Entorno de trabajo
128
7.8 Plantilla para la placa St Discovery 429i-Disc1
La plantilla se proporciona en un zip y, para usarla, se deben seguir antes los si-
guientes apartados.
129
Capítulo 7. Entorno de trabajo
Figura 7.19: Editando el archivo de lotes para crear la unidad virtual O:.
130
7.8 Plantilla para la placa St Discovery 429i-Disc1
Hacer doble click con el ratón sobre el archivo (se ejecutará) y comprobar en el
explorador de windows que aparece la unidad O:. El contenido debe ser similar
a la figura 7.20.
Rearrancar Windows para comprobar que se lanza correctamente la aplicación.
131
Capítulo 7. Entorno de trabajo
132
Capítulo 8
Prácticas
133
Capítulo 8. Prácticas
8.1.1 Objetivos
Instalar el software y controladores para St-link.
Volcar un ejecutable en la placa Discovery.
Ejecutar proyectos en la placa Discovery.
134
8.1 Práctica: Instalación de St-Link y volcado de ejecutable en la placa Discovery
135
Capítulo 8. Prácticas
136
8.2 Práctica: Instalación y prueba de Keil MDK-ARM
8.2.1 Objetivos
Instalar Keil MDK-ARM 5.
Construir proyectos para el microcontrolador.
Volcar proyectos en el microcontrolador utilizando una sonda.
137
Capítulo 8. Prácticas
138
8.3 Práctica: Plantilla para las HAL STM32F4Cube
8.3.1 Objetivos
Instalar las bibliotecas HAL.
Poner a punto un proyecto a partir de una plantilla.
Instalación de STM32CubeMX
La aplicación STM32CubeMX no es un requisito imprescindible, pero facilitará el
trabajo futuro de actualización de las HAL.
Siguiendo escrupulosamente la sección 7.6, instala/actualiza STM32CubeMX. Hay
una opción para actualizar, pero deberás también seguir escrupulosamente los pasos
que te indica.
139
Capítulo 8. Prácticas
Crear la unidad O:
Esto es un poco enrevesado, pero la culpa la tiene el entorno de trabajo que usamos.
Siguiendo la sección 7.8, crea la unidad O:. Comprueba que funciona correctamente
abriéndola desde el explorador.
Preparar la plantilla
Para facilitar el trabajo de la asignatura, se ha preparado una plantilla preconfigu-
rada. Sigue los siguientes pasos para preparar un proyecto a partir de ella:
140
8.3 Práctica: Plantilla para las HAL STM32F4Cube
Construir la plantilla
Con el entorno de desarrollo Keil, abrir el archivo Project.uvprojx que está en el
raíz de la carpeta del proyecto. Probar a construir y a volcar en la placa.
// ...
# include " s t m 3 2 f 4 2 9 i _ d i s c o v e r y . h "
// ...
/* Configure LED3 */
BSP_LED_Init ( LED3 );
while (1)
{
BSP_ LED_To ggle ( LED3 );
HAL_Delay (1000);
}
141
Capítulo 8. Prácticas
142
8.4 Práctica: Uso de la plantilla oficial
8.4.1 Objetivos
Poner a punto un proyecto a partir de la plantilla oficial.
Aprovechar la redirección de la salida estándar.
8.4.3 Introducción
Para facilitar el trabajo de la asignatura, se ha preparado una plantilla preconfigu-
rada.
Para usarla, es necesario tener las HAL instaladas previamente.
143
Capítulo 8. Prácticas
Preparar la plantilla
Sigue los siguientes pasos para preparar un proyecto a partir de ella:
Construir la plantilla
Con el entorno de desarrollo Keil, abrir el archivo Project.uvprojx que está en el
raíz de la carpeta del proyecto.
Probar a construir y a volcar en la placa para asegurarte de que todo está en perfectas
condiciones.
144
8.4 Práctica: Uso de la plantilla oficial
Arrancar el depurador.
Abrir la ventana de salida del servicio de redirección al SWV-ITM debug. Ver
figura 8.4.
145
Capítulo 8. Prácticas
146
8.4 Práctica: Uso de la plantilla oficial
{
int32_t contador = 0;
HAL_Init ();
S y s t e m C l o c k _ C o n f i g ();
while (1) {
fputc_SetXY (130 ,50);
printf ( " %6d " ,( int ) contador );
contador ++;
HAL_Delay (10);
}
}
147
Capítulo 8. Prácticas
que meter mano ahí. El procedimiento suele consistir en proveer una función con
un nombre muy concreto y bastante universal. Para Keil, bastará con proveer la
función fputc().
Para hacerse una idea, ahí va el listado del módulo fputc_lcd.c:
/* *
@file fputc . c
@author Angel Perles
@date 2016/04/10
* */
if ( first_time ) {
first_time = 0;
fputc_Init ();
}
if ( ch >= ’␣ ’) {
B S P _ L C D _ D i s p l a y C h a r ( putc_x , putc_y , ch );
putc_x += fputc_font - > Width ;
} else if ( ch == ’\ n ’) { // in this Keil case is \ x0A
putc_y += fputc_font - > Height ;
putc_x = 0;
}
return ( ch );
}
/* *
* @brief Set the coordinates bla , bla altre dia
*/
void fputc_SetXY ( uint16_t x , uint16_t y ) {
putc_x = x ;
putc_y = y ;
}
/* *
* @brief Prepares the display
*/
148
8.4 Práctica: Uso de la plantilla oficial
B SP _L C D_ S et Fo n t ( fputc_font );
B S P _ L C D _ S e t T e x t C o l o r (0 xFF000000 );
}
149
Capítulo 8. Prácticas
150
8.5 Práctica: Bibliotecas
8.5.1 Objetivos
Emplear y organizar adecuadamente las bibliotecas externas.
8.5.3 Introducción
Las bibliotecas son la base del desarrollo de sistemas embebidos en C.
En este caso, se pretende ilustrar el uso de una biblioteca muy sencilla desarrollada
por una tercera parte.
Como la biblioteca usa las HAL para hacer salida digital, no está de más echarle un
buen repaso.
151
Capítulo 8. Prácticas
LED_Init ();
while (1) {
LED_On ();
HAL_Delay (1000);
LED_Off ();
HAL_Delay (1000);
// printf (" Hola \ n ");
}
return 0;
}
152
8.6 Práctica: Salida digital con una válvula
8.6.1 Objetivos
Programar la salida digital a partir de las especificaciones electrónicas.
Desarrollar un módulo que haga uso de la salida digital.
153
Capítulo 8. Prácticas
8.6.3 Introducción
Se propone el manejo de una válvula digital para practicar como se debe crear un
módulo C que gestione una salida digital básica.
Destacar que la principal lección es darse cuenta de la importancia de la abstracción
que proporciona este tipo de módulos.
Se obvia la parte de diseño electrónico del driver de la válvula.
# ifndef VALVE_H
# define VALVE_H
# endif
valve_Init ();
while (1)
154
8.6 Práctica: Salida digital con una válvula
{
valve_Set ( VALVE_OPEN );
// printf (" Open valve \ n ");
HAL_Delay (1000);
valve_Set ( VALVE_CLOSE );
// printf (" Close valve \ n ");
HAL_Delay (1000);
}
}
155
Capítulo 8. Prácticas
156
8.7 Práctica: Entrada digital con un sensor de rebose
8.7.1 Objetivos
Programar la entrada digital a partir de las especificaciones electrónicas.
Desarrollar un módulo que haga uso de la entrada digital.
8.7.3 Introducción
Se propone el manejo de un sensor digital para practicar como se debe crear un
módulo C que gestione una entrada digital básica, de manera que se abstraiga y
oculte su implementación.
Para detectar la posibilidad de rebose de un depósito de líquidos, se propone emplear
un sensor de boya típico como el mostrado figura 8.9 con referencia GENTECH
157
Capítulo 8. Prácticas
# ifndef OVERFLOW_H
# define OVERFLOW_H
# endif
158
8.7 Práctica: Entrada digital con un sensor de rebose
159
Capítulo 8. Prácticas
160
8.8 Práctica: Entrada digital con máscaras
8.8.1 Objetivos
Programar la entrada digital a partir de las especificaciones electrónicas.
Desarrollar un módulo que haga uso de la entrada digital mediante máscaras.
8.8.3 Introducción
En esta práctica se propone el uso de máscaras para el análisis de los bits de un dato
que se toma desde una entrada digital. El conocimiento de las operaciones basadas
en máscaras son imprescindibles para la programación de sistemas embebidos.
Para prácticar este concepto se empleará el pulsador de la placa Discovery cuyo
esquemático se reproduce en la figura 8.12.
161
Capítulo 8. Prácticas
162
8.8 Práctica: Entrada digital con máscaras
B SP _L C D_ S et Fo n t (& Font16 );
B S P _ L C D _ S e t T e x t C o l o r (0 xFF000000 );
// B S P _ L C D _ S e t B a c k C o l o r (0 x66FFFF );
B S P _ L C D _ D i s p l a y S t r i n g A t (10 , 10 , ( uint8_t *) " Estoy ␣ vivo ! " , LEFT_MODE );
/* Infinite loop */
while (1)
{
HAL_Delay (1000);
B S P _ L C D _ S e t T e x t C o l o r (0 xFFFF0000 );
B S P _ L C D _ D i s p l a y S t r i n g A t (80 , 100 , ( uint8_t *) " pulsado " , LEFT_MODE );
B S P _ L C D _ F i l l C i r c l e (120 , 200 , 30);
HAL_Delay (1000);
B S P _ L C D _ S e t T e x t C o l o r (0 xFF00FF00 );
B S P _ L C D _ D i s p l a y S t r i n g A t (80 , 100 , ( uint8_t *) " suelto ␣ " , LEFT_MODE );
B S P _ L C D _ F i l l C i r c l e (120 , 200 , 30);
// printf (" Hello , world !!!!\ n ");
}
163
Capítulo 8. Prácticas
164
8.9 Práctica: Optimizaciones de código para el display de 7 segmentos
8.9.1 Objetivos
Desarrollar un módulos que hagan uso de la salida mediante máscaras.
Aplicar técnicas de tablas de look-up.
Evaluar el impacto en la memoria de programa y de variables de distintas
implementaciones.
8.9.3 Introducción
Un aspecto crítico en los desarrollos para sistemas embebidos son las restricciones
en cuanto a espacio de memoria requerido o al tiempo de ejecución.
En esta práctica se pretende mostrar el efecto en cuanto a tamaño de memoria del uso
de las técnicas de tablas de look-up. En particular, se probarán las optimizaciones
desarrolladas para el display de 7 segmentos.
165
Capítulo 8. Prácticas
d i s p l a y 7 s e g _ I n i t ();
while (1) {
for ( n =0; n <= 10; n ++) {
d i s p l a y 7 s e g _ S e t N u m b e r ( n );
HAL_Delay (500);
}
}
166
8.9 Práctica: Optimizaciones de código para el display de 7 segmentos
167
Capítulo 8. Prácticas
// ...
uint32_t tmp ;
/* EDCBA segments */
tmp = out ;
tmp = ( tmp & 0 x001F ) << 2;
H A L _ G P I O _ W r i t e P i n ( GPIOE , 0 x007B , GPIO_P IN_RES ET );
H A L _ G P I O _ W r i t e P i n ( GPIOE , tmp , GPIO_PIN_SET );
/* GF segments */
tmp = out ;
tmp = ( tmp & 0 x0060 ) >> 3; // that is >> 5 , and << 2
H A L _ G P I O _ W r i t e P i n ( GPIOG , 0 x000B , GPIO_P IN_RES ET );
H A L _ G P I O _ W r i t e P i n ( GPIOG , tmp , GPIO_PIN_SET );
168
8.10 Práctica: Usando EXTI para contar vehículos en una carretera
8.10.1 Objetivos
Configurar el periférico EXTI para generar interrupciones.
Desarrollar un módulo para gestionar la contabilidad de vehículos basado en
interrupciones.
8.10.3 Introducción
Esta práctica pretende ilustrar los beneficios de usar interrupciones con el siguiente
ejemplo motivante.
Para contabilizar vehículos que circulan por una carretera se puede utilizar un tubo
de contactos de acero que genera una salida digital cada vez que un vehículo la
presiona.
169
Capítulo 8. Prácticas
uint32_t count ;
cars_Init ();
while (1)
{
// read number of cars axis
count = cars_GetCount ();
printf ( " Con tabili zados ␣ %d ␣ ejes .\ n " , ( int ) count );
170
8.10 Práctica: Usando EXTI para contar vehículos en una carretera
# ifndef CARS_H
# define CARS_H
# endif
void E X T I 0 _ I R Q H a n d l e r ( void )
{
if ( _ _ H A L _ G P I O _ E X T I _ G E T _ I T ( GPIO_PIN_0 ) != RESET ) {
_ _ H A L _ G P I O _ E X T I _ C L E A R _ I T ( GPIO_PIN_0 );
c a r s _ I n c r e m e n t C o u n t ();
}
171
Capítulo 8. Prácticas
// ...
uint32_t count ;
cars_Init ();
B S P _ L C D _ S e t T e x t C o l o r (0 x7F00FF00 );
// B S P _ L C D _ D r a w B i t m a p (50 ,150 ,( uint8_t *) ford );
while (1)
{
// read number of cars axis
count = cars_GetCount ();
fputc_SetXY (105 ,85);
printf ( " %03 d " ,( int ) count );
172
8.10 Práctica: Usando EXTI para contar vehículos en una carretera
void E X T I 0 _ I R Q H a n d l e r ( void )
{
// St ’s HAL proposes to call this predefined function
H A L _ G P I O _ E X T I _ I R Q H a n d l e r ( GPIO_PIN_0 );
}
173
Capítulo 8. Prácticas
174
8.11 Haciendo pausas basadas en SysTick
8.11.1 Objetivos
Desarrollar código capaz de realizar pausas basada en tiempo.
8.11.3 Introducción
Una de las necesidades básicas de las aplicaciones para microcontrolador es ser
capaces de medir el tiempo con muy distintos propósitos. Un de estos proósitos es
la de realizar pausas de precisión.
Como excusa para practicarlo, se propone la necesidad de hacer parpadear un LED
a un ritmo continuo según el patrón: 300 ms encendido, 500 ms apagado, 300 ms
encendido y 1 s apagado.
175
Capítulo 8. Prácticas
176
8.11 Haciendo pausas basadas en SysTick
177
Capítulo 8. Prácticas
178
8.12 MBED: Desarrollo ARM en la nube
8.12.1 Objetivos
Conocer las posibilidades de desarrollo embebido ARM en la nube.
8.12.3 Intriducción
Hay una tendencia generalizada a que el desarrollo de aplicaciones y su gestión sea
posible a través de servicios en la nube. En esta práctica se pretender ilustrar el uso
de la plataforma ARM MBED para desarrollo embebido para microcontroladores.
MBED empezó como una plataforma en la nube que desarrollaron unos amiguetes e
incluía un compilador en la nube, un gestor de repositorios Mercurial, una plaquita
basada en un NXP LPC1768 (agafarla per dur-la a clase) y una comunidad abierta
donde te podías registrar y compartir tus proyectos.
El principio MBED está ahora presente en otras páginas, pero destacar que ARM se
ha hecho con MBED y ha elevado notablemente su valor estratégico premitiendo la
incorporación de más plataformas y creando un RTOS (real-time operating system)
para la IoT (Internet of Things).
179
Capítulo 8. Prácticas
Vamos ayá.
Registro en MBED
Accedemos a https://www.mbed.com/en/ y entraremos enla página principal del
proyecto (ver figura 8.20). Deberemos ir a la vista “clásica” para trabajar como toda
la vida pinchando en la pestaña del margen derecho hasta llegar a la página de la
figura 8.21).
Y nos registramos.
180
8.12 MBED: Desarrollo ARM en la nube
Figura 8.21: Saltamos a la página MBED clásico. Esto cambiará en pocos meses
También es posible agregar componentes extra, como sensores, actuadores, etc. para
ello seleccionar “Hardware->components” (ver figura
181
Capítulo 8. Prácticas
182
8.12 MBED: Desarrollo ARM en la nube
8.12.7 El compilador
En la esquina superior-derecha de tu navegador se puede ver el botón de acceso al
compilar. Púlsalo para abrir un nueva ventana en la que aparece un típico IDE para
desarrollo como el mostrado en la figura 8.25. Echa un vistazo a las opciones.
Importar un proyecto
Para ir al grano, dar al botón “Import”, y en buscar escribimos “DISCO-F429Z’ ’ y
damos a Buscar. Debería aparecer algo similar a lo mostrado en la figura 8.26.
Elegir “Import” del proyecto visto en el apartado anterior (es el mismo). Dará un
error al actualizar bibliotecas, pero seguir adelante.
Construir el proyecto
Le damos a compilar (se dice construir) y esperamos un poco. Si todo ha ido bien,
se abrirá un diálogo para descargar el resultado de la compilación.
183
Capítulo 8. Prácticas
184
8.12 MBED: Desarrollo ARM en la nube
Volcar a la placa
El archivo que se descarga lo sueltas en la unidad de la placa.
FIN
185
Capítulo 8. Prácticas
186
Glosario
187
Bibliografía
[2] ARM. CMSIS-CORE support for Cortex-M processor-based devices v 3.01 (co-
pia local). 2013. url: http://www.disca.upv.es/aperles/arm_cortex_m3/
curset/CMSIS/Documentation/Core/html/index.html (vid. pág. 64).
[3] ARM. MDK Microcontroller Development Kit. 2017. url: http : / / www2 .
keil.com/mdk5/ (vid. pág. 117).
[4] ARM Ltd. Acceso a la web de ARM. 2013. url: http://www.arm.com/ (vid.
pág. 10).
[6] StMicroelectronics. Discovery kit with STM32F429ZI MCU. 2017. url: http:
/ / www . st . com / content / st _ com / en / products / evaluation - tools /
product- evaluation- tools/mcu- eval- tools/stm32- mcu- eval- tools/
stm32-mcu-discovery-kits/32f429idiscovery.html (vid. pág. 111).
189
Bibliografía
[14] StMicroelectronics. User guide: Discovery kit with STM32F429ZI MCU. 2017.
url: http://www.st.com/resource/en/user_manual/dm00093903.pdf
(vid. pág. 111).
[15] Joseph Yiu, ed. The Definitive Guide to ARM CORTEX-M3 and CORTEX-
M4 Processors (Third Edition). Third Edition. Oxford: Newnes, 2014. isbn:
978-0-12-408082-9. doi: http://dx.doi.org/10.1016/B978-0-12-408082-
9.01001-0 (vid. pág. 19).
190