Realizar múltiples tareas síncronas con un microcontrolador

3

Estoy haciendo un robot que tiene varios mecanismos a bordo. Para mantener las cosas simples, digamos que tiene 2 motores de CC y 2 servos. La mayoría de las veces, los servos no están haciendo nada, y los 2 motores de CC se están moviendo en la misma dirección (utilizando un controlador de motor L239D). Por cierto, esto está usando un AVR MCU (probablemente un ATmega 328). Los motores utilizan señales PWM.

Supongamos que envío un comando para girar un motor CW, el otro CCW y activar los dos servos. Necesito que esto suceda al mismo tiempo. Entonces, ¿cómo funciona esto?

Si existe algo como el enhebrado, crearía un subproceso para los motores, un subproceso para un servo y otro para el segundo servo para que todos se activen y hagan su trabajo al mismo tiempo. Pero no creo que exista subprocesamiento, o al menos es común.

Entonces, ¿cómo ejecutaría varios comandos al mismo tiempo?

    
pregunta capcom

6 respuestas

3

BYour MCU no es un sistema muti core. Por lo tanto, es imposible que funcione al mismo tiempo. También fue lo mismo durante la era de las computadoras de un solo núcleo.

Para resolver su problema, debe usar el mismo "truco" que usa el SO común: Programación.

Básicamente, realizas un poco de control del motor A, luego un poco del motor B, luego un poco del servo A, luego B, y haces un bucle. Si haces esto muy rápido. Su sistema se comportará como si estas tareas se estuvieran ejecutando en paralelo.

En cuanto a la implementación, depende de la plataforma y de lo que quieras hacer.

  1. Si no tiene un requisito de alimentación estricto, por ejemplo, me gustaría utilizar un RTOS como FreeRTOS. Esto funciona en AVRs y es bastante fácil de usar. Esto resolvería su problema de programación.
  2. Puede implementar un sistema de programación básico utilizando un sistema de bucle infinito simple, que continuamente ejecuta diferentes máquinas de estado sin bloqueo, una por tarea. Pero aquí, es mucho más difícil respetar las restricciones de tiempo estrictas, si tiene algunas.

Pero, finalmente, si se trata de un proyecto de hobby, la pregunta es: ¿Qué quieres aprender? ¿Una programación básica? Un RTOS? ¿Otro enfoque propuesto? Estoy seguro de que todos resolverían tu problema.

    
respondido por el Blup1980
3

Hay varios factores involucrados que hacen que esto funcione lo suficientemente bien para los dispositivos que requieren menos magia para operarlos.

(1) El mundo pasa lentamente cuando eres un microcontrolador: los motores y servos de CC que funcionan a plena velocidad parecen estar parados frente a un microcontrolador. por ejemplo, un motor que funciona a 10,000 RPM gira 10,000 / 60 ~ = 160 revoluciones por segundo. Entonces, una revolución toma 1000000/160 = ~ 6,000 microsegundos. Un grado de rotación requiere 6,000 / 360 ~ = 16 uS. Dependiendo de la uC en cuestión, 16 uS está empezando a notarse, pero si un procesador puede manejar, por ejemplo, 10 MIPS, ejecutará aproximadamente 150 instrucciones en el tiempo en que un motor de 10,000 rpm gire 1 grado. En términos generales, un uC a este tipo de velocidad puede estar lo suficientemente cerca como para estar N lugares a la vez en un grado que parezca instantáneo para la mayoría de los dispositivos del mundo real.

Hay dispositivos del mundo real que comienzan a hacer que las suposiciones anteriores sean un poco sospechosas, pero en la mayoría de los casos todo estará bien.

(2) La operación simultánea verdadera o casi simultánea a la operación se logra fácilmente cuando es necesario. 8 o 16 o incluso 32 bits digitales se pueden escribir de forma auténtica simultáneamente (ignorando posibles desviaciones de nivel de nanosegundos). Si se toma una decisión de antemano para implementar un conjunto de acciones interrelacionadas en un momento determinado, se pueden implementar esencialmente de manera simultánea al compartir una sola escritura de procesador en el puerto. En la práctica, las escrituras consecutivas en los puertos se producirán con la suficiente rapidez para que cualquier diferencia de tiempo sea indiscutible por meros dispositivos mecánicos.

