Ontem no laboratório o Leonardo levantou uma questão muito comum quando se utiliza microcontroladores.

Como controlar entradas e saídas ao mesmo tempo usando um microcontrolador sem usar nenhum kernel de multitarefa ?

Um exemplo comum é como piscar 2 leds (um a cada 500 ms e outro a cada 100 ms)
e ao mesmo tempo ficar verificando um botão, que não deve fazer nada quando pressionado, mas quando for solto deve acender um terceiro led por 3 segundos.

Para resolver esse tipo de problema eu costumo usar timers, para simular um ambiente multitarefa.

Cada timer possui o horário que ele foi iniciado (usando data e hora, ou milisegundos após o start da aplicação) e de quanto em quanto tempo ele deve ser resetado.

O timer é verificado de tempos em tempos pela aplicação, e quando existe o estouro do tempo a aplicação chama a função correspondente (task), e reinicia o timer.

Dentro de cada tarefa não se pode utilizar nada que trave o processamento, delays grandes, ou chamada de funções que fiquem aguardando por alguma coisa, caso isso aconteça o sistema multitarefa não vai funcionar corretamente.

Vamos ao código !!!!!

Eu não coloquei todo o código para não ficar muito extenso, os acessos a hardware e as configurações de portas não foram feitas, esse código apenas DEMONSTRA como utilizar timers para simular multitarefa.

O código está voltado para a plataforma ARDUINO.

/*********************************************************
DEFINIÇÔES E FUNÇÔES DE TIMER
*********************************************************/
//Definição da estrutura Timer
typedef struct Timer
{
    unsigned long start;//Armazena o tempo de quando foi iniciado o timer
    unsigned long timeout;//Tempo após o start para o estouro
};

unsigned long Now ( void )
{
    return millis ( );//Retorna os milisegundos depois do reset
}

char TimerExpired ( struct Timer * timer )
{
    //Verifica se o timer estourou
    if ( Now () > timer->start + timer->timeout )
        return true;

    return false;    
}

void TimerStart ( struct Timer * timer )
{
    timer->start = Now ( );//Reseta o timer gravando o horário atual
}

//Definições dos timers utilizados na aplicação
Timer timerLed1 = { 0, 500 }; //Pisca o led1 a cada 500 ms
Timer timerLed2 = { 0, 100 }; //Pisca o led2 a cada 100 ms
Timer timerBotao = { 0, 10 }; //Verifica se o botão foi solto a cada 10 ms
Timer timerLed3 = { 0, 3000 }; //Para temporizar o led 3 por 3000 ms

/*********************************************************
FUNÇÕES DO ARDUINO - LOOP
*********************************************************/
void loop (void)
{
    //Verifica se algum timer estourou - caso positivo chama a função relacionada  
    if ( TimerExpired ( & timerLed1 ) )
    {
        taskLed1 ( );
        TimerStart ( & timerLed1 );
    }

    if ( TimerExpired ( & timerLed2 ) )
    {
        taskLed2 ( );
        TimerStart ( & timerLed2 );
    }

    if ( TimerExpired ( & timerBotao ) )
    {
        taskBotao ( );         
        TimerStart ( & timerBotao );
    }
}

/*********************************************************
                    TASKS  
*********************************************************/
void taskLed1 ( void )
{
    //Liga ou desliga o led1 (não coloquei o código para não ficar extenso!!!)
}

void taskLed2 ( void )
{
    //Liga ou desliga o led2 (não coloquei o código para não ficar extenso!!!)
}

//Indica que o led 3 está ligado
bool led3Aceso = false;

void taskSerial ( void )
{
    //Verifica quando o botão foi solto - (não coloquei o código para não ficar extenso!!!)
    if ( BotaoSolto () )
    {
        //Acende o led3 - (não coloquei o código para não ficar extenso!!!)
        
        //Inicia o timer do led 3, quando o timer estourar o led deve ser apagado !
        TimerStart ( & timerLed3 );
    }
    
    //Caso o led esteja aceso e o timer estourar, o led deverá ser apagado.
    if ( TimerExpired ( & timerLed3 ) && led3Aceso )
    {
        //Apaga o led 3 - (não coloquei o código para não ficar extenso!!!)
        led3Aceso = false;
    }
}

Exibições: 5851

Responder esta

Respostas a este tópico

Eh, eu vou deixar isso como tarefa de casa, pois aqui no lab só o unsigned long já estava me fazendo tomar um copo de água e dar uma volta...
Realmente isso era oque eu precisava, e inclusive espero criar problemas piores para essa placa maldita.
Agora só me resta ajudar lá na CNC, então, até sexta se tudo rolar bem.
Vlw!

