collapse

* Links de Robótica

* Posts Recentes

[Projecto] Bomba Airsoft por jm_araujo
[Hoje às 16:54]


Palavras Cruzadas por Njay
[Hoje às 15:47]


Apresentação por Tech_JA
[Hoje às 09:19]


Medir Agua que está no Poço por filjoa
[21 de Setembro de 2017, 20:58]


URGENTE - display de 7 segmentos com backpack por helderjsd
[20 de Setembro de 2017, 12:30]


Preços e fabricantes de pcb por Sérgio_Sena
[19 de Setembro de 2017, 10:20]


Isaac Asimov - I, Robot por senso
[18 de Setembro de 2017, 03:41]


ic SL440 da Plessey? por senso
[16 de Setembro de 2017, 13:11]


Compra Colectiva RS-Amidata por brunus
[15 de Setembro de 2017, 22:31]


Ideias para construir um quadrúpede simples por zordlyon
[15 de Setembro de 2017, 10:18]

Autor Tópico: Multitasking / Multithreading  (Lida 3496 vezes)

0 Membros e 1 Visitante estão a ver este tópico.

Offline GnobarEl

  • Mini Robot
  • *
  • Mensagens: 92
Multitasking / Multithreading
« em: 24 de Maio de 2010, 12:08 »
Abri este tópico para podermos discutir sobre Multitasking no arduino.

Por agora, o mais próximo do multitasking que consegui fazer foi alternar rápidamente entre funções.
a cada 50milis alterna entre 2 funções, e em alguns casos faz a simulação de duas funções a correr separadamente. Resulta bem, por ex. colocar dois leds a piscar a velocidades diferentes.
Mas se quisermos colocar um led a piscar a uma velocidade e ao mesmo tempo tempo correr um loop e enviar para a consola os números todos de 1 a 1000?
Na minha experiência os leds param de piscar enquanto o arduino comunica com o pc, depois de terminar esse ciclo os leds voltam a piscar.

Partilhem as vossas experiências.

Offline ngoncalves

  • Mini Robot
  • *
  • Mensagens: 145
    • Thinking Olive Tree
Re: Multitasking / Multithreading
« Responder #1 em: 24 de Maio de 2010, 12:45 »
No caso particular dos Atmegas, é possível correr algum código em paralelo utilizando interrupções de hardware. Por exemplo, envia um byte pela porta série e quando terminar chamar uma interrupção para enviar mais outro. E utilizas outra interrupção, do timer por exemplo, para piscar o led. Mas aumentando o número de interrupções, aumenta exponencialmente a probabilidade de criar bugs que não se percebem de onde vêm.

Se pretenderes fazer multitasking puro então só faz sentido se as tarefas puderem ser colocadas em pausa temporariamente. Existe a biblioteca ProtoThreads aqui  http://www.sics.se/~adam/pt/, que permite implementar no arduino algumas funcionalidades das threads. Eu nunca a utilizei mas para o teu problema terias uma thread para cada tarefa: piscar os leds e enviar bytes pela porta série.

Se por outro lado pretendes multitasking em tempo real, então terás que utilizar uma mistura de threads e interrupções. Mas novamente, a potencialidade para introduzir bugs estranhos é grande.
-----
Ambient intelligence, mobile robotics, life. 42
http://www.thinkingolivetree.blogspot.com/

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.088
    • Tróniquices
Re: Multitasking / Multithreading
« Responder #2 em: 24 de Maio de 2010, 13:05 »
Um sistema "multithreading" é complexo para máquinas de recursos limitados como um Arduino e complexo para o nível de conhecimento tipico das pessoas que nos lêm aqui, e por isso eu recomendo (e pessoalmente é o que uso) o modelo "por eventos" (também chamado "multitarefa cooperativa"). O modelo por eventos funciona bem se não houver operações "atómicas" que demorem muito tempo.

No modelo por eventos, cada tarefa é executada numa função mas essa função não pode bloquear. O ciclo principal do programa chama repetidamente todas as tarefas sem parar, por exemplo em round-robin (em sequência, uma a seguir à outra e no fim volta à primeira). Depois é uma questão de "truques" para impedir que as funções bloqueiem, geralmente usando uma máquina de estados.

Vamos pegar no teu exemplo de ter 2 LEDs a piscar a velocidades diferentes e ao mesmo tempo enviar números para a consola. Isto são 3 tarefas.

O ciclo principal invoca as 3 tarefas em sequência, e no final faz uma pausa de 10ms. Esta pausa serve para termos uma unidade de tempo conhecida, caso contrário o ciclo é executado a toda a velocidade e não poderemos controlar a cadência a que, por exemplo, os LEDs piscam:

Código: [Seleccione]
int loop ()
{
piscaLed1();
piscaLed2();
enviaNumeros();
delayMs(10);
}

