9 votos

Interrumpir el manejo de microcontroladores y ejemplo FSM

Pregunta inicial

Tengo una pregunta general sobre el manejo de las interrupciones en los microcontroladores. Estoy usando el MSP430, pero creo que la pregunta puede ser extendida a otros uCs. Me gustaría saber si es o no una buena práctica para habilitar/deshabilitar las interrupciones con frecuencia a lo largo del código. Quiero decir, si tengo una parte de código que no va a ser sensibles a las interrupciones (o peor aún, no hay que escuchar a las interrupciones, por cualquier razón), es mejor:

  • Deshabilitar las interrupciones antes y luego volver a activar después de la sección crítica.
  • Poner una bandera en el interior de los respectivos ISR y (en lugar de deshabilitar la interrupción), ajuste el indicador a false antes de la sección crítica y el restablecimiento a la verdadera, justo después. Para impedir que el código de la ISR de ser ejecutado.
  • Ninguno de los dos, así que se admiten sugerencias!

Actualización: interrupciones y el estado de los gráficos

Voy a dar una situación específica. Supongamos que queremos implementar un organigrama del estado, el cual está compuesto por 4 bloques:

  1. Las Transiciones/Efecto.
  2. Condiciones de salida.
  3. Actividad de entrada.
  4. Hacer la actividad.

Esto es lo que un profesor nos enseñó en la universidad. Probablemente, no es la mejor manera de hacerlo es siguiendo este esquema:

while(true) {

  /* Transitions/Effects */
  //----------------------------------------------------------------------
  next_state = current_state;

  switch (current_state)
  {
    case STATE_A:
      if(EVENT1) {next_state = STATE_C}
      if(d == THRESHOLD) {next_state = STATE_D; a++}
      break;
    case STATE_B:
      // transitions and effects
      break;
    (...)
  }

  /* Exit activity -> only performed if I leave the state for a new one */
  //----------------------------------------------------------------------
  if (next_state != current_state)
  {
    switch(current_state)
    {
      case STATE_A:
        // Exit activity of STATE_A
        break;
      case STATE_B:
        // Exit activity of STATE_B
        break;
        (...)
    }
  }

  /* Entry activity -> only performed the 1st time I enter a state */
  //----------------------------------------------------------------------
  if (next_state != current_state)
  {
    switch(next_state)
    {
      case STATE_A:
        // Entry activity of STATE_A
        break;
      case STATE_B:
        // Entry activity of STATE_B
        break;
      (...)
    }
  }

  current_state = next_state;

  /* Do activity */
  //----------------------------------------------------------------------
  switch (current_state)
  {
    case STATE_A:
      // Do activity of STATE_A
      break;
    case STATE_B:
      // Do activity of STATE_B
      break;
    (...)
  }
}

También vamos a suponer que, por ejemplo STATE_A, quiero ser sensibles a una interrupción proveniente de un conjunto de botones (con debouce sistema, etc. etc.). Cuando alguien pulsa uno de estos botones, se genera una interrupción y el indicador relacionado con el puerto de entrada se copia en una variable buttonPressed. Si el rebote se establece en 200 ms de alguna manera (temporizador de vigilancia, temporizador, contador, ...) estamos seguros de que buttonPressed no puede ser actualizado con un nuevo valor antes de 200 ms. Esto es lo que estoy pidiendo a usted (y a mí :) por supuesto)

Necesito habilitar la interrupción en el HACER de la actividad de STATE_A y desactivar antes de salir?

/* Do activity */
//-------------------------------------
switch (current_state)
{
  case STATE_A:
    // Do activity of STATE_A
    Enable_ButtonsInterrupt(); // and clear flags before it
    // Do fancy stuff and ...
    // ... wait until a button is pressed (e.g. LPM3 of the MSP430)
    // Here I have my buttonPressed flag ready!
    Disable_ButtonsInterrupt();
    break;
  case STATE_B:
    // Do activity of STATE_B
    break;
  (...)
}

