Gerenciador de tarefas em um PIC 8-bits

Gerenciador de tarefas

Este artigo mostra como implementar um gerenciador de tarefas (orientado à temporização) em um PIC 8-bits, objetivando mostrar que os bons e velhos microcontroladores 8-bits ainda têm (muito) uso, sobretudo em tarefas críticas em tempo.

Tal gerenciador é o embrião de um sistema operacional de tempo real (RTOS). Para mais informações sobre o que é e como é composto um RTOS, leia esta excelente série de artigos do articulista do Embarcados Rodrigo Almeida.

O PIC utilizado para o desenvolvimento deste gerenciador é o PIC 16F628A. O motivo desta escolha dá-se pelo fato de que este microcontrolador é muito popular entre os adeptos do PIC, é um microcontrolador de 8-bits já bem consolidado no mercado e atende perfeitamente às necessidades do gerenciador aqui desenvolvido. Os IDE e compilador utilizados são, respectivamente, MPLAB IDE X e compilador XC8, ambos da própria Microchip.

É importante ressaltar que o gerenciador de tarefas aqui mostrado permite executar somente uma tarefa por vez.

Pré-requisitos

Para acompanhar o conteúdo do artigo, há os seguintes pré-requisitos técnicos:

  • Ter conhecimento intermediário ou avançado de linguagem C;
  • Ter conhecimento básico de microcontroladores PIC (operação de timers e registradores).

Gerenciador de tarefas orientado à temporização – o que é?

Um gerenciador de tarefas orientado à temporização denomina um software embarcado capaz de executar tarefas periodicamente. Ou seja, é um software embarcado aplicável a situações onde o período de execução de uma ou mais tarefas é crucial para o funcionamento de um determinado sistema.

Logo, aparece o questionamento: se há um software embarcado que executa tarefas, o que seria este software embarcado principal e o que seriam as tarefas? Há assim dois ou mais softwares embarcados em execução em um mesmo microcontrolador?

O gerenciador de tarefas propriamente dito e cada uma das funções são partes distintas de um mesmo firmware. Ou seja, um mesmo programa em execução em um microcontrolador conterá o gerenciador de tarefas (análogo a uma central, um núcleo) e as N tarefas. Observe o diagrama da figura 1:

Diagrama geral do gerenciador de tarefas
Figura 1 – Diagrama geral do gerenciador de tarefas

Como o gerenciador de tarefas é a rotina principal deste software embarcado final, ele deve estar contido na função principal, main(). Cada tarefa é isolada em uma função distinta, com sua própria lógica e variáveis locais.

Controle da temporização – como é feito?

Para se ter o controle por temporização, fez-se uso do Timer0 do PIC.

Com ele é feita a técnica chamada timer tick. Nesta técnica, faz-se com que cada interrupção de estouro do timer seja gerada a cada unidade mínima de temporização desejada, logo é possível se tomar ações a cada X interrupções / estouros de tempo.

No gerenciador deste artigo, a unidade mínima de temporização de uma tarefa foi adotada como 1 ms. Portanto, o timer (Timer0 do PIC) foi configurado para gerar um estouro a cada 1 ms, cabendo à rotina de tratamento de interrupção de Timer atualizar o tempo restante, em ms, para a execução das tarefas. Veja a rotina de tratamento da interrupção do Timer0 utilizada:

//Função: função de tratamento de interrupções do PIC (chamda automaticamente no acionamento de toda e qualquer interrupção do PIC, cabendo ao programador verificar dentro desta função qual interrupção que foi gerada)
//Parâmetros: nenhum
//Retorno: nenhum
void interrupt isr(void)
{
    char  i;
	
    //se o TMR0 foi a 00h (indicado pelo registrador TMR0IF=1) e o registrador 
    //de estouro de timer0 (T0IF) foi a 1, logo houve estouro do Timer0.
	if (TMR0IF && T0IF)   
    {
        FlagGerouInterrupcaoTimer0 = SIM;
		
		//atualiza a temporização de cada uma das tarefas
        
		for (i=0; i<NUMERO_TAREFAS; i++)
		{
			if (TemporizacaoAtualTarefas[i] > 0)
				TemporizacaoAtualTarefas[i]--;			
		}
			
        if (HaTarefaEmExecucao == SIM)
        {
            TimeoutTarefa--;
            
            if (!TimeoutTarefa)
            {
                //possivel travamento. Reseta microcontrolador                    
                PCLATH=0;    
                PCL=0;                    
            }
        }
        
		T0IF = 0;              //indica que o timer0 deve ser reiniciado para uma futura interrupção
        INTCONbits.TMR0IF = 0; // reabilita interrup. TMR0
        TMR0 = VALOR_INICIALIZACAO_TIMER0; //Inicialização do registrador TMR0
    }   
}