Agora sabemos que cada tarefa é invocada a intervalos de 10ms. Se queremos o nosso LED1 a piscar 1 vez a cada segundo, temos que o inverter de estado a cada 500ms. Como sabemos que a tarefa é invocada de 10ms em 10ms, temos que inverter o estado do LED a cada 500ms / 10ms = 50, 50 invocações (50 x 10ms = 500ms), e para isso usamos uma variável para contar o número de invocações:

Código: [Seleccione]
void piscaLed1 ()
{
static int contador = 0;

contador = contador + 1;
if (contador == 50)
{
togglePin(...)
contador = 0;
}
}

Uma variável declarada com static quer dizer que é uma variável global mas que só é "visível" dentro do bloco onde está declarada ("bloco" é o conteúdo de um conjunto de chavetas). Eu podia tê-la declarado fora das funções, mas como ela só diz respeito e só é usada dentro da função, resolvi declará-la como static para a casa ficar mais organizada e arrumada. Ainda outra razão para declarar as variáveis como static sempre que possível é que desta forma podemos ter variáveis "globais" com o mesmo nome. A função da tarefa piscaLed2 pode usar exactamente o mesmo nome para a variável, "contador", uma vez que as variáveis só são "visíveis" dentro do seu bloco (o bloco onde são declaradas).
A variável tem que ser global pois a execução vai entrar e sair da função mas o valor da variável tem que ser preservado, pois vamos usá-la e queremos manter o seu valor de umas invocações para outras.

A tarefa piscaLed2() é similar à piscaLed1(), mudando apenas o número 50 para estar de acordo com a cadência que se desejar.

Agora a tarefa enviaNumeros(), que pode parecer complicada mas não é. Basta ter uma variável que guarda o último número que foi enviado, e enviar um número a cada invocação:

Código: [Seleccione]
void enviaNumeros ()
{
static int numero = 0;

numero = numero + 1;
Serial.write(numero);
}

Esta tarefa vai tentar enviar 1 número a cada 10ms, o que pode ser demasiado rápido para o que queremos ou até mesmo para a velocidade de transmissão que foi escolhida para a porta série (os bps). Nesse caso podiamos querer enviar apenas, vamos supôr, 1 número por segundo. Isto é fácil de fazer, pois é o que já fazem as tarefas dos LEDs e podemos usar o mesmo mecanismo! Para 1 segundo o nosso contador tem que contar 1000ms / 10ms = 100, 100 vezes antes de fazermos alguma coisa (enviar um numero).

Código: [Seleccione]
void enviaNumeros ()
{
static int contador = 0;

contador = contador + 1;
if (contador == 100)
{
static int numero = 0;

numero = numero + 1;
Serial.write(numero);

contador = 0;
}
}

Reparem que o código original da função está lá todo inalterado, mas agora dentro do "if (contador == ...".

Há alguns detalhes que não mencionei mas em geral a técnica é esta, e isto permite fazer bastantes coisas "em simultâneo" de uma forma simples. O importante é não deixar que as tarefas demorem muito tempo ou bloqueiem. Por exemplo, não podem fazer pausas dentro das funções. Se precisam de uma pausa, têm que usar um contador para esperar o tempo desejado, tal como fiz nos exemplos. As funções têm que entrar, fazer qualquer coisa "rápida" e sair.

Esta estrutura já serve para começarem e fazerem imensas coisas e é uma forma simples e segura de conseguir fazer algum multasking.

p.s. O código não compila, é essencialmente apenas ilustrativo.
« Última modificação: 24 de Maio de 2010, 20:50 por Njay »

Offline metRo_

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 3.726
Re: Multitasking / Multithreading
« Responder #3 em: 24 de Maio de 2010, 20:03 »
Muito bem explicado e bastante util! Parabéns Njay :)

Offline preytender

  • Mini Robot
  • *
  • Mensagens: 148
Re: Multitasking / Multithreading
« Responder #4 em: 24 de Maio de 2010, 20:17 »

Que grande artigo, devia ser colocado nos tutoriais.

Parabens. ;)

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.088
    • Tróniquices
Re: Multitasking / Multithreading
« Responder #5 em: 24 de Maio de 2010, 20:57 »
Obrigado ;)

Corrigi uma coisita na última função (faltava re-inicializar a variável "contador" a zero) e disse mais umas coisitas sobre o static.

Enjoy :)

Lanço um desafio: quem faz ou esboça o código de uma 4ª tarefa para o exemplo, que faz o echo do que é recebido pela porta série (consola)?
« Última modificação: 24 de Maio de 2010, 20:59 por Njay »

Offline RicardoSantos

  • Mini Robot
  • *
  • Mensagens: 65