De manera que estoy seguro de que la próxima vez que me execxute bloque 1 (transición/efectos) en la siguiente iteración estoy seguro de que las condiciones que se verifican a lo largo de las transiciones no son procedentes de un posterior interrupción que ha sobrescrito el anterior valor de buttonPressed que necesito (aunque es imposible que esto sucede debido a 250 ms debe transcurrir).

17voto

RelaXNow Puntos 1164

La primera táctica es el arquitecto de la general de firmware así que está bien para las interrupciones se producen en cualquier momento. Tener que desactivar las interrupciones de forma que el primer plano de código se puede ejecutar una atómica de la secuencia se debe hacer con moderación. A menudo hay una forma arquitectónica a su alrededor.

Sin embargo, la máquina está ahí para servir a usted, no al revés. Reglas generales de pulgar son sólo para mantener el mal a los programadores escribir realmente mal código. Es mucho mejor para entender exactamente cómo funciona la máquina y, a continuación, el arquitecto una buena manera de aprovechar esas capacidades para realizar la tarea deseada.

Tenga en cuenta que, a menos que realmente están apretados en los ciclos o los lugares de la memoria (ciertamente puede suceder), que de lo contrario se desea optimizar para la claridad y la mantenibilidad del código. Por ejemplo, si usted tiene un 16 bits de la máquina que las actualizaciones de una versión de 32 bits del contador en un reloj de interrupción, usted necesita para asegurarse de que cuando el primer plano de código lee el contador de que las dos mitades de la misma son consistentes. Es una manera de apagar las interrupciones, leer las dos palabras, y, a continuación, gire a la interrumpe de nuevo. Si la latencia de interrupción no es crítico, entonces eso es perfectamente aceptable.

En el caso de que usted debe tener baja interrumpir latentcy, usted puede, por ejemplo, leer la palabra alta, leer la palabra baja, leer la palabra alta de nuevo y repita si es cambiado. Esto ralentiza el primer plano de código un poco, pero no agrega latencia de interrupción. Hay varios pequeños trucos. Otra podría ser la de establecer un indicador en la rutina de interrupción que indica el contador debe ser incrementado, a continuación, hacer que en el evento principal del bucle en el primer plano de código. Que funciona bien si el contador de interrupción es lo suficientemente lenta como para que el bucle de eventos hará el incremento antes de que el indicador se establece de nuevo.

O, en lugar de un uso de la bandera, un uno-contador de palabras. El primer plano de código mantiene una variable independiente que contiene la última contador se ha actualizado el sistema. Hace un unsigned restar del contador, menos el valor guardado para determinar cuántas de las garrapatas en un tiempo que ha de manejar. Esto permite que el primer plano de código a perder hasta 2N-1 eventos en un tiempo, donde N es el número de bits en una palabra nativa de la ALU puede manejar de forma atómica.

Cada método tiene sus propias ventajas y desventajas. No hay una sola respuesta correcta. De nuevo, entender cómo funciona la máquina, entonces usted no tiene reglas de oro.

7voto

Stefan Arentz Puntos 151

Si usted necesita una sección crítica, debe asegurarse de que la operación el resguardo de su sección crítica es atómico y no puede ser interrumpido.

Por lo tanto, deshabilitar las interrupciones, que es más comúnmente manejados por una sola instrucción del procesador (y se llama utilizando un compilador función intrínseca), es una de las más seguras apuestas que usted puede tomar.

Dependiendo de su sistema, puede haber algunos problemas con los que, al igual que una interrupción puede obtener perdidas. Algunos microcontroladores establecer los indicadores, independientemente de la situación mundial de la habilitación de interrupción, y después de salir de la sección crítica, las interrupciones se ejecutan y se acaba de retraso. Pero si usted tiene una interrupción que se produce a un ritmo muy alto, se puede perder un segundo de tiempo de la interrupción sucedido si usted bloquear las interrupciones durante un plazo de tiempo demasiado largo.

Si su sección crítica requiere sólo una interrupción no para ser ejecutado, pero en otros debe ser ejecutado, el otro enfoque parece viable.

