Interfaz de comandos: switch-case vs punteros[] a función

Buenas noches frías, húmedas, grises, neblinosas... un día de gran afluencia de rioplatenses al foro :).

Estaba pensando en una interfaz de comandos hecha a mi gusto, y llego al punto donde no me gusta ninguna de las opciones que tengo.
A ver si alguno da con un argumento que defina la contienda.

Problema:
Estoy haciendo una interfaz de comandos típica, donde uno escriba en una consola de puerto serie algo como:
LED ON
aprieta enter y el led se enciende.
O si pongo
LED TOGGLE 200
el led se enciende y se apaga (parpadea) cambiando de estado cada 200 milisegundos.
¿Simple no?.
Bueno, las cosas tienen su forma de complicarse cuando se quiere trabajar con una interfaz cableada a la par de una interfaz bluetooth, donde queremos que por comandos se pueda acceder a recursos compartidos que pueden no estar disponibles al momento de dar enter, donde queremos tener un modo binario (para nuestro futuro software de puerto serie que nunca va a llegar) y un modo texto (para tipear palabritas en una consola a puerto serie), cuando empezamos a manejar argumentos, etc

No importa, vamos a la consulta en sí. En última instancia el comando led toggle se traduce a un paquete de bytes con esta estructura:
recurso (1 byte) | función (1 byte) | argumentos (opcional) (N bytes)
De alguna forma termino tirando un evento a una cola de eventos, y el main en cuando puede termina llamando al manejador de eventos correspondiente, digamos que se llama CliProcessCmdPackage().
Las opciones son:

  1. Hacer un doble switch-case bien grandote
  2. Usar punteros a función
  3. Hacer dos switch-case por separado
  4. Otro?
Doble switch-case:
PHP:
typedef enum {
    RESOURCE_LED = 0,    RESOURCE_BUZZER,    RESOURCE_MEM,
}resourceId_t;

typedef enum {
    LED_ON = 0,    LED_OFF,    LED_TOGGLE,
}ledFunction_t;

typedef enum {
    //...
}buzzerFunction_t;

typedef enum {
    //...
}memFunction_t;

cmdResult_t CliProcessCmdPackage(const uint8_t *package) {
    uint8_t resourceId = package[0];
    uint8_t functionId = package[1];
    cmdResult_t cmdResult = CMD_EXE_OK;

    switch(resourceId) {
        case RESOURCE_LED:
            switch(functionId) {
                case LED_ON:
                    //...
                break;
                case LED_OFF:
                    //...
                break;
                case LED_TOGGLE:
                    //...
                break;

                default:
                    cmdResult = CMD_EXE_UNKNOWN_FUNCTION;
            }
        break;
        
        case RESOURCE_BUZZER:
            switch(functionId) {
                case BUZZER_TONE:
                        //...
                break;
                case BUZZER_SONG:
                        //...
                break;

                default:
                    cmdResult = CMD_EXE_UNKNOWN_FUNCTION;
            }
        break;

        case RESOURCE_MEM:
            switch(functionId) {
                case MEM_WRITE:
                        //...
                break;

                case MEM_READ:
                        //...
                break;

                case MEM_ERASE:
                        //...
                break;

            default:
                cmdResult = CMD_EXE_UNKNOWN_FUNCTION;
            }
        break;

       default:
            cmdResult = CMD_EXE_UNKNOWN_RESOURCE;
    }//switch(resourceId)

    return cmdResult;
}
Las desventajas de esta implementación me parece que saltan a la vista:

  • Es un spaghetti de código, una función enorme que crece rápido y te hace perdir de vista que es lo que querés que el comando haga.
  • Pérdida de modularidad: esta todo junto, en 1 solo archivo tengo que hacer un include de todos los recursos utilizados
Y sin embargo, las ventajas:

  • Sencillo, el compilador si las constantes de identificadores están definidas va a optimizar esto con tabla de saltos, y la ejecución va a ser relativamente rápida (si el compilador hace las cosas bien, cosa que es un riesgo asumirlo).
  • Puedo usar comandos que usen varios recursos a la vez, ejemplo, que reproduzca con el buzzer una tonada guardada en memoria y que encienda un led cuando haya una reproducción en curso.
  • El manejo de errores es bastante simple (sentencias default).
  • Sencillo agregar comandos para nuevos recursos, y nuevas funciones (se agrandan los typedef enums)
