Pergunta

Eu estou usando um microcontrolador com um núcleo de C51. Eu tenho uma sub-rotina bastante demorado e grande que precisa ser chamado a cada 500ms. Um RTOS não está sendo usado.

A maneira que eu estou fazendo isso agora é que eu tenho uma interrupção do temporizador existente de 10 ms. I definir um sinalizador após 50 interrupções que está marcada para ser fiel no circuito principal do programa. Se a bandeira é verdade a sub-rotina é chamada. A questão é que pelo tempo que o loop de programa vem rodada para a manutenção da bandeira, já mais de 500ms é, às vezes até> 515 ms em caso de certos caminhos de código. O tempo necessário não é exatamente previsível.

Obviamente, a sub-rotina não pode ser chamado de dentro da interrupção do timer devido a esse grande tempo que leva para execute.The sub-rotina leva 50ms a 89ms dependendo várias condições.

Existe uma maneira de garantir que a sub-rotina é chamada exatamente 500ms cada vez?

Foi útil?

Solução

Será que isso fazer o que você precisa?

#define FUDGE_MARGIN 2    //In 10ms increments

volatile unsigned int ticks = 0;

void timer_10ms_interrupt( void )  { ticks++; }

void mainloop( void )
{
    unsigned int next_time = ticks+50;

    while( 1 )
    {
        do_mainloopy_stuff();

        if( ticks >= next_time-FUDGE_MARGIN )
        {
            while( ticks < next_time );
            do_500ms_thingy();
            next_time += 50;
        }
    }
}

NB:. Se você tem atrás com manutenção do seu tarefa a cada-500ms, então isso seria a fila-los, o que pode não ser o que você quer

Outras dicas

Eu acho que você tem algum conflito / não-pensamento-através de requisitos aqui. Você diz que você não pode chamar este código a partir da ISR temporizador, porque leva muito tempo para executar (o que implica que é uma menor prioridade do que qualquer outra coisa que seria adiada), mas então você está sendo atingido pelo fato de que algo outra coisa que deveria ter sido de baixa prioridade é atrasar-lo quando você executá-lo a partir do caminho de primeiro plano ( 'loop de programa').

Se este trabalho deve acontecer exatamente 500ms, em seguida, executá-lo a partir da rotina de timer, e lidar com a queda-out a partir daí. Esta é efetivamente o que um RTOS preventivos estaria fazendo de qualquer maneira.

Se você quiser que ele seja executado a partir do 'loop de programa', então você vai ter a certeza de que nada mais que vai de esse ciclo nunca leva mais do que o atraso máximo que você pode tolerar - muitas vezes isso significa quebrar seu outro longo executar o trabalho em estado-máquinas que podem fazer um pouco de trabalho por passagem através do laço.

Eu não acho que há uma maneira de garantir isso, mas esta solução pode fornecer uma alternativa aceitável.

Posso sugerir não definindo um sinalizador, mas sim modificar um valor?

Veja como isso poderia funcionar.

1 / Start um valor de zero.

2 / cada 10ms interromper, aumentar esse valor por 10 no ISR (rotina de serviço de interrupção).

3 / No circuito principal, se o valor for> = 500, subtrair 500 a partir do valor e fazer seus 500ms actividades.

Você tem que ter cuidado para observar as condições de corrida entre o temporizador e programa principal para modificar o valor.

Isto tem a vantagem de que a função é executada o mais próximo possível dos 500ms fronteiras, independentemente da latência ou duração.

Se, por algum motivo, sua função começa 20ms no final de uma iteração, o valor já estará 520 para que a sua função será, em seguida, configurá-lo para 20, o que significa que só vai esperar 480ms antes da próxima iteração.

Isso parece-me ser a melhor maneira de conseguir o que deseja.

Eu não toquei a 8051 durante muitos anos (assumindo que é o que C51 tem como alvo o que parece uma aposta segura dada sua descrição), mas ele pode ter uma instrução que irá subtrair 50 sem ser possível uma interrupção. No entanto, parece-me lembrar a arquitetura era muito simples para que você pode ter que interrupções desativar ou atraso enquanto ele faz o / Modificar operação de carga / loja.

volatile int xtime = 0;
void isr_10ms(void)  {
    xtime += 10;
}
void loop(void) {
    while (1) {
        /* Do all your regular main stuff here. */
        if (xtime >= 500) {
            xtime -= 500;
            /* Do your 500ms activity here */
        }
    }
}

Você também pode usar duas bandeiras - uma "pré-ação" bandeira e uma bandeira "gatilho" (usando Mike F de como ponto de partida):

#define PREACTION_HOLD_TICKS (2)
#define TOTAL_WAIT_TICKS (10)

volatile unsigned char pre_action_flag;
volatile unsigned char trigger_flag;

static isr_ticks;
interrupt void timer0_isr (void) {
   isr_ticks--;
   if (!isr_ticks) {
      isr_ticks=TOTAL_WAIT_TICKS;
      trigger_flag=1;
   } else {
      if (isr_ticks==PREACTION_HOLD_TICKS)
          preaction_flag=1;
   }
}

// ...

int main(...) {


isr_ticks = TOTAL_WAIT_TICKS;
preaction_flag = 0;
tigger_flag = 0;
// ...

   while (1) {
      if (preaction_flag) {
          preaction_flag=0;
          while(!trigger_flag)
             ;
          trigger_flag=0;
          service_routine();
      } else {
          main_processing_routines();
      }
   }
 }

Uma boa opção é usar um RTOS ou escreve sua própria simples RTOS.

