Temporização de alta precisão no Arduino para comunicação serial

11

Estou usando um Arduino Uno para enviar informações de tempo e tensão pela porta serial para o Python para plotar. No entanto, os intervalos de tempo entre carimbos de hora sucessivos parecem estar aumentando ao longo do tempo, afetando minha plotagem. Isso é especialmente verdade quando a taxa de transmissão é definida como 9600, onde minhas diferenças de tempo iniciais podem ser de 1320 e aumentam para 16400 após um período de tempo relativamente curto. Quando essa taxa é colocada no máximo em 115200 bps, a alteração é mais lenta e menos perceptível, de cerca de 1340 a 1500, mesmo após um período relativamente longo de envio. Todos os horários são dados em microssegundos.

Gostaria de saber se posso reduzir ou eliminar esse efeito e, se não entender, por que ele existe. Eu li coisas sobre interrupções e atrasos que causam isso, mas não aprecio completamente a complexidade dos eletrônicos disponíveis e gostaria de saber:

  1. Posso obter maior precisão no tempo?
  2. O que causa essa mudança no tempo?

Aqui está o que eu tenho atualmente:

#include <eHealth.h>

extern volatile unsigned long timer0_overflow_count;
float fanalog0;
int analog0;
unsigned long time;    

byte serialByte;
void setup() {
  Serial.begin(9600);
}

void loop() { 
  while (Serial.available()>0){  
    serialByte=Serial.read();
    if (serialByte=='S'){        
      while(1){
        fanalog0=eHealth.getECG();  
        // Use the timer0 => 1 tick every 4 us
        time=(timer0_overflow_count << 8) + TCNT0;        
        // Microseconds conversion.
        time=(time*4);   
        //Print in a file for simulation
        //Serial.print(time);
        //Serial.print(" ");
        Serial.print(fanalog0,5);
        Serial.print("\n");

        if (Serial.available()>0){
          serialByte=Serial.read();
          if (serialByte=='F')  break;
        }
      }
    }
  }
}
user3284376
fonte
O que você quer dizer com "preciso"? Os tempos indicados pelo contador serão razoavelmente precisos, precisos e com uma boa resolução. Deseja que os horários sejam determinísticos (ou seja, sempre os mesmos)?
Cybergibbons 11/03/14
Desculpas, sim, eu acho que é o que eu quis dizer, para a diferença entre eles para ser consistente e se não, a razão pela qual eles não são
user3284376
Adicione o registro de data e hora no final do PC, em vez do final do Arduino, ou use um módulo RTC (relógio em tempo real). Os módulos RTC são muito baratos para encontrar em várias lojas da web, basta garantir que a loja esteja vinculada à folha de dados. Outro método é programar um timer e usar uma rotina de serviço de interrupção para obter um tempo razoavelmente preciso.
jippie
O que eHealth.getECG()faz? Essa chamada sempre dura a mesma quantidade de tempo?
jfpoilpret
Você pode especificar quanto tempo dura um "período relativamente curto"? É sempre o mesmo depois de reiniciar o Arduino?
jfpoilpret

Respostas:

4

Use um timer e ISR (rotina de serviço de interrupção) para tornar o tempo mais preciso.

Veja minha Prova de conceito de interrupção cronometrada de 1 ms . A idéia é ter um batimento cardíaco razoavelmente preciso de 1ms no sistema que pode ser usado para acionar outros eventos. No PoC, é usado para piscar um LED a ½Hz, mas ter acesso às novas variáveis millisecondCountere secondCounterpermite disparar eventos no loop principal em momentos arbitrários (mas com tempo preciso).

