electronplace.com

...Just Because I Can Do It Now.

TIME-SLICED MULTITASK - EXEMPLO 1

FROM : http://www.microchipc.com/Hi-Tech_C_multitask/

O Sistema Time-Sliced Multitask descrito a seguir foi utilizado para implementar, num PIC 12CE674 e num 16F876, um firmware realizando um polling num pino para receber caracteres seriais (RX_Uart), realizar cálculos matemáticos em intervalos regulares no buffer de recepção desses caracteres, escrever dados numa EEPROM I2C do tipo 24LC256, e gerar um sinal de 50Hz com duty-cycle variável (PWM).

Tudo isso foi executado simultaneamente, estando o PIC aceitando a recepção contínua na sua serial stream. O funcionamento é perfeito e o código demonstrou-se estável. Aqui, uma explanação de como isso foi obtido. Esta técnica permite que aproveitemos todo o poder disponível de um microcontrolador desse nível, e definitivamente merece ser aprendida. Nesse exemplo são necessários:

Setar o TMR1 para um overflow e interrupção a cada 500us;

  1. O respectivo ISR será executado a cada 500us;
  2. Para cada task individualmente temos três coisas ocorrendo:
    1. Um contador que incrementa a cada interrupção de 500us;
    2. Um contador de máximo para determinada task, chamado de TASKX_COUNTER_MAX. Quando esse contador atinge esse máximo, a task é executada. Dessa forma pode-se definir a frequência com que a task é executada, por meio desse valor máximo. Determinado desses contadores pode ter o valor "1", que indica que ele executa a cada 500us, e outro com "2000", que será executado a cada 1000 ms.
    3. Uma flag booleana destinada a habilitar a task, denominada TASKX_ENABLE.
  3. Assim, podemos setar com qual frequência e prioridade de quantas vezes a task será executada

Podemos estender isso ainda mais:- O que acontece se tivermos que executar  regularmente uma task que toma muito tempo, digamos 100ms de cálculos matemáticos, mas isso só ocorre a cada 1000ms? Isso causaria a parada de tasks importantes que exigem alta frequência de execução, a cada 500us. Não podemos ter a geração de PWM parada por 100ms, por exemplo:

  1. Então, tudo o que precisamos fazer é gatilhar a lenta e complexa task a partir da interrupção, setando taskX_go=true. Então, a interrupção termina imediatamente e deixa o sistema pronto para a próxima task.
  2. Enquanto isso, um loop em main() está sondando taskX-go. Se ela estiver =true, será resetada como =false para a próxima leitura, e então executa a task no tempo ocioso. Enquanto isso acontece, as tasks de alta prioridade ainda ocorrem em ritmo normal.

Resumindo tudo o que foi dito:

  1. Podemos executar qualquer número de tasks em intervalos regulares;
  2. Podemos escolher a frequência com que cada task será executada, alterando TASKX_COUNTER_MAX;
  3. Escolher a ordem de execução das tasks, colocando as tasks de maior prioridade em primeiro lugar na rotina de interrupção, o que altera as suas prioridades;
  4. Tenha as tasks lentas, infrequente background tasks executando no main(), gatilhadas pelo setar de taskX_go=true em intervalos regulares.

Existe mais uma dica para evitar problemas. O tempo entre os ticks de 500us devem ser suficientes para realizar todas as tasks, no pior caso. Ou isso, ou apenas uma task será executada por tick de 500us, e as outras têm de esperar até que um slot livre ocorra. Para saber se o timing está muito apertado e as tarefas serão executadas novamente antes que a anterior tivesse a chance de terminar, adicione uma linha para verificar se o flag de interrupção do timer já foi setado antes de sair da interrupção.

//(c)Shane Tolmie, http://www.microchipc.com/, distribute freely for non commercial use on the condition that you include this web link somewhere in your document.
//*****
//multitasking system – handle multiple tasks with one microprocessor

//task counters used to tell when a task is ready to be executed
//all these counters are incremented every time a 500us interrupt happens
//every task has its own counter that is updated every time a 500us interrupt happens
unsigned int task0_counter=0;
unsigned int task1_counter=0;
unsigned int task2_counter=0;

//this tells when a task is going to happen again
//for example, when task0_counter==TASK0_COUNTER_MAX, set task0_counter=0 and do task
#define TASK0_COUNTER_MAX 1         //high frequency task – every 500us, maybe PWM
#define TASK1_COUNTER_MAX 2000      //low frequency task – every 1000ms
#define TASK2_COUNTER_MAX 5000      //low frequency and low priority task, every 2500ms