En el caso de las señales PWM, éstas generalmente se implementarán a una velocidad de cuadros que es lenta en comparación con los tiempos de ciclo del procesador. Se puede producir una excepción si el PWM usa un reloj PLL que es un múltiplo del reloj del procesador, pero incluso en ese caso, es posible que una trama completa tenga muchos ciclos de procesador. por ejemplo, un procesador puede tener un tiempo de ciclo (lento) de 200 ns. Un PLL puede sincronizar una unidad PWM de hardware a 50 MHz o 20 ns / bit. Incluso entonces, un registro PWM de 8 bits producirá una velocidad de cuadros de 160 nS que es del mismo orden de longitud que el ciclo de uC. Pocos y extremadamente alejados son los dispositivos de hardware que necesitan una frecuencia de cuadros PWM sub 1 uS. Cualquier persona que intente manejar un sistema de energía a esta velocidad se enfrentaría a terribles problemas de pérdida de conmutación y necesitaría requisitos muy especiales para hacerlo.

Si alguna acción requiere respuestas que estén sincronizadas con un tiempo de ciclo de uC mejor que esto, esto puede lograrse precargando los datos y activando una escritura simultánea en un puerto o puertos a la vez o sincronizando el hardware externo. Ocurre, pero no suele ser necesario en equipos domésticos o de consumo.

    
respondido por el Russell McMahon
2

Como señaló Blup, el micro solo puede hacer una cosa a la vez. Sin embargo, puede alternar entre diferentes cosas tan rápido que efectivamente puede hacer varias cosas a la vez.

Sí, la multitarea es una abstracción útil, y la uso bastante en el firmware de microcontrolador más complejo. La multitarea preventiva en toda regla es una exageración en un microcontrolador y conlleva algunos costos significativos. Sin embargo, la multitarea cooperativa puede ser muy útil. Utilizo principalmente PIC y tengo sistemas de multitarea cooperativos simples para PIC 18 y los dsPIC. Probablemente los use en más de la mitad de los proyectos en esas partes. La tarea cooperativa permite que el cambio de tareas sea rápido y simple, y no requiere estructuras de exclusión mutua, secciones críticas y similares que agregan complejidad y ciclos a los sistemas preventivos.

Por lo general, dedicar una tarea a cada flujo de entrada asíncrono es una abstracción útil. Por ejemplo, si el micro controla 2 motores y recibe comandos sobre el UART, entonces me dedico una tarea a recibir y manejar el flujo de entrada de UART. Esencialmente, la tarea se convierte en una máquina de estado con la variable de estado que es la PC. Dependiendo de lo que exactamente necesiten los motores, podrían manejarse en la interrupción PWM a partir de los datos que dejó la tarea de procesamiento de comandos. Si el micro tiene que hacer un montón de otras cosas pequeñas y tolerantes a la latencia, entonces otra tarea que usa una arquitectura de bucle de eventos principal suele ser apropiada. Esta tarea haría parpadear el LED de estado de acuerdo con el estado actual del sistema, posiblemente verifique los botones de usuario una vez por ms (un tiempo largo para un micro) y haga el rebote, verifique si hay varios "¡oh crap!" condiciones como la corriente excesiva del motor e iniciar secuencias de apagado apropiadas, etc.

Si está dispuesto a usar los PIC para esta tarea, puede tener mi multitarea (y muchos otros códigos enlatados) de forma gratuita. Busque archivos con "tarea" en sus nombres en la FUENTE > PIC y FUENTE > Los directorios de DSPIC en el directorio de instalación del software después de instalar mi Herramientas de desarrollo PIC .

    
respondido por el Olin Lathrop
2

En muchos casos, será posible dividir las tareas en aquellas que requieren cantidades muy pequeñas y predecibles de trabajo que se realizarán en momentos arbitrarios en respuesta a eventos externos, cantidades de trabajo moderadamente pequeñas que se programarán a intervalos regulares, cosas lo que se espera que se realice de manera razonablemente rápida pero que no tenga un requisito de tiempo en particular, y las cosas que deben seguir un flujo de programa a largo plazo (por ejemplo, una interfaz de usuario de solicitud). Las cosas asíncronas se hacen mediante interrupciones asíncronas; las cosas programadas se hacen por un tic temporizador. Las cosas no críticas en el tiempo se realizan mediante una rutina poll() a la que se llama, por ejemplo, a la espera de que se presione un botón, y el flujo de la interfaz de usuario se maneja usando el "flujo de programa normal" [por ejemplo, cuando el usuario ingresa a un menú, la ejecución del programa ingresará a una rutina para manejar ese menú, y solo saldrá de esa rutina cuando el usuario abandone el menú].

