As outras respostas são muito boas, mas quero elaborar como micros()
funciona. Ele sempre lê o cronômetro atual do hardware (possivelmente TCNT0
) que é constantemente atualizado pelo hardware (de fato, a cada 4 µs por causa do pré-calibrador de 64). Em seguida, ele adiciona a contagem de estouro do timer 0, que é atualizada por uma interrupção de estouro do timer (multiplicada por 256).
Assim, mesmo dentro de um ISR, você pode confiar na micros()
atualização. No entanto, se você esperar demais , perderá a atualização do estouro e o resultado retornado será reduzido (ou seja, você receberá 253, 254, 255, 0, 1, 2, 3 etc.)
Isso é micros()
um pouco simplificado para remover definições para outros processadores:
unsigned long micros() {
unsigned long m;
uint8_t oldSREG = SREG, t;
cli();
m = timer0_overflow_count;
t = TCNT0;
if ((TIFR0 & _BV(TOV0)) && (t < 255))
m++;
SREG = oldSREG;
return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
}
O código acima permite o estouro (ele verifica o bit TOV0) para que ele possa lidar com o estouro enquanto as interrupções estão desativadas, mas apenas uma vez - não há previsão para lidar com dois estouros.
TLDR;
- Não faça atrasos dentro de um ISR
- Se você precisar fazê-las, poderá cronometrar com
micros()
mas não millis()
. Também delayMicroseconds()
é uma possibilidade.
- Não demore mais de 500 µs ou mais, ou você perderá o estouro do temporizador.
- Mesmo pequenos atrasos podem fazer com que você perca os dados seriais recebidos (em 115200 baud, você obterá um novo caractere a cada 87 µs).
micros()
não é um ISR. É uma função normal. O sinalizador TOV0 permite testar o hardware para verificar se o estouro do temporizador ocorreu (mas ainda não foi processado).Não é errado usar
millis()
oumicros()
dentro de uma rotina de interrupção.Ele é errado usá-los incorretamente.
O principal aqui é que, enquanto você está em uma rotina de interrupção, "o relógio não está correndo".
millis()
emicros()
não mudará (bem,micros()
mudará inicialmente, mas assim que ultrapassar o ponto mágico de milissegundo em que é necessário um tique de milissegundo, tudo desmorona).Portanto, você certamente pode ligar
millis()
oumicros()
descobrir o horário atual no seu ISR, mas não espere que esse horário mude.É essa falta de mudança no tempo que está sendo avisado na cotação que você fornece.
delay()
depende demillis()
mudar para saber quanto tempo se passou. Uma vez que não muda,delay()
nunca pode terminar.Então, basicamente
millis()
emicros()
irá dizer-lhe o tempo quando seu ISR foi chamado , não importa quando, em seu ISR você usá-los.fonte
micros()
atualizações. Ele sempre lê o registro do temporizador de hardware.A frase citada não é um aviso, é apenas uma declaração sobre como as coisas funcionam.
Não há nada intrinsecamente errado em usar
millis()
oumicros()
em uma rotina de interrupção escrita corretamente.Por outro lado, fazer qualquer coisa dentro de uma rotina de interrupção incorretamente escrita é, por definição, errado.
Uma rotina de interrupção que leva mais do que alguns microssegundos para fazer seu trabalho é, com toda a probabilidade, gravada incorretamente.
Em resumo: uma rotina de interrupção escrita corretamente não causará ou encontrará problemas com
millis()
oumicros()
.Edit: Quanto a "por que micros ()" começa a se comportar de maneira irregular "", conforme explicado em uma página da Web " exame da função micros do Arduino ", o
micros()
código em um Uno comum é funcionalmente equivalente aIsso retorna um comprimento não assinado de quatro bytes, composto pelos três bytes mais baixos
timer0_overflow_count
e um byte do registro de contagem do timer-0.Ele
timer0_overflow_count
é incrementado cerca de uma vez por milissegundo peloTIMER0_OVF_vect
manipulador de interrupção, conforme explicado em um exame da página da função arduino millis .Antes de um manipulador de interrupção começar, o hardware do AVR desativa as interrupções. Se (por exemplo) um manipulador de interrupções fosse executado por cinco milissegundos com as interrupções ainda desativadas, pelo menos quatro estouros de timer 0 seriam perdidos. [Interrupções escritas no código C no sistema Arduino não são reentrantes (capazes de manipular corretamente várias execuções sobrepostas no mesmo manipulador), mas é possível escrever um manipulador reentrante de linguagem assembly que reativa as interrupções antes de iniciar um processo demorado.]
Em outras palavras, os estouros de temporizador não "empilham"; sempre que um estouro ocorre antes que a interrupção do estouro anterior tenha sido tratada, o
millis()
contador perde um milissegundo e a discrepância,timer0_overflow_count
por sua vez, também émicros()
equivocada por um milissegundo.Em relação a "menor que 500 μs" como limite de tempo superior para o processamento de interrupções, "para evitar o bloqueio da interrupção do timer por muito tempo", você poderia subir até 1024 μs (por exemplo, 1020 μs) e
millis()
ainda assim funcionaria, a maioria das Tempo. No entanto, considero um manipulador de interrupções que leva mais de 5 μs como preguiçoso, mais de 10 μs como preguiçoso, mais de 20 μs como caracol.fonte
micros()
"começar a se comportar de maneira irregular"? E o que você quer dizer com "rotina de interrupção escrita corretamente"? Eu suponho que significa "menor que 500us" (para evitar o bloqueio da interrupção do timer por muito tempo), "usando variáveis voláteis para comunicação" e "não chamando o código da biblioteca" o máximo possível, existe mais alguma coisa?