A manutenção do tempo no Arduino usando millis () não é precisa ou correta?


9

Eu tenho usado o Arduino para gravar alguns dados. No meu esboço do Arduino, também usei a millis()função para acompanhar o tempo em que cada valor que estou medindo é obtido. No entanto, notei que o momento não está correto. Por exemplo, 30 segundos na vida real sai apenas em 10 segundos (exemplo inventado).

Estou correto ao dizer que a função de atraso do Arduino afeta o tempo de uso millis()? Em outras palavras, suponha que eu tenha um atraso de 50ms, isso significa que a millis()função também será interrompida por essa duração e continuará durante a conexão? Percebi isso quando tentei traçar alguns dados e descobri que a frequência dos picos nos meus dados era muito frequente, dado o tempo que passava. Então, eu quero saber se esse é o motivo dessa incompatibilidade de tempo e, em caso afirmativo, como faço para corrigir isso para manter o tempo em que cada amostra ocorre?

Para dar algum contexto aqui é o meu esboço:

#include <eHealth.h>    

unsigned long time;
// The setup routine runs once when you press reset:
void setup() {
  Serial.begin(9600);  
}

// The loop routine runs over and over again forever:
void loop() {

  float ECG = eHealth.getECG();
  time = millis();
  Serial.print(time);
  Serial.print(" ");
  Serial.print(ECG, 5); 
  Serial.println("");    

  delay(50);
}

Você está usando um dos quadros oficiais da Uno?
— Peter Bloomfield

11
O tempo real em vez dos valores inventados (um monitor serial que marca as linhas é o ideal) provavelmente ajudaria a descobrir o que está acontecendo.
— Ignacio Vazquez-Abrams

3
O cálculo de millis()é acionado por interrupção, portanto delay()não deve afetá-lo.
— microtherion

Eu tenho o mesmo problema, mas apenas quando o integro (millis ()) em código complexo. Eu acho que a complexidade do código afeta sua precisão no sentido de adiar cada vez mais a complexidade do código. Existe alguma maneira de evitar isso? talvez usando o módulo RTC separado?
— Josip7171

Respostas:


10

millis()é acionado por interrupção, portanto delay()não o afetará, pelo menos não em uma placa baseada no ATmega.

Isso não quer dizer que millis()seja totalmente preciso também. Cada marca do cronômetro não é exatamente 1ms, mas é 1.024ms. Este erro se acumula gradualmente até que uma correção seja feita. Isso pode ser visto na implementação do manipulador de interrupção TIMER0_OVF (timer 0 overflow).

Outra fonte de imprecisão é o próprio oscilador / cristal, que não é exatamente 16MHz. É bem perto, porém, e desde que a temperatura não mude muito, é relativamente estável.

O exposto acima significa que você pode demorar cerca de 1 ms ao usar millis(). Isso não soa como seu problema.

Outra questão em potencial seria o que getECG()está fazendo - pode ser muito lento.

float eHealthClass::getECG(void)
    {
        float analog0;
        // Read from analogic in. 
        analog0=analogRead(0);
        // binary to voltage conversion
        return analog0 = (float)analog0 * 5 / 1023.0;   
    }

analogRead() é lento, mas não tão lento a ponto de impactar um loop como este.

Outro problema que tenho visto é que as pessoas alteram a velocidade do relógio, mas não alteram corretamente o boards.txt. Isso significa que as constantes usadas na millis()implementação estão erradas e os horários estão errados.

Se você realmente deseja ler valores a cada 50ms, uma maneira muito melhor de implementar isso é fazer o seguinte

static long lastUpdate;

if (millis() - lastUpdate > 50)
{
    lastUpdate = millis();
    //Do stuff
}

Nós realmente precisamos ver os registros de data e hora que você está recebendo. Se você realmente vê 30s aparecendo como 10s, então há outra coisa em ação.


