Saltar al contenido
Apuntes GITT 4º

Escribe al menos dos letras.

    SEDAMáquinas de estados y StateChartsPEI1 · 19:00 en 20 días

    Diseño software

    Máquinas de estados y StateCharts

    De la FSM con switch-case a los StateCharts de UML: entry, do, exit, jerarquía, historia y concurrencia

    Idea clave. Casi todo programa de un sistema empotrado reacciona a eventos (una tecla, un byte por la UART, un Timer que vence) y lo que hace depende de lo que ya ha pasado. Eso es una máquina de estados. Se diseña con un diagrama y se programa con un switch. Cuando el diagrama crece, los StateCharts lo mantienen legible. Es la parte de la asignatura que más pesa en el examen.

    Máquinas de estados finitas (FSM)

    Una FSM tiene:

    Hay dos formas de colocar las salidas:

    Cómo se programa

    typedef enum { REPOSO, LLENANDO, ALARMA } estado_t;
    static estado_t estado = REPOSO;
    
    void maquina(void) {                  // se llama periódicamente, p. ej. cada 10 ms
      switch (estado) {
        case REPOSO:
          if (nivel < 30) { bomba(1); estado = LLENANDO; }
          break;
        case LLENANDO:
          if (nivel > 80) { bomba(0); estado = REPOSO; }
          break;
        case ALARMA:
          /* ... */
          break;
      }
    }

    Las reglas que hay que cumplir:

    Pulsadores: mejor leerlos que interrumpir

    Para pulsadores y menús, en clase se recomienda no usar interrupciones de GPIO. Un pulsador rebota: al pulsarlo da varios flancos en pocos milisegundos y cada uno sería una interrupción. Es mejor leer el pin periódicamente (cada 10–50 ms, más que lo que dura el rebote) y compararlo con la lectura anterior:

    void tick_10ms(void) {
      static int antes = 1;                          // con pull-up, suelto = 1
      int ahora = (LPC_GPIO2->FIOPIN >> 11) & 1;     // Key1 en P2.11
      if (antes == 1 && ahora == 0) evento_tecla = 1; // flanco de bajada: pulsación
      antes = ahora;
    }

    Así cada pulsación genera un solo evento, y la máquina del menú lo consume en su siguiente paso.

    Limitaciones de las FSM

    Con problemas reales, una FSM plana se vuelve inmanejable:

    StateCharts

    Los propuso David Harel en 1986 y UML los adoptó (UML State Machine). Añaden a la FSM lo que le falta.

    Estados con entry, do y exit

    Cada estado puede llevar tres tipos de acciones:

    Con exit, no hay que repetir «apagar el magnetrón» en cada transición que sale de CALENTANDO: se pone una vez y no se olvida nunca.

    Transiciones: evento [condición] / acción

    tecla [tecla == 'CR'] / enviar(): con el evento tecla, si se cumple la condición, se pasa al otro estado y se ejecuta la acción. La condición (guard) permite que un mismo evento lleve a sitios distintos; en el diagrama se dibuja también con un rombo de decisión y una rama [else].

    El orden al cruzar una transición es siempre:

    1. exit del estado de origen;
    2. acción de la transición;
    3. entry del estado de destino.

    Una autotransición (de un estado a sí mismo) también sale y vuelve a entrar: ejecuta exit y entry.

    Pseudoestados

    Jerarquía

    Un superestado agrupa varios subestados y lleva su propio estado inicial dentro. Una transición que sale del superestado vale para todos sus subestados: un único «cancelar» o «parada de emergencia» en vez de una flecha desde cada estado. Se diseña de arriba abajo: primero los superestados y después el detalle de cada uno.

    En el microondas, IDLE e INGRESANDO forman el superestado Esperando. Pulsar START solo tiene efecto desde INGRESANDO (sin tiempo no hay nada que calentar).

    Con histórico. En el ejemplo del control de nivel de agua de clase, ACTIVO contiene los subestados de llenado. Si se apaga (P_ROJO) y se vuelve a encender (P_VERDE), sin H se empezaría por el inicial; con H, se retoma justo donde se dejó.

    Concurrencia

    Un estado se puede dividir con una línea discontinua en regiones ortogonales que funcionan a la vez y de forma independiente: por ejemplo, el control del magnetrón en una región y la luz o el display en otra. Evita multiplicar los estados de una por los de la otra. Una región puede consultar el estado de otra («si la otra está en Esperando»).

    Transiciones locales y externas

    Cuando una transición va del borde de un superestado a uno de sus subestados:

    La diferencia importa cuando el superestado tiene entry o exit con efectos (reiniciar un contador, encender algo): con una externa, se repiten.

    El microondas, paso a paso

    Pulsa los eventos y fíjate en el orden en que se ejecutan exit, la acción y entry en cada transición. Los eventos que no hacen nada en el estado actual están desactivados.

    Para entender

    Máquina de estados: el microondas

    EsperandoteclateclacancelarstartfincancelarabrircerrarIDLEentry / display ← horaIngresandoentry / display ← tiempoCalentandoentry / magnetrón ON, Timer ONdo / tiempo−− cada 1 sexit / magnetrón OFF, Timer OFFPuerta abiertaentry / display parpadea
    EsperandoteclateclacancelarstartfincancelarabrircerrarIDLEentry / display ← horaIngresandoentry / display ← tiempoCalentandoentry / magnetrón ON, Timer ONdo / tiempo−− cada 1 sexit / magnetrón OFF, Timer OFFPuerta abiertaentry / display parpadea
    EstadoEvento / acciónVa a
    IDLEtecla / guardar dígitoIngresando
    Ingresandotecla / añadir dígitoIngresando
    Ingresandocancelar / tiempo ← 0IDLE
    Ingresandostart / luz ONCalentando
    Calentandofin / pitido, luz OFFIDLE
    Calentandocancelar / tiempo ← 0, luz OFFIDLE
    CalentandoabrirPuerta abierta
    Puerta abiertacerrarCalentando

    Cómo se programa un StateChart

    La jerarquía se traduce en switch anidados: el de fuera elige el superestado y el de dentro, el subestado. Lo que es común a todo el superestado (como «cancelar») se comprueba en el de fuera, una sola vez.

    switch (estado) {
      case ESPERANDO:
        switch (subestado) {
          case IDLE:       /* ... */ break;
          case INGRESANDO: /* ... */ break;
        }
        break;
      case CALENTANDO:
        /* ... */
        break;
    }

    Para respetar el orden exit → acción → entry sin repetir código, conviene una función que haga el cambio:

    static void salir(estado_t e) {
      if (e == CALENTANDO) { magnetron(0); timer(0); }   // exit
    }
    
    static void entrar(estado_t e) {                     // entry
      if (e == CALENTANDO) { magnetron(1); timer(1); }
      else if (e == IDLE) display_hora();
    }
    
    static void transicion(estado_t destino, void (*accion)(void)) {
      salir(estado);                // 1. exit del origen
      if (accion) accion();         // 2. acción de la transición (puede no haber)
      estado = destino;
      entrar(destino);              // 3. entry del destino
    }
    
    // En el case CALENTANDO:
    //   if (ev == FIN) transicion(IDLE, pitido);

    void (*accion)(void) es un puntero a función: se pasa el nombre de la función que hay que ejecutar como acción (o NULL si la transición no tiene). Las acciones do van fuera de transicion: se ejecutan en cada llamada a la máquina mientras se está en el estado.

    El histórico se programa guardando el subestado en una variable al salir del superestado y usándola, en vez del inicial, al volver a entrar.

    Preguntas de examen

    ¿Qué ventajas da un StateChart frente a una FSM plana? Pon un ejemplo de cada una.

    Jerarquía: una sola transición de error desde el superestado en vez de una por estado. entry/exit: las acciones de encendido y apagado se escriben una vez y no se olvidan en ninguna salida. Histórico: tras una pausa se vuelve donde se estaba. Concurrencia: dos partes independientes (control y display) sin combinar sus estados. (Respuesta redactada a partir de los apuntes y las diapositivas.)

    Un estado tiene exit: motor OFF y entry: motor ON, y una autotransición con el evento e. ¿Qué le pasa al motor con e?

    Se apaga (exit) y se vuelve a encender (entry) en el mismo paso: hay un instante con el motor apagado. Si eso no se quiere, el evento e se trata como transición interna (escrita dentro del estado, sin flecha), que no ejecuta ni exit ni entry. (Respuesta redactada a partir de las diapositivas.)

    ¿Dónde se llama a la función de la máquina de estados y por qué no puede bloquearse?

    Desde una ISR periódica (SysTick, un Timer) o desde la ISR del periférico que trae los eventos (la UART al recibir cada byte). Si se bloquease esperando, la ISR no terminaría: el resto del programa y las interrupciones de menor prioridad se quedarían sin atender. (Respuesta redactada a partir de los apuntes.)

    Material del curso usado: Apuntes de Miguel · Máquinas de estados finitas programadas y StateCharts (diapositivas de clase).