//Note: every variable referenced in both interrupt and main() must be declared volatile. You have been warned!
//this enables/disables a task
volatile unsigned char task0_enable=TRUE;
volatile unsigned char task1_enable=TRUE;
volatile unsigned char task2_enable=TRUE;

//this allows tasks triggered by interrupt to run in the background in main()
volatile unsigned char task2_go=FALSE;
void setup_multitasking(void)
{
   //set up tmr1  to interrupt every 500us
   TMR1CS=0;
   T1CKPS0=0;
   T1CKPS1=0;

/*We want to wait 2000 clock cycles, or 500us @ 16MHz (instructions are 1/4 speed of clock).  Timer 1 interrupts when it gets to 0xFFFF or 65535.  Therefore, we set timer 1 to 65535 minus 2000 = 63535, then wait 2000 ticks until rollover at 65535.  To test, use simulator to find that its exactly correct*/

   #define  TICKS_BETWEEN_INTERRUPTS      2000
   #define  INTERRUPT_OVERHEAD            19
   #define TMR1RESET (0xFFFF-(TICKS_BETWEEN_INTERRUPTS-INTERRUPT_OVERHEAD))
   #define TMR1RESET_HIGH TMR1RESET >> 8
   #define TMR1RESET_LOW TMR1RESET & 0xFF
   TMR1ON=0;
   TMR1H=TMR1RESET_HIGH;
   TMR1L=TMR1RESET_LOW;
   TMR1ON=1;      
   TMR1IF=0;
   TMR1IE=1;
   PEIE=1;
   GIE=1;   
}

void interrupt isr(void)
{
   //one tick every 500us at 16Mhz
   if (TMR1IF)
   {
      //set up timer 1 again to interrupt 500us in future
      TMR1IF=0;
      TMR1ON=0;
      TMR1H=TMR1RESET_HIGH;
      TMR1L=TMR1RESET_LOW;
      TMR1ON=1;
      task0_counter++;
      if (task0_counter>=TASK0_COUNTER_MAX)     //high frequency task – every 1 tick
      {
            task0_counter=0;
            if (task0_enable==TRUE)
            {
                  //do high frequency important task 0, for example PWM
            }
      }
      task1_counter++;
      if (task1_counter>=TASK1_COUNTER_MAX)     //low priority task - every 2000 ticks
      {
            task1_counter=0;
            if (task1_enable==TRUE)
            {
                  //do low frequency yet important task 1
            }
      }

/*this task takes a long time, 100ms for example, lots of maths.  Is extremely low priority, but has to be done at regular intervals, so all this does is trigger it.  In main(), it will, at leisure, poll ‘task2_go’ and then execute it in the background.*/

      task2_counter++;
      if (task2_counter>=TASK2_COUNTER_MAX)     //every 250ms
      {
          task2_counter=0;
          if (task2_enable==TRUE)
          {
               //every 250ms take 100ms to do maths, do this in main() so the we can get back to doing the high frequency tasks.
               task2_go=TRUE;
          }
      }
   }  //if (TMR1IF)
} //interrupt routine