Re: Multitasking / Multithreading
« Responder #6 em: 25 de Maio de 2010, 01:33 »
Boas, ultimamente tenho andado a consumir tudo o que é robótica até não poder mais...
E numa das minhas pesquisas encontrei uma coisa chamada "DuinOS" ...

E se não me engano será mesmo o que procuram:
http://www.multiplo.org/duinos/wiki/index.php?title=Main_Page

Espero que seja isto que procuram e que seja útil...

Cumprimentos

Offline TigPT

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 5.234
    • Tiago Rodrigues
Re: Multitasking / Multithreading
« Responder #7 em: 25 de Maio de 2010, 08:53 »
Muito curioso estar-se a discutir o multithreading preemptivo quando eu andava a magicar fazer uma abordagem simplista ao assunto.

Boa abordagem Njay.

Offline Sérgio_Sena

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 1.641
    • Electronic Gear for Musicians
Re: Multitasking / Multithreading
« Responder #8 em: 25 de Maio de 2010, 09:39 »
Pegando neste tópico, partilho com vocês algumas amostras de código que tenho feito ao longo dos anos. Adoptei a estratégia de multithread em interrupções, com recurso a flags, faz já algum tempo. Revelou-se muito importante e poupador de tempo e recursos.

O TIMER expira a cada 50ms.
Quando se entra na interrupção devido a rollover do TIMER, pode fazer-se a análise e tratamento de dezenas/centenas de outros contadores independentes que, por si, têem outras funções no programa principal.
Este é um pequeno exmplo, que depois pode ser ajustado às necessidades.

Código: [Seleccione]
;############################
_TMR0
btfss INTCON, T0IF ;houve interrupcao de TMR0?
goto FIM_TMR0 ;nao

CARREGA_TMR0 ;carrega os valores para a temporizacao do TMR0
movlw .61 ;configura contador do TMR0 para rolar a cada 50ms, clock de 4MHz, 1:256, (tem que contar 195 vezes)
movwf TMR0
   bcf   INTCON, T0IF ;limpa flag de interrupcao de TMR0

CONTADOR_1
decfsz cnt_tmr0, f ;já fez o tempo necessário?
goto FIM_CONTADOR_1
movlw .5
movwf cnt_tmr0 ;carrega o contador que o TMR0 vai decrementar
   bsf   FLAGS.CONTADOR1_TERMINOU
FIM_CONTADOR_1


CONTADOR_2
;;;;;; executa funções
FIM_CONTADOR_2

CONTADOR_3
;;;;;; executa funções
FIM_CONTADOR_3

CONTADOR_4
;;;;;; executa funções
FIM_CONTADOR_4

CONTADOR_XPTO
;;;;;; executa funções
FIM_CONTADOR_XPTO


FIM_TMR0
bcf INTCON, T0IF ;limpa flag de interrupcao de TMR0



Agora um pequeno exemplo em C de HITECH ::

Código: [Seleccione]

//**************************************************************************************************************************
// TMR2 para temporizador de ~10ms
//**************************************************************************************************************************
if ((TMR2IF == 1) && (TMR2IE == 1))
{
TMR2IF = 0; //flag de interrupcao de TMR2


//**************************************************************
// os contadores seguintes são destinados a diferentes funcoes *
//**************************************************************

//*****************************************
//contador de calculo de posicao de motores
if (FLAGS.temporizador_posicao_motores == 1) //se o temporizador do calculo da posicao de motores estiver autorizado, faz contagem
{
temporizador_posicao_motores--; //decrementa o contador de tempo
if (temporizador_posicao_motores == 0) //se já tiver chegado ao fim, entao executa a funcao abaixo
{
temporizador_posicao_motores = tempo_posicao_motores; //carrega o contador para nova volta

if ( state_machine == espera) //dah permissao para avancar para o calculo de posicao, apenas se estiver no estado de ESPERA
FLAGS.calcular_posicao_motores = 1;
}
}


//**********************************************************************************************
//contador de conversor AD para ler os valores analogicos dados pelos posicionadores dos motores
temporizador_AD--; //decrementa o contador de tempo
if (temporizador_AD == 0) //se já tiver chegado ao fim, entao executa a funcao abaixo
{
temporizador_AD = tempo_AD; //carrega o contador para nova volta

ADCON0 = 0b11010001; //canal AN4
GODONE = 1; //arranca a conversao
while (GODONE == 1); //espera até acabar a conversao
analogue0 = ADRESH; //guarda conversao
if (analogue0 < adj_elevacao_bot) //se o fim-de-curso virtual tiver excedido o nivel inferior
FLAGS.FC_ELEV_BAIXO_virtual = 1; // activa a FLAG correspondente
if (analogue0 > adj_elevacao_top) //se o fim-de-curso virtual tiver excedido o nivel superior
FLAGS.FC_ELEV_CIMA_virtual = 1; // activa a FLAG correspondente

ADCON0 = 0b11011101; //canal AN7
GODONE = 1; //arranca a conversao
while (GODONE == 1); //espera até acabar a conversao
analogue1 = ADRESH; //guarda conversao
}



//**************************************
//contador de temporizador da luz do LCD
if (FLAGS.temporizador_LCD_permissao == 1) //se o temporizador_LCD intermedio estiver autorizado, faz contagem
{
LCD_LIGHT = 1; //liga a LUZ do LCD
temporizador_LUZ_LDC--; //decrementa o contador de tempo
if (temporizador_LUZ_LDC == 0) //se já tiver chegado ao fim, entao executa a funcao abaixo
{
temporizador_LUZ_LDC = tempo_LUZ_LCD; //carrega o contador para nova volta
LCD_LIGHT = 0; //desliga a LUZ do LCD
FLAGS.temporizador_LCD_permissao = 0; //desliga permissao
}
}


}//if ((T0IF == 1) && (T0IE == 1))