Agora estamos levantando acampamento aki, então t+
Que post! Parabéns e obrigado Bruno!
Esse método realmente funciona bem.
No meu caso, como trabalho com PIC, deixo sempre um timer dedicado para as tasks de multitarefa. Para cada Task eu uso uma variável e a cada interrupção do timer ele vai incrementando ou decrementando as variáveis até ocorrer um overflow. Quando isso acontece a única coisa que ele faz é levantar um flag e reiniciar a variável correspondente. O importante é que TODO o processamento do task é realizado fora da interrupção do timer.
A interrupção do timer tem que ser o mais breve possível. O que define a quantidade de tasks de execução em multitarefa é definido como a capacidade de executar a task antes de entrar com outra task, portanto perder tempo na int do timer deve ser evitado.
Já consegui fazer um PIC controlar 8 servo-motores com precisão de 1000 passos, ler e escrever na porta serial, processar os dados de latitude, longitude de um GPS e ler e escrever dados em um SDCARD.
Para ter controle de quanto de processamento esta sendo gasto, nesse esquema é possível implementar um sisteminha que informa a taxa de ocupação para cada task.
No caso acima, ficou "sobrando" uns 20% de processamento para ser usado ainda.

desculpe a pergunta, pode ser tosca até, sou iniciante em programação de microcontroladores e estou trabalhando com arduino (você disse que usa o PIC), mas a minha pergunta na verdade é: Como faço pra calcular essa capacidade de processamento para uma determinada tarefa?

MAS ARDUINO JÁ VEM TUDO PRONTO.

Muito interessante isso hein!! Valeu Bruno!!!! Isso vai ajudar muita gente!!! hahaha
Cara, usamos muito essa hoje para os projetores de texto a laser.
olha na wiki o resultado final, ta simples agora, mas antes, quando ainda tava na gambiarra estava cheio de tasks diferentes. Acho que é assim que se aprende o conceito :)
//tudo bem que no final agente nem usou...
Como você implementou pra saber quanto de processamento ta sobrando?

Fiquei curioso agora!



Igor Miranda Matias disse:
Esse método realmente funciona bem.
No meu caso, como trabalho com PIC, deixo sempre um timer dedicado para as tasks de multitarefa. Para cada Task eu uso uma variável e a cada interrupção do timer ele vai incrementando ou decrementando as variáveis até ocorrer um overflow. Quando isso acontece a única coisa que ele faz é levantar um flag e reiniciar a variável correspondente. O importante é que TODO o processamento do task é realizado fora da interrupção do timer.
A interrupção do timer tem que ser o mais breve possível. O que define a quantidade de tasks de execução em multitarefa é definido como a capacidade de executar a task antes de entrar com outra task, portanto perder tempo na int do timer deve ser evitado.
Já consegui fazer um PIC controlar 8 servo-motores com precisão de 1000 passos, ler e escrever na porta serial, processar os dados de latitude, longitude de um GPS e ler e escrever dados em um SDCARD.
Para ter controle de quanto de processamento esta sendo gasto, nesse esquema é possível implementar um sisteminha que informa a taxa de ocupação para cada task.
No caso acima, ficou "sobrando" uns 20% de processamento para ser usado ainda.
Não sei como o Igor implementou, mas eu estou pensando em fazer um mais ou menos assim:

Um timer que estoura a cada 1000 ms que fica rodando o tempo todo.
Ao entrar em cada task guarda o horário de entrada na rotina e o de saída (calcula o tempo total de processamento em milisegundos) e armazena em uma variável.
Assim após o estouro do timer terei quanto foi gasto por todas as tasks em milisegundos em um segundo, ai já dá pra saber qto de processamento está sendo utilizado.

Vou implementar essa idéia no código do ROV e posto no wiki assim que estiver pronto.

Abraços
Implementei o código no Arduino.

Funcionou bem, o software está utilizando de 5 a 7% de processamento, como teste eu diminui um pouco o intervalo de cada timer, o processamento aumentou para 8 a 10%.

Abraços

existe a possibilidade de fazer uma espécie de upload de um código para microcontroladores?

algo como as multitarefas...

como receber este codigo e operar conforme por exemplo os resultados obtidos de um sensor?

Valeu

Guilherme

Existem os chamados CLPs (controladores lógicos programáveis), são microcontroladores programados especificamente para receber um código que deve ser executado.

 

Existem linguagens que são utilizadas para programar um CLP, normalmente na indústria se usa LADDER, mas existem alguns que aceitam C entre outras linguagens.

 

Abraços

 

Guilherme disse:

existe a possibilidade de fazer uma espécie de upload de um código para microcontroladores?

algo como as multitarefas...

como receber este codigo e operar conforme por exemplo os resultados obtidos de um sensor?

Valeu

RSS

© 2024   Criado por Marcelo Rodrigues.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço