Temporizadores de manejo de máquina de estado finito con gracia?

4

He estado trabajando principalmente con MCU de 8 bits, donde la mayoría de los RTOS tienen demasiada sobrecarga.

La mayoría de las aplicaciones en las que he trabajado, solo han sido una interrupción periódica con if / else encadena toda la lógica de procesamiento, y luego la MCU vuelve al modo de suspensión.

Esto ha funcionado bien para muchas cosas y tiene una sobrecarga mínima. Pero para un sistema, estoy llegando al punto en que hay tantos indicadores de control que estoy listo para llamar a mi propio sistema "espagueti". Sería horrible para alguien nuevo elegir este sistema e implementar alguna nueva funcionalidad.

(Tengo un LED de dos colores, que debe tener como 8 estados diferentes y patrones de parpadeo dependientes del tiempo, dependiendo de en qué estado se encuentre el resto del sistema. Es un ejercicio horrible, por lo que debería ser tan simple ...)

Estaba mirando tal vez haciendo una máquina de estados finitos, y tratando de eliminar tantas banderas de control.

Un problema conceptual que estoy viendo es el uso de temporizadores en una máquina de estados. Actualmente, tengo un temporizador de hardware y luego un grupo de contadores de temporizadores definidos que incrementan / deincremento, una variable de indicador de control va a 0/1, por lo que vamos a través de la cadena if / else.

En mi etapa de planificación para una máquina de estado más estricta, ¿utilizaría más cronómetros de hardware y desencadenaría interrupciones externas como eventos para volver a la máquina de estado?

Mi reacción intestinal (sea correcta o no) es usar tantas interrupciones externas como sea posible para la máquina de estado si usted 1) introduce todo tipo de problemas de prioridad de interrupción potenciales que traen su propio conjunto de problemas, donde actualmente el tiempo es muy determinista pero la lógica de control es simplemente confusa y 2) está usando más corriente ejecutando un montón de temporizadores en lugar de manejar la lógica del temporizador como variables.

Veo cómo todavía puedes incrementar / eliminar los temporizadores de variables en tu máquina de estado, pero ¿no es eso anti-ético al patrón de la máquina de estado?

Me siento bastante cómodo con los punteros de función frente al debate de las declaraciones de cambio sobre cómo codificar la máquina de estados, o si desea usar una tabla de transición, etc.

Me pregunto específicamente cómo las personas han manejado el aspecto de la administración del temporizador de sus máquinas de estado de una manera elegante.

    
pregunta Leroy105

3 respuestas

2

Una forma común de hacer esto sería establecer un tiempo máximo de ejecución para cada estado y luego evaluar cada uno de ellos (con una cobertura de código del 100%) y asegurar que nunca excedan el tiempo máximo de ejecución. Con algo de suerte, incluso puedes usar el guardián en chip para garantizar esto, si puede funcionar con tiempos de espera suficientemente bajos.

Ahora, lo que probablemente está buscando no es una, sino varias máquinas estatales. Es decir, puede tener una máquina de estado universal como

STATE_MACHINE[state++](); 
if(state == STATES_N) 
{ /* reset state machine */ 
} 

que no hace más que pasar por los distintos módulos de software, dándoles a cada uno un "intervalo de tiempo". O bien puede ejecutar todos los controladores de hardware de una sola vez y volver al modo de suspensión, o puede elegir ejecutar solo uno de ellos. Por supuesto, esto depende de los requisitos en tiempo real.

Uno de estos estados podría ser led_execute() , que sería la rutina del LED que realiza un seguimiento de lo que está sucediendo en los LED en este momento. Esta rutina reside dentro del controlador de LED y, a su vez, puede realizar un seguimiento de cada estado de LED, de modo que se parezca a:

typedef enum
{
  LED_OFF,
  LED_RED_LIT, // whatever names make sense
  LED_RED_BLUE_LIT,
  ...
  LED_DONE,
  LED_N
} led_state_t;
...    
static led_state_t led_state = LED_OFF;
...

void led_execute (void)
{
  led_state = LED_STATE_MACHINE[led_state]();
}

Si los estados dependen de la entrada externa, entonces tal vez omita la parte del estado de retorno y haga que el estado se actualice solo a través de los configuradores / captadores.