Conforme pode ser visto na rotina acima, a cada “tick” (equivalente a cada 1ms), o tempo restante para a execução de cada tarefa é atualizado, dado que será utilizado posteriormente para decidir se uma determinada tarefa deve entrar em execução ou não.

Note que, além do controle de temporização por tarefa, há o controle de Timeout de realização de tarefa. Este consiste no seguinte: se há qualquer tarefa sendo executada e se esta demorar mais que o tempo máximo estipulado para execução, o microcontrolador reseta.

Este tipo de controle deve existir no gerenciador de tarefas deste artigo por duas razões:

  1. Este gerenciador de tarefas executa somente uma tarefa por vez;
  2. Se este se trata de um gerenciamento de tarefas voltado a aplicações onde o tempo para executar é o primordial, se uma das tarefas travar (ou ficar executando por muito tempo), todo o software embarcado (e aplicação) será comprometido.

Outro ponto interessante no quesito temporização é que, como o tempo é algo crítico aqui, a fonte de clock do sistema deve vir de um oscilador à cristal. O motivo é que, desta maneira, garante-se mais precisão na frequência do microcontrolador se comparado ao seu oscilador interno. Portanto, neste gerenciador, foi utilizando um cristal de 4MHz como oscilador.

É importante observar também a variável FlagGerouInterrupcaoTimer0. Ela é muito importante para o gerenciador como um todo, pois informará ao programa principal que já se passou o “tick” e que deve ser verificado se alguma tarefa deve ser executada.

Agendamento das tarefas

O agendamento de tarefas/inicialização das tarefas é uma das primeiras coisas a serem feitas, pois ele determina quais tarefas serão executadas e de quanto em quanto tempo. Observe o código abaixo, que corresponde à rotina de agendamento de tarefas do gerenciador deste artigo:

//Função: função que inicializa tarefas e a temporização das mesmas
//Parâmetros: nenhum
//Retorno: nenhum
void InicializaTarefas(void)
{
	//inicialização das tarefas
	TarefasAgendadas[TAREFA_1] = Tarefa1;
	TarefasAgendadas[TAREFA_2] = Tarefa2;
	TarefasAgendadas[TAREFA_3] = Tarefa3;
    TarefasAgendadas[TAREFA_4] = Tarefa4;
	
	//inicialização das temporizações
	PeriodosTarefasAgendadas[TAREFA_1] = PERIODO_EXECUCAO_TAREFA1;
	PeriodosTarefasAgendadas[TAREFA_2] = PERIODO_EXECUCAO_TAREFA2;
	PeriodosTarefasAgendadas[TAREFA_3] = PERIODO_EXECUCAO_TAREFA3;
    PeriodosTarefasAgendadas[TAREFA_4] = PERIODO_EXECUCAO_TAREFA4;
	
	//inicializaçãop da temporização atual (esta variará ao longo do programa)
	TemporizacaoAtualTarefas[TAREFA_1] = PERIODO_EXECUCAO_TAREFA1;
	TemporizacaoAtualTarefas[TAREFA_2] = PERIODO_EXECUCAO_TAREFA2;
	TemporizacaoAtualTarefas[TAREFA_3] = PERIODO_EXECUCAO_TAREFA3;	
    TemporizacaoAtualTarefas[TAREFA_4] = PERIODO_EXECUCAO_TAREFA4;	
    
    //sinaliza que não ha tarefas em execução ainda
    HaTarefaEmExecucao = NAO;
}

Note que as tarefas (no programa C, são funções) estão sendo referenciadas ao array TarefasAgendadas[]. Este array consiste em um array de ponteiros de função, ou seja, ele armazena os endereços das funções a serem executadas. Quanto a limites, este array pode ser tão maior quanto a memória RAM do microcontrolador suportar.