Vamos con la implementación de tabla de funciones. Podría ser algo así:
PHP:
typedef cmdResult_t (cmdFunction_t *)(void *args);



//la definición y/o la declaración pueden estar en un archivo .c/.h diferente

extern cmdResult_t CmdLedOn(void *args);
extern cmdResult_t CmdLedOff(void *args);
extern cmdResult_t CmdLedToggle(void *args);


const cmdFunction_t ledFunctions[] = {&CmdLedOn, &CmdLedOff, &CmdLedToggle} ;


//...prototipos buzzer...

const cmdFunction_t buzzerFunctions[] = {&CmdBuzzerTone, &CmdBuzzerSong} ;


//...prototipos mem...
//en realidad sería mejor definir las tablas de funciones así, para no
//preocuparse por el orden y que el compilador avise si hay error en tabla
typedef enum {
    CMD_MEM_READ = 0,    CMD_MEM_WRITE,    CMD_MEM_ERASE,
    CMD_MEM_MAX
}memFunction_t;


const cmdFunction_t memFunctions[CMD_MEM_MAX] = {
    [CMD_MEM_READ] = &CmdMemRead,     [CMD_MEM_WRITE] = &CmdMemWrite, 
    [CMD_MEM_ERASE] = &cmdMemErase
} ;



//--------------------------------------------

const cmdFunction_t * const cmdResources[] = {&ledFunctions, &buzzerFunctions, &memFunctions};
//no se si esta bien esta definición, pero la idea es no ocupar ram


cmdResult_t CliProcessCmdPackage(const uint8_t *package) {
    uint8_t resourceId = package[0];
    uint8_t functionId = package[1];
 
    //chequear si resourceId es valido
    //chequear si functionId es valido  
    return (
        (cmdResources[resourceId][functionId])(args)
    );
}
Ventajas:

  • No hay spaghetti de código
  • Cada comando tiene su función, agregar un nuevo comando es agregar una nueva función (modularidad). Puedo hacer 1 archivo para los comandos de memoria, otro para los comandos de led; o poner todo juntos, como uno quiera
Desventajas:

  • Chequear si functionId es valido puede requerir tener otro arreglo con los tamaños de cada tabla de recursos
  • Declaración de un nuevo comando puede ser más engorrosa, hay que tocar no solo los typedef enums, sino también las tablas.
  • Depurar puede ser más complicado porque el depurador no sabe a priori cual función se va a llamar, donde con los switch-case pasa lo contrario.
Otra cuestión es que con el doble switch-case puedo verificar disponibilidad del recurso para todos los comandos a la vez. Ejemplo


PHP:
cmdResult_t CliProcessCmdPackage(const uint8_t *package) {
    uint8_t resourceId = package[0];
    uint8_t functionId = package[1];
    cmdResult_t cmdResult = CMD_EXE_OK;

    switch(resourceId) {
        //....otros recursos
        case RESOURCE_MEM:
            if( mem_busy() ) {
                cmdResult = CMD_EXE_RESOURCE_BUSY_ERROR;
            } else {
                MemWakeUp();
                switch(functionId) {
                    case MEM_WRITE:
                            //...
                    break;

                    case MEM_READ:
                            //...
                    break;

                    case MEM_ERASE:
                            //...
                    break;

                default:
                    cmdResult = CMD_EXE_UNKNOWN_FUNCTION;
                }
            }
        break;
        //....otros recursos
    }
    return cmdResult;
}
Cosa que con los punteros a función realizarla requeriría definir una función por cada recurso (si hace falta) y sería más ad-hoc.
Notar también que incluí MemWakeUp(); para decir que la memoria puede ser una flash externa y que para operarla primero preciso sacarla de sleep con un comando si es que no está siendo utilizada por otra parte del programa. Es decir, puedo realizar fácilmente acciones comunes antes de llamar a cualquier función del recurso X.