Si el tiempo total pasado en el peor de los casos en eventos asíncronos en cada marca está por debajo de la fluctuación de tiempo permitida para los eventos periódicos, y si el tiempo combinado en el peor caso para los eventos periódicos y eventos asíncronos en una marca es menor que la duración La garrapata, entonces las cosas son muy simples. Si uno tiene dos conjuntos de eventos que se supone que ocurren a intervalos de 1 ms, y si el tiempo del caso más desfavorable para cada uno es significativamente inferior a 500us, puede ser útil definir una interrupción que se ejecute cada 500us y maneje los dos eventos alternativamente.

El uso de un sistema operativo en tiempo real puede permitirle cumplir con una variedad de requisitos de tiempo complicados en los casos en que una combinación de programación fija e interrupciones de prioridad fija no sería adecuada, pero en los casos en que los métodos más simples serían suficientes utilizando una El sistema operativo en tiempo real puede ser excesivo.

En algunos procesadores, si hay más de una tarea que requiere un "flujo de programa lógico" en lugar de tener una rutina que siempre ingresa en la parte superior y usa indicadores para controlar su estado, puede ser útil definir un simple " método de cambio de tarea ". En el 8051, si hay exactamente dos de estas tareas, tal método sería:

_TaskSwitch: ; Assumes all registers may be freely trashed on a subroutine call
    mov a,AltSP
    xchg a,SP
    mov AltSp,A
    ret

No hay mucho que hacer. Un "sistema operativo" de cuatro instrucciones! Para configurarlo, uno debe definir una región de memoria para usar como la pila de la segunda tarea, almacenar al principio la dirección de la primera rutina que se ejecutará usando la segunda pila y configurar AltSP justo después de esa dirección almacenada. La primera llamada a TaskSwitch cambiará a esa pila, abrirá la dirección almacenada y comenzará a ejecutar el código desde allí.

Tenga en cuenta que el código anterior no es un sistema operativo en tiempo real, pero he usado ese enfoque en varios proyectos. Tenga en cuenta que no hay nada en la lógica de cambio de tarea para manejar la programación. Si una tarea tiene mucho trabajo que hacer pero llama a _taskswitch periódicamente para garantizar que la otra tarea tenga la oportunidad de ejecutarse, y la otra tarea no hace más que esperar a que expire el temporizador, entonces cada llamada _taskswitch hará que la ejecución cambie a la segunda tarea que luego verá que el temporizador aún no ha expirado y llama a _taskswitch para volver a la primera. Todo el cambio de tareas puede parecer inútil, pero en realidad ocurre en menos tiempo de lo que gastaría un RTOS más sofisticado al determinar que no era necesario un cambio de tarea.

    
respondido por el supercat
1

Arquitecturas de planificación de tareas integradas simples:

1) Round robin. Los intervalos de tiempo se asignan a todos los procesos en orden.

2) Mitades inferiores. La mayoría de las tareas utilizan arquitectura round robin. Los más importantes están ligados a las rutinas de interrupción. Es decir, la interrupción establece una bandera, y cuando el ciclo de round robin alcanza un punto específico en el ciclo, comprueba la bandera y, si está establecida, completa algunas acciones.

3) Cola: se mantiene una cola de tareas a ejecutar. Las nuevas tareas se agregan a la cola y las ejecutadas se eliminan.

4), 5) ... otros que no conozco ...

    
respondido por el Vorac
1

En realidad, en casos seleccionados es posible que sucedan cosas al mismo tiempo: por ejemplo, en algunos chips puede hacer todo esto programando temporizadores de hardware y haciendo toda la configuración antes de habilitarlos. Entonces, esa operación de activación única y común iniciará todas estas operaciones que haya configurado, sin demora relativa (a excepción de la inclinación de la sección a la sección en el propio dispositivo).

Pero para el caso en cuestión, eso no es necesario de forma remota. Tenga en cuenta que los servos ni siquiera pueden comenzar a actualizar la posición hasta el final del pulso del servo de 1-2 milisegundos. Eso es mucho tiempo para un microcontrolador que podría girar e inmediatamente escribir en un periférico diferente. Y eso es solo el servo que comienza a moverse: el mecanismo y también llevará tiempo.

En este caso, es probable que ni siquiera desee subprocesos, o al menos no un subproceso por periférico. Más típicamente, solo tendría un ciclo de programa principal, o un hilo común que maneja cada periférico a su vez. Otras partes del programa pueden tratar con la comunicación, la planificación, el monitoreo de la salud, etc., pero nuevamente con la excepción de las rutinas de servicio de interrupción para mover datos entre E / S y buffers de software, a menudo en un sistema tan pequeño como lo contempla, todos los otras tareas simplemente se manejarán una tras otra en un bucle.

    
respondido por el Chris Stratton

Lea otras preguntas en las etiquetas