Temporizadores e interrupciones de software en un microcontrolador

Tengo algunas preguntas sobre temporizadores de software e interrupciones en un microcontrolador. Solo para información, uso un microcontrolador dsPIC33E.

El objetivo final es implementar un protocolo de comunicación serie: RS485 con Modbus. Logré transmitir y recibir un mensaje, y ahora tengo que hacer una parte de procesamiento de mensajes.

Dado que necesito un montón de temporizadores para todo tipo de tareas diferentes (por ejemplo, se necesita un retraso de 3,5 caracteres para la comunicación en serie, se necesita algo de retraso para eliminar el rebote de los botones, etc.), planeo implementar temporizadores en un software usando un solo temporizador de hardware. Por ejemplo, el período del temporizador de hardware se establece en 100 us, y se genera una interrupción en cada "desbordamiento", es decir, cada 100 us. En el ISR, solo actualizo los contadores globales, que son básicamente temporizadores de software con una resolución de 100 us, mientras que el temporizador de hardware ISR tiene la prioridad más alta. ¿Es esta una buena manera de hacer esto, o hay alguna mejor manera?

Cada contador (es decir, un temporizador de software) ha definido su propio período, y una vez que el contador alcanza su "valor de período", quiero llamar a alguna función. Ahora bien, no es una muy buena idea llamar a esta función desde el interior del temporizador de hardware ISR, porque esa función se procesará con la máxima prioridad ya que la rutina de llamada es el temporizador de hardware ISR. Lo que quiero es definir alguna función, digamos:

void Modbus_Protocol(void);

y poder definirlo como una interrupción, que no será de máxima prioridad. Solo el temporizador de hardware principal tiene la máxima prioridad en este concepto. De esa manera, una vez que el contador en el temporizador de hardware ISR alcanza su valor de período, no llamará a su función, sino que simplemente establecerá un indicador para activar una interrupción (por ejemplo, void Modbus_Protocol (void)), que será se activa después de que regresa el ISR principal, dependiendo de su prioridad. ¿Se puede hacer algo así, es decir, puedo definir interrupciones de software?

Sé que existe la posibilidad de usar interrupciones de hardware que no se usan, y simplemente establecer un indicador de interrupción desde el software. Pero no creo que esta sea una forma elegante de implementar interrupciones si las interrupciones de software definidas por el usuario son posibles.

¿Ha intentado implementar su código usando una segunda interrupción del temporizador para establecer un indicador para luego llevar a cabo su código dentro de su ciclo while principal?
Puedo implementar esto usando una interrupción de hardware (temporizador) sin ningún problema, pero quiero usar temporizadores de software en su lugar, por las razones mencionadas en la pregunta. En ese caso, también necesito interrupciones de software, ya que los temporizadores de software se actualizan dentro del ISR de mayor prioridad y no quiero llamar a otras funciones desde dentro de esa interrupción.
puede usar la interrupción para contar los distintos tiempos para cada tarea que creo que está diciendo que tiene diferentes intervalos de tiempo. if(aflag) { do a } if(bflag) { do b } y así sucesivamente en el bucle principal de primer plano con el manejador de interrupciones configurando las banderas en el momento apropiado. Básicamente, no tiene que usar interrupciones alternativas, solo puede tener la tarea en primer plano, sondear estas banderas. Muestrear los botones que puede hacer en el controlador de interrupción del temporizador 100us, no cada vez que sea necesario, cada enésima vez. luego guarde el resultado en una bandera que la tarea en primer plano ve cuando tiene la oportunidad.
Si el controlador solo está generando un temporizador basado en 100us preescalado como la respuesta de Batuu, es posible que no necesite la complicación de interrupción en absoluto, podría usar un preescalador de hardware y todos sondean el temporizador real en lugar de un contador suave artificial global. En primer lugar, ¿estás bromeando en serie? Obtenga una parte diferente con un uart si ese es el caso.
No estoy bitbanging en serie, estoy usando un UART dedicado para hacer eso. ¿Qué quiere decir con "un preescalador de hardware" y "el temporizador real"? Intenté buscar en Google sobre el temporizador de CPU de sondeo, pero no encontré nada. ¿Hay un registro que se actualiza en cada tic de la CPU?

Respuestas (2)

No necesita "interrupciones de software" especiales. Eche un vistazo al siguiente código. Descuidé algunas cosas técnicas como la declaración de variables para mayor claridad.

Use su ISR solo para contar un temporizador (tick). En el main() está esperando para sincronizar con este temporizador. Use un temporizador dedicado para cada tarea que tenga que procesar. Todos estos temporizadores se incrementan cada tic de ISR.

Si el temporizador de la tarea expira, se procesa la tarea dedicada. Hay algunas características especiales en este tipo de implementación. Cuando resta el tiempo EXPITADO de su temporizador en lugar de establecerlo en 0, su software es más robusto si alguna de las tareas lleva más de 100 µs. Es una especie de criterio suave en tiempo real.

Obtienes un pseudo sistema multitarea.