jippie
fonte
2
Seu PoC é muito interessante, mas possui uma falha (fácil de corrigir) no fato de ler um valor de 2 bytes enquanto as interrupções são ativadas (in loop()), esse valor sendo modificado por um ISR. Pode acontecer que loop()leia um valor ruim (no meio de uma modificação pelo ISR). Eu publiquei um comentário no seu blog sobre isso.
jfpoilpret
@jfpoilpret um ponto interessante que você faz lá, nunca pensou em uma interrupção ocorrendo no meio do caminho para recuperar o valor da RAM. Vou verificar a desmontagem hoje à noite e atualizar o artigo. Talvez uma boa razão para escrever um outro artigo também: o)
jippie
Criei uma amostra do seu PoC e pude ver o problema ocorrer pelo menos uma vez a cada 10 segundos no meu UNO. Mas é claro que, na realidade, depende muito do que você faz loop(): minha amostra acabou de obter o valor de milissegundos, omite-o para o valor de leitura anterior e, se a diferença> 0 (diferente de redefinir o contador para 0), exibir uma mensagem.
Jfpoilpret 12/03/2014
@jfpoilpret nunca percebeu isso. Eu apenas o uso como um batimento cardíaco para monitorar os baldes de comida para meus gatos e fazer um flash LED quando meus gatos ficarem potencialmente decepcionados ...; o) Definitivamente mudará a maneira como eu uso ISRs no futuro.
jippie
1
Ele mostra um cristal conectado a um bloco ATMEGA16U2 e um ressonador conectado ao ATMEGA328P-PU. O 16U2 é para a interface serial, o 328P é "O Arduino". Curiosamente, o 16U2 seria capaz de pressionar o relógio para outro chip, por exemplo, o 328P.
Udo Klein
3

Posso pensar em algumas coisas que podem afetar a "consistência" dos tempos de gravação em série:

  • tamanho dos dados a serem impressos

isso pode ser a coisa mais óbvia em que pensar, mas, na verdade, quanto mais você imprimir, mais será necessário para lidar com isso.

Solução: imprima o formato da sequência em uma sequência de comprimento conhecido.

  • usando serial tamponada

no unix, você pode acessar a porta serial usando uma maneira em buffer ou sem buffer. Usar o modo de buffer por um longo tempo pode torná-lo um pouco mais lento à medida que o buffer é preenchido, geralmente acontece quando os dados são recebidos mais rapidamente do que você está lendo…

Solução: use a linha serial sem buffer ( por exemplo : no Darwin / OSX, em /dev/cu.usbmodemXXXvez de /dev/tty.usbmodemXXX)

  • prioridade dos temporizadores

parece que você está usando uma interrupção de TC e os AVRs têm prioridades na maneira como as interrupções são tratadas, não sei a ordem de prioridade do Atmega328 e não é um dos recursos mais documentados, por isso não sei quão seguro é o TC0 versus a interrupção UART.

Solução: consulte mais detalhadamente na documentação / folha de dados sobre prioridades de interrupção e altere o cronômetro, se necessário; e / ou faça um teste sem ter o outro timer em execução.

  • os dados dos quais você está lendo levam mais tempo para ler ao longo do tempo

alguns drivers precisam calcular a média ou executar algumas operações em relação aos valores anteriores. Portanto, quanto mais valores você mede, mais longo o buffer e mais demorado para calcular o valor, até atingir o tamanho máximo do buffer.

Solução: observe o código-fonte da biblioteca que você está usando e otimize-o, remova o cálculo se houver um ou leve em consideração esse tempo de processamento crescente.

  • evitando a sobrecarga da estrutura do arduino

mas se você realmente deseja otimizar a saída serial do arduino, evite usar a sobrecarga do arduino ... Mas é muito menos elegante e confortável de usar.

Tenho certeza de que há outros pontos que estou perdendo, mas essas são as primeiras coisas que eu verificaria antes de prosseguir.

HTH

zmo
fonte
2

Seu código inclui a duração da saída em medições subsequentes. Assim, dependendo do comprimento da saída, você medirá diferentes tempos. Isso pode ser corrigido através da formatação na saída de comprimento fixo.

A próxima questão é que a ONU tem uma base de tempo muito ruim. Veja aqui uma comparação dos diferentes tipos de Arduino versus a referência de tempo do DCF77.

Conclusão: se você precisar de tempo preciso, adquira um Arduino com cristal ou opte por um RTC. Eu recomendo os RTCs DS3231 / DS3232, pois eles geralmente atingem uma precisão de 2 ppm imediata.

Udo Klein
fonte