Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

Como fazer multithread um Arduino (tutorial de protothreading)

Componentes e suprimentos

Arduino UNO
× 1
Sunfounder Blue 16x2 Tela de cristal líquido (LCD)
× 1
Breadboard (genérico)
e fios, é claro.
× 1
Potenciômetro rotativo (genérico)
Não tenho certeza da classificação de resistência, provavelmente 1Kohm seria suficiente.
× 1

Sobre este projeto


Este vídeo mostra algo que você pode ter desejado fazer durante sua carreira de prototipagem incipiente, persuadindo um arduino de núcleo único a fazer três coisas ao mesmo tempo. Nesse caso, somos:
  • Pulsando a luz de fundo a uma taxa constante sem interrupção
  • Incrementar um número inteiro a cada segundo e gravá-lo no visor sem interrupção
  • Girar algumas mensagens a cada poucos segundos e gravá-las na tela sem interrupção





Você viu o título!


Protothreading é uma maneira de realizar o que normalmente seria uma operação multitarefa (fazer duas ou mais coisas ao mesmo tempo ou em intervalos diferentes) em um Arduino . Em outras palavras, é "multithread"! Mas espere aí, Sparky, o Arduino é um chip de núcleo único com código procedural, então o multithreading verdadeiro é impossível. Porquê? Como o protothreading é diferente?





Multithreading "real" vs protothreading


Para entender o protothreading corretamente, primeiro precisamos entender por que NÃO é realmente multithreading.

Lembra-se do dia em que a Intel estava nos vendendo esse novo "Hyperthreading" nos processadores Pentium? Não? Você ainda não nasceu? Bem, hora para uma aula de história, filho! Hyperthreading é uma tecnologia que a Intel emprega para fazer um único núcleo em um processador "agir" como se fossem dois núcleos, ou dois núcleos "agirem" como se fossem 4 núcleos, etc. Mas por que e como isso é relevante para o Arduino? A resposta é ciclos.

Tanto os microcontroladores quanto as CPUs funcionam em "ciclos". O quão rápido eles os executam (quantos por segundo) é a taxa de clock. Você viu uma classificação Ghz da CPU e provavelmente sabe que está relacionada à sua velocidade. Quanto mais Ghz, melhor, certo? mas por que? Porque esse é o número de ciclos por segundo que um processador pode atingir (sem superaquecer e pegar fogo - sério!).

Se você é um nerd de folha de dados, deve saber que o chip microprocessador do Arduino Uno, o Atmel ATMega328P, funciona a 16Mhz fora da caixa. Ele é capaz de 20 MHz, mas é discado de volta para não bagunçar coisas como gravar dados na memória (ou, você sabe, pegar fogo). 16 MHz significa que, a cada segundo, seu Arduino está processando 16 milhões de ciclos, ou seja, fazendo 16 milhões de peças de trabalho. Agora, essas NÃO são linhas de código - isso seria incrivelmente rápido e o Arduino é relativamente lento. Estas são instruções do processador, como mover dados para dentro e para fora dos registradores. Ir para um nível inferior ao desta visão geral é bastante técnico, então vou deixar isso como um exercício para o leitor, mas esse é o ponto principal :)

Então, se pudermos ir tão rápido em um núcleo antes que o melhor chip disponível pegue fogo, estaremos presos nessa velocidade para sempre? É o mais rápido que podemos trabalhar? Acontece que não! Insira CPUs multicore e multithreading. Na CPU de um computador, os aplicativos multithread são dois processos separados que funcionam paralelamente um ao outro em diferentes núcleos de uma CPU. Esses processos interagem para fazer o trabalho juntos, mas não necessariamente dividem o trabalho igualmente como você pode supor. Normalmente, há um processo principal / "thread" que funciona como um gerenciador dos outros threads e, em seguida, um ou mais threads de trabalho que ele gerencia, cada um podendo realizar tarefas específicas. Um bom exemplo é o Chrome. O Chrome é o gerenciador de todas as guias de sua página da web (threads), mas como o Chrome é multithread, cada guia é seu próprio pequeno programa. Isso significa que não só pode ser executado mais rápido se você tiver vários núcleos para distribuir cada guia, mas também tem outros benefícios, como não travar o navegador inteiro quando uma guia trava. Esta é a primeira razão pela qual Protothreading não é multithreading - nós temos apenas um núcleo para trabalhar em um MCU, então multithreading tradicional é totalmente impossível. Precisamos gerenciar o trabalho em apenas um único núcleo, mas ainda fazer várias coisas ao mesmo tempo. Precisamos de protothreading.





