Proyectos con microcontroladores
Un proyecto no es un programa más largo: es una manera de trabajar. Lo que separa a un prototipo que anda en el banco de un equipo que funciona un año seguido en una planta no es el código, es el método.
01De la idea al equipo
| Etapa | Qué se define | Qué se entrega |
|---|---|---|
| 1 | Requisitos: qué tiene que hacer, con qué exactitud, en qué condiciones, para quién. | Lista de requisitos verificables. |
| 2 | Arquitectura: bloques, señales entre ellos, elección del microcontrolador y de los sensores. | Diagrama en bloques y presupuesto de pines. |
| 3 | Prototipo por partes: cada bloque se prueba solo antes de integrarlo. | Módulos verificados uno a uno. |
| 4 | Integración: se juntan los bloques y aparecen los problemas de tiempos y de ruido. | Prototipo completo funcionando. |
| 5 | Placa: esquemático, circuito impreso, montaje. | Equipo en su forma definitiva. |
| 6 | Pruebas: funcionales, de límites, de duración y de recuperación ante falla. | Protocolo de ensayos con resultados. |
| 7 | Documentación y entrega. | Esquemas, código, manual y respaldo. |
El esquemático y la placa se trabajan con lo visto en esquemáticos y circuito impreso.
«Que mida bien la temperatura» no es un requisito: no se puede comprobar. «Medir entre −10 y +80 °C con error menor a ±0,5 °C, actualizando cada segundo, alimentado a 12 V y funcionando entre 0 y 50 °C de ambiente» sí lo es. La diferencia parece formal y no lo es: el requisito verificable es lo que después se ensaya, y lo que evita la discusión sobre si el equipo está terminado.
02La máquina de estados
typedef enum { REPOSO, LLENANDO, ESTABILIZANDO, DESCARGANDO, FALLA } estado_t; estado_t estado = REPOSO; void maquina(void) { switch (estado) { case REPOSO: if (hay_pedido() && recipiente_ok()) { abrir_valvula(); estado = LLENANDO; } break; case LLENANDO: if (peso() >= objetivo) { cerrar_valvula(); t0 = ahora(); estado = ESTABILIZANDO; } else if (ahora() - t0 > T_MAX) { cerrar_valvula(); estado = FALLA; } break; case ESTABILIZANDO: if (ahora() - t0 > 500) { registrar(peso()); estado = DESCARGANDO; } break; case DESCARGANDO: if (peso() < VACIO) { estado = REPOSO; } break; case FALLA: todo_seguro(); if (rearme()) { estado = REPOSO; } break; } }
Porque la función no bloquea: entra, evalúa, sale. Se la puede llamar mil veces por segundo desde el bucle principal, mientras en el mismo bucle se atiende la pantalla, la comunicación y los pulsadores. Con esperas dentro de la secuencia, todo lo demás se detiene.
Además, cada estado tiene su tiempo máximo. Un sistema que puede quedarse esperando para siempre un evento que no llega es un sistema que en algún momento se cuelga.
03Un bucle principal que no se bloquea
int main(void) { inicializar(); wdt_habilitar(WDT_2S); /* perro guardian de 2 segundos */ while (1) { uint32_t t = ms_actual(); if (t - t_control >= 10) { t_control = t; leer_sensores(); maquina(); } if (t - t_pantalla >= 300) { t_pantalla = t; refrescar_pantalla(); } if (t - t_registro >= 1000){ t_registro = t; guardar_dato(); } atender_serie(); /* no bloqueante: procesa lo que haya */ wdt_refrescar(); /* si el bucle se traba, el equipo se reinicia */ } }
Cada tarea corre a su ritmo y ninguna espera. Es lo más parecido a un sistema operativo de tiempo real que se puede tener sin usar uno, y para la enorme mayoría de los equipos alcanza y sobra.
La condición es que ninguna función tarde demasiado: si
guardar_dato() se toma 300 ms escribiendo en una memoria, el control de 10 ms se pierde
treinta veces. Las funciones largas se parten en pasos, o pasan a la máquina de estados.
04Que siga funcionando el mes que viene
- Perro guardián habilitado y refrescado en un solo lugar. Nunca dentro de una interrupción: se perdería su función.
- Valores por defecto seguros si la configuración guardada está corrupta, con suma de verificación.
- Rangos validados en toda entrada: un sensor desconectado suele leer cero o fondo de escala, y eso no puede tomarse como dato bueno.
- Estado de falla explícito, que deja todo en condición segura y exige rearme deliberado.
- Registro de eventos: cuántas veces se reinició, cuándo y por qué. Es lo que permite diagnosticar a distancia.
- Capacitores de desacople de 100 nF en cada pin de alimentación, pegados al chip.
- Entradas con filtro y protección; salidas con diodo de rueda libre si manejan bobinas.
- Reset con su capacitor y, si hace falta, supervisor de alimentación.
- Pines no usados definidos, no al aire.
- Alimentación con margen: un regulador al límite es una falla esperando el verano.
- Apagar y encender en el peor momento, muchas veces, incluso durante una escritura en memoria.
- Desconectar cada sensor y cada actuador con el equipo andando.
- Dejarlo funcionando varios días seguidos: los desbordamientos de contadores y las pérdidas de memoria aparecen recién ahí.
- Entradas fuera de rango, botones apretados a la vez, comandos serie mal formados.
- Temperatura alta y baja, y variación de la tensión de alimentación en todo su rango.
05Versionado y documentación
| Elemento | Qué debe contener |
|---|---|
| Control de versiones | El código en un repositorio, con cambios chicos y mensajes que digan por qué. Cada versión entregada, etiquetada. |
| Versión en el equipo | El número de versión visible en la pantalla o por comando serie. Sin eso, no se sabe qué está corriendo en cada unidad. |
| Esquemático y placa | Archivos fuente y PDF, con la revisión indicada en la propia placa. |
| Manual de uso | Cómo se opera, qué significa cada indicación y qué hacer ante cada mensaje de error. |
| Manual técnico | Cómo se calibra, cómo se actualiza el programa, qué se revisa en el mantenimiento y qué repuestos usa. |
| Respaldo | Código, esquemas, archivos de fabricación y hojas de datos de los componentes, todo junto y guardado fuera de la máquina de trabajo. |
Que otra persona pueda tomar el proyecto, entenderlo, compilarlo y modificarlo sin preguntar nada. Si eso no es posible, el proyecto está incompleto aunque el equipo funcione. Y conviene medirlo de verdad: entregarle la carpeta a un compañero y ver hasta dónde llega solo.
06En el laboratorio
Tomar una idea de proyecto y escribir sus requisitos de manera verificable, con números y condiciones. Intercambiar con otro grupo: si alguien puede interpretar un requisito de dos maneras distintas, está mal escrito y hay que corregirlo.
Escribir un control sencillo con esperas y verificar que la pantalla y los botones dejan de responder mientras corre. Reescribirlo como máquina de estados no bloqueante y comprobar que todo responde al mismo tiempo.
Habilitar el perro guardián y provocar un bloqueo deliberado dentro de una función. Verificar que el equipo se reinicia solo y que registra el reinicio. Después mover el refresco del guardián a un lugar equivocado y comprobar que deja de proteger.
Llevar un proyecto propio por las siete etapas, con entrega de cada documento. Se evalúa el recorrido completo, no sólo que el equipo encienda: los requisitos, los ensayos y la documentación pesan tanto como el funcionamiento.
07Errores frecuentes
| Error | Consecuencia |
|---|---|
| Empezar a programar sin requisitos | El proyecto no termina nunca: siempre falta algo que nadie definió. |
| Integrar todo de una vez | Cuando falla, no se sabe cuál de los diez bloques es. Cada bloque se prueba solo primero. |
| Esperas bloqueantes en el bucle principal | La interfaz deja de responder y el control pierde su ritmo. |
| Estados sin tiempo máximo | El equipo puede quedarse esperando para siempre un evento que no llega. |
| Refrescar el perro guardián en una interrupción | El programa principal puede estar colgado y el guardián nunca actúa. |
| No validar rangos de entrada | Un sensor desconectado lee cero y el control actúa sobre un dato falso. |
| Sin número de versión en el equipo | Imposible saber qué programa tiene cada unidad en campo. |
| Probar sólo en el banco, unos minutos | Los problemas de duración, temperatura y corte de alimentación aparecen después, en el cliente. |
08Autoevaluación
¿Qué hace que un requisito sea verificable?
Que tenga números y condiciones: rango, error admisible, tiempo de respuesta, condiciones de trabajo. Es lo que después se ensaya y lo que define si el equipo está terminado.
¿Por qué se prueba cada bloque por separado antes de integrar?
Porque si se integra todo de una vez y falla, no hay manera de saber cuál de los bloques es el problema. Probar por partes acorta muchísimo el diagnóstico.
¿Qué ventaja tiene una máquina de estados sobre una secuencia con esperas?
Que no bloquea: se la puede llamar continuamente desde el bucle principal mientras se atienden pantalla, comunicación y botones. Con esperas, todo lo demás se detiene.
¿Por qué cada estado debe tener un tiempo máximo?
Para que el sistema no quede esperando indefinidamente un evento que no va a llegar. Vencido el plazo, pasa a un estado de falla seguro.
¿Qué es la planificación cooperativa?
Un bucle principal donde cada tarea se ejecuta a su propio ritmo comparando tiempos, sin esperas. Requiere que ninguna función tarde demasiado.
¿Dónde se refresca el perro guardián y por qué?
En un solo lugar del bucle principal. Si se refrescara desde una interrupción, el bucle podría estar colgado y el guardián no lo detectaría.
¿Por qué hay que validar el rango de las entradas?
Porque un sensor desconectado o en falla suele leer cero o fondo de escala, y el control tomaría ese valor como bueno y actuaría sobre un dato falso.
Nombrá tres ensayos que no pueden faltar.
Corte de alimentación en momentos críticos, desconexión de sensores y actuadores con el equipo andando, y funcionamiento continuo durante varios días.
¿Por qué el equipo debe mostrar su número de versión?
Porque en campo hay unidades con distintas versiones del programa, y sin ese dato es imposible saber cuál tiene cada una ni reproducir un problema.
¿Cuál es la prueba de fuego de la documentación?
Que otra persona pueda tomar el proyecto, entenderlo, compilarlo y modificarlo sin preguntar nada.