Estou tentando bit bang dados DMX e isso requer 4us pulsos. Não tendo muita sorte com os resultados, estou verificando o quão bom o Arduino está atrasando ... Parece ser terrível nisso.
Aqui está um pequeno teste rápido que eu fiz:
unsigned long ptime;
void setup() {
Serial.begin(9600);
}
void loop() {
ptime = micros();
delayMicroseconds(4);
Serial.println(micros() - ptime);
delay(1000); //just to keep the serial monitor from going nuts
}
E os resultados:
8 4 8 4 4 4 4 4 8 8 8 8 4 8 8 4 4 8 4 4 4 8 8 8 8 8 8 4
Fiquei meio chocado com o quão ruim é a precisão. É o dobro do tempo que eu queria adiar, mas nem mesmo é consistente com o que eu poderia dividir por 2!
Há algo que eu possa fazer para obter resultados corretos e consistentes?
arduino-mega
timers
time
bwoogie
fonte
fonte
Respostas:
Conforme explicado nas respostas anteriores, seu problema real não é a precisão de
delayMicroseconds()
, mas a resolução demicros()
.No entanto, para responder à sua pergunta real , existe uma alternativa mais precisa
delayMicroseconds()
: a função_delay_us()
do AVR-libc é precisa do ciclo e, por exemplo,Faça exatamente o que isso diz. A principal ressalva é que o argumento deve ser uma constante em tempo de compilação. Você precisa para
#include <util/delay.h>
ter acesso a esta função.Observe também que você deve bloquear as interrupções se desejar algum tipo de atraso preciso.
Edit : Como um exemplo, se eu fosse gerar um pulso de 4 µs no PD2 (pino 19 no Mega), procederia da seguinte maneira. Primeiro, observe que o seguinte código
produz um pulso de 0,125 µs (2 ciclos de CPU), porque é o tempo necessário para executar a instrução que define a porta
LOW
. Em seguida, adicione o tempo que falta em um atraso:e você tem uma largura de pulso com precisão de ciclo. Vale a pena notar que isso não pode ser alcançado com
digitalWrite()
, pois uma chamada para esta função leva cerca de 5 µs.fonte
Os resultados do seu teste são enganosos.
delayMicroseconds()
realmente atrasa com bastante precisão (para atrasos de mais de 2 ou 3 microssegundos). Você pode examinar seu código-fonte no arquivo /usr/share/arduino/hardware/arduino/cores/arduino/wiring.c (em um sistema Linux; ou em algum caminho semelhante em outros sistemas).No entanto, a resolução de
micros()
é de quatro microssegundos. (Veja, por exemplo, a página do garretlab sobremicros()
.) Portanto, você nunca verá uma leitura entre 4 microssegundos e 8 microssegundos. O atraso real pode demorar apenas alguns ciclos em 4 microssegundos, mas seu código o reportará como 8.Tente fazer 10 ou 20
delayMicroseconds(4);
chamadas seguidas (duplicando o código, não usando um loop) e depois relate o resultado demicros()
.fonte
digitalWrite()
, o que leva vários microssegundos para ser executado.delayMicroseconds()
? Não vejo isso melhor do que arredondar para baixo. ¶ Em relação à fonte de imprecisão, se a rotina for incorporada, o tempo depende do código circundante. Você pode ler as listagens de montagem ou desmontagem para ver. (Consulte “Efectuar a montagem listagens” seção na minha resposta à pergunta equivalente para PORTB em Arduino mega 2560 , o que pode mesmo assim ser relevante para o seu projecto bitbangingmicros()
tem uma resolução bem documentada de 4 µs.Você pode melhorar a resolução alterando o prescaler para o Timer 0 (é claro que mostra os números, mas você pode compensar isso).
Como alternativa, use o Temporizador 1 ou o Temporizador 2 com um pré-calibrador de 1, que fornece uma resolução de 62,5 ns.
Isso vai ser lento de qualquer maneira.
Sua saída é exatamente consistente com a resolução de 4 µs,
micros()
juntamente com o fato de que às vezes você recebe dois "ticks" e às vezes um, dependendo exatamente quando você iniciou o tempo.Seu código é um exemplo interessante de erro de medição.
delayMicroseconds(4);
atrasará cerca de 4 µs. No entanto, suas tentativas de medi-lo são falhas.Além disso, se ocorrer uma interrupção, isso aumentará um pouco o intervalo. Você precisa desativar as interrupções se desejar um atraso exato.
fonte
Quando medido com um osciloscópio, descobri que:
delayMicroseconds(0)
=delayMicroseconds(1)
= 4 μs de atraso real.Portanto, se você quiser um atraso de 35 μs, precisará:
fonte
A implementação do Arduino é bastante genérica, portanto, pode não ser tão eficaz em alguns aplicativos. Existem algumas maneiras de atrasos curtos, cada um com seus próprios déficits.
Use nop. Cada uma é uma instrução, portanto, 16 de nós.
Use tcnt0 diretamente. Cada um tem 4us, pois o pré-calibrador está definido como 64. Você pode alterar o pré-seletor para alcançar a 16ª resolução.
Use ticks, você pode implementar um clone de systick e usá-lo como base do atraso. Oferece resolução mais fina e precisão.
editar:
Usei o seguinte bloco para cronometrar as várias abordagens:
antes disso, eu havia redefinido o pré-calibrador timer0 para 1: 1 para que cada marca de TCNT0 seja 1/16 de um microssegundo.
espero que ajude.
fonte
TCNT0
leva 1 ciclo da CPU.