Ok, então qual é a diferença do protothreading?


O protothreading é muito semelhante ao Hyperthreading que mencionei, até certo ponto. Hyperthreading iria emular um segundo núcleo e literalmente dividir o trabalho que um núcleo está fazendo fingindo ser dois núcleos virtuais. Isso funcionou porque eles realmente existiam no mesmo núcleo e, portanto, compartilhavam o mesmo espaço de recursos. Como o arduino MCU não oferece suporte a hyperthreading, não podemos fazer isso aqui. Protothreading é semelhante, exceto que no lugar de ciclos de CPU e instruções, podemos quebrar o trabalho por 'loops' ou 'linhas' de código sendo executado por nosso esboço. Como você pode imaginar, se estivermos fazendo mais coisas, os loops demorarão mais, então cada projeto terá 'loops por segundo' muito diferentes. Existem diferentes implementações de protothreading, e a que uso aqui provavelmente é de má qualidade, mas funciona. Basicamente, em cada loop não temos outro trabalho a fazer, fazemos algum trabalho menos exigente ou menos frequente no loop principal (ou nada). Quando não estamos ocupados, verificamos se já é hora de fazer uma dessas outras peças de trabalho. Se for assim, nós ramificamos e vamos fazer isso. É importante observar que as ações que estão "bloqueando", o que significa que devem ser concluídas todas de uma vez sem interrupção e, assim, prender o MCU por um período de tempo (como ler dados de um cartão SD e algumas outras tarefas) ainda irá bloquear outros protothreads ocorram "no tempo", mas para coisas simples como dois loops acontecendo ao mesmo tempo executando ações rápidas como mudanças de variáveis ​​ou valores de saída de mudança, ele funcionará perfeitamente. Isso é mais ou menos o que faremos aqui. Alguns MCUs oferecem suporte a um sistema operacional em tempo real (RTOS) que pode fornecer mais habilidades multitarefa do tipo hyperthreading, o que pode ajudar a mitigar problemas causados ​​por tarefas de "bloqueio".





Vamos começar.


Primeiro, descobrimos quais tarefas precisamos realizar. No meu caso, eu escolhi (a) aumentar e diminuir a luz de fundo do meu painel LCD para um efeito "pulsante" puro, enquanto (b) contando um número em um intervalo muito mais lento (e possivelmente não divisível), e (c) girar algumas mensagens string em um intervalo ainda muito mais lento. Algumas diretrizes a serem seguidas para garantir que esse processo funcione sem problemas são classificar suas funções do menos bloqueador ao mais bloqueador. Ações (vamos chamá-las de "funções" a partir deste ponto) que demoram mais, como ler dados ou têm outros longos atrasos, e funções com intervalos maiores entre os disparos são as funções de maior bloqueio. Funções que disparam com muita frequência, senão em todos os loops, e não demoram muito para serem concluídas, são as funções de menor bloqueio. A função de bloqueio mínimo é o que você deve usar como seu "thread" primário. Você consegue adivinhar o que está acima?

Isso mesmo, é "a", pulsando a luz de fundo para dentro e para fora. Isso será em um intervalo regular e muito rápido, perpétuo, sem atrasos entre os incêndios, a não ser na realização do trabalho, e o trabalho em si é muito rápido. O tópico de gerenciamento perfeito.

Usaremos este thread (e quaisquer loops dentro dele) para verificar se os outros threads precisam fazer algum trabalho. Provavelmente, é melhor ler o código neste ponto - ele está amplamente documentado. Veja o loop principal na parte inferior. Você pode me ver verificando se os threads precisam de algum trabalho onde eu chamo numberThread.check () e textThread.check () .

Também preciso fazer isso em quaisquer loops no thread principal, pois eles serão bloqueados até a conclusão se eu não fizer isso. Eu defino o intervalo no qual os threads precisam ser disparados quando eu os inicializo durante o init ou a parte de configuração do código. Se for hora de esses threads dispararem, .check () verá isso e realizará seu trabalho antes de continuar o tópico principal.

