repos/tdse-tp2_03-model_integration

Commits Files Refs README
README.md (235 lines)

Taller de Sistemas Embebidos

En este proyecto se implementa una aplicación para la placa STM32 Nucleo-F103RB. Esta aplicación es de tipo no bloqueante y dirigida por eventos, ademas se actualiza en cada milisegundo. A su vez esta aplicación incluye 3 tareas task_sensor, task_system y task_actuator las cuales se modelan mediante maquinas de estado. De estas 3 tareas en esta instancia solo se ha implementado la maquina de estados de la primeras 2 task_sensor y task_system, de la restante solo se ha implementado lo suficiente como para que la aplicación se ejecute sin errores.

Estructura del proyecto

Para el proyecto se utiliza la IDE de STM: STM32CubeIDE. Una vista de árbol nivel 3 (desde la raíz del directorio) se muestra a continuación.

.
|-- app
|   |-- inc
|   |   |-- app.h
|   |   |-- board.h
|   |   |-- dwt.h
|   |   |-- logger.h
|   |   |-- systick.h
|   |   |-- task_actuator_attribute.h
|   |   |-- task_actuator.h
|   |   |-- task_actuator_interface.h
|   |   |-- task_sensor_attribute.h
|   |   |-- task_sensor.h
|   |   |-- task_system_attribute.h
|   |   |-- task_system.h
|   |   `-- task_system_interface.h
|   |-- src
|   |   |-- app.c
|   |   |-- logger.c
|   |   |-- systick.c
|   |   |-- task_actuator.c
|   |   |-- task_actuator_interface.c
|   |   |-- task_sensor.c
|   |   |-- task_system.c
|   |   `-- task_system_interface.c
|   |-- app.txt
|   |-- readme.txt
|   |-- task_actuator.png
|   |-- task_actuator.txt
|   |-- task_sensor.png
|   |-- task_sensor.txt
|   |-- task_system.png
|   `-- task_system.txt
|-- Core
|   |-- Inc
|   |   |-- main.h
|   |   |-- stm32f1xx_hal_conf.h
|   |   `-- stm32f1xx_it.h
|   |-- Src
|   |   |-- main.c
|   |   |-- stm32f1xx_hal_msp.c
|   |   |-- stm32f1xx_it.c
|   |   |-- syscalls.c
|   |   |-- sysmem.c
|   |   `-- system_stm32f1xx.c
|   `-- Startup
|       `-- startup_stm32f103rbtx.s
|-- Drivers
|   |-- CMSIS
|   |   |-- Device
|   |   |-- Include
|   |   `-- LICENSE.txt
|   `-- STM32F1xx_HAL_Driver
|       |-- Inc
|       |-- Src
|       `-- LICENSE.txt
|-- README.md
|-- STM32F103RBTX_FLASH.ld
`-- tdse-tp2_01-model_integration.ioc

15 directories, 44 files

Archivo Core/Startup/startup_stm32f103rbtx.s

Este archivo está escrito en arm assembly y se ejecuta al inicializar el microcontrolador, el propósito es inicializar los registros principales (como el stack pointer o el program counter) la memoria y periféricos, para finalmente transferir el control a la aplicación principal, cuyo punto de entrada es la función main (definida en el archivo Core/Src/main.c) esto se puede ver en la linea 100 del archivo de inicialización, en la cual se ejecuta una instrucción bl (branch with link) y luego el símbolo main. El único tipo de dato utilizado es word que representa una variable de 4 bytes (o 32 bits).

Archivo Core/Src/main.c

El archivo main.c es un archivo escrito en C que contiene el punto de entrada del programa principal, este punto de entrada es invocado al momento de inicializar el microcontrolador, como se mencionó previamente. El patrón de diseño de este archivo es procedural, ya que la ejecución del programa se lleva a cabo mediante la invocación de diferentes funciones o subrutinas.

Los métodos que se encuentran en este archivo además de la (función principal main) son: initialise_monitor_handles, HAL_Init, SystemClock_Config, MX_GPIO_Init, MX_USART2_UART_Init, app_init, app_update y Error_Handler.

La función o método initialise_monitor_handles se encarga de inicializar o configurar como se manipula la entrada y salida, por ejemplo con printf al momento de depurar el código, en particular se utiliza el protocolo UART para comunicarse con la computadora, es por esto que se inicializa con la función MX_USART2_UART_Init.

La función SystemClock_Config como se indica en el nombre configura los relojes internos del microprocesador, como ser las fuentes y los pre-escaladores de los buses y los timers. La velocidad del microprocesador se almacena en la variable global SystemCoreClock, esta variable se debe actualizar si se cambia la velocidad del microprocesador.

Cuando se ejecuta la función HAL_Init se inicializa los estados internos de la librería HAL (Hardware Abstraction Layer) lo que incluye el timer SysTick, cuyo estado se almacena en la estructura global del mismo nombre. SysTick se utiliza para generar una interrupción por cada milisegundo, para esto cuenta desde SystemCoreClock/1000 hasta 0, por ejemplo, para una frecuencia de 64MHz, el el timer cuenta desde 64000 hasta 0, lo que produce una interrupción exacta por milisegundo.

La función Error_Handler se ejecuta en caso de error pero no tiene una funcionalidad definida. Por ultimo en la función main se inicializa la aplicación con app_init y en cada ciclo se actualiza el estado de la aplicación con app_update.

Archivo app/src/app.c

En este archivo se definen las funciones app_init y app_update que se invocan en la función main, y se define el callback HAL_SYSTICK_Callback, este ultimo es una función que previamente ya estaba definida pero de manera débil en la librería HAL, y se inicializa al momento de invocar HAL_Init, esta función se ejecuta cada 1 milisegundo debido a una interrupción generada por un timer. El callback se define de manera tal de incrementar el contador global de ticks de la aplicación g_app_tick_cnt y el interno de cada tarea g_task_*_tick_cnt, de esta manera, cada vez que el contador de cada tarea y de la aplicación dejan de ser nulos (en cada milisegundo) la aplicación y las tareas se deben actualizar.

Cada vez que se ejecutan las tareas se mide el tiempo de ejecución contando los microsegundos que se tarde en ejecutar esa tarea, el peor de los tiempos de ejecución se guarda en el dato WCET de cada tarea. La aplicación también lleva un registro del tiempo de ejecución en microsegundos que se tarda en ejecutar todas las tareas, y resulta de la suma del tiempo de ejecución cada tarea, el resultado se guarda en la variable global g_app_runtime_us, esta variable se actualiza en cada ejecución de las tareas, ademas se ha agregado la variable g_app_WCET_us en la cual se guarda el peor tiempo de ejecución de todas las tareas desde que se inicio el microcontrolador.

Analizando la evolución de la variable g_app_WCET_us se ha detectado el impacto de imprimir texto en la consola con LOGGER_INFO en el tiempo de ejecución de la aplicación, así como analizando el dato WCET de cada tarea. Sin impresión el tiempo aproximado de ejecución de una tarea es menor a 10 us, mientras que utilizando LOGGER_INFO el tiempo se incrementa 95 ms aproximadamente, esto hace que el peor tiempo de ejecución de la aplicación se incremente de 13 us a 106 us.

Archivo app/src/task_sensor.c

En este archivo se implementa las funciones del modelo sensor, el cual se modela, como multiples botones, utilizando una maquina de estados por cada uno.

Las subrutinas que se presentan son task_sensor_init, task_sensor_update y task_sensor_statechart. La primera task_sensor_init se invoca por única vez al iniciar la tarea (cuando se invoca app_init) e inicializa las variables de todas las maquinas de estados de modelo sensor disponibles (definidas en task_sensor_cfg_list y task_sensor_dta_list). Las ultimas dos subrutinas task_sensor_update y task_sensor_statechart se invocan continuamente en el superloop, al invocar app_update, en particular en app_update se llama a task_sensor_update y esta internamente invoca la función task_sensor_statechart, la cual actualiza el estado de cada una de la maquina de estados disponibles del modelo task_sensor.

Archivo app/src/task_sensor_attribute.h

En este archivo se describen los atributos de la maquina de estados del modelo sensor, como son los eventos que recibe y los estados posibles, también se definen las estructuras que representan esta maquina de estados, de manera tal de poder representar multiples sensores.

Archivo app/task_sensor.png

En este archivo se presenta el diagrama de estados completo de la maquina de estados que representa al sensor. Por ser un diagrama de estados se muestran todos los posibles estados, como también el inicial, y como cambian estos al recibir eventos.

Archivo app/src/task_system.c

En este archivo se implementa las funciones del modelo system, el cual se modela, como una única maquina de estados.

Las subrutinas que se presentan son task_system_init, task_system_update y task_system_statechart. La primera task_system_init se invoca por única vez al iniciar la tarea (cuando se invoca app_init) e inicializa las variables de la maquina de estados del modelo system (definidas en la instancia de la estructura task_system_dta_t, task_system_dta). Las ultimas dos subrutinas task_system_update y task_system_statechart se invocan continuamente en el superloop, al invocar app_update, en particular en app_update se llama a task_system_update y esta internamente invoca la función task_system_statechart, la cual actualiza el estado de la maquina de estados del modelo system de acuerdo al estado en que este y los eventos que se reciben.

Archivo app/src/task_system_attribute.h

En este archivo se describen los atributos de la maquina de estados del modelo system, como son los eventos que recibe y los estados posibles, también se definen las estructuras que representan esta maquina de estados.

Archivo app/src/task_system_interface.h

En este archivo se define una serie de subrutinas para interactuar con el modelo system, de manera de enviar y recibir eventos, por ejemplo recibir eventos del modelo system y enviar eventos al modelo actuator.

Las subrutinas que se definen son init_queue_event_task_system, put_event_task_system, task_system_ev_t get_event_task_system y any_event_task_system.

Archivo app/task_system.png

En este archivo se presenta el diagrama de estados completo de la maquina de estados que representa al sistema. Por ser un diagrama de estados se muestran todos los posibles estados, incluyendo el estado inicial, y como cambian estos al recibir eventos.

   1 # Taller de Sistemas Embebidos
   2 
   3 En este proyecto se implementa una aplicación para la placa STM32
   4 [Nucleo-F103RB](https://www.st.com/en/evaluation-tools/nucleo-f103rb.html). Esta
   5 aplicación es de tipo no bloqueante y [dirigida por
   6 eventos](https://es.wikipedia.org/wiki/Programaci%C3%B3n_dirigida_por_eventos),
   7 ademas se actualiza en cada milisegundo. A su vez esta aplicación incluye 3
   8 tareas `task_sensor`, `task_system` y `task_actuator` las cuales se modelan
   9 mediante maquinas de estado. De estas 3 tareas en esta instancia solo se ha
  10 implementado la maquina de estados de la primeras 2 `task_sensor` y
  11 `task_system`, de la restante solo se ha implementado lo suficiente como para
  12 que la aplicación se ejecute sin errores.
  13 
  14 ## Estructura del proyecto
  15 
  16 Para el proyecto se utiliza la IDE de STM:
  17 [STM32CubeIDE](https://www.st.com/en/development-tools/stm32cubeide.html). Una
  18 vista de árbol nivel 3 (desde la raíz del directorio) se muestra a continuación.
  19 
  20 ```console
  21 .
  22 |-- app
  23 |   |-- inc
  24 |   |   |-- app.h
  25 |   |   |-- board.h
  26 |   |   |-- dwt.h
  27 |   |   |-- logger.h
  28 |   |   |-- systick.h
  29 |   |   |-- task_actuator_attribute.h
  30 |   |   |-- task_actuator.h
  31 |   |   |-- task_actuator_interface.h
  32 |   |   |-- task_sensor_attribute.h
  33 |   |   |-- task_sensor.h
  34 |   |   |-- task_system_attribute.h
  35 |   |   |-- task_system.h
  36 |   |   `-- task_system_interface.h
  37 |   |-- src
  38 |   |   |-- app.c
  39 |   |   |-- logger.c
  40 |   |   |-- systick.c
  41 |   |   |-- task_actuator.c
  42 |   |   |-- task_actuator_interface.c
  43 |   |   |-- task_sensor.c
  44 |   |   |-- task_system.c
  45 |   |   `-- task_system_interface.c
  46 |   |-- app.txt
  47 |   |-- readme.txt
  48 |   |-- task_actuator.png
  49 |   |-- task_actuator.txt
  50 |   |-- task_sensor.png
  51 |   |-- task_sensor.txt
  52 |   |-- task_system.png
  53 |   `-- task_system.txt
  54 |-- Core
  55 |   |-- Inc
  56 |   |   |-- main.h
  57 |   |   |-- stm32f1xx_hal_conf.h
  58 |   |   `-- stm32f1xx_it.h
  59 |   |-- Src
  60 |   |   |-- main.c
  61 |   |   |-- stm32f1xx_hal_msp.c
  62 |   |   |-- stm32f1xx_it.c
  63 |   |   |-- syscalls.c
  64 |   |   |-- sysmem.c
  65 |   |   `-- system_stm32f1xx.c
  66 |   `-- Startup
  67 |       `-- startup_stm32f103rbtx.s
  68 |-- Drivers
  69 |   |-- CMSIS
  70 |   |   |-- Device
  71 |   |   |-- Include
  72 |   |   `-- LICENSE.txt
  73 |   `-- STM32F1xx_HAL_Driver
  74 |       |-- Inc
  75 |       |-- Src
  76 |       `-- LICENSE.txt
  77 |-- README.md
  78 |-- STM32F103RBTX_FLASH.ld
  79 `-- tdse-tp2_01-model_integration.ioc
  80 
  81 15 directories, 44 files
  82 ```
  83 
  84 ### Archivo `Core/Startup/startup_stm32f103rbtx.s`
  85 
  86 Este archivo está escrito en arm assembly y se ejecuta al inicializar el
  87 microcontrolador, el propósito es inicializar los registros principales (como el
  88 stack pointer o el program counter) la memoria y periféricos, para finalmente
  89 transferir el control a la aplicación principal, cuyo punto de entrada es la
  90 función `main` (definida en el archivo `Core/Src/main.c`) esto se puede ver en
  91 la linea 100 del archivo de inicialización, en la cual se ejecuta una
  92 instrucción `bl` (branch with link) y luego el símbolo `main`. El único tipo de
  93 dato utilizado es `word` que representa una variable de 4 bytes (o 32 bits).
  94 
  95 ### Archivo `Core/Src/main.c`
  96 
  97 El archivo `main.c` es un archivo escrito en C que contiene el punto de entrada
  98 del programa principal, este punto de entrada es invocado al momento de
  99 inicializar el microcontrolador, como se mencionó previamente. El patrón de
 100 diseño de este archivo es procedural, ya que la ejecución del programa se lleva
 101 a cabo mediante la invocación de diferentes funciones o subrutinas.
 102 
 103 Los métodos que se encuentran en este archivo además de la (función principal
 104 `main`) son: `initialise_monitor_handles`, `HAL_Init`, `SystemClock_Config`,
 105 `MX_GPIO_Init`, `MX_USART2_UART_Init`, `app_init`, `app_update` y
 106 `Error_Handler`.
 107 
 108 La función o método `initialise_monitor_handles` se encarga de inicializar o
 109 configurar como se manipula la entrada y salida, por ejemplo con `printf` al
 110 momento de depurar el código, en particular se utiliza el protocolo UART para
 111 comunicarse con la computadora, es por esto que se inicializa con la función
 112 `MX_USART2_UART_Init`.
 113 
 114 La función `SystemClock_Config` como se indica en el nombre configura los
 115 relojes internos del microprocesador, como ser las fuentes y los pre-escaladores
 116 de los buses y los timers. La velocidad del microprocesador se almacena en la
 117 variable global `SystemCoreClock`, esta variable se debe actualizar si se cambia
 118 la velocidad del microprocesador.
 119 
 120 Cuando se ejecuta la función `HAL_Init` se inicializa los estados internos de la
 121 librería HAL (Hardware Abstraction Layer) lo que incluye el timer `SysTick`,
 122 cuyo estado se almacena en la estructura global del mismo nombre. `SysTick` se
 123 utiliza para generar una interrupción por cada milisegundo, para esto cuenta
 124 desde `SystemCoreClock/1000` hasta 0, por ejemplo, para una frecuencia de 64MHz,
 125 el el timer cuenta desde 64000 hasta 0, lo que produce una interrupción exacta
 126 por milisegundo.
 127 
 128 La función `Error_Handler` se ejecuta en caso de error pero no tiene una
 129 funcionalidad definida. Por ultimo en la función `main` se inicializa la
 130 aplicación con `app_init` y en cada ciclo se actualiza el estado de la
 131 aplicación con `app_update`.
 132 
 133 ### Archivo `app/src/app.c`
 134 
 135 En este archivo se definen las funciones `app_init` y `app_update` que se
 136 invocan en la función `main`, y se define el callback `HAL_SYSTICK_Callback`,
 137 este ultimo es una función que previamente ya estaba definida pero de manera
 138 débil en la librería HAL, y se inicializa al momento de invocar `HAL_Init`, esta
 139 función se ejecuta cada 1 milisegundo debido a una interrupción generada por un
 140 timer. El callback se define de manera tal de incrementar el contador global de
 141 ticks de la aplicación `g_app_tick_cnt` y el interno de cada tarea
 142 `g_task_*_tick_cnt`, de esta manera, cada vez que el contador de cada tarea y de
 143 la aplicación dejan de ser nulos (en cada milisegundo) la aplicación y las
 144 tareas se deben actualizar.
 145 
 146 Cada vez que se ejecutan las tareas se mide el tiempo de ejecución contando los
 147 microsegundos que se tarde en ejecutar esa tarea, el peor de los tiempos de
 148 ejecución se guarda en el dato `WCET` de cada tarea. La aplicación también lleva
 149 un registro del tiempo de ejecución en microsegundos que se tarda en ejecutar
 150 todas las tareas, y resulta de la suma del tiempo de ejecución cada tarea, el
 151 resultado se guarda en la variable global `g_app_runtime_us`, esta variable se
 152 actualiza en cada ejecución de las tareas, ademas se ha agregado la variable
 153 `g_app_WCET_us` en la cual se guarda el peor tiempo de ejecución de todas las
 154 tareas desde que se inicio el microcontrolador.
 155 
 156 Analizando la evolución de la variable `g_app_WCET_us` se ha detectado el
 157 impacto de imprimir texto en la consola con `LOGGER_INFO` en el tiempo de
 158 ejecución de la aplicación, así como analizando el dato `WCET` de cada tarea.
 159 Sin impresión el tiempo aproximado de ejecución de una tarea es menor a 10 us,
 160 mientras que utilizando `LOGGER_INFO` el tiempo se incrementa 95 ms
 161 aproximadamente, esto hace que el peor tiempo de ejecución de la aplicación se
 162 incremente de 13 us a 106 us.
 163 
 164 ### Archivo `app/src/task_sensor.c`
 165 
 166 En este archivo se implementa las funciones del modelo sensor, el cual se
 167 modela, como multiples botones, utilizando una maquina de estados por cada uno.
 168 
 169 Las subrutinas que se presentan son `task_sensor_init`, `task_sensor_update` y
 170 `task_sensor_statechart`. La primera `task_sensor_init` se invoca por única vez
 171 al iniciar la tarea (cuando se invoca `app_init`) e inicializa las variables de
 172 todas las maquinas de estados de modelo sensor disponibles (definidas en
 173 `task_sensor_cfg_list` y `task_sensor_dta_list`). Las ultimas dos subrutinas
 174 `task_sensor_update` y `task_sensor_statechart` se invocan continuamente en el
 175 superloop, al invocar `app_update`, en particular en `app_update` se llama a
 176 `task_sensor_update` y esta internamente invoca la función
 177 `task_sensor_statechart`, la cual actualiza el estado de cada una de la maquina
 178 de estados disponibles del modelo `task_sensor`.
 179 
 180 ### Archivo `app/src/task_sensor_attribute.h`
 181 
 182 En este archivo se describen los atributos de la maquina de estados del modelo
 183 sensor, como son los eventos que recibe y los estados posibles, también se
 184 definen las estructuras que representan esta maquina de estados, de manera tal
 185 de poder representar multiples sensores.
 186 
 187 ### Archivo `app/task_sensor.png`
 188 
 189 En este archivo se presenta el diagrama de estados completo de la maquina de
 190 estados que representa al sensor. Por ser un diagrama de estados se muestran
 191 todos los posibles estados, como también el inicial, y como cambian estos al
 192 recibir eventos.
 193 
 194 ![](app/task_sensor.png)
 195 
 196 ### Archivo `app/src/task_system.c`
 197 
 198 En este archivo se implementa las funciones del modelo system, el cual se
 199 modela, como una única maquina de estados.
 200 
 201 Las subrutinas que se presentan son `task_system_init`, `task_system_update` y
 202 `task_system_statechart`. La primera `task_system_init` se invoca por única vez
 203 al iniciar la tarea (cuando se invoca `app_init`) e inicializa las variables de
 204 la maquina de estados del modelo system (definidas en la instancia de la
 205 estructura `task_system_dta_t`, `task_system_dta`). Las ultimas dos subrutinas
 206 `task_system_update` y `task_system_statechart` se invocan continuamente en el
 207 superloop, al invocar `app_update`, en particular en `app_update` se llama a
 208 `task_system_update` y esta internamente invoca la función
 209 `task_system_statechart`, la cual actualiza el estado de la maquina de estados
 210 del modelo system de acuerdo al estado en que este y los eventos que se reciben.
 211 
 212 ### Archivo `app/src/task_system_attribute.h`
 213 
 214 En este archivo se describen los atributos de la maquina de estados del modelo
 215 system, como son los eventos que recibe y los estados posibles, también se
 216 definen las estructuras que representan esta maquina de estados.
 217 
 218 ### Archivo `app/src/task_system_interface.h`
 219 
 220 En este archivo se define una serie de subrutinas para interactuar con el modelo
 221 system, de manera de enviar y recibir eventos, por ejemplo recibir eventos del
 222 modelo system y enviar eventos al modelo actuator.
 223 
 224 Las subrutinas que se definen son `init_queue_event_task_system`,
 225 `put_event_task_system`, `task_system_ev_t get_event_task_system` y
 226 `any_event_task_system`.
 227 
 228 ### Archivo `app/task_system.png`
 229 
 230 En este archivo se presenta el diagrama de estados completo de la maquina de
 231 estados que representa al sistema. Por ser un diagrama de estados se muestran
 232 todos los posibles estados, incluyendo el estado inicial, y como cambian estos
 233 al recibir eventos.
 234 
 235 ![](app/task_system.png)