Esto debería eliminar completamente la necesidad de las banderas, en particular las banderas no relacionadas ubicadas en el mismo ámbito, lo que puede ser una pesadilla. La parte más importante aquí es no mezclar la complejidad del LED con la complejidad de algún otro hardware.

Digamos que simultáneamente estás rebotando un botón. Diga que debe terminar el rebote antes de que se enciendan los LED, eso no significa que los botones tengan que saber sobre los LED o que los LED tengan que saber sobre los botones. El código de la persona que llama debe realizar un seguimiento de estas cosas. Lo que significa que podría necesitar una capa de abstracción entre la máquina de estado más externa y los controladores en sí. Si el controlador LED solo obtiene una entrada, "haz esto!" de la persona que llama, entonces no podría importarle menos las razones detrás de esto.

    
respondido por el Lundin
2

Si quieres una "multitarea" de tecnología súper baja, la interrupción del temporizador puede tener este aspecto:

timer_isr()
{
    process1();
    process2();
    process3();
}

Entonces, si su temporizador se dispara, digamos 100 veces por segundo, entonces cada vez que se llama a cada función process (). Estas funciones son FSM que implementan sus diferentes tareas "multitarea". Si todos son independientes, entonces es fácil.

Ahora, si sus tareas son dependientes, puede hacer algo como:

timer_isr()
{
    check_buttons();
    blink_red();
    blink_blue();
}

En este caso, las tareas se comunicarán a través de variables globales desagradables (no vamos a hacer cuadros de mensajes y FIFO en un 8-bit somos). Por ejemplo, check_buttons () debería rebotar, etc., y establecer algunas marcas y / o influir directamente en el estado de los otros dos FSM que parpadean los LED.

Incluso podemos usar esta tecnología de vanguardia llamada C ++:

timer_isr()
{
    check_buttons();
    red.blink();
    blue.blink();
}

En este caso, check_buttons () llamaría "red.setBlinkMode (algún valor)" cuando se presiona el botón apropiado, por ejemplo. "rojo" y "azul" son objetos globales. En este caso, este pequeño bit de OO le permite implementar el mismo algo para ambos sin tener que meterse con toneladas de globales, o punteros a estructuras, etc.

Es bueno manejar tus botones en un solo lugar en tu código. Especialmente si los botones controlan varias cosas, por ejemplo, un botón para seleccionar el LED y otro para modificar el patrón de parpadeo del LED seleccionado.

El método .blink (), por ejemplo, incrementaría un contador específico de LED hasta que alcance el período de parpadeo, o ajustaría un PWM en una forma dependiente del tiempo para que parpadee con elegancia, ese tipo de cosas.

Por ejemplo, si sus veces se llama milisegundos, su método blink () podría ser:

LED::blink()
{
    if( led_on) {
        if( counter++ > period ) {
            counter=0;
            led_pin = !led_pin;
        }
    } else { led_pin = 0; counter=0; }
}

... algo así. Se les llama al mismo período de tiempo, por lo que todas estas pequeñas máquinas de estado conocen el tiempo contando la cantidad de veces que se les llama. En este caso, el estado del FSM es led_on and counter.

    
respondido por el peufeu
1

En general, he descubierto que es mejor elegir un pequeño incremento de tiempo (quizás un par de milisegundos hasta quizás 250usec para un micro de 8 bits) y usarlo para la mayoría o la totalidad del tiempo. Esto es comparable a la granularidad en un RTOS.

Es más fácil si tiene un micro con una arquitectura decente que permita interrupciones anidadas.

    
respondido por el Spehro Pefhany

Lea otras preguntas en las etiquetas

Comentarios Recientes

Noté un uso excesivo de la CPU en las siguientes pruebas. ¡La forma más fácil de confirmar si su código está usando una carga excesiva es con dos llamadas a funciones (: OnAllCPU);) ¿Limitaciones de rendimiento de bytes raíz? No he encontrado una causa específica en esta área. Gracias a Dios, Stack Overflow o algo muy simple está buscando problemas porque todavía no he tenido que ejecutar y examinar todo el código que discutí contigo durante horas con respecto al uso. Resumiendo ~ ¿Fue útil este artículo?... Lees verder