Além disso, há outros dois arrays em questão: PeriodosTarefasAgendadas[]TemporizacaoAtualTarefas[]. Estes tem a seguinte utilidade:

  1. PeriodosTarefasAgendadas: armazena de quanto em quanto tempo as tarefas devem ser executadas. Seus valores são utilizados na atualização da temporização de cada tarefa (para permitir que ela seja executada novamente no futuro);
  2. TemporizacaoAtualTarefas: contém quanto tempo (em ms) falta pra executar cada tarefa. Seus dados são utilizados na camada executora de tarefas, assim é possível decidir se uma tarefa deve ser executada ou não.

Observe, ainda, a variável HaTarefaEmExecucao. Ela é usada para indicar se há qualquer tarefa em execução no momento e, consequentemente, permite que seja contabilizado o timeout de execução de uma determinada tarefa (na interrupção do timer).

É importante ressaltar que, por ser usado ponteiro de função para armazenar o endereço das tarefas, cada tarefa (em C, trata-se de uma função) deve ser declarada com os mesmos parâmetros de entrada e retorno (no caso das tarefas deste gerenciador, não há parâmetros nem retorno, ou seja, ambos são void).

Execução das tarefas

Uma vez que a parte do controle de temporização está sendo resolvida por interrupção do Timer0 do PIC, a camada de execução de tarefas deverá simplesmente decidir se já está na hora de executar uma determinada tarefa ou não. Observe o código da execução de tarefas a seguir:

//Função: executa tarefa (se esta estiver na hora de ser executada, 
//        de acordo com seu agendamento)
//Parâmetros: nenhum
//Retorno: nenhum
void ExecutaTarefa(void)
{
    char i;
	for (i=0; i<NUMERO_TAREFAS; i++)
	{
		//se deve executar, realiza a execução e reinicia temporização
		if ((TarefasAgendadas[i] != 0) && (TemporizacaoAtualTarefas[i] == 0))
        {
			HaTarefaEmExecucao = SIM;
            TimeoutTarefa = TIMEOUT_TAREFA;
            TarefasAgendadas[i]();  //execução da tarefa
            HaTarefaEmExecucao = NAO;
            
            //reagendamento da tarefa
			TemporizacaoAtualTarefas[i] = PeriodosTarefasAgendadas[i];
		}
	}
}

Conforme pode ser notado, tudo que o executor de tarefas faz é varrer a temporização de todas as tarefas e verificar quem deve ser executada ou não e atualizar a temporização das mesmas (para permitir execução posterior).

Loop principal e programa principal

O programa principal do gerenciador é bem simples e compacto, conforme pode ser observado a seguir:

void main(void) 
{
	TRISB = 0x00;            //configura todos os pinos do port b como saída
    PORTB = 0;               //força todas as saídas do port b para 0
    
    TRISA = 0xFF;            //configura todos os pinos do port a como entrada
    
    //inicialização da parte de tarefas
    InicializaTarefas();     //inicializa tarefas
	ConfiguraTimer0();       //configura Timer0 para ser acionado conforme desejamos
    
	//loop principal
    while(1)                      
    {
        //verifica continuamente se deve executar alguma tarefa agendada
        if ((FlagGerouInterrupcaoTimer0 == SIM)  && (NUMERO_TAREFAS)) 
        {
            FlagGerouInterrupcaoTimer0 = NAO;  
            ExecutaTarefa();			
        }
    }
}

Nota-se que ele é composto de duas partes:

  • inicializações gerais e;
  • loop principal ( while(1) ).

No loop principal, é feito um controle de quando se deve chamar a execução de tarefas.

Conforme dito no tópico de temporização, a variável FlagGerouInterrupcaoTimer0 serve para indicar que já houve um “tick” e que o loop principal deve utilizar esta variável para decidir se a checagem de tarefas a serem executadas deve ser chamada. Isto é feito desta forma para evitar chamadas desnecessárias à função ExecutaTarefa(), o que poderia ocasionar perda de tempo no software como um todo.

Gerenciador de tarefas – código completo

A seguir, o código na íntegra do gerenciador de tarefas (núcleo):

