Mi enfoque habitual para esto es incluir un procesador de comandos en el firmware que se comunica a través de un flujo bidireccional de bytes a un host, generalmente utilizando el UART. La mayoría de los proyectos incluyen la comunicación con un host, por lo que a menudo se necesita este código de todos modos. En algunos casos no hay conexión de host en el sistema final. Dependiendo de los recursos disponibles, puede incluir el código del procesador de comandos en el firmware solo cuando así lo habiliten los conmutadores de depuración en tiempo de compilación.
Por ejemplo, estoy trabajando en un proyecto en este momento que funcionará con baterías y se comunicará con el resto del mundo a través de MiWi utilizando solo radios 802.15. Desde que tenía espacio en el tablero, incluí almohadillas para conectar a las líneas UART. En la producción final, este conector no se llenará, por lo que no agrega ningún costo adicional. Dado que el consumo de energía es muy importante en el producto final, el interfaz de UART solo se habilita si un interruptor de tiempo de compilación para ese propósito se establece en el modo de depuración. Sin ese interruptor establecido, el UART nunca se enciende y el código para él ni siquiera está vinculado.
Tener una forma sencilla de habilitar / deshabilitar la interfaz UART opcional es útil incluso después de que se haya depurado el firmware. En algún momento en el futuro, es posible que sea necesario agregar funciones o realizar otras modificaciones. La interfaz UART volverá a ser útil para el desarrollo y las pruebas. De esta manera, solo es necesario cambiar un solo "falso" a "verdadero" para habilitarlo, y no es necesario tener que averiguar de nuevo cómo vincular el código, agregar los enlaces a los lugares correctos, inicializar el UART , etc.
Una vez que tengo un flujo bidireccional de bytes a un host, generalmente uso un protocolo de paquetes simple. Cada paquete comienza con un byte de código de operación, seguido de los bytes de datos especificados para ese código de operación. Llamo a los paquetes del host a la unidad "comandos" y a los de la unidad al host "respuestas", pero las respuestas no solo se envían necesariamente en respuesta directa a los comandos.
Este tipo de protocolo de datos de código de operación es fácil de analizar en el microcontrolador (lo fácil o no que es en una PC es irrelevante, pero también lo es allí). Los índices de bytes de opcode en una tabla de despacho, lo que hace que se ejecute una rutina específica para ese opcode. Las rutinas de comando luego obtienen cualquier byte de datos del comando, ejecutan el comando y luego vuelven al circuito de distribución principal. Este bucle generalmente se ejecuta en una tarea separada, por lo que parece que se ejecuta de forma asíncrona e independiente de otras cosas en el sistema.
El conjunto de comandos, por supuesto, incluye comandos para hacer que el sistema realice sus funciones previstas, pero también un grupo de comandos de depuración que no se usan en el sistema final. Estos son utilizados por un programa de prueba en el host para permitir la prueba de partes de bajo nivel del firmware.
Por ejemplo, en el proyecto MiWi que mencioné anteriormente, el código MiWi enlatado debe leer y escribir en la EEPROM interna a través de una interfaz definida que debo proporcionar. Para probar esa interfaz por separado, agregué comandos para leer y escribir ubicaciones de EEPROM. Usando esos comandos, probé y verifiqué la interfaz EEPROM de bajo nivel. Encontrar errores en los síntomas extraños de MiWi más tarde habría sido mucho más difícil.
Hago esto tan a menudo que tengo plantillas tanto para el procesador de comandos en el firmware como para el programa de prueba en el host. Normalmente reservo el código de operación 0 como NOP, 1 para PING y 2 para FWINFO. PING simplemente responde con PONG, que también es una respuesta de un solo byte. Eso puede ser útil para probar el enlace de comunicación. FWINFO hace que se envíe la respuesta FWINFO, que proporciona el tipo de firmware, la versión y los números de secuencia. Otros comandos se agregan desde allí.
Algunas veces ha sido útil tener comandos para operaciones de bajo nivel disponibles en el sistema final, aunque esto no estaba en la especificación. Estas cosas pasan. Más de una vez, tener un nuevo software capaz de leer y escribir EEPROM directamente en las unidades de campo existentes, por ejemplo, ha ahorrado muchos problemas. La gente del software por lo general se burla de los comandos adicionales al principio, luego se sienten realmente agradecidos un año más tarde cuando guarda sus nalgas.