Um RTOS para atender às suas necessidades só precisará fazer o seguinte:

  • agendar tarefas periódicas
  • tarefas cronograma Round Robin
  • de comutação pré-molde contexto

Os requisitos são os seguintes:

  • executar uma tarefa periódica, a cada 500ms
  • no tempo extra entre executar tarefas round robin (fazendo operações críticas não-tempo)

Um RTOS como este irá garantir uma chance de 99,9% de que seu código será executado no tempo. Eu não posso dizer 100%, porque o que operações o faz no seu de ISR pode interferir com o RTOS. Este é um problema com micro-controladores de 8 bits que só pode executar uma instrução de cada vez.

Escrevendo um RTOS é complicado, mas capaz de fazer. Aqui está um exemplo de pequenas (900 linhas) RTOS dirigida a plataforma AVR de 8 bits da Atmel.

A seguir é a Relatório e Código criado para a classe CSC 460:. Sistemas operacionais Tempo real (da Universidade de Victoria)

Uma solução simples é ter um temporizador de interrupção que dispara em 500ms ...
Se você tem alguma flexibilidade em seu design de hardware, você pode cascata a saída de um temporizador para um segundo contador palco para receber uma base de tempo longo. I esquecer, mas Lembro-me vagamente de ser capaz de temporizadores em cascata no x51.

Ah, mais uma alternativa para consideração - a arquitetura x51 permite que dois níveis de prioridades de interrupção. Se você tem alguma flexibilidade de hardware, você pode causar um dos pinos de interrupção externos a ser levantada pela ISR temporizador em intervalos de 500ms, e deixar que o de nível mais baixo de interrupção de processamento do seu código a cada-500ms a ocorrer.

Dependendo do seu x51 particular, você pode ser capaz de também gerar uma prioridade menor interrupção completamente interna para o seu dispositivo.

Veja a parte 11.2 neste documento que encontrei na web: http: / /www.esacademy.com/automation/docs/c51primer/c11.htm

Por que você tem uma rotina de tempo crítico que leva muito tempo para ser executado?

Concordo com alguns dos outros que pode haver um problema de arquitetura aqui.

Se o propósito de ter 500ms precisos (ou qualquer outro) intervalos é ter sinal de mudanças ocorrendo em intervalos de tempo específicos, você pode ser melhor fora com um ISR rápido que Ouputs os novos sinais com base em um cálculo anterior e defina um bandeira que faria com que o novo cálculo para executar fora do ISR.

Você pode descrever melhor o que esta rotina de longa duração está a fazer, e que a necessidade para o intervalo específico é para?


A adição com base nos comentários:

Se você pode garantir que o tempo na rotina do serviço é de uma duração previsível, você pode ir longe com falta o temporizador de interrupção postagens ...

Para tomar o seu exemplo, se a sua interrupção do timer é definido para 10 períodos ms, e você sabe sua rotina de serviço terá 89ms, basta ir em frente e contar até 41 interrupções do timer, em seguida, fazer a sua actividade 89 ms e perder oito interrupções do timer (42a - 49a).

Então, quando suas saídas ISR (e limpa a interrupção pendente), o "primeiro" interrupção da próxima rodada de 500ms ocorrerão cerca de um ms mais tarde.

Uma vez que você está "recurso maxed" sugere que você tem seus outros temporizador e interrupção fontes também em uso - o que significa que confiar no circuito principal a ser cronometrado com precisão não vai funcionar, porque aqueles outra interrupção fontes poderiam disparar no momento errado.

Se eu estou interpretting sua pergunta, você tem:

  • um loop principal
  • alguma operação de alta prioridade que precisa ser executado a cada 500ms, por um período de até 89ms
  • a 10ms temporizador que também executa um pequeno número de operações.

Há três opções como eu vê-lo. A primeira é usar um segundo temporizador de uma prioridade mais baixa para as suas operações de 500ms. Você ainda pode processar seus 10ms interromper, e, uma vez concluída continuar atendendo seus 500ms temporizador de interrupção.

A segunda opção - doe você realmente precisa para serviço de seus 10ms interromper cada 10ms? Ele está fazendo outra coisa senão manter o tempo? Se não, e se o seu hardware lhe permitirá determinar o número de 10ms carrapatos que passaram ao processar seus 500ms de op (ie., Não utilizando os próprios interrupções), então você pode começar o seu 500ms op de dentro dos 10ms interromper e processar o 10ms carrapatos que você perdeu quando você está feito.

Terceira opção: Para seguir a partir resposta de Justin Tanner, parece que você pode produzir o seu próprio kernel multitarefa preemptiva para preencher suas necessidades sem muita dificuldade. Parece que tudo o que você precisa é de duas tarefas -. Um para o ciclo de super principal e um para o seu 500ms tarefa

O código para trocar entre dois contextos (ie. Duas cópias de todos os seus registros, usando diferentes ponteiros pilha) é muito simples, e geralmente consiste de uma série de empurrões de registro (para salvar o contexto atual), uma série de registrar pops (para restaurar o novo contexto) e um retorno de instrução de interrupção. Uma vez que seus 500ms OP estão completos, você restaurar o contexto original.

(acho que estritamente este é um híbrido de multitarefa preemptiva e cooperativo, mas isso não é importante agora)


edit: Existe uma quarta opção simples. Liberalmente pimenta seu loop principal super com verifica se os 500ms tenha decorrido, tanto antes como após quaisquer operações longas. Não exatamente 500ms, mas você pode ser capaz de reduzir a latência para um nível tolerável.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top