01 · Punto de partida
Abrir el chip
En el tema anterior usamos números como «32 KB de flash» o «2 KB de RAM» para dimensionar un producto. Ahora miramos adentro para saber qué hay detrás de esas cifras. En el curso de Microcontroladores ya conociste las partes por separado; acá las unimos en una arquitectura: cómo se conectan, qué comparten y dónde están los cuellos de botella.
Para un programador, esa mirada responde preguntas muy concretas. ¿Por qué el programa no arranca si no se habilita un reloj? ¿Por qué leer un pin parece una variable pero se comporta distinto? ¿Por qué una pequeña placa puede atender a la vez un puerto serie, un temporizador y un conversor sin que el programa haga nada? Las respuestas están en la organización del hardware.
Usamos como ejemplo una familia de microcontroladores de 32 bits con núcleo ARM Cortex-M, la más difundida del mercado. Los nombres de los registros y las direcciones son de un chip real (de la familia STM32F103, la de la placa «Blue Pill»), pero la idea es la misma en cualquier otro: lo que cambia son las direcciones, no el concepto.
02 · Vista general
Los bloques de un microcontrolador
Un microcontrolador integra en un único encapsulado todo lo necesario para ejecutar un programa y comunicarse con el exterior. Sin simplificar demasiado, hay seis grupos de bloques:
- El núcleo ejecuta las instrucciones del programa.
- Las memorias guardan el programa (flash) y los datos de trabajo (SRAM).
- Los periféricos hacen el trabajo especializado: generar tiempos, convertir señales, comunicar.
- Los pines unen los periféricos con el mundo exterior; cada pin puede atender varias funciones según cómo se configure.
- Los buses son los caminos internos por donde viajan direcciones y datos.
- El reloj, la alimentación y el reinicio sostienen el funcionamiento de todo lo anterior.
La diferencia esencial con un procesador de PC es la integración: en una computadora el procesador, la memoria y los periféricos son chips separados unidos por una placa. En un microcontrolador están dentro del mismo silicio, lo que reduce el costo, el tamaño y el consumo, y permite arrancar en microsegundos sin ningún componente de apoyo.
03 · El cerebro
El núcleo de procesamiento
El núcleo (CPU) lee instrucciones de la memoria, las decodifica y las ejecuta. Tiene unos pocos registros, que son memorias internas de muy alta velocidad: en un núcleo Cortex-M hay trece de propósito general (R0 a R12) y tres con función especial: el puntero de pila SP, el registro de enlace LR (donde se guarda la dirección de retorno de una función) y el contador de programa PC, que señala la próxima instrucción. La ALU hace las operaciones aritméticas y lógicas sobre esos registros.
Una variable de tu programa vive en RAM, pero para sumar dos variables el procesador las carga en registros, suma y vuelve a guardar el resultado. Es la arquitectura load-store y explica por qué una línea simple de C genera varias instrucciones, y por qué una instrucción no siempre es «atómica» (tema 16).
Ancho de datos y familias de núcleos
El «ancho» (8, 16 o 32 bits) es el tamaño natural de los datos que maneja el núcleo. Operar con datos más grandes que el ancho exige varias instrucciones: sumar dos enteros de 32 bits en un microcontrolador de 8 bits lleva cuatro operaciones. Por eso los de 32 bits dominan los diseños nuevos, aun para tareas sencillas.
| Núcleo | DMIPS/MHz | Características | Uso típico |
|---|---|---|---|
| Cortex-M0+ | 0,93 | El más sencillo y eficiente en energía; juego de instrucciones reducido | Sensores, juguetes, dispositivos a pila |
| Cortex-M3 | 1,25 | Instrucciones Thumb-2 completas, división por hardware | Control general, industria |
| Cortex-M4 / M4F | 1,25 | Instrucciones de procesamiento de señales; unidad de punto flotante opcional (F) | Control de motores, audio, sensores con cálculo |
| Cortex-M7 | 2,14 | Caché, mayor frecuencia, punto flotante de doble precisión opcional | Gráficos, comunicaciones de alta velocidad |
Los números sirven para comparar a igual frecuencia. Un M4 a 80 MHz rinde cerca de 100 DMIPS (80 × 1,25), que es el orden de magnitud de los chips del tema anterior. Un detalle importante es la unidad de punto flotante: sin ella, cada operación con float se resuelve por software con decenas o cientos de ciclos. Por eso en los núcleos que no la tienen se prefiere aritmética de enteros.
Von Neumann y Harvard
En la arquitectura von Neumann, programa y datos comparten memoria y un único bus; en la Harvard, hay memorias y buses separados para cada uno, de modo que se puede traer la próxima instrucción mientras se lee o escribe un dato. Muchos microcontroladores son Harvard o casi (los AVR, por ejemplo, tienen espacios de direcciones distintos para el código y los datos); los Cortex-M tienen buses separados para instrucciones, datos y periféricos, pero un único espacio de direcciones. Para el programador de C, la consecuencia práctica de los AVR es que las constantes en flash necesitan un acceso especial (PROGMEM); en los Cortex-M, una constante const se lee como cualquier otra variable.
04 · Dónde se guarda todo
Memorias: flash, SRAM y las demás
Un microcontrolador tiene varios tipos de memoria, cada uno con un papel. Conocerlos evita errores clásicos, como guardar un dato en un lugar que se borra al apagar:
| Memoria | ¿Conserva sin energía? | Qué guarda | Detalles importantes |
|---|---|---|---|
| Registros de la CPU | No | Datos que se están operando | Pocos, velocidad máxima |
| SRAM | No | Variables, pila y buffers | Rápida y escasa; sin límite de escrituras |
| Flash | Sí | Programa y constantes | Se borra por bloques y se programa; vida de unos 10.000 a 100.000 ciclos de borrado |
| EEPROM | Sí | Configuración y calibración | Se escribe por byte; unos 100.000 ciclos; muchos chips no la tienen y la emulan con flash |
| ROM | Sí | Código de fábrica, como el cargador de arranque | No se modifica |
| Memorias externas | Según el tipo | Datos masivos, imágenes, registros | Flash SPI, tarjetas SD; accesos más lentos (tema 41) |
Dos propiedades de la flash condicionan al programador. Primera: no se puede escribir como la RAM. Para cambiar un valor hay que borrar un bloque entero (una «página») y reprogramarlo, lo cual lleva milisegundos y bloquea la lectura de la misma flash en muchos chips. Segunda: se desgasta. Guardar un contador en flash cada segundo agota los ciclos en unos días; por eso los datos que cambian seguido van a la RAM y se pasan a flash con cuidado (tema 40).
Y una tercera, de rendimiento: la flash es más lenta que el núcleo. A frecuencias altas, el procesador debe esperar varios ciclos a que la flash entregue una instrucción (los llamados estados de espera). En la familia de ejemplo, hasta 24 MHz no hace falta ninguno; entre 24 y 48 MHz, uno; entre 48 y 72 MHz, dos. Para disimularlo, los chips incluyen un buffer de precarga que trae las próximas instrucciones por adelantado. Es la razón por la cual duplicar la frecuencia no duplica la velocidad real del programa.
05 · Hardware especializado
Periféricos: la puerta al mundo físico
Un periférico es un bloque de hardware dentro del chip que hace una tarea específica sin que la CPU la repita instrucción por instrucción. Un temporizador cuenta pulsos por su cuenta, un conversor mide una tensión y avisa cuando termina, una UART envía un byte bit por bit al ritmo correcto. La CPU solo lo configura, lo pone en marcha y recoge el resultado.
| Periférico | Qué hace | En el curso |
|---|---|---|
| GPIO | Lee y escribe el nivel de cada pin digital | Temas 9 y 12 |
| Temporizadores | Cuentan tiempo, generan PWM, miden pulsos | Tema 17 |
| ADC | Convierte una tensión analógica en un número (por ejemplo, 12 bits) | Cursos previos y tema 9 |
| UART, SPI, I²C | Comunican con otros chips y equipos | Curso anterior |
| Controlador de interrupciones | Avisa a la CPU cuando ocurre un evento | Tema 15 |
| DMA | Copia datos entre memoria y periféricos sin usar la CPU | Drivers de comunicación (tema 10) |
| RTC y watchdog | Reloj de tiempo real; reinicio ante cuelgues | Temas 34 y 38 |
| USB, CAN, Ethernet | Interfaces de comunicación avanzadas (según el modelo) | Curso anterior |
Los periféricos son el motivo por el cual un microcontrolador de 8 MHz puede hacer cosas que un procesador de 1 GHz sin ellos no lograría con precisión: un temporizador genera un pulso de 1,000 ms exactos mientras el programa hace otra cosa. Cuanto más trabajo se delega al hardware, menos tiempo de CPU se gasta, y más tiempo puede estar dormida (tema 34).
Los pines del chip son limitados y compartidos. Cada uno puede ser una entrada o salida digital simple, una entrada del ADC o la línea TX de una UART, según cómo se configure. Elegir qué función va en qué pin forma parte del diseño de hardware, y el firmware debe respetarlo.
06 · Cómo se conecta todo
Buses y mapa de memoria
Los bloques se comunican por buses: conjuntos de líneas que llevan una dirección y un dato. El núcleo es el maestro y pide «leer lo que hay en la dirección X» o «escribir este valor en la dirección X»; la memoria o el periférico dueño de esa dirección responde. Los periféricos rápidos y los lentos cuelgan de buses distintos (en nuestra familia de ejemplo, AHB, APB2 y APB1), para que los lentos no frenen a los demás.
En un Cortex-M, todos los componentes comparten un único espacio de direcciones de 32 bits (4 GB teóricos). Cada bloque ocupa un rango; el conjunto se llama mapa de memoria:
| Desde | Hasta | Contenido |
|---|---|---|
0x0800_0000 | 0x0800_FFFF | Flash (64 KB): el programa |
0x1FFF_F000 | 0x1FFF_F7FF | Memoria del sistema: cargador de arranque de fábrica |
0x2000_0000 | 0x2000_4FFF | SRAM (20 KB): variables y pila |
0x4000_0000 | 0x4000_FFFF | Periféricos del bus APB1 (temporizadores, USART2, I²C…) |
0x4001_0000 | 0x4001_3FFF | Periféricos del bus APB2 (GPIO, ADC1, SPI1, USART1…) |
0x4002_0000 | 0x4002_33FF | Periféricos del bus AHB (DMA, RCC, interfaz de la flash) |
0xE000_0000 | 0xE00F_FFFF | Periféricos del propio núcleo (SysTick, NVIC) |
Lo decisivo es que los periféricos están en ese mismo espacio. Cada periférico expone unos pocos registros: posiciones de 32 bits con una dirección fija, que no guardan datos comunes sino que controlan el hardware. Esta técnica se llama entrada y salida mapeada en memoria. Escribir en el registro de salida de un puerto cambia la tensión de los pines; leer el registro de estado de la UART dice si llegó un byte. Para el programa son direcciones de memoria; para el chip, órdenes.
Por eso los registros no se comportan como variables: leer uno puede cambiar su valor (al leer el dato recibido, se borra el aviso de «dato disponible»), un bit puede ser de solo lectura o de solo escritura, y el contenido puede variar sin que el programa haga nada (un pin cambia cuando se le aplica una señal). Para que el compilador no «optimice» esos accesos, los registros se declaran volatile, como se ve en el ejemplo siguiente.
07 · Lo que mantiene vivo al chip
Reloj, alimentación y reinicio
Tres sistemas de apoyo condicionan el comportamiento de todo lo demás y son la fuente de muchos errores iniciales:
- El reloj. Todo en el chip avanza al ritmo de una señal periódica. Puede venir de un oscilador interno RC (barato, poco preciso, arranca enseguida) o de un cristal de cuarzo externo (preciso, imprescindible para comunicaciones y relojes). Un circuito multiplicador, el PLL, permite generar frecuencias más altas a partir de una base baja. La distribución del reloj es un árbol: el reloj principal se divide para alimentar los distintos buses y periféricos.
- La alimentación. El chip trabaja con una o varias tensiones (3,3 V, 1,8 V). Incluye reguladores internos y detectores de caída de tensión. Los modos de bajo consumo apagan partes del chip (tema 34).
- El reinicio. Un circuito de reinicio por encendido mantiene al procesador detenido hasta que la tensión se estabiliza, y ante una caída peligrosa lo reinicia (brown-out). Otras fuentes de reinicio son el pin de reset, el watchdog y el propio software.
Una consecuencia sorprendente para quien llega desde la PC: los periféricos están apagados al arrancar. Para ahorrar energía, el reloj de cada periférico está cortado de fábrica (clock gating) y el programa debe habilitarlo antes de usarlo. Escribir en los registros de un puerto sin haberle dado reloj no produce error ni efecto: la escritura se pierde en silencio. Es, probablemente, el error número uno de quien configura un microcontrolador por primera vez, y lo vas a reproducir en el apartado de Python.
La frecuencia del reloj es un compromiso entre rendimiento y consumo: a mayor frecuencia, más potencia dinámica (aproximadamente proporcional a la frecuencia), más estados de espera en la flash y más ruido eléctrico. Muchos productos arrancan con el oscilador interno de pocos megahercios y solo suben a la frecuencia máxima cuando hay trabajo pesado.
08 · Más allá del encapsulado
Los componentes que rodean al chip
El microcontrolador casi nunca trabaja solo. Alrededor suyo hay componentes sin los cuales no arranca o no es confiable. Reconocerlos en un esquema y en una placa es parte del oficio del programador de sistemas embebidos:
| Componente | Función | Si falta o está mal |
|---|---|---|
| Regulador de tensión | Convierte la entrada (batería, 5 V, 12 V) a la tensión del chip | El chip se reinicia o se daña; el ruido de la fuente perturba las medidas |
| Capacitores de desacople (≈ 100 nF por cada par de pines de alimentación) | Entregan picos de corriente y filtran ruido | Reinicios aleatorios, lecturas del ADC ruidosas, fallas intermitentes |
| Cristal y capacitores de carga | Reloj preciso | El chip no arranca o la velocidad serie no coincide |
| Circuito de reinicio (resistencia y pulsador) | Permite reiniciar y mantiene el pin en un nivel definido | Reinicios espontáneos por pin flotante |
| Conector de programación y depuración | Grabar el firmware y depurar (SWD: 2 señales más masa) | Hay que recurrir a un cargador de arranque o no se puede depurar |
| Resistencias de pull-up y pull-down | Definen el nivel de pines que de otro modo flotan | Lecturas erráticas y consumo extra |
| Protecciones (diodos, filtros) | Defienden contra descargas, polaridad invertida y picos | Fallas en campo por descargas electrostáticas |
Muchas fallas «de software» son en realidad fallas de hardware. Un reinicio aleatorio puede ser un desacople faltante, una lectura saltarina un problema de masa, una comunicación que falla un cristal de poca precisión. Aprender a sospechar del hardware antes de cambiar el código ahorra mucho tiempo (tema 46).
09 · Práctica en C
Un periférico es una dirección: LED por registros
Para ver el mapa en acción hacemos algo muy simple: encender y apagar el LED de una placa «Blue Pill» (conectado al pin PC13) sin ninguna biblioteca, escribiendo directamente en los registros. Hacen falta tres pasos: darle reloj al puerto C (bit 4 de RCC_APB2ENR), configurar el pin 13 como salida (campo de 4 bits en GPIOC_CRH) y cambiar su nivel en GPIOC_ODR.
#include <stdint.h>
#define RCC_APB2ENR (*(volatile uint32_t *)0x40021018) // habilitación de relojes
#define GPIOC_CRH (*(volatile uint32_t *)0x40011004) // configuración de pines 8 a 15
#define GPIOC_ODR (*(volatile uint32_t *)0x4001100C) // nivel de salida
int main(void) {
RCC_APB2ENR |= (1u << 4); // 1) dar reloj al puerto C (IOPCEN)
GPIOC_CRH &= ~(0xFu << 20); // 2) limpiar la configuración del pin 13...
GPIOC_CRH |= (0x2u << 20); // ...y dejarlo como salida (push-pull, 2 MHz)
while (1) {
GPIOC_ODR ^= (1u << 13); // 3) invertir el nivel del pin 13
for (volatile uint32_t i = 0; i < 400000; i++) { } // espera tosca
}
}
La línea (*(volatile uint32_t *)0x4001100C) se lee así: tomar el número 0x4001100C, interpretarlo como un puntero a un entero de 32 bits volatile, y acceder a lo que apunta. Después, GPIOC_ODR ^= ... es un acceso normal a memoria. Dos detalles: el LED de esa placa está conectado de forma que se enciende cuando el pin está en bajo, y la espera con un bucle vacío es provisoria: depende del compilador y de la frecuencia, y desperdicia CPU. La reemplazaremos por un temporizador en el bloque de tiempo real.
¿Y por qué volatile? Considerá este fragmento que espera a que un bit de estado se ponga en 1:
uint32_t *estado = (uint32_t *)0x40013800; // SIN volatile (incorrecto)
while ((*estado & (1u << 5)) == 0) { } // esperar a que llegue un dato
// El compilador razona: «nadie en este programa modifica *estado, así que su
// valor no cambia». Lee una sola vez y convierte el bucle en uno infinito.
Con volatile se le indica al compilador que el valor puede cambiar por causas ajenas al programa, y que debe leerlo realmente en cada acceso. Es una de las diferencias más importantes entre programar un registro y programar una variable común, y vuelve a ser clave con las interrupciones (tema 16).
10 · Analogía con programación
Un mapa de memoria en Python
Para sentir cómo se comporta un mapa de memoria sin placa, este programa modela un chip diminuto: una lista de regiones con su rango de direcciones, una SRAM, el registro de habilitación de relojes y el registro de salida del puerto C. Muestra a qué región pertenece cada dirección, intenta escribir el registro de salida antes y después de darle reloj al puerto, y finalmente escribe en una dirección sin asignar.
Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
REGIONES = [ # (inicio, fin sin incluir, nombre)
(0x08000000, 0x08010000, "Flash"),
(0x20000000, 0x20005000, "SRAM"),
(0x40011000, 0x40011400, "GPIOC"),
(0x40021000, 0x40021400, "RCC"),
]
RCC_APB2ENR = 0x40021018
GPIOC_ODR = 0x4001100C
def region(direccion):
for inicio, fin, nombre in REGIONES:
if inicio <= direccion < fin:
return nombre
return None
class Chip:
def __init__(self):
self.sram = {}
self.apb2enr = 0 # tras el reinicio: todos los relojes cortados
self.gpioc_odr = 0
def escribir(self, direccion, valor):
if direccion == RCC_APB2ENR:
self.apb2enr = valor
elif direccion == GPIOC_ODR:
if self.apb2enr & (1 << 4): # ¿el puerto C tiene reloj?
self.gpioc_odr = valor & 0xFFFF
else:
print(" (el puerto C no tiene reloj: la escritura se pierde)")
elif region(direccion) == "SRAM":
self.sram[direccion] = valor
else:
raise MemoryError(f"dirección {direccion:#010x} sin asignar")
for d in (0x08000100, 0x20000010, 0x4001100C, 0x40021018, 0x50000000):
print(f"{d:#010x} -> {region(d) or 'sin asignar'}")
chip = Chip()
print("\n1) escribir ODR sin dar reloj al puerto:")
chip.escribir(GPIOC_ODR, 1 << 13)
print(" ODR =", hex(chip.gpioc_odr))
print("2) habilitar el reloj (bit 4 de RCC_APB2ENR) y repetir:")
chip.escribir(RCC_APB2ENR, 1 << 4)
chip.escribir(GPIOC_ODR, 1 << 13)
print(" ODR =", hex(chip.gpioc_odr))
print(" LED (se enciende con el pin en bajo):",
"apagado" if chip.gpioc_odr & (1 << 13) else "encendido")
print("3) escribir en una dirección sin asignar:")
try:
chip.escribir(0x50000000, 1)
except MemoryError as error:
print(" HardFault:", error)
Resultado: las cinco direcciones caen en Flash, SRAM, GPIOC, RCC y «sin asignar». En el paso 1 la escritura se pierde sin error y ODR sigue en 0x0. En el paso 2, con el reloj habilitado, ODR vale 0x2000 y el LED queda «apagado» (el pin en alto). En el paso 3, la dirección 0x50000000 no pertenece a nadie y el acceso falla. Probá comentar la línea que habilita el reloj: es exactamente el error silencioso de la vida real. Probá también escribir 0 en ODR para encender el LED.
El modelo es un juguete, pero reproduce dos comportamientos reales: un registro puede ignorar una escritura según el estado del hardware (aquí, el reloj) y un acceso a una dirección inexistente es un error de bus, no un valor cualquiera. Ambos son causa frecuente de horas de depuración.
11 · Exploración interactiva
Acceder a una dirección del mapa
Este laboratorio implementa el mapa de memoria de la familia de ejemplo. Elegí una dirección de la lista, o escribí cualquiera en hexadecimal, y decidí si querés leer o escribir. Verás a qué región pertenece, por qué bus se llega, qué dispositivo o registro la atiende, y qué efecto tendría realmente el acceso.
Espacio de direcciones del microcontrolador
Elegí una dirección para ver qué hay allí.
Recorrido guiado
- Con
0x4001100Cy «Escribir»: es el registro ODR del puerto C. Escribir ahí no guarda un dato, da una orden al hardware. Cambiá a «Leer»: ahora consulta el estado del hardware. - Pasá a
0x40011008(IDR) y escribí: es de solo lectura y la escritura se ignora. Probá0x40011010(BSRR) y leé: es de solo escritura. - Elegí
0x08000100y alterná entre leer y escribir. Leer es lo normal (así se obtiene el programa); escribir no modifica la flash, que se borra por páginas con una secuencia especial. - Compará
0x20000010(SRAM) con0x4001100C: ambas se leen y se escriben, pero una guarda datos y la otra controla el hardware. Para el programa son el mismo tipo de acceso. - Probá
0x08010000y0x20005000: son las primeras direcciones después de la flash y de la SRAM. Los 64 KB y los 20 KB terminan exactamente ahí, y el acceso falla con un HardFault. Es lo que pasa cuando un puntero se desborda. - Escribí una dirección propia, como
40010C0C. Pertenece a GPIOB (0x40010C00) con desplazamiento0xC: el registro ODR del puerto B. La estructura de los puertos se repite con 0x400 de separación.
Desafío: dos registros, un mismo bit
El pin 13 del puerto C se puede apagar o encender con GPIOC_ODR (0x4001100C) o con GPIOC_BSRR (0x40011010). Con ODR se hace «leer, modificar, escribir», que puede dar problemas si una interrupción cambia otros bits del mismo registro entre la lectura y la escritura. Con BSRR basta una sola escritura de un bit que pone a 1 o a 0 solo el pin elegido, sin tocar los demás: es atómica. Por eso los fabricantes agregan registros de «poner» y «borrar» bits, y se prefieren en código con interrupciones.
12 · Método de trabajo
Cómo leer la arquitectura de un chip nuevo
Cuando te toque un microcontrolador desconocido, no empieces por el código. Recorré los documentos del fabricante en este orden:
- Hoja de datos (datasheet). Núcleo, frecuencia máxima, tamaños de flash y RAM, periféricos disponibles, tensiones, consumo y distribución de pines.
- Diagrama de bloques. Qué periférico cuelga de qué bus y qué relojes necesita.
- Mapa de memoria. Dónde están la flash, la RAM y los bloques de periféricos.
- Árbol de reloj. De dónde sale la frecuencia de cada bloque y cómo se habilita.
- Tabla de funciones de pines. Qué señales pueden salir por cada pin y cómo se eligen.
- Manual de referencia. Registros del periférico que vas a usar: dirección, bits y valores de reinicio.
- Notas de errores (errata). Fallas conocidas del silicio y cómo evitarlas; ahorran días de depuración.
Al configurar un periférico, el orden casi siempre es el mismo: dar reloj, configurar pines, configurar el periférico, habilitar interrupciones si hacen falta, y por último activarlo. Es el patrón de la inicialización del hardware (tema 9).
13 · Comprensión
Confusiones frecuentes
«Escribir en un registro es como escribir en una variable»
Se hace con la misma sintaxis, pero el efecto es distinto: un registro controla hardware. Su valor puede cambiar por sí solo, leerlo puede tener efectos, algunos bits son de solo lectura y la escritura puede ignorarse si el periférico no tiene reloj. Por eso se declara volatile y se lee el manual.
«Más megahercios siempre es más velocidad»
La flash impone estados de espera y los periféricos lentos limitan lo que se puede hacer. Además, el consumo sube con la frecuencia. El rendimiento real depende del núcleo (DMIPS por MHz), de la memoria y del código.
«La flash se usa como una memoria más»
Se lee como cualquier otra, pero no se escribe igual: se borra por páginas, es lenta de programar y tiene vida limitada. Los datos que cambian seguido van en RAM, y los permanentes se guardan en flash con cuidado.
«Si el código es correcto, el hardware responde»
Un periférico sin reloj, un pin con otra función asignada o un cristal que no arranca ignoran el código más perfecto. Cuando algo no responde, revisá primero la alimentación, el reloj y la configuración de pines.
«El microcontrolador es un procesador pequeño»
Es una computadora completa en un chip, con memoria y periféricos propios. Esa integración lo hace distinto de un procesador, que necesita memoria externa y chips auxiliares para funcionar.
«Los componentes externos son detalles del electrónico»
Un desacople faltante o un cristal inadecuado se manifiesta como fallas de software: reinicios al azar, comunicaciones con errores. Quien programa debe conocerlos para poder diagnosticar.
14 · Práctica guiada
Predecí antes de comprobar
1. Dónde vive cada cosa
Indicá en qué memoria se guarda cada elemento de un programa: (a) el código de main(); (b) una tabla const de 256 valores; (c) un contador global; (d) las variables locales de una función; (e) la calibración de un sensor que debe sobrevivir a un apagón.
Ver solución
(a) y (b) en la flash. (c) y (d) en la SRAM (el contador en la zona de globales, las locales en la pila o en registros). (e) en memoria no volátil: EEPROM, o una página de flash reservada para configuración.
2. Una dirección de periférico
En la familia de ejemplo, GPIOB empieza en 0x40010C00 y su registro ODR está en el desplazamiento 0xC. ¿Cuál es la dirección de GPIOB_ODR? Escribí la macro en C.
Ver solución
0x40010C00 + 0xC = 0x40010C0C. La macro: #define GPIOB_ODR (*(volatile uint32_t *)0x40010C0C).
3. Fin de la SRAM
La SRAM de 20 KB empieza en 0x20000000. ¿Cuál es la última dirección válida y cuál es la primera que ya no existe?
Ver solución
20 KB = 20 × 1024 = 20480 bytes = 0x5000. La última dirección válida es 0x20000000 + 0x5000 − 1 = 0x20004FFF y la primera inexistente es 0x20005000. Un acceso allí produce un HardFault.
4. Estados de espera
A 72 MHz la flash necesita 2 estados de espera, de modo que cada lectura tarda 3 ciclos. Sin el buffer de precarga, ¿cuántas lecturas de flash por segundo puede hacer el procesador?
Ver solución
72 millones de ciclos por segundo / 3 ciclos por lectura = 24 millones de lecturas por segundo. A 24 MHz sin estados de espera haría también 24 millones por segundo: en cuanto a la flash, correr a 72 MHz no es más rápido que a 24, y el buffer de precarga es lo que recupera parte de la diferencia.
5. El puerto que no responde
Escribís GPIOA_ODR = 0xFFFF y los pines siguen en bajo. El código compila sin errores y el pin está bien configurado como salida. ¿Cuál es la primera sospecha?
Ver solución
Lo más probable es que no se habilitó el reloj del puerto A (bit 2 de RCC_APB2ENR en esta familia). Mientras no tiene reloj, el periférico ignora las escrituras. Otras causas: el pin tiene asignada otra función alternativa o hay un problema de alimentación.
6. Resolución de un conversor
Un ADC de 12 bits mide entre 0 y 3,3 V. ¿Cuántos voltios representa cada paso (el LSB)? Si la lectura es 2048, ¿qué tensión hay en el pin?
Ver solución
12 bits = 4096 niveles. LSB = 3,3 / 4096 ≈ 0,806 mV. Para 2048: 2048 × 0,806 mV ≈ 1,65 V, la mitad de la escala. Esta cuenta aparece en cada firmware que lee un sensor analógico.
15 · Síntesis
Ideas para recordar
- Un microcontrolador integra en un chip el núcleo, la flash, la SRAM, los periféricos, los relojes y la gestión de energía, unidos por buses internos.
- El núcleo opera sobre registros (arquitectura load-store); el ancho de datos y la familia (Cortex-M0+, M3, M4, M7) determinan el rendimiento por megahercio y si hay punto flotante por hardware.
- La flash guarda el programa y las constantes, es lenta de escribir y se desgasta; la SRAM guarda variables y pila, es rápida y se pierde sin energía; la EEPROM o una página de flash guardan la configuración.
- Los periféricos hacen trabajo especializado sin la CPU; el programa los configura, los activa y recoge los resultados.
- En un Cortex-M, memorias y periféricos comparten un único espacio de direcciones; cada periférico expone registros con direcciones fijas (entrada y salida mapeada en memoria).
- Los registros no son variables: pueden cambiar solos, tener bits de solo lectura o escritura y ser ignorados sin reloj. Se declaran
volatiley se leen en el manual de referencia. - Al arrancar, los periféricos están sin reloj: hay que habilitarlos antes de usarlos. El orden habitual es dar reloj, configurar pines, configurar el periférico y activarlo.
- Alrededor del chip hay componentes críticos (regulador, desacople, cristal, reinicio, conector de depuración); muchas fallas «de software» son fallas de hardware.
¿Listo para el tema 4?
Antes de avanzar, dibujá de memoria los bloques de un microcontrolador, explicá por qué leer un registro no es lo mismo que leer una variable y decí qué pasa si escribís en un puerto al que no le diste reloj. Si podés justificar las tres respuestas, entendés cómo se organiza el hardware sobre el que corre el firmware.
En el próximo tema pasamos al otro lado: la arquitectura de software. Veremos cómo se organiza el programa que corre sobre este hardware: bare-metal, superloop, RTOS y Linux embebido, y cuándo conviene cada uno.