Finalmente (que manera de escribir :oops:), un híbrido podría ser separar los switch case.


PHP:
cmdResult_t CliProcessCmdPackage(const uint8_t *package) {
    uint8_t resourceId = package[0];
    uint8_t functionId = package[1];
    cmdResult_t cmdResult = CMD_EXE_OK;

    switch(resourceId) {
        case RESOURCE_LED:
            cmdResult = CmdLed(functionId, args); 
        break;
        
        case RESOURCE_BUZZER:
            cmdResult = CmdBuzzer(functionId, args); 
        break;

        case RESOURCE_MEM:
            cmdResult = CmdMem(functionId, args);         
        break;

       default:
            cmdResult = CMD_EXE_UNKNOWN_RESOURCE;
    }//switch(resourceId)

    return cmdResult;
}


//Y en algun otro archivo o en el mismo definir

cmdResult_t CmdLed(uint8_t functionId, void *args) {
    cmdResult_t cmdResult = CMD_EXE_OK;
    //aca puedo ejecutar codigo comun para chequear disponibilidad recurso

    switch(functionId) {
        case LED_ON:
            //...
        break;
        case LED_OFF:
            //...
        break;
        case LED_TOGGLE:
            //...
        break;

        default:
            cmdResult = CMD_EXE_UNKNOWN_FUNCTION;
    }    
    return cmdResult;
}
A lo mejor el último caso es menos proclive a la "spaghetización" y me permite combinar lo mejor de los 2 casos?, no sé.


Escucho ofertas :D
 
Escucho ofertas :D

Pues te ofrezco implementar una cola dinámica de mensajes. Es un sistema similar a los punteros a funciones con varias ventajas:

-No es complejo de implementar.
-Permite comunicación entre procesos simultaneos, o entre Interrupciones.
-Al ser dinámico, no hace falta mantener índices cada vez que añades un escuchador a la cola.

En realidad se trata de implementar dos listas dinámicas, una abierta y otra cerrada (cíclica). La abierta es una cola compuesta por punteros a estructura que contiene el comando, el parámetro del mensaje, y un puntero al siguiente miembro de la lista, o a null si no hay más mensajes. Entonces una estructura tipo mensaje se crea y enlaza en una función que se puede llamar PonMensaje (se añade al final de la cola) y se "lee" y libera con otra función que podemos llamar TomaMensaje (saca el primer mensaje de la lista y el segundo lo hace serel primero, o Null si no quedan mensajes).

