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);
}
user3284376
fonte
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.

Cybergibbons
fonte
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.

JRobert
fonte
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)

user48711
fonte
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