Me encuentro en la programación de las rutinas de servicio de interrupción tan corto como sea posible. Es así que se establece un indicador que se verificó durante el programa normal de las rutinas. Pero si lo haces, ten cuidado con las condiciones de carrera mientras se espera para que la bandera se establezca.

Hay un montón de opciones y seguramente no una sola respuesta correcta a esta, este es un tema que requiere de un diseño cuidadoso y merece un poco más de pensamiento que de otras cosas.

5voto

Federico Builes Puntos 1940

Si se ha determinado que una sección de código se debe ejecutar ininterrumpida a continuación, excepto en circunstancias inusuales, debe deshabilitar las interrupciones para la duración mínima posible para completar la tarea, y las vuelve a activar después.

Poner una bandera en el interior de los respectivos ISR y (en lugar de deshabilitar la interrupción), ajuste el indicador a false antes de la sección crítica y el restablecimiento a la verdadera, justo después. Para impedir que el código de la ISR de ser ejecutado.

Esto permitiría que se produzca una interrupción, un código de salto, un cheque, un retorno. Si su código puede manejar esta cantidad de la interrupción, entonces usted probablemente debería simplemente el diseño de la ISR para establecer un indicador en lugar de realizar el check - sería más corto y manejar la bandera en tu rutina de código. Esto suena como alguien puso demasiado código en la interrupción, y que están usando la interrupción para realizar más acciones que deben llevarse a cabo en el salón de código.

Si se trata de un código donde las interrupciones de largo, una bandera como sugieren podría resolver el problema, pero va a ser mejor, simplemente deshabilitar las interrupciones si usted no puede volver a factor-el código para eliminar el exceso de código en la interrupción.

El principal problema con la bandera de manera que no se ejecute la interrupción en todo lo que puede tener repercusiones más adelante. La mayoría de los microcontrolers hará un seguimiento de interrupción de banderas, incluso mientras las interrupciones globalmente deshabilitado y, a continuación, ejecute la interrupción cuando vuelva a habilitar las interrupciones:

  • Si no hay interrupciones que se produjeron durante la sección crítica, ninguno se ejecutan después.
  • Si una interrupción se produce durante la sección crítica, uno es ejecutado después.
  • Si varias interrupciones que se producen durante la sección crítica, sólo uno es ejecutado después.

Si el sistema es complejo y tiene para el seguimiento de las interrupciones más completo, tendrás que diseñar un sistema más complicado para el seguimiento de las interrupciones y operar en consecuencia.

Sin embargo, si usted siempre el diseño de sus interrupciones para realizar el trabajo mínimo necesario para cumplir su función, y retrasar todo lo demás, para la tramitación ordinaria, a continuación, las interrupciones rara vez influyen en otros código negativamente. Tienen la interrupción de captura o de liberación de datos, si fuera necesario, o set/reset salidas, etc según sea necesario, a continuación, tiene la principal ruta de acceso del código prestar atención a las banderas, buffers, variables y de la interrupción afecta de modo que el largo proceso se puede hacer en el bucle principal, en lugar de la interrupción.

Esto debería eliminar todo pero muy, muy pocas situaciones en las que usted puede ser que necesite un servicio ininterrumpido de sección de código.

1voto

Al pacino Puntos 415

Poner una bandera en el interior de la ISR como usted describe, probablemente no funcionará, ya que básicamente lo que hace es ignorar el suceso que provocó la interrupción. La deshabilitación de las interrupciones en el mundo es generalmente la mejor opción. Como otros han dicho, usted no debería tener que hacer esto muy a menudo. Tenga en cuenta que cualquier lectura o escritura, que se realiza a través de una sola instrucción no necesita ser protegida ya que la instrucción se ejecuta o no.

Mucho depende de qué tipo de recursos con los que estamos tratando de compartir. Si estás alimentando los datos de la ISR para el programa principal (o viceversa), se podría implementar algo así como un búfer FIFO. La única operación atómica sería la actualización de la lectura y escritura de los punteros, lo que minimiza la cantidad de tiempo que usted pasa con las interrupciones deshabilitadas.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X