2
Observe que, para o Uno, o relógio não é movido a cristal, mas apenas usa um ressonador de cerâmica que é menos preciso que um cristal.
— jfpoilpret

@jfpoilpret Ah, bom saber. Observando o esquema , este seria o dispositivo CSTCE16M0V53-R0 Murata CERALOCK .
— 22814 Chris O em

Os ressonadores têm uma tolerância inicial baixa (geralmente de 0,5 a 2%) e baixa estabilidade de temperatura, mas se você calibrar os intervalos de tempo ao usá-los, eles podem ficar bem, desde que a temperatura não se mova.
— 9788 Cybergibbons

2
Millis () ainda funciona em um timer que marca a cada 1.024ms, mas eles adicionam compensação de erro, na forma de incrementar sempre que uma variável do medidor de erro fica muito alta. Eu acho que é o algoritmo de Roman Black, na verdade. Portanto, o tempo deve estar muito mais próximo de 1ms exatamente. github.com/arduino/Arduino/blob/master/hardware/arduino/cores/…
— EternityForest

Para aqueles que ainda estão interessados, veja o comentário que publiquei na resposta de JRobert, não queria responder com minha própria resposta, pois não tenho uma, apenas reformulei o problema.
— user3284376

2

Se as interrupções forem desativadas por qualquer eHealth.getECG()duração significativa da chamada de fração , millis()a contagem poderá ficar para trás. Caso contrário, você millis()deve retornar um tempo muito mais preciso do que os erros de 3x que você descreveu.

Você disse que o sinal amostrado parece ter uma frequência mais alta do que o esperado, o que pode acontecer se a taxa de amostragem for menor do que o pretendido. Você está assumindo uma taxa de amostragem de 20Hz? Seu loop pode demorar um pouco mais de 50 ms, o que você veria nos tempos impressos, mas eles ainda devem acompanhar o tempo do relógio. Se você não considerou isso, mas assumiu 50ms / amostra, verá uma aceleração aparente dos dados.

Se esse não for o problema, o próximo passo seria alternar uma saída enquanto você estiver dentro loop()e medir a frequência da onda quadrada resultante com um medidor de frequência (alguns DVMs baratos podem fazer isso) ou um escopo. Faça a mesma coisa com um vazio loop(). O primeiro experimento será sua taxa ou intervalo de amostragem real; o segundo dirá se millis()(ou seja, a frequência do timer0) é o que você esperava.


11
Eu andei brincando com isso e percebi que o problema não está no Arduino, a função millis () geralmente funciona muito bem, alguns dos valores não têm exatamente 8ms (atraso) de diferença, mas sim o que você disse que isso é esperado. O erro 3x que eu descrevi é verdadeiro no lado Python das coisas que eu estou usando para receber os dados. Qualquer idéia do que isso poderia ser o resultado, estou usando o pyserial do Python e é lento como o inferno.
— user3284376

Eu não sei o suficiente sobre sua implementação para lhe dar mais do que um palpite de 1/2, mas o lado do Python é lento o suficiente para soltar amostras?
— JRobert # 11/14

0

O mesmo aqui. Posso acrescentar que, se as interrupções forem desativadas, o tempo medido será "tempo real". Enfim, eu não entendo por que esse atraso, porque se o loop demorar muito, de qualquer maneira o millis () deve retornar valores em tempo real (apenas com mais distância entre cada valor)


11
A que se refere "mesmo aqui"? As respostas devem ser independentes, pois o StackExchange pode reordenar as coisas (diferente de um fórum). Portanto, "o mesmo aqui" pode significar qualquer coisa, dependendo de qual resposta / pergunta sua resposta aparece embaixo.
— Nick Gammon

Esta postagem seria mais apropriada como comentário, embora (reconhecidamente) você não tenha reputação suficiente.
— Greenonline

Desculpe, eu embora quando você responder alguma coisa, seu óbvio que está se referindo ao posto principal, caso contrário, seria um comentário
— user48711
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.