main()
{
   setup_multitasking();
   while(1)
   {
      if (task2_go==TRUE)
      {
            task2_go=FALSE;
            //take our time, doing heaps of complex maths at our leisure in the background
      }
   }
//(c)Shane Tolmie, http://www.microchipc.com/, distribute freely for non commercial use on the condition that you include this web link somewhere in your document.
//*****
//multitasking system – handle multiple tasks with one microprocessor

//task counters used to tell when a task is ready to be executed
//all these counters are incremented every time a 500us interrupt happens
//every task has its own counter that is updated every time a 500us interrupt happens
unsigned int task0_counter=0;
unsigned int task1_counter=0;
unsigned int task2_counter=0;

//this tells when a task is going to happen again
//for example, when task0_counter==TASK0_COUNTER_MAX, set task0_counter=0 and do task
#define TASK0_COUNTER_MAX 1         //high frequency task – every 500us, maybe PWM
#define TASK1_COUNTER_MAX 2000      //low frequency task – every 1000ms
#define TASK2_COUNTER_MAX 5000      //low frequency and low priority task, every 2500ms

//Note: every variable referenced in both interrupt and main() must be declared volatile. You have been warned!
//this enables/disables a task
volatile unsigned char task0_enable=TRUE;
volatile unsigned char task1_enable=TRUE;
volatile unsigned char task2_enable=TRUE;

//this allows tasks triggered by interrupt to run in the background in main()
volatile unsigned char task2_go=FALSE;
void setup_multitasking(void)
{
   //set up tmr1  to interrupt every 500us
   TMR1CS=0;
   T1CKPS0=0;
   T1CKPS1=0;

/*We want to wait 2000 clock cycles, or 500us @ 16MHz (instructions are 1/4 speed of clock).  Timer 1 interrupts when it gets to 0xFFFF or 65535.  Therefore, we set timer 1 to 65535 minus 2000 = 63535, then wait 2000 ticks until rollover at 65535.  To test, use simulator to find that its exactly correct*/

   #define  TICKS_BETWEEN_INTERRUPTS      2000
   #define  INTERRUPT_OVERHEAD            19
   #define TMR1RESET (0xFFFF-(TICKS_BETWEEN_INTERRUPTS-INTERRUPT_OVERHEAD))
   #define TMR1RESET_HIGH TMR1RESET >> 8
   #define TMR1RESET_LOW TMR1RESET & 0xFF
   TMR1ON=0;
   TMR1H=TMR1RESET_HIGH;
   TMR1L=TMR1RESET_LOW;
   TMR1ON=1;      
   TMR1IF=0;
   TMR1IE=1;
   PEIE=1;
   GIE=1;   
}

void interrupt isr(void)
{
   //one tick every 500us at 16Mhz
   if (TMR1IF)
   {
      //set up timer 1 again to interrupt 500us in future
      TMR1IF=0;
      TMR1ON=0;
      TMR1H=TMR1RESET_HIGH;
      TMR1L=TMR1RESET_LOW;
      TMR1ON=1;
      task0_counter++;
      if (task0_counter>=TASK0_COUNTER_MAX)     //high frequency task – every 1 tick
      {
            task0_counter=0;
            if (task0_enable==TRUE)
            {
                  //do high frequency important task 0, for example PWM
            }
      }
      task1_counter++;
      if (task1_counter>=TASK1_COUNTER_MAX)     //low priority task - every 2000 ticks
      {
            task1_counter=0;
            if (task1_enable==TRUE)
            {
                  //do low frequency yet important task 1
            }
      }

/*this task takes a long time, 100ms for example, lots of maths.  Is extremely low priority, but has to be done at regular intervals, so all this does is trigger it.  In main(), it will, at leisure, poll ‘task2_go’ and then execute it in the background.*/

      task2_counter++;
      if (task2_counter>=TASK2_COUNTER_MAX)     //every 250ms
      {
          task2_counter=0;
          if (task2_enable==TRUE)
          {
               //every 250ms take 100ms to do maths, do this in main() so the we can get back to doing the high frequency tasks.
               task2_go=TRUE;
          }
      }
   }  //if (TMR1IF)
} //interrupt routine

main()
{
   setup_multitasking();
   while(1)
   {
      if (task2_go==TRUE)
      {
            task2_go=FALSE;
            //take our time, doing heaps of complex maths at our leisure in the background
      }
   }
 
Código Exemplo: 
/* c)Shane Tolmie, http://www.microchipc.com/. DistribuíDO gratuitamente para u
so não comercial, na condição de incluir este link em algum lugar DO seu documento. 
Sistema Multitarefas (Multitsking System) - trata múltiplas tasks num pequeno m
icrocontrolador.
Contadores de tasks são usados para avisar quando a task está pronta para execu
ção. Todos esses contadores são incrementados a cada período de 500us na ocorrência 
da interrupção. Cada task tem o seu próprio contador, incrementado a cada interrupção de 500us 
(tick)*/

unsigned INT task0_counter=0;
unsigned INT task1_counter=0;
unsigned INT task2_counter=0;

/* Isto avisa quando a task vai ocorrer novamente. Por exemplo, quando task0_co
unter=TASK0_COUNTER_MAX, 
reseta task0_counter=0 e executa a task.*/

//Task de alta frequência - a cada 500us, talvez PWM
#define TASK0_COUNTER_MAX 1
//Task de baixa frequência - a cada 1000ms
#define TASK1_COUNTER_MAX 2000
//Task de baixa frequência/baixa prioridade - a cada 2500ms
#define TASK2_COUNTER_MAX 5000
  
//Aviso: toda a variável referenciada em ambos, interrupção e main(), 
//deve ser declarada como VOLATILE. Você foi avisado!

//Isto habilita/desabilita uma task
volatile UNSIGNED char task0_enable=TRUE;
volatile UNSIGNED char task1_enable=TRUE;
volatile UNSIGNED char task2_enable=TRUE;
//Isto permite que tasks sejam gatilhadas pela interrupção para executar em bac
kground no main()
volatile UNSIGNED char task2_go=FALSE;
void setup_multitasking(VOID)
{
   //Seta o TMR1 para interromper a cada 500us
   TMR1CS = 0;
   T1CKPS0 = 0;
   T1CKPS1 = 0;
   /* Queremos aguardar 2000 ciclos de clock, ou 500us @ 16MHz (instruções equiv
   alem ao clock / 4) .
   TMR1 interrompe quando atinge 0XFFFF (ou 65535) . Assim, ajustamos o TMR1 em
   65535 - 2000 = 63535,
   quando então ele aguarda 2000 ticks até atingir 65535. Para testar, utilize
   um simulador para descobrir se isto está absolutamente correto*/
   #define TICKS_BETWEEN_INTERRUPTS 2000
   #define INTERRUPT_OVERHEAD 19
   #define TMR1RESET (0xFFFF - (TICKS_BETWEEN_INTERRUPTS - INTERRUPT_OVERHEAD))
   #define TMR1RESET_HIGH TMR1RESET >> 8
   #define TMR1RESET_LOW TMR1RESET&0xFF
   TMR1ON = 0;
   TMR1H = TMR1RESET_HIGH;
   TMR1L = TMR1RESET_LOW;
   TMR1ON = 1;
   TMR1IF = 0;
   TMR1IE = 1;
   PEIE = 1;
   GIE = 1;
}

void interrupt isr(VOID)
{
   //Obtendo um tick a cada 500us @ 16MHz
   IF (TMR1IF)
   {
      //Setando novamente TMR1 para interromper em 500us no futuro
      TMR1IF = 0;
      TMR1ON = 0;
      TMR1H = TMR1RESET_HIGH;
      TMR1L = TMR1RESET_LOW;
      TMR1ON = 1;
      task0_counter++;
      //Task de alta frequência a cada 1 tick
      IF (task0_counter >= TASK0_COUNTER_MAX
      {
         task0_counter = 0;
         IF (task0_enable == TRUE)
         {
            //Inserir task de alta frequência importante, PWM por ex.
         }
      }

      task1_counter++;
      
      //Task de baixa prioridade, a cada 2000 ticks
      IF (task1_counter >= TASK1_COUNTER_MAX)
      {
         task1_counter = 0;
         IF (task1_enable == TRUE)
         {
            //Inserir task de baixa frequência, mas ainda importante task1
         }
      }

      /* Esta task toma um tempo longo, 100ms por exemplo, em muitos cálculos. P
      ossui extremamente baixa prioridade, mas tem
      de ser executada a intervalos regulares, então isso é gatilhado aqui. No
      main () será, quando sistema ocioso,
      verificar "task2 - go" e executá - la em background.*/
      task2_counter++;
      IF (task2_counter >= TASK2_COUNTER_MAX) //a cada 250ms
      {
         task2_counter = 0;
         IF (task2_enable == TRUE)
         {
            // a cada 250ms leva 100ms para cálculos, executando isso no main  (
            ), após o que retorna
            // executando as tasks de alta frequência.
            task2_go = TRUE;
         }
      }
   } //IF (TMR1IF)
} //interrupt routine

main()
{
   setup_multitasking ();
   WHILE (1)
   {
      IF (task2_go == TRUE)
      {
         task2_go = FALSE;
         //take our time, doing heaps of complex maths at our leisure in the ba
         ckground
      }

c)Shane Tolmie, http://www.microchipc.com/. Distribuído gratuitamente para uso não comercial, na condição de incluir este link em algum lugar do seu documento.

Sistema Multitarefas (Multitasking System) – trata múltiplas tasks num pequeno microcontrolador.

Contadores de tasks são usados para avisar quando a task está pronta para execução. Todos esses contadores são incrementados a cada período de 500us na ocorrência da interrupção. Cada task tem o seu próprio contador, incrementado a cada interrupção de 500us (tick).

unsigned int task0_counter=0;
unsigned int task1_counter=0;
unsigned int task2_counter=0;

Isto avisa quando a task vai ocorrer novamente. Por exemplo, quando task0_counter=TASK0_COUNTER_MAX, reseta task0_counter=0 e executa a task.

Task de alta frequência – a cada 500us, talvez PWM

#define TASK0_COUNTER_MAX 1

Task de baixa frequência – a cada 1000ms

#define TASK1_COUNTER_MAX 2000

Task de baixa frequência/baixa prioridade – a cada 2500ms
#define TASK2_COUNTER_MAX 5000

Aviso: toda a variável referenciada em ambos, interrupção e main(), deve ser declarada como volatile. Você foi avisado!

Isto habilita/desabilita uma task

volatile unsigned char task0_enable=TRUE;
volatile unsigned char task1_enable=TRUE;
volatile unsigned char task2_enable=TRUE;


Isto permite que tasks sejam gatilhadas pela interrupção para executar em background no main()

volatile unsigned char task2_go=FALSE;

void setup_multitasking(void)

{

Seta o TMR1 para interromper a cada 500us

   TMR1CS=0;
   T1CKPS0=0;
   T1CKPS1=0;

Queremos aguardar 2000 ciclos de clock, ou 500us @ 16MHz (instruções equivalem ao clock/4). TMR1 interrompe quando atinge 0XFFFF (ou 65535). Assim, ajustamos o TMR1 em 65535-2000=63535, quando então ele aguarda 2000 ticks até atingir 65535. Para testar, utilize um simulador para descobrir se isto está absolutamente correto. 

   #define  TICKS_BETWEEN_INTERRUPTS      2000
   #define  INTERRUPT_OVERHEAD            19
   #define  TMR1RESET (0xFFFF-(TICKS_BETWEEN_INTERRUPTS-INTERRUPT_OVERHEAD))
   #define TMR1RESET_HIGH TMR1RESET >> 8
   #define TMR1RESET_LOW TMR1RESET & 0xFF
   TMR1ON=0;
   TMR1H=TMR1RESET_HIGH;
   TMR1L=TMR1RESET_LOW;
   TMR1ON=1;      
   TMR1IF=0;
   TMR1IE=1;
   PEIE=1;
   GIE=1;   
}

void interrupt isr(void)
{
Obtendo um tick a cada 500us @ 16MHz
   if (TMR1IF)
   {

Ajustando novamente TMR1 para interromper em 500us no futuro
      TMR1IF=0;
      TMR1ON=0;
      TMR1H=TMR1RESET_HIGH;
      TMR1L=TMR1RESET_LOW;
      TMR1ON=1;
      task0_counter++;

Task de alta frequência a cada 1 tick
      if (task0_counter>=TASK0_COUNTER_MAX 

      {
            task0_counter=0;
            if (task0_enable==TRUE)
            {

Inserir task de alta frequência importante, PWM por ex.
            }
      }
      task1_counter++;

Task de baixa prioridade, ocorre a cada 2000 ticks
      if (task1_counter>=TASK1_COUNTER_MAX)

      {

            task1_counter=0;
            if (task1_enable==TRUE)
            {

Inserir task de baixa ocorrência - mas ainda importante - task1

            }
      }
 

Esta task toma muito tempo, 100ms por exemplo, em muito cálculos. É de extrema baixa prioridade, mas deve ser executada em intervalos regulares. Então, isso é feito no seu gatilho. No main(), quando permitido, é inspecionada "task2_go" e ela é executada em background.

      task2_counter++;
      if (task2_counter>=TASK2_COUNTER_MAX)     //every 250ms
      {
          task2_counter=0;
          if (task2_enable==TRUE)
          {

A cada 250ms leva 100ms para realizar cálculos, fazer isso em main(), de modo que podemos voltar a fazer as tasks de alta frequência.

               task2_go=TRUE;
          }
      }
   }       
//if (TMR1IF)
}     //interrupt routine

main()
{
   setup_multitasking();
   while(1)
   {
      if (task2_go==TRUE)
      {
            task2_go=FALSE;

Gaste aqui nosso tempo fazendo muitos cálculos matemáticos complexos no tempo ocioso em segundo plano

     }
   }
}

User Feedback

Eu observei em detalhes a sua descrição do sistema multitasking mas ainda tenho algumas poucas dúvidas. Perdoe a minha ignorância e por ter de perguntar sobre elas.

Darei o meu melhor para respondê-las.

A questão concreta refere-se a alguns parágrafos:
1) ...Ou isso, ou apenas uma task será executada por tick de 500us, e as outras têm de esperar até que um slot livre ocorra. Para saber se o timing está muito apertado e as tarefas serão executadas novamente antes que a anterior tivesse a chance de terminar, adicione uma linha para verificar se o flag de interrupção do timer já foi setado antes de sair da interrupção.
O parágrafo inteiro me causa problemas. Fica mais ou menos evidente para mim que o processo inteiro dentro da ISR dever ser executado entre "ticks".

Sim, isso está correto. Existem dois tipos de tasks: tasks curtas (short) e tasks longas (long). A short task é tão rápida que deve ser executada inteiramente dentro da ISR, por ex. ela demora menos de 500us para ser executada. Um exemplo é "incrementar um timer e setar uma flag, caso o timer atinja um certo valor".

Uma long task é muito lenta, então não deve ser executada inteiramente dentro da ISR, por ex. ela leva 50ms para executar. Um exemplo de uma long task pode ser "calcular o cosseno de um angulo e enviar o resultado pela porta serial".

Esta é a chave da questão: coloque short tasks dentro da ISR, e mova as long tasks para o main(), assim elas rodarão em background. As short tasks obterão execução imediata, inteiramente dentro da ISR e as long tasks são executadas no main(), em background. Long tasks podem ainda ser gatilhadas para executar a partir de short tasks dentro da ISR.

Q1 - Você está se referindo (com esse parágrafo) para o caso o qual uma única task dentro da ISR demandaria muito tempo e um novo "tick" ocorreria antes de finalizadas todas as tasks?

Sim.

Q2 - O que exatamente quer dizer com "time slot"?

Significa a quantia de tempo alocada pela ISR. Por exemplo, se você setar a ISR para ocorrer 10 vezes por segundo, então haveria 100ms de tempo entre cada interrupção. Se setar a ISR para ocorrer 100 vezes por segundo, haverá 10ms entre cada interrupção. O tempo entre cada interrupção pode ser encurtado ou alongado, dependendo do tempo que ela levará para executar algo dentro da ISR.

Q3 - Por que uma task pode novamente ser executada antes de uma outra task ter tido a chance de finalizar?

Se isso está ocorrendo, você deverá reduzir o número de "ticks" por segundo, na ISR. Outra maneira de pensar sobre isso é que precisa dimensionar o montante de tempo que a ISR dispõe para executar suas tasks.

Q4 - Onde exatamente deve ser adicionada a linha mencionada por você para testar se o TMR1IF encontra-se setado antes de sair da ISR? Estaria se referindo à primeira linha da ISR?

Isto deverá ser testado ao final da ISR. Aqui está um exemplo que ilustra isso:

Após 10ms o timer estoura e o fluxo do programa pula para a ISR. Em circunstâncias normais a ISR levaria 2ms para ser executada, e dela sairia com um tempo = 12ms. Todavia, se a ISR levou 25ms para ser executada, o programa nunca iniciaria a execução de nada rodando em segundo plano (background), dentro do main().

Sobre esta situação: 99% das interrupções levam 2ms, mas uma interrupção estranha leva 25ms. Como poderíamos identificar isso? Bem, ao final da ISR, apenas antes da sua saída e o controle de programa retornar ao main(), checamos a flag TMR1IF. Se estiver setada mostrará que a ISR deve ter levado mais de 10ms, pois o timer reativou a flag de interrupção antes mesmo do seu final. Numa situação como esta, devemos setar uma variável para nos avisar. Com isso, podemos reagir incrementando o montante de tempo de cada interrupção para, digamos, 50ms.

Q5 - Quando fala a respeito de "Overhead". Eu entendo que isto tenta compensar algumas contagens do TMRx antes do seu overflow, a partir de algum tempo gasto no processamento de outras instruções. Poderia explanar melhor sobre o assunto?

Isto é apenas um esforço para que as interrupções ocorram em bases regulares. Aqui vai um exemplo:

  1. Imagine que setamos o TMR2 para gatilhar a cada 10ms e assim setar o seu interrupt flag;
  2. Quando a interrupt flag for setada, o fluxo do programa saltaria para dentro da ISR;
  3. Uma vez dentro da ISR, resetamos o TMR2 para gatilhar em outro 10ms. Isso significa que outra interrupção ocorrerá em 10ms.

Porém, se dessa forma medirmos o tempo entre interrupções, poderemos encontrar que estão ocorrendo uma vez a cada 11ms. De onde este tempo extra de 1ms veio? Bem, demorou 1ms para realmente entrar na interrupção até o ponto onde poderíamos setar o timer para o "tick" em outro 10ms. Outra maneira de dizer isso é que isto levou 1ms entre os passos 2 e 3, o que pode incrementar o montante de tempo para 11ms entre interrupções.

Assim, tudo o que fazemos é setar o TMR2 para gatilhar em outros 9ms. Isso ajusta o erro e a ISR ocorrerá precisamente a cada 10ms.

Q6 - Numa das primeiras respostas você disse "...trabalhar com isso para que ainda exista tempo suficiente para tudo, mesmo que todas as interrupções ocorram de uma vez." O que significa "todas as interrupções" ocorrendo de uma vez?

Quer dizer que dentro do evento que cada linha de código dentro da interrupção é executada, deve existir ainda tempo suficiente para finalizar a ISR antes que uma outra aconteça. Outra forma de dizer isso é que o caminho do maior código deve ainda ser mais rápido que o tempo alocado pela ISR.

Como exemplo, imagine que setou uma interrupção para ocorrer a cada 10ms. Isso significa que o fluxo do programa desviará para dentro da ISR a cada 10ms. Imagine que normalmente a ISR leva 2ms. Agora imagine que adicionamos um condicional "if" nessa ISR o qual apenas é gatilhado ocasionalmente - mas quando gatilhado, adiciona 15ms ao tempo de processamento da ISR.

Isto pode não funcionar, como agora a ISR toda leva 17ms para executar, o que significa 7ms mais que o tempo máximo disponível de 10ms. Para corrigir isso, devemos mover aquele condicional "if" para o main(), e gatilhar a execução desse código rodando em main() por meio de uma flag a qual é setada pela ISR.

Q7 - Eu verifiquei que a task0 (ou qualquer outra task dentro da ISR que primeiramente é chamada) é executada num período preciso de tempo esperado. No entanto, o que ocorre se a task0 possuir um condicional que faz com que o caminho do seu código a faça variar desta interrupção para a seguinte?  

Download Complete Example Project

For the complete C source code which illustrates this time-sliced method, follow the link to the "Complete Design for Giant 8-foot LCD Counter" (see the picture showing the completed product). The sample project includes C source code, hardware, and VB controller.

-----Original Message-----

From: Clive Wilson
Sent: 07 March 2003 09:01
To: support@microchipc.com 
Subject: Time-sliced multitasking system for Hi-Tech C

Shane,

I see that task0 (or whatever the first 'task' in the ISR is called) gets executed at precisely the time expected. However, what happens if task0 has some conditional code which causes the execution path through the task0 code to vary from one interrupt to the next? Then, the next task in the ISR call, e.g. task1, would get executed at varying times after the point when the ISR was triggered. The same goes for any task due to run in the current interrupt - the timeliness of each task is dependent upon all previous

tasks in this interrupt instance having non-varying execution times.

Have I got the wrong end of the stick or is my theory correct? How do you cope with this when doing serial comms? Presumably the variable latency limits the speed at which serial comms can be performed?

Many thanks again for the great web site. I look forward to hearing your opinion on my query.

Kind regards,

Clive Wilson

-----Original Message-----
From: Shane Tolmie
Sent: 07 March 2003 10:47
To: Clive Wilson
Subject: RE: Time-sliced multitasking system for Hi-Tech C

Hi Clive,

Yes, you're right. This is known as interrupt jitter. However, with serial, there is a 2 byte internal buffer, so this can cope with a lot of variance in the exact time that the time-sliced interrupts happen.

The trick is to do all the processing in the background, triggered from the interrupts, and have minimal processing done in the actual interrupt. Work it out so that even if all the interrupts happen at once, there's still enough time for everything.

Regards,
Shane Tolmie

www.microchipc.com 

 http://www.romanblack.com/PICthread.htm 

Forum - Postagens Recentes

Noticias Recentes

Nossa Loja

Componentes Eletrônicos Para Venda On-Line

Oops! This site has expired.

If you are the site owner, please renew your premium subscription or contact support.