1 ### **Análisis de `board.h` (Abstracción de Hardware)** 2 3 Este archivo es un encabezado (header) de configuración que actúa como una 4 **Capa de Abstracción de Hardware (Hardware Abstraction Layer - HAL)** a nivel 5 de placa. Su principal objetivo es centralizar y abstraer las definiciones de 6 hardware específicas de la placa (pines, puertos, lógica de activación), 7 permitiendo que el resto del código sea más portable y legible. 8 9 #### **Funcionamiento Detallado:** 10 11 1. **Selección de Placa (Board Selection):** 12 * Se define una macro `BOARD` que especifica la placa de desarrollo para la 13 cual se está compilando el código. En este caso, está configurada para la 14 `NUCLEO_F103RC`. 15 * `#define BOARD (NUCLEO_F103RC)` 16 17 2. **Compilación Condicional:** 18 * El archivo utiliza directivas de preprocesador (`#if`, `#endif`) para 19 incluir bloques de definiciones que solo son válidos para la placa 20 seleccionada. 21 * El bloque que se compilará es el que corresponde a `((BOARD == 22 NUCLEO_F103RC) || ...)`. 23 24 3. **Mapeo de Pines y Puertos:** 25 * Dentro del bloque condicional, se asignan nombres simbólicos y legibles 26 (ej. `BTN_ENT_PIN`, `LED_A_PORT`) a las definiciones de pines y puertos 27 específicos del hardware, que provienen de las librerías de STM32Cube (ej. 28 `D10_Pin`, `LD2_GPIO_Port`). 29 * Por ejemplo, en lugar de escribir `HAL_GPIO_ReadPin(D10_GPIO_Port, 30 D10_Pin)`, el programador puede usar `HAL_GPIO_ReadPin(BTN_ENT_PORT, 31 BTN_ENT_PIN)`, lo cual es más claro. 32 33 4. **Abstracción de la Lógica Eléctrica:** 34 * Define el estado eléctrico que corresponde a una acción lógica. Por 35 ejemplo, `BTN_ENT_PRESSED` se define como `GPIO_PIN_RESET`. Esto significa 36 que el botón "Enter" se considera presionado cuando el pin correspondiente 37 está en estado bajo (LOW). Esto es muy útil porque abstrae si el botón es 38 activo-alto o activo-bajo. 39 * De manera similar, `LED_A_ON` se define como `GPIO_PIN_SET`, indicando que 40 el LED se enciende con un nivel de voltaje alto (HIGH). 41 42 **En resumen, este archivo permite que el resto del código fuente se refiera a 43 los botones y LEDs por nombres lógicos (como "Enter" o "LED A") sin preocuparse 44 por los detalles físicos de qué pin específico o qué nivel de voltaje se utiliza 45 en la placa NUCLEO-F103RC.** Si se quisiera portar el código a otra placa, solo 46 sería necesario modificar este archivo. 47 48 --- 49 50 ### **Análisis de `dwt.h` (Contador de Ciclos de Alta Precisión)** 51 52 Este archivo de encabezado proporciona un conjunto de funciones para medir 53 tiempos de ejecución con una resolución muy alta (a nivel de ciclos de reloj del 54 CPU). Para ello, utiliza el periférico **DWT (Data Watchpoint and Trace)** del 55 núcleo ARM Cortex-M. 56 57 #### **Funcionamiento Detallado:** 58 59 El archivo define una serie de funciones `static inline`, lo que sugiere al 60 compilador que inserte el código de la función directamente en el lugar de la 61 llamada, eliminando la sobrecarga de una llamada a función y haciéndolas muy 62 eficientes. 63 64 1. **`cycle_counter_init(void)`:** 65 * Esta función inicializa el contador de ciclos del DWT. 66 * `CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;`: Habilita el bloque de 67 hardware de traza y depuración, que es necesario para que el DWT funcione. 68 * `DWT->CYCCNT = 0;`: Reinicia el registro del contador de ciclos a cero. 69 * `DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;`: Activa el contador para que 70 empiece a contar los ciclos de reloj del CPU. 71 72 2. **`cycle_counter_reset(void)`:** 73 * Simplemente reinicia el contador de ciclos a cero. Es útil para empezar a 74 medir un intervalo de tiempo desde cero. 75 76 3. **`cycle_counter_get(void)`:** 77 * Devuelve el valor actual del contador de ciclos, es decir, cuántos ciclos 78 de reloj han transcurrido desde la última vez que se reinició. 79 80 4. **`cycle_counter_get_time_us(void)`:** 81 * Esta es la función más útil para la medición de rendimiento. Convierte el 82 número de ciclos de reloj a microsegundos ($µs$). 83 * La fórmula utilizada es: `DWT->CYCCNT / (SystemCoreClock / 1000000)`. 84 * **Explicación de la fórmula:** 85 * `SystemCoreClock` es una variable global que contiene la frecuencia 86 del CPU en Hercios (ciclos por segundo). 87 * `(SystemCoreClock / 1000000)` calcula cuántos ciclos de reloj ocurren 88 en un microsegundo. 89 * Al dividir el número total de ciclos transcurridos (`DWT->CYCCNT`) por 90 los ciclos por microsegundo, se obtiene el tiempo total transcurrido 91 en microsegundos. 92 93 **En resumen, este archivo es una herramienta de *profiling* o medición de 94 rendimiento. Permite medir con gran precisión cuánto tiempo tarda en ejecutarse 95 un bloque de código, lo cual es fundamental para calcular variables como el 96 `WCET` (Worst-Case Execution Time) en el archivo `app.c`.** 97 98 --- 99 100 ### **Análisis de `systick.c` (Retardo Bloqueante por SysTick)** 101 102 Este archivo implementa una función de retardo (delay) bloqueante con precisión 103 de microsegundos, utilizando el temporizador del sistema **(SysTick)** del 104 núcleo ARM Cortex-M. 105 106 #### **Funcionamiento Detallado:** 107 108 La única función, `systick_delay_us(uint32_t delay_us)`, funciona de la 109 siguiente manera: 110 111 1. **Cálculo de Ticks Necesarios:** 112 * Calcula el número total de "ticks" o ciclos de reloj del SysTick que se 113 necesitan para alcanzar el retardo deseado en microsegundos (`delay_us`). 114 * La fórmula `target = delay_us * (SystemCoreClock / 1000000UL)` es la misma 115 que se usa en `dwt.h` para determinar el número de ciclos de CPU 116 equivalentes al tiempo deseado. 117 118 2. **Bucle de Espera (Bloqueo):** 119 * La función entra en un bucle infinito `while(1)` que es el núcleo del 120 bloqueo. El microcontrolador no ejecutará ninguna otra instrucción de la 121 aplicación principal mientras esté dentro de este bucle. 122 * Dentro del bucle, lee continuamente el valor actual del contador SysTick 123 (`SysTick->VAL`), que es un contador que cuenta hacia abajo. 124 125 3. **Manejo del "Wrap-Around" (Reinicio del Contador):** 126 * El código maneja correctamente el caso en que el contador SysTick llega a 127 cero y se recarga. 128 * **Caso normal (`current <= start`):** Si el valor actual es menor que el 129 inicial, el tiempo transcurrido es simplemente la resta `start - current`. 130 * **Caso de reinicio (`current > start`):** Si el contador se reinició, el 131 valor actual será mayor que el inicial. El tiempo transcurrido se calcula 132 como `SysTick->LOAD + start - current`, donde `SysTick->LOAD` es el valor 133 desde el que se recarga el contador. Esto asegura que el tiempo se mida 134 correctamente incluso si el retardo abarca un reinicio del temporizador. 135 136 4. **Condición de Salida:** 137 * En cada iteración del bucle, se comprueba si el tiempo transcurrido 138 (`elapsed`) ha alcanzado o superado el objetivo (`target`). 139 * Cuando `elapsed >= target`, el bucle se rompe con `break` y la función 140 termina, completando el retardo. 141 142 **En resumen, este archivo ofrece una función de espera precisa pero 143 *bloqueante*. Es utilizada por el driver del display (`display.c`) para generar 144 los pequeños retardos necesarios para la comunicación con la pantalla LCD. El 145 término "bloqueante" es clave, ya que significa que mientras la función se 146 ejecuta, el microcontrolador no puede realizar ninguna otra tarea en el bucle 147 principal de la aplicación.**