/*
 * Principal.c
 * Autor: Pedro Bertoleti
 * Data: Janeiro/2016
 * Descrição: 
 * Gerenciador de tarefas (por temporização).
 * IMPORTANTE: uma tarefa é executada de cada vez (não há threads)
 */

#include <xc.h>
//Colocar aqui os includes dos headers de bibliotecas e rotinas externas desejadas

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

// CONFIG
#pragma config FOSC = XT        //Oscilador a cristal
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = OFF      // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

//defines gerais
#define _XTAL_FREQ                 4000000      //define para utilizar funções de tempo

#define CICLO_MAQUINA              (4/_XTAL_FREQ)   //nos PICs, uma insctrução é executada a cada 4 oscilações do clock (ou, em outras palavras, a cada 4 ciclos de máquina)
#define SIM                        1
#define NAO                        0
#define VALOR_INICIALIZACAO_TIMER0 131

//defines do agendamento de tarefas
#define TAREFA_1                 0
#define TAREFA_2                 1
#define TAREFA_3                 2
#define TAREFA_4                 3
#define NUMERO_TAREFAS           4
#define PERIODO_EXECUCAO_TAREFA1 200   //tempo em ms
#define PERIODO_EXECUCAO_TAREFA2 1000   //tempo em ms
#define PERIODO_EXECUCAO_TAREFA3 1000  //tempo em ms
#define PERIODO_EXECUCAO_TAREFA4 100  //tempo em ms
#define TIMEOUT_TAREFA           500   //tempo em ms

//variáveis globais
void (*TarefasAgendadas[NUMERO_TAREFAS])(void);  //armazena os ponteiros para as funções das tarefas a serem executadas
int PeriodosTarefasAgendadas[NUMERO_TAREFAS];       //armazena a temporização de cada tarefa
int TemporizacaoAtualTarefas[NUMERO_TAREFAS];       //armazena quantos milisegundos falta pra executar cada tarefa
char FlagGerouInterrupcaoTimer0;   //sinaliza se foi gerada interrupção de Timer0
volatile char HaTarefaEmExecucao;
int TimeoutTarefa;

//prototypes: 
//1-funções  gerais
void ConfiguraTimer0(void);
void InicializaTarefas(void);
void ExecutaTarefa(void);

//2-tarefas a serem executadas
void Tarefa1(void);
void Tarefa2(void);
void Tarefa3(void);
void Tarefa4(void);

//Função: configura todos os registradores necessários para colocar o Timer0 operante (em modo temporizador)
//Parâmetros: nenhum
//Retorno: nenhum
void ConfiguraTimer0(void)
{
    //Para o cálculo do Timer0, considerar as seguintes fórmulas e observações:
    //- o Timer0 tem 8 bits, logo o valor de seu "contador interno" (o registrador TMR0) vai de 00h até FFh no máximo 
    //- Neste caso, usaremos Pre-scaler igual a 1:8. Logo, o valor preScaler usado nos calculos a seguir é preScaler = 8
    //- Nos PICs 8bits, 1 instrução é executada somente após 4 oscilações do clock. Logo, é correto dizer que a frequencia útil do PIC é 1/4 da frequência de entrada. Portanto:
    //   TempoCicloMaquina = 4*CicloOsc) = 4*(1/Fosc) =  4 / Fosc  
    //- O tempo de estouro (para o registrador TMR0 chegar até seu valor FFh) é calculado pela seguinte fórmula:
    //  Testouro = 256 * TempoCicloMaquina * preScaler  (256 = FFh, valor de TMR0 para dar tempo máximo de estouro)
    // Neste caso, temos: Testouromaximo = 256 * 0,000001 * 8 = 2,048ms
    // 
    // Para atingir 1ms de tempo de estouro:
    // 0,001 = (256-TMR0) * 0,000001 * 8 -> TMR0 = 131
    
    
    OPTION_REGbits.PS = 0b010; //configuração do pre-scaler em 1:8
    OPTION_REGbits.PSA = 0;    //O pre-scaler setado será usado para o timer0 (necessário definir isso pois, neste PIC, o Timer0 pode ser usado ou para Timer de propósito geral ou para o Watchdog)
    OPTION_REGbits.T0CS=0;     //O timer0 considera como clock a frequencia util do microcontrolador
    INTCONbits.TMR0IE =1;      //Habilita interrupção de estouro do Timer0    
    INTCONbits.GIE=1;          //Habilita TODAS as interrupções (exigÊncia do compilador XC8 para habilitar toda e qualquer interrupção)
    TMR0 = VALOR_INICIALIZACAO_TIMER0; //Inicialização do registrador TMR0
    FlagGerouInterrupcaoTimer0 = NAO;
}