La lista cíclica va a ser una lista compuestas por estructuras que contienen el ID del mensaje, un puntero a función, y un puntero al siguiente elemento de la lista. Como es cíclica da igual donde se inserte el nuevo miembro, siendo lo más sencillo insertarlo como primer elemento. Si la lista está vacía (Null) el primer elemento creado pone su propia dirección en el miembro siguiente de la estructura, apuntándose a si mismo para crear una lista cerrada de un elemento. La función que añade un elemento a la lista de comandos puede llamarse InstalarEvento, puesto que ahora tus funciones son eventos que manejan mensajes. Entonces el bucle principal es un bucle tipo while((msg=TomaMensaje()!=Null) do { if(evento->id==msg->id) { evento->func(msg); break;}else evento=evento->siguiente;}while(1); Así, si una interrupción u otra parte de código ejecuta un PonMesaje mientras se está procesando un mensaje anterior, el último no se descarta sino que se pone en cola para ser procesado más tarde.

Este es el enfoque que se utiliza en el intérprete de comandos AT/T de los módulos WiFi ESP8266.
 
Última edición:
Hola,
que microcontrolador utilizas?
Hice justamente lo que describes para un Atmega, Segun tu lista de la forma 2, punteros a funcion y con las modificaciones muy parecidas a las que te recomienda palurdo. Estoy terminando mi proyecto y agregando comandos.

El proyecto integro lo programe en assembler. (espagueti del bueno, pero extremadamente rapido) La verdad me gustaria saber como se las arreglaria el compilador de C para en tu ejemplo "LED TOGGLE 200" leer ese 200, y pasarlo a binario en un uint8_t

Mi pgrograma tiene 3 modulos principales:

1- Buffer de recepcion.
Es un Queue ciclico de 256 bytes, Creo que a esto llama palurdo "cola dinamica de mensajes" pues es justamente eso en mi caso. los comandos estan separados por <Enter> y cuando la Int de la UART recibe un enter activa un bit nuevo comando en buffer CMD_bit".

2- command_interpreter:
Es una funcion que se llaama cuando CMD_bit=1 La ventaja de punteros a funcion es que el interprete es tambien una funcion (al menos en mi caso) y si te quieres complicar la vida pasa como parametro de un comando otro comando. (Yo desisti de eso). Mi lista es de 32 comandos adaptable a max.128. cada comando tiene una funcion asociada. El interprete de comandos realiza una busqueda binaria en la tabla y llama a la funcion correspondiente. No tengo idea como se programaria una busqueda binaria en C. En assm es posible, aunque si queda codigo casi incomprensible. la lista debe estar ordenada. es muy rapido en encontrar y ejecutar el cmd! El Queue me da la posibilidad de saber si es el ultimo comando de la lista.
Tengo dos tipos de comandos, y por el planteo de tu pregunta veo que tambien necesitaras de lo mismo.
El primer tipo de comandos se ejecuta inmediatamente ej: "output 5 = 1<CR>". algo asi de simple no tiene mucho sentido ponerlo en una lista de eventos, creo yo..
El segundo tipo de comandos agrega un puntero a funcion en una lista de eventos, yo a esta funcion o evento lo llamo Task. Por lo tanto este segundo tipo de comandos tambien tiene una task asociada.

3-task Scheduler:
al administrador de la lista de eventos me permiti llamarlo asi, :) Esta en el Main. La estructura de un Task tiene 3 modulos, Start, Run y Shutdown: T.Start: El commando pone la direccion de T.Start en la tabla de eventos. Aqui se inicializan variables y principalmente se controla que el hardware necesario este disponible. Si todo esta ok entonces T.Start pone en la tabla T.Run. T.Run pues eso, hace lo que tiene que hacer y chequea las condiciones para parar la tarea, si son validas entonces se cambia en la tabla a T.shutdown. Y este ultimo modulo libera el hardware, limpia la tabla del task sheduler y alguna que otra cosita que fuera necesaria como mensajes y eso.

Al programar todo esto en assembler y querer modificar algo es bastante mas complejo que en C. pero igualmente creo que estos consejos es no estan de mas:
1ro Define de antemano las reglas para programar los eventos. Pues un evento no podria adueñarse del handler y dejar el resto de los comandos en cola sin ejecutar. Y si sobre la marcha debes modificar algo documenta bien lo hecho.
mientras programes notaras que es lo que debes hacer, tocar, modificar cada vez que quieras agregar un comando. Documenta eso en una lista. que sera como tu guia para expandir tu programa, pues cuando la estructura basica este funcionando solo sera cuestion de programar nuevos eventos. Debes agregarlos sin alterar la estructura.

Bueno, todo esto para darte mi opinion: "opcion 2, punteros a funcion es una buena eleccion"

Este es mi primer mensaje en este foro, y me inscribi justamente porque buscaba exactamente este tema,
Saludos,
 
Última edición:
Realmente nunca usé ese recurso, solo en forma indirecta cuando usé el FreeRTOS.

No es una mala idea, no sé como queda el uso de la memoria RAM, supongo que solo es apuntar a la dirección de código, pero no me queda del todo claro.

Me parece un recurso muy interesante para el uso de CallBacks en una función (como en el FreeRTOS) y puede ser interesante para armar estructuras que tengan una función adentro, al mejor estilo de la programación por objetos.

Ahora bajando a la pregunta original, hay que ver que tan complicado es el debug/seguimiento del programa usando este método, si es cómodo o no.

Lo que si puedo decir, es que el uso de switch case, para nada es considerado programación espagueti, de hecho C impide eso por ser estructural (salvo que uno se ponga a usar el goto (n)). De hecho, yo soy bastante "fanaticón" del uso de máquinas de estado usando switchs.
 
Pues te ofrezco implementar una cola dinámica de mensajes. Es un sistema similar a los punteros a funciones con varias ventajas:
Hola palurdo, me estás proponiendo un esquema basado en listas enlazadas... lo agregamos a la lista de opciones.
Corrijo... leyendo abajo de todo me parece que estás proponiendo un sistema de listeners (escuchadores suena feo, oyentes?) de eventos (cola de eventos o mensajes, el procesador de eventos busca oyentes en la lista enlazada cuyo parámetro "evento a escuchar" coincida con el actual.

Tengo que admitir que normalmente trato de evitar el uso de memoria dinámica (heap). La lista enlazada tiene la cuestión de que el acceso es secuencial (hay que escanear la lista de a 1 item, vi otras formas más con algoritmos de búsqueda sofisticados pero bueno, no son muy comunes) vs acceso aleatorio.

-No es complejo de implementar.
-Permite comunicación entre procesos simultaneos, o entre Interrupciones.
-Al ser dinámico, no hace falta mantener índices cada vez que añades un escuchador a la cola.

Más que de una interfaz de comandos sería un principio de framework + interfaz de comandos.

Seguramente al ser dinámico tiene mayor flexibilidad que un esquema estático como los que yo puse. Si me preocupa lo dinámico y el heap, puedo hacer un pool de N nodos estático y listo (claro, esta el temita de los huecos... pero mientras sea fifo no debería ser problema).

Estaba traduciendo a código, pero lo voy a poner luego que tengo que revisar unas cositas.

Este es el enfoque que se utiliza en el intérprete de comandos AT/T de los módulos WiFi ESP8266.

Ah que interesante... ese módulo se supone que puede correr código hecho por el usuario no?. Con eso en vista tiene mucho sentido un esquema de listeners.



...
Lo que si puedo decir, es que el uso de switch case, para nada es considerado programación espagueti, de hecho C impide eso por ser estructural (salvo que uno se ponga a usar el goto (n)).
Sí... digamos que lo que no me gusta es andar toqueteando la función que maneja todos los comandos para agregar uno nuevo, o que termine con una función de varios cientos de líneas de código. Pero es cierto que hay que esforzarse bastante para embarrarla con un esquema tan claro como ese.


...
De hecho, yo soy bastante "fanaticón" del uso de máquinas de estado usando switchs.

Jajaja, también me gustan las máquinas de estado (de hecho tengo una para el manejo de la interfaz de cli, por fuera del interprete en si).
Pregunta offtopic para Don Cosme: cual te gusta más? el framework de Quantum Leaps de Miro Samek (basado en funciones de evento con switch-case de eventos):
http://www.state-machine.com/

o el esquema de tablas estado/evento de la gente de RKH? (son de Mar del Plata creo?, fui a una charla de ellos en el sase 2013 o por ahí, me gustó como se las arreglaron para que las tablas evento/estado no tuvieran huecos) (el uso que hacen de macros de preprocesador es tremendo):
https://sourceforge.net/projects/rkh-reactivesys/

A lo mejor te referías solo a máquinas de estados simple (no jerárquicas), que sí, el switch-case es simple y directo.



Hola,
que microcontrolador utilizas?
msp430 por ahora, pero quiero separar bien el código que maneja hardware (uart) del intérprete de comandos, y de los comandos que defina el usuario.

El proyecto integro lo programe en assembler. (espagueti del bueno, pero extremadamente rapido)
Tenemos un valiente :), un vago vaguísimo traidor que se dejo caer en el abrazo de C te saluda.

La verdad me gustaria saber como se las arreglaria el compilador de C para en tu ejemplo "LED TOGGLE 200" leer ese 200, y pasarlo a binario en un uint8_t
Depende que librería traiga o no el compilador C que estés usando.
Se puede hacer atoi (ascii to integer):
uint8_t argVal = atoi("123"); //argVal = 123
O implementar tu propio atoi usando división entera y resto.

Mi pgrograma tiene 3 modulos principales:

1- Buffer de recepcion.
Es un Queue ciclico de 256 bytes, Creo que a esto llama palurdo "cola dinamica de mensajes" pues es justamente eso en mi caso. los comandos estan separados por <Enter> y cuando la Int de la UART recibe un enter activa un bit nuevo comando en buffer CMD_bit".
Creo que no... vos al parecer estás usando como "evento" setear un bit/flag en alguna variable global.
El problema con eso es que pasa si recibís un nuevo evento X (cualquiera) cuando todavía no terminaste de procesar el anterior evento X.
Por eso se hace una cola de eventos, en tu caso en la interrupción del uart se agregaría el evento ON_UART_ENTER_KEY (por llamarlo de alguna manera) a la cola de eventos, y luego en el main si la cola de eventos no está vacía se procesan de a uno.
Me parece que estás confundiendo la cola de eventos vs el buffer de recepción de datos.

2- command_interpreter:
Es una funcion que se llaama cuando CMD_bit=1 La ventaja de punteros a funcion es que el interprete es tambien una funcion (al menos en mi caso) y si te quieres complicar la vida pasa como parametro de un comando otro comando. (Yo desisti de eso). Mi lista es de 32 comandos adaptable a max.128. cada comando tiene una funcion asociada. El interprete de comandos realiza una busqueda binaria en la tabla y llama a la funcion correspondiente.
Epa... busqueda binaria (y)
No tengo idea como se programaria una busqueda binaria en C. En assm es posible, aunque si queda codigo casi incomprensible. la lista debe estar ordenada. es muy rapido en encontrar y ejecutar el cmd! El Queue me da la posibilidad de saber si es el ultimo comando de la lista.
http://quiz.geeksforgeeks.org/binary-search/

Tengo dos tipos de comandos, y por el planteo de tu pregunta veo que tambien necesitaras de lo mismo.
El primer tipo de comandos se ejecuta inmediatamente ej: "output 5 = 1<CR>". algo asi de simple no tiene mucho sentido ponerlo en una lista de eventos, creo yo..
Mmmm prefiero manejar un solo tipo de comandos, para usar el mismo código para todos y listo.
Sí, es cierto que a uno le da lástima decir: "pero este comando lo podría interpretar hasta en la rutina de interrupción misma!", seguro, pero hay comandos que no. Prefiero programar 1 solo caso general que cubra (con lo justito...) lo que se precisa y no múltiples casos particulares.

El segundo tipo de comandos agrega un puntero a funcion en una lista de eventos, yo a esta funcion o evento lo llamo Task. Por lo tanto este segundo tipo de comandos tambien tiene una task asociada.

3-task Scheduler:
al administrador de la lista de eventos me permiti llamarlo asi, :) Esta en el Main. La estructura de un Task tiene 3 modulos, Start, Run y Shutdown: T.Start: El commando pone la direccion de T.Start en la tabla de eventos. Aqui se inicializan variables y principalmente se controla que el hardware necesario este disponible. Si todo esta ok entonces T.Start pone en la tabla T.Run. T.Run pues eso, hace lo que tiene que hacer y chequea las condiciones para parar la tarea, si son validas entonces se cambia en la tabla a T.shutdown. Y este ultimo modulo libera el hardware, limpia la tabla del task sheduler y alguna que otra cosita que fuera necesaria como mensajes y eso.
Claro, aca como dice Don Cosme, larga vida a la máquina de estados, la ejecución del comando tiene distintos estados dependiendo de si está el recurso disponible o no. Mi cadena de estados es:
idle->recibiendo comando -> validar comando -> ejecutar: esperar recurso -> ejecutar:ejecutar -> transmitiendo respuesta -> idle

Al programar todo esto en assembler y querer modificar algo es bastante mas complejo que en C. pero igualmente creo que estos consejos es no estan de mas:
1ro Define de antemano las reglas para programar los eventos. Pues un evento no podria adueñarse del handler y dejar el resto de los comandos en cola sin ejecutar. Y si sobre la marcha debes modificar algo documenta bien lo hecho.
Si señor, que las funciones vuelvan rapidito e informen que lograron hacer o no.

