Zamiast próbować aktualizować TCNT0, lepiej byłoby śledzić liczbę cykli utraconych w wyniku konwersji ADC i w procedurze pośredniej - np. omillis ()
- skompensować te cykle. [Edytuj: zobacz lepszą alternatywę poniżej]
Bardziej szczegółowo:
• Przy każdej konwersji dodaj około 128 · 13 cykli (lub być może 128 · 13.5, aby uwzględnić średnie opóźnienie preskalera) do licznika cykli:
dawno utracone cykle; ...; lostcycles + = ADCcycles;
• W ramach każdego wywołania omillis ()
powiedz return millis () + lostcycles / 16000
• Albo powiedz return millis () + lostcycles>>14
, aby uniknąć marnowania czasu na podział. W przypadku korekty byłoby to o 2% mniejsze, ale zakładam, że korekta jest niewielka, powiedzmy 5% całkowitego czasu, co prowadzi do około 0,1% błędu z powodu użycia przesunięcia zamiast dzielenia.
Re: „Przegapiłbym zdarzenie przepełnienia (tego, którego Arduino używa wewnętrznie do śledzenia czasu) za każdym razem, gdy konwersja ADC jest rozpoczynana z TCNT0> 230”
To nieprawidłowe [jeśli używasz sugerowanego podejście]. Nawet jeśli przepełnienie timera 0 zostanie obsłużone sto mikrosekund z opóźnieniem, zostanie obsłużone poprawnie. [Jak zauważono w komentarzu Edgara Boneta, metoda, która po prostu dodaje do TCNT0 bez sprawdzania przepełnienia arytmetycznego, jak w oryginalnym podejściu, może przeoczyć takt timera.]
Uwaga, przykładowy kod wykorzystujący SLEEP_MODE_ADC
w sekcji „Uśpij podczas czytania” w sekcji Konwersja ADC na Arduino na forum Gammon sugeruje, że należy sprawdzić przypadek, w którym występuje licznik czasu lub inne przerwanie podczas konwersji ADC. Podczas gdy SLEEP_MODE_ADC
zatrzymuje clk I / O , clk CPU i clk FLASH , opuszcza Timer 2, WDT, i kilka innych potencjalnych aktywnych źródeł przerwań.
Lepsza alternatywa:
Jak zauważa Gerben, nieco czystszym rozwiązaniem jest aktualizacja zmiennej count obsługiwanej przez millis ()
, zamiast używania procedury pośredniej. Więcej informacji znajdziesz w jego odpowiedzi na wcześniejsze pytanie. Oto adaptacja [nieprzetestowana] pomysłu do obecnego przypadku:
unsigned int lostcycles = 0; ...; void accountForADC () {enum {ADCcycles = 13 * 128 + 64; ms_cycles = 16000}; // dostosuj odpowiednio zewnętrzny volatile unsigned long timer0_millis; zgubione motocykle + = ADCycles; while (lostcycles > = ms_cycles) {// Ta pętla zwykle wykonuje tylko jeden przebieg bajtów statusReg = SREG; // Zapisz stan przerwania cli (); // Wyłącz przerwania ++ timer0_millis; // Potrzebujesz przerwania dla atomowości SREG = statusReg; // Przywróć stary status lostcycles - = ms_cycles; }}
Po uruchomieniu, przy dziesiątym wywołaniu funkcji accountForADC ()
ten kod doda 1 do timer0_millis
, aby uwzględnić milisekundę przegrane z konwersjami ADC. lostcycles - = ms_cycles
ustawi lostcycles
na 1280. Po kolejnych 9 rozmowach ponownie dodajemy 1 do timer0_millis
i zmniejszamy liczbę lostcycles
; i tak dalej.