Resumindo, é isso mesmo, o resto você provavelmente pode descobrir por si mesmo, percorrendo o código. Deixe-me terminar dizendo, embora possa parecer isso, eu NÃO sou um protothreading profissional de forma alguma, este é apenas um exemplo simples que eu criei. Se você tiver alguma dica ou se eu estava errado sobre alguma coisa, encorajo feedback e correções! Obrigado :)

Código

  • Código LCD multithread - multithread.ino (atualizado, v1.1)
Código LCD multithread - multithread.ino (atualizado, v1.1) Arduino
Este bit de código usa a biblioteca para realizar 3 ações repetidas com intervalos separados ao mesmo tempo em um processador Arduino Uno. Ele irá (a) Fade a luz de fundo dentro e fora, enquanto (b) incrementa um número, e (c) gira entre algumas strings de texto. Veja o vídeo acima para uma demonstração :)
 / * Arduino Protothreading Example v1.1by Drew Alden (@ReanimationXP) 12/01/2016- Atualização:v1.1 - 18/08/17 Prototipagem do Arduino 1.6.6+ alterada , pequenas correções. (cria funções antes do uso, remove o foreach e a biblioteca relacionada). Observe que TimedAction agora está desatualizado. Certifique-se de ler as notas sobre os erros TimedAction e WProgram.h / Arduino.h. * /// COMPONENTS / * Este código foi feito usando o LCD azul do kit inicial Sunfounder Arduino. Ele pode ser encontrado na Amazon.com em uma variedade de kits . * /// BIBLIOTECAS DE TERCEIROS // devem ser adicionados manualmente à instalação do Arduino IDE // TimedAction // nos permite definir ações a serem executadas em intervalos cronometrados separados // http://playground.arduino.cc/Code /TimedAction//http://wiring.uniandes.edu.co/source/trunk/wiring/firmware/libraries/TimedAction#include  // NOTA:Esta biblioteca tem um problema nas versões mais recentes do Arduino. Após // baixar a biblioteca, você DEVE ir ao diretório da biblioteca e // editar TimedAction.h. Dentro de, sobrescreva WProgram.h com Arduino.h // BIBLIOTECAS NATIVAS # include  / * Biblioteca LiquidCrystal - Hello World Demonstra o uso de um display LCD 16x2. A biblioteca LiquidCrystal funciona com todos os monitores LCD compatíveis com o driver Hitachi HD44780. Existem muitos deles por aí, e geralmente você pode identificá-los pela interface de 16 pinos. Um exemplo de circuito:* LCD RS pino para pino digital 12. * LCD habilitado / E / EN pino para digital pino 11 * LCD D4 pino para digital pino 5 * LCD D5 pino para digital pino 4 * LCD D6 pino para digital pino 3 * LCD D7 pino para pino digital 2 * LCD R / W pino para aterramento * LCD VSS pino para aterramento * LCD VCC / VDD pino para 5V * 10K resistor:* termina em + 5V e aterramento * limpador (meio) para pino LCD VO ( pino 3) * Telas retroiluminadas:* Pino K do LCD para aterramento (se houver) * Pino A do LCD para resistor de 220 ohm (vermelho vermelho preto preto (marrom)) e, em seguida, resistor para pino 9 Este código de exemplo é de domínio público. http://www.arduino.cc/en/Tutorial/LiquidCrystal * /// GLOBALSint backlightPin =9; // usado para fadingint da luz de fundo timerCounter =0; // incrementando o contador. irá travar eventualmente.int stringNo =0; // qual string de texto mostrar // "16 CHARACTER MAX" char * stringArray [] ={"Dê uma olhada ...", "Eu tenho 3 tópicos", "indo de uma vez ...", "Legal, hein ?!:D "}; // INIT // Isso provavelmente deve ser feito dentro de setup (), mas seja o que for.// inicializar a biblioteca LCD com os números da interface pinsLiquidCrystal lcd (12, 11, 5, 4, 3, 2); // FUNÇÕES / / esta é nossa primeira tarefa, imprima um número incremental no LCDvoid incrementNumber () {// defina o cursor para a coluna 0, linha 1 // (nota:a linha 1 é a segunda linha, já que a contagem começa com 0):lcd. setCursor (0, 1); // adicione um ao contador e, em seguida, exiba-o. timerCounter =timerCounter + 1; lcd.print (timerCounter);} // nossa segunda tarefa, dispara a cada poucos segundos e gira o texto stringsvoid changeText () {// Imprime uma mensagem no LCD. lcd.setCursor (0, 0); lcd.print (stringArray [stringNo]); // hack desagradável para obter o número de elementos Array if (stringNo> =sizeof (stringArray) / sizeof (char *)) {stringNo =0; changeText (); } else {stringNo =stringNo + 1; }} // Crie alguns temporizadores que serão acionados repetidamente a cada x ms // edição:essas linhas costumavam estar na frente das funções incrementNumber e changeText //. isso não funcionou porque as funções ainda não foram definidas! TimedAction numberThread =TimedAction (700, incrementNumber); TimedAction textThread =TimedAction (3000, changeText); // onde está nossa terceira tarefa? bem, é o próprio loop principal :) a tarefa // que se repete com mais freqüência deve ser usada como o loop. outras // tarefas são capazes de "interromper" a repetição mais rápida task.void setup () {// define o número de colunas e linhas do LCD:lcd.begin (16, 2); // acione changeText uma vez para pintar a string inicial [0] changeText ();} void loop () {// verifique nossos threads. com base em quanto tempo o sistema está // em execução, eles precisam disparar e funcionar? se sim, faça! numberThread.check (); textThread.check (); // terceira tarefa, atenuação da luz de fundo do brilho mínimo ao máximo // em incrementos de 5 pontos:digitalWrite (13, HIGH); for (int fadeValue =0; fadeValue <=255; fadeValue + =10) {// espere um segundo, por que estou verificando os tópicos aqui? porque // este é um loop for. você deve verificar seus tópicos durante QUALQUER // loops que ocorram, incluindo o principal! numberThread.check (); textThread.check (); // define o valor (intervalo de 0 a 255):analogWrite (backlightPin, fadeValue); // aguarde 20 milissegundos para ver o efeito de escurecimento // mantenha os atrasos no loop principal CURTO. estes IRÃO impedir // outros threads de disparar a tempo. atraso (20); } // desvanece-se do máximo para o mínimo em incrementos de 5 pontos:digitalWrite (13, LOW); for (int fadeValue =255; fadeValue> =0; fadeValue - =10) {// verifique nossos tópicos novamente numberThread.check (); textThread.check (); // define o valor (intervalo de 0 a 255):analogWrite (backlightPin, fadeValue); // aguarde 20 milissegundos para ver o efeito de escurecimento delay (20); } / * Para alguma diversão com a rolagem de mensagens no futuro ... lcd.setCursor (15,0); // define o cursor para a coluna 15, linha 0 para (int positionCounter1 =0; positionCounter1 <26; positionCounter1 ++) {lcd.scrollDisplayLeft (); // Rola o conteúdo do display um espaço à esquerda. lcd.print (array1 [positionCounter1]); // Imprime uma mensagem no LCD. atraso (tim); // aguarde 250 microssegundos} lcd.clear (); // Limpa a tela LCD e posiciona o cursor no canto superior esquerdo. lcd.setCursor (15,1); // define o cursor para a coluna 15, linha 1 para (int positionCounter =0; positionCounter <26; positionCounter ++) {lcd.scrollDisplayLeft (); // Rola o conteúdo do display um espaço à esquerda. lcd.print (array2 [positionCounter]); // Imprime uma mensagem no LCD. atraso (tim); // aguarde 250 microssegundos} lcd.clear (); // Limpa a tela LCD e posiciona o cursor no canto superior esquerdo. * /} 

Processo de manufatura

  1. Como medir a qualidade do ar em OpenSensors
  2. Tutorial de bloqueio RFID do Arduino
  3. Como hackear IR Remotes
  4. Qual é sua altura?
  5. Quão fácil é usar um termistor ?!
  6. Como fazer música com um Arduino
  7. Como usar NMEA-0183 com Arduino
  8. Tutorial do sensor de impressão digital Arduino
  9. Como usar o Modbus com Arduino
  10. Arduino Tutorial 01:Começando