//Função: função que inicializa tarefas e a temporização das mesmas
//Parâmetros: nenhum
//Retorno: nenhum
void InicializaTarefas(void)
{
	//inicialização das tarefas
	TarefasAgendadas[TAREFA_1] = Tarefa1;
	TarefasAgendadas[TAREFA_2] = Tarefa2;
	TarefasAgendadas[TAREFA_3] = Tarefa3;
    TarefasAgendadas[TAREFA_4] = Tarefa4;
	
	//inicialização das temporizações
	PeriodosTarefasAgendadas[TAREFA_1] = PERIODO_EXECUCAO_TAREFA1;
	PeriodosTarefasAgendadas[TAREFA_2] = PERIODO_EXECUCAO_TAREFA2;
	PeriodosTarefasAgendadas[TAREFA_3] = PERIODO_EXECUCAO_TAREFA3;
    PeriodosTarefasAgendadas[TAREFA_4] = PERIODO_EXECUCAO_TAREFA4;
	
	//inicializaçãop da temporização atual (esta variará ao longo do programa)
	TemporizacaoAtualTarefas[TAREFA_1] = PERIODO_EXECUCAO_TAREFA1;
	TemporizacaoAtualTarefas[TAREFA_2] = PERIODO_EXECUCAO_TAREFA2;
	TemporizacaoAtualTarefas[TAREFA_3] = PERIODO_EXECUCAO_TAREFA3;	
    TemporizacaoAtualTarefas[TAREFA_4] = PERIODO_EXECUCAO_TAREFA4;	
    
    //sinaliza que não ha tarefas em execução ainda
    HaTarefaEmExecucao = NAO;
}

//Função: executa tarefa (se esta estiver na hora de ser executada, de acordo com seu agendamento)
//Parâmetros: nenhum
//Retorno: nenhum
void ExecutaTarefa(void)
{
    char i;
	for (i=0; i<NUMERO_TAREFAS; i++)
	{
		//se deve executar, realiza a execução e reinicia temporização
		if ((TarefasAgendadas[i] != 0) && (TemporizacaoAtualTarefas[i] == 0))
        {
			HaTarefaEmExecucao = SIM;
            TimeoutTarefa = TIMEOUT_TAREFA;
            TarefasAgendadas[i]();  //execução da tarefa
            HaTarefaEmExecucao = NAO;
			TemporizacaoAtualTarefas[i] = PeriodosTarefasAgendadas[i];  //reagendamento da tarefa
		}
	}
}

//Função: rotina da tarefa 1
//Parâmetros: nenhum
//Retorno: nenhum
void Tarefa1(void)
{
    
}

//Função: rotina da tarefa 2
//Parâmetros: nenhum
//Retorno: nenhum
void Tarefa2(void)
{

}

//Função: rotina da tarefa 3
//Parâmetros: nenhum
//Retorno: nenhum
void Tarefa3(void)
{
    
}

//Função: rotina da tarefa 4
//Parâmetros: nenhum
//Retorno: nenhum
void Tarefa4(void)
{
    
}

