Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Manufacturing Technology >> Proceso de manufactura

Cómo realizar múltiples subprocesos en un Arduino (Tutorial de protothreading)

Componentes y suministros

Arduino UNO
× 1
Pantalla de cristal líquido (LCD) Sunfounder Blue 16x2
× 1
Breadboard (genérico)
y cables, por supuesto.
× 1
Potenciómetro giratorio (genérico)
No estoy seguro de la clasificación de resistencia, probablemente 1Kohm sería suficiente.
× 1

Acerca de este proyecto

Este video muestra algo que quizás haya querido hacer durante su incipiente carrera de creación de prototipos, persuadir a un arduino de un solo núcleo para que haga 3 cosas a la vez. En este caso, somos:

  • Pulsando la luz de fondo a un ritmo constante sin interrupción
  • Incrementar un número entero cada segundo y escribirlo en la pantalla sin interrupciones
  • Rotar algunos mensajes cada pocos segundos y escribirlos en la pantalla sin interrupción

¡Viste el título!

Protothreading es una forma de realizar lo que normalmente sería una operación multitarea en (hacer dos o más cosas a la vez o en diferentes intervalos) en un Arduino . En otras palabras, es "multiproceso". Pero espera, Sparky, el Arduino es un chip de un solo núcleo con código de procedimiento, por lo que es imposible un verdadero multihilo. ¿Por qué sin embargo? ¿En qué se diferencia la lectura de protuberancias?

Subprocesos múltiples "reales" frente a prototipos

Para entender protothreading correctamente, primero debemos entender por qué NO es realmente multiproceso.

¿Recuerda el día en que Intel nos vendía esta nueva cosa de "Hyperthreading" en los procesadores Pentium? ¿No? ¿Aún no naciste? ¡Entonces es hora de una lección de historia, hijo! Hyperthreading es una tecnología que emplea Intel para hacer que un solo núcleo en un procesador "actúe" como si fueran dos núcleos, o dos núcleos "actúan" como si fueran 4 núcleos, etc. Pero, ¿por qué y cómo es eso relevante para Arduino? La respuesta son los ciclos.

Tanto los microcontroladores como las CPU funcionan en "ciclos". Qué tan rápido los hacen (cuántos en un segundo) es la frecuencia del reloj. Ha visto la clasificación Ghz de una CPU y probablemente sepa que se relaciona con su velocidad. Cuanto más Ghz, mejor, ¿verdad? ¿pero por qué? Porque esa es la cantidad de ciclos por segundo que puede lograr un procesador (sin sobrecalentarse ni prenderse fuego, ¡de verdad!).

Si eres un nerd de las hojas de datos, es posible que sepas que el chip del microprocesador de Arduino Uno, el Atmel ATMega328P, funciona a 16Mhz de fábrica. Es capaz de 20Mhz, pero se vuelve a marcar para que no estropee cosas como escribir datos en la memoria (o, ya sabes, incendiarse). 16Mhz significa que cada segundo, su Arduino está procesando 16,000,000 ciclos, también conocido como 16 millones de piezas de trabajo. Ahora, estas NO son líneas de código, eso sería increíblemente rápido y Arduino es relativamente lento. Estas son instrucciones del procesador, como mover datos dentro y fuera de los registros. Ir a un nivel más bajo que este resumen es bastante técnico, así que lo dejo como un ejercicio para el lector, pero esa es la esencia :)

Entonces, si solo podemos ir tan rápido en un núcleo antes de que el mejor chip disponible se incendie, ¿estaremos atrapados a esa velocidad para siempre? ¿Es eso lo más rápido que podemos hacer el trabajo? ¡Resulta que no! Ingrese CPU multinúcleo y multiproceso. En una CPU de computadora, las aplicaciones multiproceso son dos procesos separados que trabajan en paralelo entre sí en diferentes núcleos de una CPU. Estos procesos interactúan para hacer el trabajo en conjunto, pero no necesariamente dividen el trabajo de manera uniforme como podría suponer. Por lo general, hay un proceso / "subproceso" principal que funciona como administrador de los otros subprocesos, y luego uno o más subprocesos de trabajo que administra, cada uno de los cuales puede realizar tareas específicas. Un buen ejemplo es Chrome. Chrome es el administrador de todas las pestañas de su página web (subprocesos), pero debido a que Chrome es multiproceso, cada pestaña es su propio programa. Eso significa que no solo puede ejecutarse más rápido si tiene varios núcleos para distribuir cada pestaña, sino que también tiene otros beneficios, como no bloquear todo el navegador cuando una pestaña falla. Esta es la primera razón por la que Protothreading no es multiproceso:solo tenemos un núcleo con el que trabajar en una MCU, por lo que el multiproceso tradicional es directamente imposible. Necesitamos administrar el trabajo en un solo núcleo, pero aún hacer varias cosas a la vez. Necesitamos protothreading.

Bien, ¿en qué se diferencia Protothreading entonces?

Protothreading es muy similar a lo de Hyperthreading que mencioné, hasta cierto punto. Hyperthreading emularía un segundo núcleo y literalmente dividiría el trabajo que hace un núcleo pretendiendo ser dos núcleos virtuales. Esto funcionó porque realmente existían en el mismo núcleo y, por lo tanto, compartían el mismo espacio de recursos. Dado que el arduino MCU no admite hyperthreading, no podemos hacer eso aquí. Protothreading es similar, excepto que en lugar de ciclos e instrucciones de CPU, podemos dividir el trabajo por los 'bucles' o 'líneas' de código que ejecuta nuestro boceto. Como puede imaginar, si estamos haciendo más cosas, los bucles tomarían más tiempo, por lo que cada proyecto tendrá 'bucles por segundo' muy diferentes. Hay diferentes implementaciones de protothreading, y la que uso aquí es probablemente de mala calidad, pero funciona. Básicamente, en cada bucle no tenemos otro trabajo que hacer, hacemos un trabajo menos exigente o menos frecuente en el bucle principal (o nada en absoluto). Cuando no estamos ocupados, verificamos si es hora de hacer uno de esos otros trabajos. Si es así, nos ramificamos y vamos a hacerlo. Es importante tener en cuenta que las acciones que están "bloqueando", lo que significa que deben completarse todas a la vez sin interrupción y, por lo tanto, inmovilizar la MCU durante un período de tiempo (como leer datos de una tarjeta SD y algunas otras tareas) seguirá bloqueando otros protothreads no ocurran "a tiempo", pero para cosas simples como dos bucles que van a la vez realizando acciones rápidas como cambios de variables o cambios de valores de salida, funcionará magníficamente. Esto es más o menos lo que haremos aquí. Algunas MCU admiten un sistema operativo en tiempo real (RTOS) que puede proporcionar más capacidades multitarea similares a las de hiperproceso que pueden ayudar a mitigar los problemas causados ​​por las tareas de "bloqueo".

Empecemos.

Primero averiguamos qué tareas debemos realizar. En mi caso, elegí (a) atenuar la luz de fondo de mi panel LCD hacia adentro y hacia afuera para obtener un efecto de "pulsación" ordenado, mientras (b) cuento un número en un intervalo mucho más lento (y posiblemente no divisible), y (c) rotar algunos mensajes de cadena en un intervalo aún mucho más lento. Algunas pautas a seguir para asegurarse de que este proceso funcione sin problemas son calificar sus funciones desde las que menos bloquean hasta las que más bloquean. Las acciones (llamémoslas "funciones" de ahora en adelante) que toman más tiempo, como leer datos o tener otros retrasos largos, y las funciones con intervalos más grandes entre el momento en que se activan son las funciones que más bloquean. Las funciones que se activan con mucha frecuencia, si no todos los bucles, y que no tardan en completarse son las funciones que menos bloquean. La función de bloqueo mínimo es la que debe utilizar como su "hilo" principal. ¿Puedes adivinar cuál está arriba?

Así es, es "a", que enciende y apaga la luz de fondo. Esto será a un intervalo regular y muy rápido, perpetuo sin demoras entre incendios que no sean hacer el trabajo, y el trabajo en sí es muy rápido. El hilo de administrador perfecto.

Usaremos este hilo (y cualquier bucle dentro de él) para verificar si los otros hilos necesitan hacer algún trabajo. Probablemente sea mejor leer el código en este punto, está muy documentado. Vea el bucle principal hacia la parte inferior. Puede verme verificando si los hilos necesitan algún trabajo donde llamo a numberThread.check () y textThread.check () .

También necesito hacer esto dentro de cualquier bucle en el hilo principal, ya que se bloquearán hasta que se complete si no lo hago. Establezco el intervalo en el que los subprocesos deben activarse cuando los inicializo durante el inicio o la parte de configuración del código. Si es hora de que se activen estos hilos, .check () lo verán y realizarán su trabajo antes de continuar con el hilo principal.

Eso es realmente todo en pocas palabras, el resto probablemente pueda resolverlo usted mismo revisando el código. Permítanme terminar diciendo que, aunque pueda parecerlo, NO soy un profesional de protothreading de ninguna manera, este es solo un ejemplo simple que pirateé. Si tiene algún consejo o si me equivoqué en algo, ¡le animo a recibir comentarios y correcciones! Gracias :)

Código

  • Código LCD multiproceso:multiproceso.ino (actualizado, v1.1)
Código LCD multiproceso:multithread.ino (actualizado, v1.1) Arduino
Este bit de código usa la biblioteca para realizar 3 acciones repetidas con intervalos separados al mismo tiempo en un procesador Arduino Uno. (A) Desvanecerá la luz de fondo hacia adentro y hacia afuera, mientras (b) incrementará un número y (c) rotará entre algunas cadenas de texto. Vea el video de arriba para ver una demostración :)
 / * Arduino Protothreading Example v1.1 por Drew Alden (@ReanimationXP) 1/12 / 2016- Actualización:v1.1 - 8/18/17 Arduino 1.6.6+ Prototyping cambiado , pequeñas correcciones. (cree funciones antes de su uso, eliminó foreach y la biblioteca relacionada). Tenga en cuenta que TimedAction ahora está desactualizado. Asegúrese de leer las notas sobre los errores de TimedAction y WProgram.h / Arduino.h. * /// COMPONENTS / * Este código se creó utilizando la pantalla LCD azul del kit de inicio Sunfounder Arduino. Se puede encontrar en Amazon.com en una variedad de kits . * /// BIBLIOTECAS DE TERCEROS // estos deben agregarse manualmente a su instalación de Arduino IDE // TimedAction // nos permite configurar acciones para realizar en intervalos de tiempo separados // http://playground.arduino.cc/Code /TimedAction//http://wiring.uniandes.edu.co/source/trunk/wiring/firmware/libraries/TimedAction#include  // NOTA:Esta biblioteca tiene un problema con las versiones más recientes de Arduino. Después de // descargar la biblioteca, DEBE ir al directorio de la biblioteca y // editar TimedAction.h. Dentro, sobrescriba WProgram.h con Arduino.h // BIBLIOTECAS NATIVAS # include  / * LiquidCrystal Library - Hello World Demuestra el uso de una pantalla LCD de 16x2. La biblioteca LiquidCrystal funciona con todas las pantallas LCD que son compatibles con el controlador Hitachi HD44780. Hay muchos de ellos y, por lo general, puede distinguirlos mediante la interfaz de 16 pines. Un ejemplo de circuito:* LCD RS pin a digital pin 12. * LCD Enable / E / EN pin a digital pin 11 * LCD D4 pin a digital pin 5 * LCD LCD D5 pin a digital pin 4 * LCD D6 pin a digital pin 3 * LCD D7 pin a digital pin 2 * LCD R / W pin a tierra * LCD VSS pin a tierra * LCD VCC / VDD pin a 5V * Resistencia de 10K:* extremos a + 5V y tierra * limpiador (medio) a LCD VO pin ( pin 3) * Pantallas retroiluminadas:* LCD K pin a tierra (si está presente) * LCD A pin a 220ohm (rojo rojo negro negro (marrón)) resistor, luego resistor al pin 9 Este código de ejemplo es de dominio público. http://www.arduino.cc/en/Tutorial/LiquidCrystal * /// GLOBALSint backlightPin =9; // utilizado para retroiluminación fadingint timerCounter =0; // contador incremental. se bloqueará eventualmente.int stringNo =0; // qué cadena de texto mostrar // "16 CHARACTER MAX" char * stringArray [] ={"Míralo ...", "Tengo 3 hilos", "yendo a la vez ...", "Genial, eh ?! :D "}; // INIT // Esto probablemente debería hacerse dentro de setup (), pero lo que sea .// Inicialice la biblioteca LCD con los números de los pines de la interfaz LiquidCrystal lcd (12, 11, 5, 4, 3, 2); // FUNCTIONS / / esta es nuestra primera tarea, imprime un número creciente en LCDvoid incrementNumber () {// coloca el cursor en la columna 0, línea 1 // (nota:la línea 1 es la segunda fila, ya que el conteo comienza con 0):lcd. setCursor (0, 1); // agrega uno al contador, luego lo muestra. timerCounter =timerCounter + 1; lcd.print (timerCounter);} // nuestra segunda tarea, se dispara cada pocos segundos y rota el texto stringsvoid changeText () {// Imprime un mensaje en la pantalla LCD. lcd.setCursor (0, 0); lcd.print (stringArray [stringNo]); // truco desagradable para obtener el número de elementos de la matriz if (stringNo> =sizeof (stringArray) / sizeof (char *)) {stringNo =0; changeText (); } else {stringNo =stringNo + 1; }} // Crea un par de temporizadores que se dispararán repetidamente cada x ms // editar:estas líneas solían estar delante de las funciones incrementNumber y changeText //. esto no funcionó porque las funciones aún no estaban definidas! TimedAction numberThread =TimedAction (700, incrementNumber); TimedAction textThread =TimedAction (3000, changeText); // ¿Dónde está nuestra tercera tarea? bueno, es el bucle principal en sí :) la tarea // que se repite con más frecuencia debería usarse como bucle. otras // tareas pueden "interrumpir" la tarea repetida más rápida.void setup () {// definir el número de columnas y filas de la pantalla LCD:lcd.begin (16, 2); // dispara changeText una vez para pintar la cadena inicial [0] changeText ();} void loop () {// revisa nuestros hilos. en función de cuánto tiempo ha estado // funcionando el sistema, ¿necesitan dispararse y funcionar? si es así, ¡hazlo! numberThread.check (); textThread.check (); // tercera tarea, atenuar la retroiluminación del brillo mínimo al máximo // en incrementos de 5 puntos:digitalWrite (13, HIGH); for (int fadeValue =0; fadeValue <=255; fadeValue + =10) {// espera un segundo, ¿por qué estoy revisando los hilos aquí? porque // este es un bucle for. debe verificar sus hilos durante CUALQUIER // bucle que ocurra, ¡incluido el principal! numberThread.check (); textThread.check (); // establece el valor (rango de 0 a 255):analogWrite (backlightPin, fadeValue); // esperar 20 milisegundos para ver el efecto de atenuación // mantener los retrasos en el bucle principal CORTO. estos evitarán que // otros subprocesos se activen a tiempo. retraso (20); } // se desvanece de máximo a mínimo en incrementos de 5 puntos:digitalWrite (13, LOW); for (int fadeValue =255; fadeValue> =0; fadeValue - =10) {// verifica nuestros hilos de nuevo numberThread.check (); textThread.check (); // establece el valor (rango de 0 a 255):analogWrite (backlightPin, fadeValue); // espere 20 milisegundos para ver el retardo del efecto de atenuación (20); } / * Para un poco de diversión con mensajes de desplazamiento en el futuro ... lcd.setCursor (15,0); // coloca el cursor en la columna 15, línea 0 para (int positionCounter1 =0; positionCounter1 <26; positionCounter1 ++) {lcd.scrollDisplayLeft (); // Desplaza el contenido de la pantalla un espacio a la izquierda. lcd.print (matriz1 [contador de posiciones1]); // Imprime un mensaje en la pantalla LCD. retraso (tim); // espera 250 microsegundos} lcd.clear (); // Limpia la pantalla LCD y coloca el cursor en la esquina superior izquierda. lcd.setCursor (15,1); // coloca el cursor en la columna 15, línea 1 para (int positionCounter =0; positionCounter <26; positionCounter ++) {lcd.scrollDisplayLeft (); // Desplaza el contenido de la pantalla un espacio a la izquierda. lcd.print (matriz2 [contador de posiciones]); // Imprime un mensaje en la pantalla LCD. retraso (tim); // espera 250 microsegundos} lcd.clear (); // Limpia la pantalla LCD y coloca el cursor en la esquina superior izquierda. * /} 

Proceso de manufactura

  1. Cómo medir la calidad del aire en OpenSensors
  2. Tutorial de bloqueo RFID de Arduino
  3. Cómo piratear mandos a distancia por infrarrojos
  4. ¿Qué tan alto eres?
  5. ¡¿Qué tan fácil es usar un termistor ?!
  6. Cómo hacer música con un Arduino
  7. Cómo utilizar NMEA-0183 con Arduino
  8. Tutorial del sensor de huellas dactilares Arduino
  9. Cómo utilizar Modbus con Arduino
  10. Tutorial de Arduino:Mini piano
  11. Tutorial de Arduino 01:Primeros pasos