mientras programes notaras que es lo que debes hacer, tocar, modificar cada vez que quieras agregar un comando. Documenta eso en una lista. que sera como tu guia para expandir tu programa, pues cuando la estructura basica este funcionando solo sera cuestion de programar nuevos eventos. Debes agregarlos sin alterar la estructura.
Sí, por eso decía antes de que me gusta separar en otro archivo los comandos que agrega el usuario del código que las llama

Bueno, todo esto para darte mi opinion: "opcion 2, punteros a funcion es una buena eleccion"

Este es mi primer mensaje en este foro, y me inscribi justamente porque buscaba exactamente este tema,
Saludos,
Punteros a función... agregamos una piedrita a la bolsa.
Sí, una interfaz de comandos te puede librar (al menos por un rato) de tener que hacer un software para PC que corra en cualquier en SO (y hoy también todos quieren la App de Android :contrato:) y permite ir viendo que es útil y no antes de empezar a tirar botoncitos por todos lados en la interfaz gráfica.

Bienvenido!!!!
 
Última edición:
Por si les sirve, les paso un diagrama UML de un subsistema de agentes reactivos que hice en JAVA hace muchos años. Usa varios patrones de diseño, pero a la larga, es mas o menos lo mismo que quieren hacer.

agentes-reactivos.jpg
 
msp430 por ahora, [...]
tengo tres M430F499 que recibi como samples en el 2003 y nunca los use.
Pregunta
Se pueden programar por rs232 algo asi como los micros de NXP con flashmagic? O es necesario una jtag o programador especial?

Creo que no... vos al parecer estás usando como "evento" setear un bit/flag en alguna variable global.
El problema con eso es que pasa si recibís un nuevo evento X (cualquiera) cuando todavía no terminaste de procesar el anterior evento X.
El bit es solo una señal justamente para indicar cuando aun hay comandos sin procesar.

Seria interesante si logras codigo transportable Mi nivel en C no es el suficiente para tal desafio. Hasta aqui mi aporte...
Saludos,
 
tengo tres M430F499 que recibi como samples en el 2003 y nunca los use.
Pregunta
Se pueden programar por rs232 algo asi como los micros de NXP con flashmagic? O es necesario una jtag o programador especial?
Si no tienen bootloader precisas un programador. Pero muchos "launchpad" de ellos ya incluyen un programador/depurador onboard del tipo ezFET. Es decir, no hagan como yo que en su momento gasté más de U$S200 cuando empecé con MSP430, con un launchpad tan barato como U$S10 tenés la misma funcionalidad.

Entonces, si no tienen bootloader -> comprate un launchpad.
Viendo la hoja de datos parece que ya viene con un bootloader uart. A lo mejor lo podes programar con Energia (energia.nu), que es el arduino portado a msp430. Aunque desconozco si el protocolo del bootloader serie del msp430f499 es compatible con el protocolo de energía.

El bit es solo una señal justamente para indicar cuando aun hay comandos sin procesar.

Claro, es otro esquema de ejecución de código. Lo podríamos señalar como flags de operación. Yo nombré un sistema de eventos, luego una capa más de complejidad te agrega los escuchadores de eventos, y complementario a eso están las máquinas de estado (que precisan manejar eventos de alguna manera).

Seria interesante si logras codigo transportable Mi nivel en C no es el suficiente para tal desafio. Hasta aqui mi aporte...
Saludos,

Es doloroso ver como se multiplica la cantidad de archivos para hacer algo simple... pero sí, voy a tratar, quizás podría experimentar haciendo algun video, ya veremos.
 
Ardogan dijo:
A lo mejor te referías solo a máquinas de estados simple (no jerárquicas), que sí, el switch-case es simple y directo.

Máquinas de estados simples con switch que las aplico en eventos, funciones y en rutinas principales. Me gusta usar defines para fijar el nombre de los estados y ya.
 
Atrás
Arriba