Há muita forma de o fazer, até implementando prioridades dentro das interrupções, caso queiramos mais importância nuns eventos. Isto para os microcontroladores que não têem prioridades nas interrupções. Podendo atribuir pioridades, a coisa fica mais robusta.

Espero que seja útil.


Offline RicardoSantos

  • Mini Robot
  • *
  • Mensagens: 65
Re: Multitasking / Multithreading
« Responder #9 em: 26 de Maio de 2010, 20:08 »
Boas desculpem voltar a bater na mesma tecla, mas alguém já experimentou o DuinOS?

Quem experimentou o que acha?

Offline GnobarEl

  • Mini Robot
  • *
  • Mensagens: 92
Re: Multitasking / Multithreading
« Responder #10 em: 26 de Maio de 2010, 20:59 »
Bem, quando iniciei este tópico estava longe de pensar que seriam colocadas respostas tão detalhadas como as que apareceram, só mostra a qualidade das pessoas que por aqui passam.
Podemos ser poucos, mas já deu para ver que são bons!

Em relação ao DuinOS ainda não experimentei, mas fiquei curioso. Ainda está em desenvolvimento? Só vi uma versão.
Existe também o  Pyxis OS, já vi uns vídeos no youtube e aquilo promete.


Offline guibot

  • Mini Robot
  • *
  • Mensagens: 643
    • Guibot
Re: Multitasking / Multithreading
« Responder #11 em: 27 de Maio de 2010, 00:57 »
Também existe o concurrency.cc, gostava de experimentar mas ainda não tive tempo...

Offline TigPT

  • Administrator
  • Mini Robot
  • *****
  • Mensagens: 5.234
    • Tiago Rodrigues
Re: Multitasking / Multithreading
« Responder #12 em: 27 de Maio de 2010, 01:06 »
Também existe o concurrency.cc, gostava de experimentar mas ainda não tive tempo...

Já tinha investigado e foi o que mais me aliciou... é um remake da computação concorrente idealizada muitos anos antes dos multi cores... na altura em que os computadores computavam quase tanto como os microcontroladores computam hoje em dia. Fiquei completamente rendido com o que li sobre Concurrency e também gostava de vir a experimentar!

Offline GnobarEl

  • Mini Robot
  • *
  • Mensagens: 92
Re: Multitasking / Multithreading
« Responder #13 em: 27 de Maio de 2010, 17:47 »
Retirado do forum arduino.cc

Citar
Hi!
...
Second, is DuinOS still in development? Well, "yes, but". We have developed a new board based on the 32U4 (the DuinoBot, soon will upload links), and were working on the standard Arduino core for this "native USB" microcontroller (which is a lot of work since there is no FTDI chip, and the core has to integrate the USB firmware too). The good news are that we worked very hard to migrate the LUFA library to the standard Arduino environment v0018, and it's working fine in our first internal tests (at least by now). Will release all these stuff as soon as it reaches at least an alpha state. And yes, we stopped to work in DuinOS by the moment. We want to integrate it with this new core too, and with our new Sanguino based 644/Multiplo.Brain.M644 core ported to the 0018. So, a lot of work, both hardware and software, and little time. Sorry!

Regards!
Julián
« Última modificação: 27 de Maio de 2010, 17:49 por TigPT »

Offline GnobarEl

  • Mini Robot
  • *
  • Mensagens: 92
Re: Multitasking / Multithreading
« Responder #14 em: 30 de Junho de 2010, 11:24 »
 DuinOS v0.2 Alpha

What's new: (2010.06.30)
- Arduino updated to version 0018
- FreeRTOS updated to version 6.0.5

Download: http://novell.chel.ru/get.php?file=DuinOS_v0.2_Alpha