Esenciales de C embebido: Preguntas clave de programación de microcontroladores
Los programadores de microcontroladores deben dominar las particularidades del lenguaje C bajo restricciones estrictas de recursos. A continuación, una lista curada de preguntas prácticas centradas en compilación, memoria y optimización. Estos temas surgen con frecuencia en entrevistas de empresas de sistemas embebidos.
Palabras clave principales y su uso
Static limita la visibilidad de una variable al archivo o función actual, preservando valores entre llamadas para estáticos locales. Volatile impide que el compilador optimice variables modificadas externamente (registros, memoria, interrupciones).
Ejemplo de código problemático:
int square(volatile int *ptr) {
return *ptr * *ptr;
}
Aquí, volatile solo aplica al primer desreferenciamiento; el segundo podría optimizarse. Solución: (ptr) (*ptr).
Const volatile es útil para datos externos de solo lectura como registros de estado. Register sugiere almacenar una variable en un registro de CPU (deprecado en C11). Restrict indica al compilador que los punteros no se solapan, permitiendo vectorización.
Gestión de memoria y estructuras
Las variables globales const van en memoria Flash. Para un array constante de 32 KB lleno de 0xFF, usa una sección en el script del enlazador con fill(0xFF).
Operaciones con bits: verifica bit con n & (1 << k), limpia con n & ~(1 << k). Comparación de flotantes: fabs(a - b) < EPSILON.
El tamaño de estructuras depende del alineamiento.
struct Foo {
int iiii;
char c;
};
el tamaño es 8 bytes (4 bytes de relleno). Empaquetado en GCC: __attribute__((packed)).
Ejemplos de declaración de cadenas:
char str5[]={'s','t','r','i','n','g'}; // tamaño:7 long:6
char *str1="string"; // tamaño:4 long:6 (puntero)
char str3[]="string"; // tamaño:7 long:6
char str4[10]="string"; // tamaño:10 long:6
Sistemas de construcción y cadenas de herramientas
Los sistemas de construcción automatizan la compilación, enlace y dependencias. Make maneja cambios en archivos, mientras que Ninja es más rápido gracias al paralelismo y simplicidad.
VPATH en GNU Make busca fuentes en directorios especificados.
Pipeline de código: .c → .i (preprocesador) → .s (ensamblador) → .o → .elf → .bin/.hex.
Desactivar advertencia de GCC: #pragma GCC diagnostic ignored "-Wrestrict".
Reducir firmware: -ffunction-sections -fdata-sections, ld --gc-sections y optimización -Os.
Artefactos de construcción
.o— archivos objeto.elf— ejecutable con símbolos.hex/.bin— para flashear (hex incluye direcciones)
Binutils: objdump (desensamblador), objcopy (convertidor), readelf (analizador ELF).
Verificar construcción: make V=1 o -n (ejecución en seco).
Estructuras de datos y algoritmos
Buffer circular — array en anillo con índices cabeza/cola, a diferencia de FIFO que desplaza elementos.
Eliminación en lista enlazada: prev->next = node->next (necesita prev) o node->next->prev = node->prev (doble enlace).
Polimorfismo en C: punteros a funciones en structs (estilo vtable).
Tarea sin if/switch:
void print_num(int n) {
const char *nums[] = {"","Uno","Dos"};
printf("%s", nums[n]);
}
do-while(0) para macros — una iteración con múltiples sentencias.
Interrupciones y RTOS
Interrupción — mecanismo hardware que salta a un ISR ante un evento. La tabla de vectores contiene direcciones de ISR. Cortex-M4: 12–16 ciclos para entrada (guardado de contexto).
Flujo: hardware guarda PC/LR, carga vector, salta a ISR, retorna vía POP.
Función reentrante — segura para llamadas concurrentes (sin estáticos/locales sin sincronización).
En RTOS:
- Thread — unidad de ejecución con su propio stack
- Mutex — exclusión mutua
- Semaphore — contador de eventos
- Spinlock — bloqueo busy-wait
Problema de código: interbloqueo por orden de bloqueos (TareaA: ma→mb, TareaB: mb→mc→ma).
Inversión de prioridad: tarea de alta prioridad espera a baja. Solución: herencia de prioridad.
Operaciones atómicas
Ejemplo: __atomic_exchange_n(&x, &y, __ATOMIC_SEQ_CST).
Bit-banding — acceso directo a bits sin máscaras (Cortex-M3/4).
Cadena de herramientas y depuración
ABI — interfaz binaria (convención de llamada).
Segmentos: .text (código Flash), .data (RAM inicializada), .bss (RAM ceros), .rodata (constantes Flash).
Expandir preprocesador: gcc -E file.c > expanded.i.
Documentos clave: hoja de datos, manual de referencia, manual de programación, esquemáticos.
Lecciones clave
- Volatile es esencial para registros periféricos
- Estructuras empaquetadas ahorran RAM pero ralentizan acceso
- RTOS requiere código seguro para hilos y protección de secciones críticas
- Optimización de construcción: secciones + gc-sections
- Interrupciones usan el stack del hilo actual
Para relleno 0xFF en structs: script del enlazador con fill o __attribute__((packed)) + relleno manual.
— Editorial Team
Aún no hay comentarios.