isr() //100µs
{
    tick++;
}

main()
{    
  while(true)
  {
    while(tick == last_tick);
    last_tick = tick;

    modbus_timer++;
    task1_timer++;
    task2_timer++;

    if(modbus_timer >= MODEBUS_TIMER_EXPIRED)
    {
      modbus_timer -= MODEBUS_TIMER_EXPIRED;
      Modbus_Protocol();
    }

    if(task1_timer >= TASK1_TIMER_EXPIRED)
    {
      task1_timer -= TASK1_TIMER_EXPIRED;
      Task1();
    }

    if(task2_timer >= TASK2_TIMER_EXPIRED)
    {
      task2_timer -= TASK2_TIMER_EXPIRED;
      Task2();
    }
  }//forever-loop
}// main()
Por supuesto, esta es otra forma de implementar "temporizadores de software". Pero de esta forma, no puedo asignar niveles de prioridad a las "interrupciones". Por ejemplo, quiero que una interrupción de software Modbus_Protocol() tenga mayor prioridad que alguna otra interrupción de hardware. No creo que pueda hacer eso usando tu ejemplo. Otra cosa: no usaré estos temporizadores constantemente, por lo que realmente no estoy buscando una operación en tiempo real. Lo que quiero decir con esto es que un temporizador Modbus se iniciará solo cuando el UART reciba un mensaje y una vez que se reciba el mensaje, el temporizador se detendrá y se restablecerá.
Dos puntos: Piense en su arquitectura de software. Modbus_Protocol() parece ser una especie de intérprete/analizador de mensajes modbus. Por lo general, esta es una tarea de baja prioridad. Use polling o usart isr para recibir los caracteres/marcos en la E/S del hardware y coloque estos marcos en una cola de software o fifo. Si el cuadro está completo, llame a su intérprete (baja prioridad).
Segundo: dependiendo de su controlador específico, puede enmascarar algunas interrupciones para darse cuenta de la prioridad de la tarea de nivel principal () sobre las interrupciones de hardware. Pero normalmente prefiero el primer enfoque, porque es más determinista.
Así es exactamente como voy a implementar la parte de Modbus: la interrupción de alta prioridad (UART_RX ISR) llenará un búfer, y una vez que se reciba el mensaje completo, se llamará a un intérprete de Modbus (baja prioridad) para vaciar el búfer y para interpretar el mensaje. Pero sería mucho más flexible si pudiera definir una función (por ejemplo, un intérprete de Modbus u otra cosa) que se declare como una interrupción. De esa manera, no tengo que preocuparme de qué ISR está llamando a qué función, ya que la función se llama configurando su indicador de interrupción. Nuevamente, sé que puedo usar otras interrupciones de hardware no utilizadas.
Modificar mi ejemplo. Declare un indicador "modbus_frame_received" y configúrelo como verdadero en su controlador de uso, si es así. En lugar de esperar un modbus_timer, simplemente haga if(modbus_frame_received) { Modbus_Protocol(); modbus_frame_received = falso; }. Aquí obtiene una demora/inestabilidad máxima de 100 µs entre la recepción completa y la llamada del intérprete. En mi humilde opinión, no hay necesidad de hacerlo en ninguna prioridad de interrupción. El manejo de otros recursos de hardware suele ser más importante que el protocolo de software. En la mayoría de los casos, la E/S de comunicación es un proceso asíncrono.
Gracias por las sugerencias, pero necesito interrupciones de software independientemente de este ejemplo. Digamos que tengo dos "interrupciones de software": (i) un intérprete que tarda 10 ms en procesarse y (ii) un algoritmo antirrebote que tarda 10 us en procesarse. En un momento, se llama al intérprete desde dentro del bucle principal. El problema es que el algoritmo antirrebote debe esperar a que finalice el intérprete de Modbus. Si ambos se realizan como interrupciones (de funciones), entonces podría establecer la prioridad en ambos, y el intérprete de Modbus se puede "pausar" para permitir que el algoritmo antirrebote realice su tarea.

Después de buscar en Google y debatir, me di cuenta de que intentaba lograr tareas priorizadas, que es básicamente un RTOS (sistema operativo en tiempo real). Aunque un RTOS se puede implementar en un microcontrolador, requiere una gran cantidad de recursos (RAM, etc.).

La otra opción es usar un programador como explicó Batuu. De esa manera, las tareas no se pueden priorizar (no hay multitarea) y todas las tareas deben terminar en un solo tic.

Esto es 'multitarea cooperativa' en.wikipedia.org/wiki/Cooperative_multitasking
Tienes razón, que básicamente pides un RTOS. Una observación: al usar un programador como se explica, no es necesario que todas las tareas se completen en un solo tic. Puede distribuir tareas en varias subtareas con una máquina de estado simple, por ejemplo. De esta manera, puede pretender usar un sistema multitarea ejecutando un programador estático. En mis aplicaciones, esto funciona muy bien. La necesidad de un verdadero RTOS aumenta debido a la complejidad del sistema y los requisitos de procesamiento dinámico.