tdse-tp3_04-interactive_menu

Index Commits Files Refs
app/gemini_01.txt (11433B)
   1 ### **Resumen General de la Arquitectura del Software**
   2 
   3 El código implementa un sistema embebido basado en un microcontrolador STM32. La
   4 arquitectura es un sistema cooperativo, no apropiativo y controlado por eventos,
   5 comúnmente conocido como "super-loop" o bucle principal.
   6 
   7 El flujo de ejecución es el siguiente: 1.  **Inicialización (`main.c` ->
   8 `app_init`)**: Se configuran el hardware, los periféricos y se inicializan todas
   9 las tareas (módulos) del sistema una sola vez. 2.  **Bucle Infinito (`main.c` ->
  10 `app_update`)**: El programa entra en un bucle infinito donde se ejecutan las
  11 funciones de actualización de las tareas de manera secuencial. 3.  **Base de
  12 Tiempo (`stm32f1xx_it.c`)**: Una interrupción periódica del temporizador
  13 `SysTick` (configurada típicamente a 1 ms) actúa como el "corazón" del sistema,
  14 proporcionando una base de tiempo para que las tareas se ejecuten a intervalos
  15 regulares. 4.  **Modelo de Tareas**: El sistema se divide en dos tareas
  16 principales:
  17     * `task_sensor`: Se encarga de leer el estado de los botones, implementar un
  18       antirrebote (debounce) por software y generar eventos.
  19     * `task_menu`: Gestiona la lógica de un menú interactivo, procesa los
  20       eventos generados por `task_sensor` y actualiza una pantalla LCD. 5.
  21       **Comunicación entre Tareas**: Las tareas se comunican de forma asíncrona
  22       mediante una cola de eventos (`queue`). `task_sensor` es el productor de
  23       eventos y `task_menu` es el consumidor.
  24 
  25 ---
  26 
  27 ### **Análisis Detallado de los Archivos**
  28 
  29 #### **1. `main.c`** Este archivo es el punto de entrada de la aplicación.
  30 * **`main()`**:
  31     * Llama a `HAL_Init()` y otras funciones de configuración del hardware
  32       (`SystemClock_Config`, `MX_GPIO_Init`, etc.), que son generadas por
  33       STM32CubeMX.
  34     * Llama a **`app_init()`** una sola vez para inicializar la lógica de la
  35       aplicación y todas sus tareas.
  36     * Entra en un bucle infinito `while(1)` donde llama repetidamente a
  37       **`app_update()`**. Esta es la esencia del planificador "super-loop".
  38 
  39 #### **2. `stm32f1xx_it.c`** Este archivo contiene las rutinas de servicio de
  40 interrupción (ISR).
  41 * **`SysTick_Handler()`**: Es la función más importante para la lógica del
  42   programa. Se ejecuta cada vez que el temporizador SysTick se desborda
  43   (usualmente, cada 1 ms).
  44     * Incrementa tres contadores de "ticks" globales y volátiles:
  45         * `g_app_tick_cnt`: Un contador general para el planificador en `app.c`.
  46         * `g_task_sensor_tick_cnt`: Un contador específico para la tarea del
  47           sensor.
  48         * `g_task_menu_tick_cnt`: Un contador específico para la tarea del menú.
  49     * Estos contadores actúan como semáforos de tiempo, indicando a las
  50       funciones `update` que ha transcurrido un milisegundo y que deben
  51       ejecutarse.
  52 
  53 #### **3. `app.c`** Este archivo actúa como el planificador principal o
  54 "dispatcher" de tareas.
  55 * **`task_cfg_list[]`**: Define un arreglo con las tareas del sistema. Cada
  56   tarea tiene una función de inicialización (`task_x_init`) y una de
  57   actualización (`task_x_update`).
  58 * **`app_init()`**:
  59     * Inicializa un contador de ciclos de alta precisión (`cycle_counter_init`)
  60       para medir tiempos de ejecución.
  61     * Recorre `task_cfg_list` y llama a la función `_init` de cada tarea (ej.,
  62       `task_sensor_init()` y `task_menu_init()`).
  63     * Inicializa en `0` el `WCET` (Worst-Case Execution Time o Tiempo de
  64       Ejecución de Peor Caso) para cada tarea.
  65 * **`app_update()`**:
  66     * Verifica si `g_app_tick_cnt` es mayor que cero. Si lo es, significa que la
  67       ISR de `SysTick` ha ocurrido y es momento de ejecutar el ciclo de tareas.
  68     * Dentro de un bucle `while`, procesa cada "tick" pendiente.
  69     * En cada ciclo: 1.  Reinicia `g_app_runtime_us` a cero. 2.  Recorre la
  70       lista de tareas. Para cada una:
  71             * Reinicia el contador de ciclos.
  72             * Ejecuta la función `_update` de la tarea (ej.,
  73               `task_sensor_update()`).
  74             * Obtiene el tiempo de ejecución de la tarea en microsegundos.
  75             * **Acumula este tiempo en `g_app_runtime_us`**.
  76             * **Compara el tiempo de ejecución actual con el `WCET` almacenado.
  77               Si es mayor, actualiza el `WCET`**. 3.  Decrementa
  78               `g_app_tick_cnt` para marcar que un tick ha sido procesado.
  79     * El uso de `__asm("CPSID i")` (deshabilitar interrupciones) y `__asm("CPSIE
  80       i")` (habilitar interrupciones) protege el acceso a las variables de tick
  81       para evitar condiciones de carrera.
  82 
  83 #### **4. `task_sensor.c`** Implementa una máquina de estados para leer botones,
  84 incluyendo un filtro antirrebote.
  85 * **`task_sensor_cfg_list[]`**: Configura los pines de los botones (ENT, NEX,
  86   ESC), el estado que indica que están presionados, y los eventos que deben
  87   generar.
  88 * **`task_sensor_update()`**: Se ejecuta periódicamente gracias a su contador
  89   `g_task_sensor_tick_cnt`. Su única función es llamar a
  90   `task_sensor_statechart()`.
  91 * **`task_sensor_statechart()`**:
  92     * Lee el estado físico de cada botón (`HAL_GPIO_ReadPin`).
  93     * Implementa una máquina de estados de 4 estados para cada botón (`UP`,
  94       `FALLING`, `DOWN`, `RISING`) para filtrar el ruido (debounce).
  95     * Cuando se detecta una transición válida (ej., de `UP` a `DOWN` después de
  96       un tiempo de espera), no genera el evento inmediatamente. Espera un tiempo
  97       (`tick_max`) en el estado `FALLING`. Si al final de ese tiempo el botón
  98       sigue presionado, considera la pulsación válida.
  99     * Al confirmar una pulsación válida, llama a **`put_event_task_menu()`**
 100       para encolar el evento correspondiente (ej., `EV_MEN_ENT_ACTIVE`).
 101 
 102 #### **5. `task_menu_interface.c` y `task_menu_attribute.h`** Estos archivos
 103 definen la interfaz de comunicación para la tarea del menú.
 104 * **`task_menu_attribute.h`**: Define los tipos de datos para los eventos
 105   (`task_menu_ev_t`) y estados (`task_menu_st_t`) de la máquina de estados del
 106   menú.
 107 * **`task_menu_interface.c`**: Implementa una **cola de eventos** circular
 108   (buffer circular) muy simple.
 109     * `put_event_task_menu()`: Añade un evento a la cola (llamada por
 110       `task_sensor`).
 111     * `get_event_task_menu()`: Extrae un evento de la cola (llamada por
 112       `task_menu`).
 113     * `any_event_task_menu()`: Verifica si hay eventos pendientes en la cola.
 114 
 115 #### **6. `task_menu.c`** Gestiona la lógica del menú y la interacción con la
 116 pantalla LCD.
 117 * **`task_menu_init()`**:
 118     * Inicializa la cola de eventos.
 119     * Inicializa la pantalla LCD (`displayInit`).
 120     * Escribe los mensajes de bienvenida en la pantalla.
 121 * **`task_menu_update()`**: Se ejecuta periódicamente gracias a
 122   `g_task_menu_tick_cnt` y llama a `task_menu_statechart()`.
 123 * **`task_menu_statechart()`**:
 124     * Primero, comprueba si hay un evento en la cola con
 125       `any_event_task_menu()`. Si es así, lo extrae.
 126     * Implementa una máquina de estados simple con dos estados: `ST_MEN_XX_IDLE`
 127       y `ST_MEN_XX_ACTIVE`.
 128     * Se transita de `IDLE` a `ACTIVE` al recibir el evento `EV_MEN_ENT_ACTIVE`.
 129     * En el estado `ACTIVE`, un contador de tiempo (`tick`) se decrementa.
 130       Cuando llega a cero, se actualiza un número en la pantalla LCD (un
 131       contador basado en `g_task_menu_cnt`) y se reinicia el contador de tiempo.
 132       Esto hace que la pantalla se refresque periódicamente.
 133     * Se vuelve al estado `IDLE` con el evento `EV_MEN_ENT_IDLE`.
 134 
 135 #### **7. `display.c`** Este es el driver de bajo nivel para controlar una
 136 pantalla LCD de caracteres, probablemente compatible con el estándar Hitachi
 137 HD44780, en modo de 4 bits.
 138 * **`displayInit()`**: Envía la secuencia de comandos necesaria para inicializar
 139   la pantalla LCD (configurar modo de 4 u 8 bits, número de líneas, encender
 140   display, etc.).
 141 * **`displayCharPositionWrite()`**: Envía un comando para ubicar el cursor en
 142   una posición (fila y columna) específica.
 143 * **`displayStringWrite()`**: Envía una cadena de caracteres para ser mostrada
 144   en la pantalla a partir de la posición actual del cursor.
 145 * Las funciones de bajo nivel como `displayCodeWrite`, `displayPinWrite` y
 146   `displayDataBusWrite` se encargan de manipular los pines GPIO para enviar los
 147   datos y comandos a la pantalla.
 148 
 149 ---
 150 
 151 ### **Análisis de la Evolución de Variables**
 152 
 153 A continuación, se detalla cómo evolucionan las variables que consultaste.
 154 
 155 #### **Nota sobre `g_task_test_tick_cnt`** La variable `g_task_test_tick_cnt`
 156 **no existe** en ninguno de los archivos proporcionados. Es posible que sea un
 157 error tipográfico. Las variables similares que sí existen y que son
 158 incrementadas por `SysTick_Handler` son:
 159 * `g_app_tick_cnt`
 160 * `g_task_sensor_tick_cnt`
 161 * `g_task_menu_tick_cnt`
 162 
 163 Todas ellas se incrementan en 1 cada milisegundo. Luego, son decrementadas en
 164 las funciones `_update` correspondientes a medida que se procesa el trabajo
 165 asociado a ese "tick".
 166 
 167 #### **`g_app_runtime_us`**
 168 * **Unidad de medida**: Microsegundos ($µs$).
 169 * **Inicialización (`app_init`)**: No se inicializa aquí.
 170 * **Evolución durante `app_update`**: 1.  Al comienzo de cada ciclo de ejecución
 171   principal (es decir, cada vez que `g_app_tick_cnt > 0`), esta variable **se
 172   reinicia a `0`**. 2.  Inmediatamente después, el planificador ejecuta
 173   `task_sensor_update()`. El tiempo que tarda esta función en ejecutarse (medido
 174   en $µs$) se suma a `g_app_runtime_us`. 3.  Luego, el planificador ejecuta
 175   `task_menu_update()`. Su tiempo de ejecución también se suma a
 176   `g_app_runtime_us`. 4.  Al final del bucle `for` que recorre las tareas,
 177   `g_app_runtime_us` contiene la **suma total del tiempo de ejecución de todas
 178   las tareas para un único tick de 1 ms**.
 179     * **Ejemplo**: Si `task_sensor_update` tarda 15 $µs$ y `task_menu_update`
 180       tarda 80 $µs$, al final del ciclo, `g_app_runtime_us` valdrá `95 µs`. En
 181       el siguiente ciclo de 1 ms, se volverá a poner a cero antes de medir de
 182       nuevo. Su valor fluctúa constantemente.
 183 
 184 #### **`task_dta_list[index].WCET`**
 185 * **Unidad de medida**: Microsegundos ($µs$).
 186 * **Inicialización (`app_init`)**: En `app_init`, el `WCET` de cada tarea
 187   (`task_dta_list[0].WCET` para el sensor y `task_dta_list[1].WCET` para el
 188   menú) se inicializa a **`0`**.
 189 * **Evolución durante `app_update`**: 1.  Después de que una tarea (ej.
 190   `task_sensor_update`) se ejecuta, su tiempo de ejecución de ese ciclo
 191   (`cycle_counter_time_us`) se compara con el `WCET` almacenado para esa tarea
 192   (`task_dta_list[0].WCET`). 2.  **Si `cycle_counter_time_us` es mayor que el
 193   `WCET` actual, el `WCET` se actualiza a este nuevo valor más alto.** 3.  Si el
 194   tiempo de ejecución es menor o igual, el `WCET` no cambia.
 195     * **Comportamiento**: Esta variable **solo puede aumentar o mantener su
 196       valor; nunca disminuye**. Representa el "récord" o el tiempo máximo que ha
 197       tardado una tarea en ejecutarse desde que se encendió el sistema. Es una
 198       métrica crucial en sistemas de tiempo real para garantizar que la suma de
 199       los peores tiempos de ejecución de todas las tareas no exceda el período
 200       del sistema (en este caso, 1 ms). Por ejemplo, si una vez la tarea del
 201       menú tarda 120 $µs$ debido a una actualización de la pantalla, el `WCET`
 202       para esa tarea se establecerá en 120 y no cambiará a menos que en una
 203       ejecución futura tarde aún más.