//Função: função de tratamento de interrupções do PIC (chamda automaticamente no acionamento de toda e qualquer interrupção do PIC, cabendo ao programador verificar dentro desta função qual interrupção que foi gerada)
//Parâmetros: nenhum
//Retorno: nenhum
void interrupt isr(void)
{
    char  i;
	
	if (TMR0IF && T0IF)   //se o TMR0 foi a 00h (indicado pelo sinalizador TMR0IF) e o sinalizador de interrupção de estouro de timer0 (T0IF) indicou estouro de Timer0, logo houve estouro de timer.
    {
        FlagGerouInterrupcaoTimer0 = SIM;
		
		//atualiza a temporização de cada uma das tarefas
        
		for (i=0; i<NUMERO_TAREFAS; i++)
		{
			if (TemporizacaoAtualTarefas[i] > 0)
				TemporizacaoAtualTarefas[i]--;			
		}
			
        if (HaTarefaEmExecucao == SIM)
        {
            TimeoutTarefa--;
            
            if (!TimeoutTarefa)
            {
                //possivel travamento. Reseta microcontrolador                    
                PCLATH=0;    
                PCL=0;                    
            }
        }
        
		T0IF = 0;              //indica que o timer0 deve ser reiniciado para uma futura interrupção
        INTCONbits.TMR0IF = 0; // reabilita interrup. TMR0
        TMR0 = VALOR_INICIALIZACAO_TIMER0; //Inicialização do registrador TMR0
    }   
}

void main(void) 
{
	TRISB = 0x00;            //configura todos os pinos do port b como saída
    PORTB = 0;               //força todas as saídas do port b para 0
    
    TRISA = 0xFF;            //configura todos os pinos do port a como entrada
    
    //inicialização da parte de tarefas
    InicializaTarefas();     //inicializa tarefas
	ConfiguraTimer0();       //configura Timer0 para ser acionado conforme desejamos neste programa
    
	//loop principal
    while(1)                      
    {
        //verifica continuamente se deve executar alguma tarefa agendada
        if ((FlagGerouInterrupcaoTimer0 == SIM)  && (NUMERO_TAREFAS)) 
        {
            FlagGerouInterrupcaoTimer0 = NAO;  
            ExecutaTarefa();			
        }
    }
}

Conforme informado, este é apenas o núcleo do gerenciador de tarefas com as tarefas “vazias” (as funções das tarefas não contêm códigos). Portanto, este código sozinho não realiza nenhuma operação. Para realizar operações, basta implementar as rotinas desejadas nas funções Tarefa1, Tarefa2, Tarefa3 e Tarefa4, bem como configurar suas temporizações conforme desejar.

Para melhor ilustrar o funcionamento deste gerenciador, foi preparado um exemplo de utilização, onde as tarefas são:

  1. Tarefa 1: escrever um valor de 3 dígitos em 3 displays de 7 segmentos. Nesta tarefa, está inclusa a multiplexação dos displays;
  2. Tarefa 2: controlar uma breathing light (luz que pisca indicando que o software embarcado está sendo executado). No caso, a breathing light pisca a uma frequência de 0,5Hz (1 segundo aceso e 1 segundo apagado);
  3. Tarefa 3: armazena o tempo decorrido de execução (em segundos) e disponibiliza os valores para a tarefa 2;
  4. Tarefa 4: verifica a entrada RA0. Se esta estiver em nível alto, a tarefa 3 contabiliza o tempo normalmente. Caso contrário, o tempo não é contabilizado.

Cada camada (controle de temporização para display, controle do display e breathing light) está isolada (ou seja, tem os seus próprios arquivos de código e headers, .c e .h respectivamente). Logo, preservou-se o máximo possível do gerenciador original.

No exemplo, consta também uma simulação no Proteus do exemplo. Para baixar o projeto e simulação, visite o GitHub do projeto clicando aqui.

Agradecimentos

Agradecimento ao Fábio Souza por todo o apoio no projeto desde gerenciador de tarefas e no exemplo desenvolvido.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Comentários:
Notificações
Notificar
2 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Vinicius
Vinicius
10/05/2016 02:31

Olá, gostei muito da explicação… Trabalho semelhante a essa forma em processadores de 8, 16 e 32 bits da família Microchip e atualmente estou desenvolvendo meu TCC sobre um processador NXP 32 bits. Bom, acredito ter uma melhoria para não gerar erros de timeout indevidos… Seria: carregar o timeout antes de setar o flag de HaTarefaEmExecucao, pois, caso o último timeout carregado tenha atingido o valor 1, porém, a tarefa terminou antes do timeout… ao setar o flag e antes de carregar o próximo timeout ocorrer uma interrupção, o timeout vem a 0 e o processador será reiniciado. A solução… Leia mais »

Home » Software » Sistemas Operacionais » Gerenciador de tarefas em um PIC 8-bits

EM DESTAQUE

WEBINARS

VEJA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Talvez você goste: