01 · Punto de partida
Del editor al chip
Cuando programás una aplicación para tu computadora, el ciclo es corto y casi invisible: escribís, compilás y ejecutás en la misma máquina. Con un microcontrolador, cada vuelta de ese ciclo atraviesa varias fronteras: el código se traduce para otro tipo de procesador, se ensambla en una imagen que respeta el mapa de memoria del tema 3, se transfiere por un cable a la memoria flash del chip y recién entonces se ejecuta. Y para saber si funcionó, hay que mirar algo que ocurre afuera de la pantalla: un LED, un pin, una medición.
Muchos principiantes usan un entorno que esconde todo eso detrás de un botón «Subir». Es cómodo, pero cuando algo falla (un error de enlace, una placa que no responde, un firmware que no entra) hay que entender qué pasa detrás. Y a escala profesional, el ciclo es una de las variables que más pesa en la productividad: la diferencia entre probar un cambio cada 10 segundos o cada 60 se nota en la calidad del producto.
Nos apoyamos en la cadena de herramientas GNU para ARM, la más común, pero el recorrido es el mismo para AVR, ESP32 o RISC-V: cambian los nombres de los programas, no la secuencia.
02 · Una máquina escribe, otra ejecuta
Compilación cruzada
Un compilador traduce código fuente a instrucciones de un procesador concreto. El compilador que viene con tu sistema genera instrucciones para el procesador de tu PC (x86-64 o ARM de 64 bits). Pero el programa debe correr en un Cortex-M, un AVR o un ESP32, con otro juego de instrucciones. La solución es la compilación cruzada (cross-compilation): un compilador que corre en una máquina (el host, tu PC) y genera código para otra (el target, el microcontrolador).
Por eso el archivo que obtenés no se puede ejecutar en la PC: sus instrucciones son de otro procesador. Y por eso cada familia de chips tiene su propia cadena de herramientas (toolchain): el conjunto de programas que generan, enlazan y examinan el código para ese destino.
| Familia | Compilador (prefijo de las herramientas) | Se encuentra en |
|---|---|---|
| ARM Cortex-M (STM32, RP2040, nRF…) | arm-none-eabi-gcc | GNU Arm Embedded Toolchain; también Arm Compiler, IAR, Keil |
| AVR (Arduino Uno, ATmega) | avr-gcc | Arduino IDE, PlatformIO, paquetes del sistema |
| ESP32 (Xtensa) | xtensa-esp32-elf-gcc | ESP-IDF y Arduino-ESP32 |
| ESP32-C3 y otros RISC-V | riscv32-esp-elf-gcc o riscv-none-elf-gcc | ESP-IDF, SDK de cada fabricante |
El nombre del prefijo sigue una convención: arm-none-eabi significa «arquitectura ARM, sin sistema operativo (none), con la interfaz binaria para sistemas embebidos (eabi)». Compará con aarch64-linux-gnu, que genera código ARM de 64 bits para Linux. Que el sistema sea none es la marca de un programa bare-metal: nadie va a cargarlo, ninguna biblioteca del sistema está disponible, y todo lo que no escribas (el arranque, la salida de printf) no existe.
03 · La línea de montaje
Las etapas de la construcción
El paso de «archivos .c» a «firmware grabable» no es uno solo. Atraviesa una cadena de herramientas, cada una con una tarea:
- Preprocesamiento. Se expanden
#include,#definey#ifdef. Una macro de configuración puede cambiar qué código existe. - Compilación. Cada archivo
.cse traduce a instrucciones del procesador destino, y se guarda en un archivo objeto (.o). Cada uno se compila por separado y sin saber dónde quedará en memoria. - Enlace. El enlazador une los objetos, resuelve las referencias entre ellos (la llamada a una función de otro archivo) y decide la dirección final de cada pieza siguiendo el linker script, que describe el mapa de memoria del chip (flash en
0x08000000, RAM en0x20000000…). También agrega el código de arranque (startup.o). Genera el.elfy, si se pide, un.mapque lista dónde quedó todo. - Conversión. El ELF contiene mucho más que el programa (símbolos, información de depuración). Para grabar se extrae la imagen pura con
objcopy, en el formato que pida el programador. - Grabación. Un programador transfiere la imagen a la memoria flash del chip, lo reinicia y el firmware arranca.
Los mismos pasos, con las herramientas de la familia ARM, en la línea de comandos:
$ arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -Os -Wall -c main.c -o main.o
$ arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -Os -Wall -c sensor.c -o sensor.o
$ arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -nostartfiles -T stm32f103.ld \
main.o sensor.o startup.o -o firmware.elf -Wl,-Map=firmware.map
$ arm-none-eabi-size firmware.elf
$ arm-none-eabi-objcopy -O binary firmware.elf firmware.bin
Conviene entender las opciones. -mcpu=cortex-m3 -mthumb indica el procesador y el juego de instrucciones destino: si no coincide con el chip real, el programa puede no arrancar (lo vas a ver como un HardFault inmediato). -Os optimiza por tamaño y -Wall activa los avisos útiles. -nostartfiles y -T reemplazan el arranque y el mapa de memoria genéricos por los del chip. En los próximos temas (7 y 8) vemos qué contienen el linker script y el código de arranque.
04 · Qué se graba
Formatos del firmware: ELF, BIN, HEX y UF2
Un mismo firmware puede presentarse en distintos formatos, y cada herramienta de grabación prefiere uno:
| Formato | Qué contiene | Para qué sirve |
|---|---|---|
| ELF | Código y datos con sus direcciones, tabla de símbolos e información de depuración | Depurar y grabar con programadores avanzados; es la salida «completa» del enlazador |
| BIN | Los bytes crudos de la imagen, sin direcciones | Grabación simple y actualización por bootloader; hay que conocer la dirección de destino |
| HEX (Intel HEX) | Texto con la dirección, los datos y un checksum en cada línea | Herramientas de grabación clásicas, envío por canales de texto; se pueden grabar zonas no contiguas |
| UF2 | Bloques de 512 bytes con la dirección de destino y marcas de control | Grabar copiando un archivo a una «unidad USB» que expone el bootloader (RP2040, placas Adafruit) |
Para ver qué contiene un Intel HEX, mirá esta línea de ejemplo (escrita con espacios para separar los campos; en el archivo real va todo junto: :10010000214601360121470136007EFE09D2190140):
: 10 0100 00 214601360121470136007EFE09D21901 40
│ │ │ │ │ │
│ │ │ │ │ └ checksum
│ │ │ │ └ 16 bytes de datos
│ │ │ └ tipo 00: registro de datos
│ │ └ dirección 0x0100
│ └ cantidad de bytes: 0x10 = 16
└ marca de inicio de registro
Cada línea es autosuficiente: lleva su dirección y su verificación. El checksum es el complemento a dos de la suma de todos los bytes anteriores de la línea, de modo que sumar la línea completa da cero (módulo 256). Un archivo HEX dañado por el camino se detecta por línea, algo que un BIN crudo no permite: si se pierde un byte, el resto se desplaza y el firmware queda corrupto sin ningún aviso. Por eso, en cualquier canal poco confiable, los formatos con verificación son preferibles (tema 43).
05 · Automatizar
Sistemas de construcción
Escribir esos comandos a mano funciona para dos archivos y no para doscientos. Un sistema de construcción sabe qué archivos dependen de cuáles y recompila solo lo que cambió. Hay tres niveles de herramientas, de menos a más automático:
- Make (con un
Makefile) o CMake: explícitos, reproducibles, fáciles de ejecutar desde una terminal o un servidor de integración continua. Es lo que usan los proyectos profesionales y los SDK de muchos fabricantes (por ejemplo, el de la Raspberry Pi Pico usa CMake). - Entornos integrados del fabricante: STM32CubeIDE, MCUXpresso, Keil, IAR. Generan los archivos de proyecto y de arranque, e integran el depurador. Muy cómodos para empezar; a veces difíciles de automatizar.
- Plataformas de alto nivel: el IDE de Arduino y PlatformIO. Ocultan casi todo detrás de una biblioteca y un botón; PlatformIO además permite configurar placa y bibliotecas en un archivo de texto y ejecutarse por línea de comandos.
Un Makefile mínimo para el ejemplo anterior se ve así:
CC = arm-none-eabi-gcc
CFLAGS = -mcpu=cortex-m3 -mthumb -Os -Wall -Wextra -ffunction-sections -fdata-sections
LDFLAGS = -T stm32f103.ld -nostartfiles -Wl,--gc-sections -Wl,-Map=build/firmware.map
OBJS = build/main.o build/sensor.o build/startup.o
all: build/firmware.bin
build/firmware.elf: $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $@
arm-none-eabi-size $@
build/%.o: src/%.c
@mkdir -p build
$(CC) $(CFLAGS) -c $< -o $@
build/firmware.bin: build/firmware.elf
arm-none-eabi-objcopy -O binary $< $@
flash: build/firmware.elf
openocd -f interface/stlink.cfg -f target/stm32f1x.cfg \
-c "program build/firmware.elf verify reset exit"
Cada regla dice «para construir esto necesito aquello y se hace con este comando». Si modificás sensor.c, make recompila solo sensor.o y vuelve a enlazar: es la compilación incremental. (Las líneas de comandos de cada regla empiezan con un carácter de tabulación.) Las opciones -ffunction-sections -fdata-sections junto con --gc-sections hacen que el enlazador descarte las funciones y variables que nadie usa, y reducen el tamaño del firmware sin tocar el código.
Dos hábitos valen oro. El primero: que compilar sea un solo comando, el mismo en tu máquina, la de un colega y el servidor. El segundo: activar los avisos del compilador (-Wall -Wextra) y tratarlos como errores en serio. Muchos errores de firmware que tardan días en aparecer (variables sin inicializar, conversiones que truncan, comparaciones con signo) los marca el compilador en el primer segundo.
06 · Cargar el programa
Grabar el firmware en el chip
Una vez construida la imagen, hay que llevarla a la flash del microcontrolador. Existen cuatro vías principales, con distinto costo y comodidad:
| Método | Cómo funciona | Ventajas | Límites |
|---|---|---|---|
| Programador/depurador (SWD o JTAG) | Un adaptador (ST-Link, J-Link, CMSIS-DAP) se conecta a 2 pines (SWD) o 4 (JTAG) del chip y escribe la flash directamente | Funciona con el chip vacío; permite depurar; ~40 KB/s y más | Hace falta el adaptador y acceso a los pines |
| Bootloader por puerto serie o USB (DFU, UF2) | Un pequeño programa ya grabado recibe la imagen por UART o USB y la escribe en la flash | Solo un cable; ideal para placas de aprendizaje | Hay que grabar el bootloader antes; por UART, ~11 KB/s a 115200 baudios |
| ISP (AVR y similares) | Un protocolo SPI de programación escribe la flash con el chip en el circuito | Muy barato, no necesita bootloader | Poca o nula depuración |
| Actualización por aire (OTA) | El propio firmware descarga la nueva imagen y la escribe | Se actualiza en el campo, sin cables | Exige un esquema seguro de arranque (temas 42 y 43) |
SWD merece una explicación: es una interfaz de dos hilos (reloj SWCLK y datos SWDIO) más masa y reinicio, que da acceso al mismo bus de memoria que ve el núcleo. A través de ella, el adaptador puede leer y escribir flash y RAM, detener el procesador y mirar sus registros. Por eso el programador sirve a la vez para grabar y depurar (tema 45), y es el método estándar en el desarrollo con Cortex-M.
La velocidad del bootloader serie sale de una cuenta simple. A 115200 baudios cada byte ocupa 10 bits en el cable (inicio, 8 de datos y parada), de modo que se transfieren 11.520 bytes por segundo, unos 11,25 KB/s: un firmware de 128 KB tarda más de 11 segundos solo en el cable, sin contar el borrado ni la verificación. Con SWD o USB el mismo firmware baja a pocos segundos. Cada segundo que ganás se multiplica por las decenas de veces que grabás por hora.
07 · Acortar el ciclo
Probar sin la placa
Cada vez que grabás la placa pagás compilación, transferencia y reinicio. Y no siempre hay una placa a mano (el equipo físico es caro, hay una sola para varios desarrolladores, o está dentro de una máquina). Por eso, el trabajo serio de firmware recurre a distintos niveles de prueba:
- En el PC (host). La lógica que no depende del hardware (máquinas de estados, protocolos, filtros, cálculos) se compila también para el PC y se prueba con un marco de pruebas unitarias. El ciclo baja a un par de segundos. Es el hábito que más productividad regala, y exige separar bien la lógica del hardware (capas del tema 4).
- En un emulador. Programas como QEMU, Renode o simavr ejecutan el binario del microcontrolador sobre el PC, simulando el procesador y algunos periféricos. Sirven para pruebas automáticas sin placa.
- En la placa, con el hardware real. Imprescindible para lo que depende de tiempos, pines, sensores y consumo. Se puede automatizar con hardware-in-the-loop: una placa conectada a un equipo de pruebas que graba, ejecuta y verifica sola (tema 47).
La pregunta práctica es siempre ¿puedo probar esto sin grabar? Si la respuesta es sí, hacelo: es 5 a 20 veces más rápido. Reservá la placa para lo que solo ella puede responder. El laboratorio de este tema te permite medir exactamente cuánto cambia el ciclo.
08 · Trazabilidad
Versiones y construcciones reproducibles
Un firmware en el campo es una caja negra. Cuando alguien reporta una falla, la primera pregunta es «¿qué versión tiene?» y la segunda, «¿puedo reconstruir exactamente esa versión?». Para responder hay que prepararse desde el comienzo:
- Incluir la versión dentro del firmware, de modo que el equipo pueda informarla (por puerto serie, por pantalla, en el mensaje de arranque).
- Usar control de versiones (Git) y marcar cada versión entregada con una etiqueta.
- Hacer construcciones reproducibles: el mismo código y las mismas herramientas deben dar el mismo binario. Guardá también la versión del compilador.
Una manera sencilla de incrustar la versión es pasarla desde el sistema de construcción:
// En el Makefile: CFLAGS += -DFW_VERSION=\"$(shell git describe --always --dirty)\"
#ifndef FW_VERSION
#define FW_VERSION "desconocida"
#endif
const char fw_version[] = "fw " FW_VERSION;
void imprimir_arranque(void) {
uart_imprimir("Termostato ");
uart_imprimir(fw_version); // por ejemplo: "fw v1.2.0-3-g7a9c1e2-dirty"
}
La salida de git describe --dirty dice la última etiqueta, cuántos cambios hay desde entonces, el código abreviado del cambio y, si aparece -dirty, que se construyó con modificaciones sin guardar: una señal de alarma si ese binario llega a un cliente. Cuidado con las macros __DATE__ y __TIME__: incluyen la hora de la compilación, así que dos construcciones del mismo código dan binarios distintos, y se pierde la reproducibilidad.
09 · Analogía con programación
El costo de cada paso, en Python
Vamos a poner números a un ciclo de trabajo. El programa calcula cuánto tarda en grabarse un firmware de distintos tamaños con cada método (usando velocidades didácticas), y cuántos ciclos completos por hora permite hacer un desarrollador según dónde pruebe: en la placa a mano, o en el PC sin grabar.
Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
VELOCIDADES = { # KB/s (valores didácticos)
"Bootloader UART 115200": 115200 / 10 / 1024, # 10 bits por byte
"Programador SWD": 40,
"USB (UF2)": 50,
"Actualización OTA": 20,
}
SOBRECOSTO_S = 2 # borrado, reinicio y verificación
def segundos_grabar(kb, kb_por_s):
return kb / kb_por_s + SOBRECOSTO_S
print("Tiempo de grabación (s)")
print(f"{'Método':26s}" + "".join(f"{kb:>8d} KB" for kb in (32, 128, 512)))
for nombre, velocidad in VELOCIDADES.items():
fila = "".join(f"{segundos_grabar(kb, velocidad):11.1f}" for kb in (32, 128, 512))
print(f"{nombre:26s}{fila}")
COMPILAR_S = 3 # compilación incremental
PROBAR_PLACA_S = 30 # mirar la placa, pulsar, medir
PROBAR_PC_S = 2 # prueba automática en el PC
TAMANO_KB = 128
print("\nCiclos por hora (firmware de 128 KB)")
for nombre, velocidad in VELOCIDADES.items():
total = COMPILAR_S + segundos_grabar(TAMANO_KB, velocidad) + PROBAR_PLACA_S
print(f"{nombre:26s} ciclo de {total:5.1f} s → {3600 / total:6.1f} por hora")
total_pc = COMPILAR_S + PROBAR_PC_S
print(f"{'Prueba en el PC':26s} ciclo de {total_pc:5.1f} s → {3600 / total_pc:6.1f} por hora")
Resultado: grabar 128 KB tarda unos 13,4 s por UART, 5,2 s por SWD, 4,6 s por USB y 8,4 s por OTA; con 512 KB, el bootloader serie llega a 47,5 s. Con la prueba manual en la placa de 30 s, el ciclo completo ronda 38 a 46 s (entre 78 y 96 ciclos por hora). Probando en el PC (3 s de compilación y 2 s de prueba) el ciclo es de 5 s: 720 ciclos por hora, entre 7 y 9 veces más. Probá PROBAR_PLACA_S = 10 (prueba automática) y mirá cómo la grabación pasa a ser protagonista.
La lección es doble. Primero, cuando la prueba es manual, acelerar la grabación mejora poco, porque el cuello de botella son tus 30 segundos mirando la placa. Segundo, y más importante, cambiar de dónde se prueba vale más que cualquier mejora de velocidad en el cable. Por eso este curso insiste tanto en separar la lógica del hardware.
10 · Exploración interactiva
La duración del ciclo
Elegí el tamaño del firmware, el método de grabación, el tipo de compilación y dónde se prueba. El laboratorio suma los tiempos del ciclo, calcula cuántas veces por hora podés repetirlo y te dice qué paso pesa más. Los tiempos son didácticos, pero las proporciones reflejan lo que ocurre en la práctica.
Compilar, grabar y probar
Cambiá cualquier opción para ver cómo se modifica el ciclo.
Recorrido guiado
- Con los valores iniciales (128 KB, UART, prueba a mano) el ciclo dura unos 46 s y hay unos 78 ciclos por hora. El paso más largo es probar, no grabar.
- Cambiá a programador SWD: la grabación baja de 13 a 5 s, pero el ciclo solo mejora de 46 a 38 s. Acelerar un paso que no es el principal rinde poco.
- Volvé a UART y pasá la prueba a automática (10 s). El ciclo baja a unos 26 s y ahora el paso más largo es la grabación (13,4 s). Cuando un paso mejora, el cuello de botella se corre al siguiente.
- Elegí 512 KB con UART: la grabación sube a casi 48 s y el ciclo se vuelve lento. Un proyecto grande con un bootloader serie desanima a cualquiera.
- Con compilación completa (40 s) sube el ciclo en más de 35 s. Por eso importa que el sistema de construcción sea incremental y que no recompile todo en cada cambio.
- Pasá la prueba a en el PC: el ciclo cae a 5 s (con compilación incremental) y no se graba nada. Son unos 720 ciclos por hora, contra unos 78 al inicio.
Desafío: el desgaste de la flash
Cada grabación borra y reescribe la flash, y la flash tiene una vida limitada de ciclos de borrado (el tema 3 dio 10.000 como orden de magnitud mínimo). Si grabás 100 veces por hora durante 6 horas, hacés 600 grabaciones por día. En la práctica no se agota tan rápido, porque el borrado se hace solo en las páginas que ocupa el firmware y las cifras del fabricante son un mínimo garantizado, no el promedio. Pero es un motivo más para probar en el PC lo que se pueda, y para no usar en producción la placa que se maltrató en el desarrollo.
11 · Método de trabajo
Cómo armar un buen entorno de trabajo
Un entorno de desarrollo bien armado se paga solo en la primera semana. Antes de escribir la primera línea del producto, verificá estos puntos:
- Compilar es un comando. Cualquiera del equipo, en cualquier máquina, ejecuta lo mismo y obtiene el mismo binario.
- El compilador está fijado. Anotá su versión (o usá un contenedor): una versión nueva puede cambiar tamaños y comportamientos.
- Los avisos están activados y el proyecto compila sin ellos. Si hay avisos viejos, nadie mira los nuevos.
- Grabar es un comando.
make flashcompila si hace falta, graba, verifica y reinicia, sin tocar el mouse. - El tamaño se mira en cada compilación. La salida de
sizeaparece sola, y alguien nota cuando el firmware crece de golpe. - La lógica se prueba en el PC. Lo que no necesita hardware se verifica en segundos y de forma automática.
- Hay una vía de depuración confiable. Un depurador SWD o, al menos, una salida de mensajes por UART disponible desde el primer día.
- El firmware informa su versión y todo lo entregado está etiquetado en el control de versiones.
Y un consejo para el trabajo diario: hacé cambios chicos y probalos enseguida. Con un ciclo corto, cuando algo se rompe, la causa está en las últimas tres líneas que escribiste. Con un ciclo largo, está en algún lugar de las últimas tres horas.
12 · Comprensión
Confusiones frecuentes
«Compila sin errores, así que funciona»
Compilar solo verifica la sintaxis y los tipos. Un firmware puede compilar perfecto y fallar al arrancar por un reloj mal configurado, una pila desbordada o un mapa de memoria que no coincide con el chip. El control de verdad lo da la prueba en el hardware.
«El archivo .elf es lo que se carga en el chip»
El ELF contiene el programa y además símbolos e información de depuración que no se graban. Los programadores avanzados lo leen y extraen lo necesario; los simples necesitan el .bin o el .hex que genera objcopy. Grabar un ELF como si fuera un BIN es una causa clásica de un chip que no arranca.
«Un BIN se puede grabar en cualquier dirección»
El programa se enlazó para vivir en direcciones concretas (por ejemplo, 0x08000000). Si lo grabás en otra dirección, las referencias internas apuntan a lugares equivocados y el firmware se cuelga. El BIN no lleva la dirección; hay que conocerla.
«Un error de enlace es un error de código»
Lo es a menudo, pero también puede ser de configuración: un archivo que falta en el proyecto, una biblioteca compilada para otro procesador, o un linker script que no corresponde al chip. «undefined reference» significa que se usó algo que nadie definió.
«Más optimización es siempre mejor»
Los niveles altos pueden reducir el tamaño o aumentar la velocidad, pero también cambian el comportamiento de código con errores latentes (una variable que debía ser volatile, por ejemplo). Se recomienda desarrollar con una optimización moderada y probar el producto final con el nivel con que se va a entregar.
«Si funciona en la placa de desarrollo, funciona en el producto»
El producto tiene otro reloj, otra alimentación, otro cableado y, a menudo, un chip de otra variante. Además, con el depurador conectado el chip a veces se comporta distinto (por ejemplo, los modos de bajo consumo). Hay que probar la placa final, sin el programador.
13 · Práctica guiada
Predecí antes de comprobar
1. ¿Qué dice el prefijo?
Compilás un programa con arm-none-eabi-gcc y obtenés firmware.elf. ¿Por qué no podés ejecutarlo en tu PC? ¿Qué significa «none» en el prefijo?
Ver solución
Porque contiene instrucciones del procesador ARM Cortex-M y espera el mapa de memoria del microcontrolador, no los de tu PC (x86-64 o ARM de 64 bits con sistema operativo). «none» indica que el destino no tiene sistema operativo: el programa debe arrancar solo y no hay bibliotecas del sistema.
2. ¿En qué etapa falla?
Ubicá en qué etapa aparece cada mensaje: (a) «error: expected ';' before '}' token»; (b) «undefined reference to `sensor_leer`»; (c) «region `FLASH` overflowed by 1480 bytes»; (d) «No target connected».
Ver solución
(a) compilación (error de sintaxis). (b) enlace: la función se usó pero ningún objeto la define (falta el archivo en el proyecto o la declaración no tiene definición). (c) enlace: la imagen no entra en la región de flash del linker script, 1480 bytes de más. (d) grabación: el programador no encuentra el chip (cable, alimentación o pines de depuración).
3. El costo del cable serie
Un firmware de 64 KB (65.536 bytes) se envía por un bootloader a 115200 baudios. ¿Cuánto tarda la transferencia? ¿Y a 921600 baudios?
Ver solución
Cada byte ocupa 10 bits. A 115200: 65.536 × 10 / 115.200 ≈ 5,7 s. A 921600: 65.536 × 10 / 921.600 ≈ 0,71 s. Subir la velocidad ocho veces acorta la espera, siempre que el cable, el conversor USB-serie y el bootloader la soporten.
4. Un checksum de Intel HEX
Calculá el checksum de la línea :03000000010203?? (3 bytes de datos en la dirección 0, tipo 00, datos 01 02 03).
Ver solución
Suma de todos los bytes: 03 + 00 + 00 + 00 + 01 + 02 + 03 = 09. El checksum es el complemento a dos: (−9) mod 256 = 0xF7. La línea queda :03000000010203F7. Al sumar todos los bytes, incluido el checksum, el resultado es 0x100, es decir 0 módulo 256: así el receptor verifica la línea.
5. Dónde conviene acelerar
Un ciclo dura 5 s de compilación, 8 s de grabación y 20 s de prueba manual. ¿Cuántos ciclos por hora son? Si la lógica se pudiera probar en el PC (compilación de 5 s y prueba de 3 s, sin grabar), ¿cuánto mejora?
Ver solución
Ciclo = 5 + 8 + 20 = 33 s → 3600 / 33 ≈ 109 ciclos por hora. En el PC: 5 + 3 = 8 s → 3600 / 8 = 450 por hora, unas 4 veces más. Cortar la grabación a cero con el mismo método de prueba daría 25 s (144 por hora): mucho menos mejora que cambiar el lugar de la prueba.
6. Reproducir una versión
Un cliente informa un error en el equipo con la versión «fw v1.2.0-3-g7a9c1e2-dirty». ¿Qué te dice esa cadena y qué problema tiene?
Ver solución
Quiere decir: se construyó 3 cambios después de la etiqueta v1.2.0, sobre el cambio 7a9c1e2, y con modificaciones sin guardar (-dirty). El problema es ese último dato: ese binario no se puede reconstruir exactamente a partir del control de versiones. Las versiones entregadas deberían construirse siempre desde un estado limpio y etiquetado.
14 · Síntesis
Ideas para recordar
- La compilación cruzada usa un compilador que corre en el PC (host) y genera código para otro procesador (target); cada familia de chips tiene su cadena de herramientas, como
arm-none-eabi-gcc. - El firmware se construye en etapas: preprocesar, compilar cada archivo a un objeto, enlazar con el linker script y el código de arranque, y convertir la imagen al formato de grabación.
- El enlazador decide las direcciones definitivas según el mapa de memoria del chip; un error de enlace puede significar que algo falta o que la imagen no entra.
- ELF es la salida completa con símbolos; BIN son bytes crudos sin direcciones; Intel HEX lleva dirección y checksum por línea; UF2 se graba copiando un archivo a una unidad USB.
- Un sistema de construcción (Make, CMake, PlatformIO) recompila solo lo que cambió y hace que compilar y grabar sean un comando reproducible.
- Se graba por SWD o JTAG, por un bootloader serie o USB, por ISP o por aire; SWD sirve además para depurar, y la velocidad del cable pesa en cada grabación.
- El ciclo editar-compilar-grabar-probar se acorta atacando el paso más largo; lo que más rinde es probar en el PC la lógica que no depende del hardware.
- Todo firmware debe informar su versión y poder reconstruirse a partir del control de versiones; evitar construcciones «sucias» y fechas incrustadas mantiene la reproducibilidad.
¿Listo para el tema 6?
Antes de avanzar, explicá con tus palabras qué hace el enlazador y por qué necesita un linker script; calculá cuánto tarda en grabarse un firmware de 100 KB por un bootloader a 115200 baudios; y proponé tres cosas de tu proyecto que podrías probar en el PC sin grabar. Si podés justificar las tres respuestas, tenés el recorrido completo del código al chip.
En el próximo tema cerramos el bloque de fundamentos con las plataformas y familias: AVR, ARM Cortex-M, ESP32 y RP2040, qué ofrece cada una y cómo comparar y elegir la adecuada para un proyecto.