Soru:
ADC_sleep modunu kullanırken doğru millis () nasıl korunur?
FarO
2016-10-10 22:07:18 UTC
view on stackexchange narkive permalink

millis (), zamanı saymak için timer0 (CPU saatine bağlı) kullanır, ancak ADC_sleep modu CPU saatini durdurur, bu nedenle, ADC_sleep modunda gerçekleştirilen her ADC dönüşümünden sonra milis () kayar (geride kalır).

ADC dönüşümü için gereken standart CPU döngüsü sayısıyla (ADC ön ölçekleyici = 128, ADC saat döngüleri ile çarpılır = 13) ve zamanlayıcı0 (64) için standart ön ölçekleyici ile, milis () 13,5 tik kaybediyor gibi görünür ( ADC_sleep modunda gerçekleştirilen her ADC dönüşümü için yaklaşık 0,1 ms).

timer0 yazmacını (TCNT0) manuel olarak 14 artırarak güncelleyebilirdim, ancak her şeyden önce hala 0.5 tıklama kayması (veya biraz daha az, çünkü güncelleme işlemi bazı döngüler alacaktır), ikinci olarak, ADC dönüşümü TCNT0> 230 ile her başlatıldığında taşma olayını (Arduino'nun dahili olarak zaman izleme için kullandığı olay) kaçırırdım.

Nasıl ADC dönüşümleri sırasında ADC_sleep modunu kullanmaya devam etmeliyim, ancak yine de milis () değerini doğru tutmalı mıyım?

Bu SLEEP_MODE_ADC ile mi ilgili? Ve hangi AVR / Arduino hakkında? Olası bir çözüm, Arduino millis-ISR için Timer2 kullanmaktır. Örnek için Cosa RTT'ye bakın; https://github.com/mikaelpatel/Cosa/blob/master/cores/cosa/Cosa/RTT.hh (ve RTT.cpp, RTT_Config.hh dosyaları)
Dört yanıtlar:
James Waldby - jwpat7
2016-10-10 23:24:22 UTC
view on stackexchange narkive permalink

TCNT0'ı güncellemeye çalışmak yerine, ADC dönüşümlerinde kaybedilen döngülerin sayısını izlemek ve bir ara rutinde - örneğin, omillis () - bu döngüleri telafi etmek daha iyi olabilir. [Düzenleme: Aşağıda daha iyi bir alternatif görün]

Daha ayrıntılı olarak:

• Her dönüşümde yaklaşık 128 · 13 döngü (veya belki de 128 · 13.5, ortalama ön ölçekleyici gecikmesini hesaba katmak için) bir döngü sayacına:
uzun kayıp çevrimler; ...; lostcycles + = ADCcycles;

• Her omillis () çağrısında, return millis () + lostcycles / 16000 p deyin >

• Veya bir bölümde zaman kaybetmemek için return millis () + lostcycles>>14 deyin. Bu, düzeltmede% 2 oranında düşecektir, ancak düzeltmenin küçük olduğunu varsayıyorum, örneğin toplam sürenin% 5'i, bölme yerine bir kaydırma kullanılması nedeniyle yaklaşık% 0.1 hataya yol açıyor.

Re: “ADC dönüşümü TCNT0> 230 ile her başlatıldığında taşma olayını (Arduino'nun dahili olarak zaman izleme için kullandığı olay) kaçırırdım”

Bu yanlış [önerilen kullanıyorsanız yaklaşmak]. Zamanlayıcı 0 taşması yüz mikrosaniye gecikmeli olarak ele alınsa bile, sorunsuz olarak ele alınacaktır. [Edgar Bonet'in yorumunda belirtildiği gibi, orijinal yaklaşımda olduğu gibi aritmetik taşmayı kontrol etmeden TCNT0'a eklenen bir yöntem, bir zamanlayıcı işaretini kaçırabilir.]

SLEEP_MODE_ADC Arduino'daki ADC dönüşümünün "Okuma sırasında uyku" bölümünde bulunan / code>, bir ADC dönüşümü sırasında bir zamanlayıcı veya başka bir kesintinin meydana geldiği durumu kontrol etmesini önerir. SLEEP_MODE_ADC clk I / O , clk CPU ve clk FLASH 'ı durdururken, Timer 2, WDT, ve diğer bazı olası kesinti kaynakları etkin.

Daha iyi bir alternatif:

Gerben'in işaret ettiği gibi, ara bir rutin kullanmak yerine millis () tarafından tutulan count değişkenini güncellemek biraz daha temizdir. Arka plan için önceki bir soruya verdiği cevaba bakın. İşte fikrin mevcut duruma uyarlanması [denenmemiş]:

  unsigned int lostcycles = 0; ...; void accountForADC () {enum {ADCcycles = 13 * 128 + 64; ms_cycles = 16000}; // harici uçucu işaretsiz uzun timer0_millis'i uygun şekilde ayarlayın; kayıp çevrimler + = ADCcycles; while (lostcycles > = ms_cycles) {// Bu döngü genellikle yalnızca bir geçiş çalıştırır byte statusReg = SREG; // kesinti durumunu kaydet cli (); // kesintileri kapat ++ timer0_millis; // Atomiklik için kesintilerin kapatılması gerekiyor SREG = statusReg; // Eski durum kayıp döngülerini geri yükleyin - = ms_cycles; }}  

Başladıktan sonra, onuncu accountForADC () çağrısında bu kod bir milisaniyeyi hesaba katmak için timer0_millis 'e 1 ekler ADC dönüşümlerinde kaybetti. lostcycles - = ms_cycles , lostcycles 'ı 1280 olarak ayarlar. Başka 9 aramadan sonra tekrar timer0_millis ' e 1 ekler ve kayıp döngüleri ; ve benzeri.

Sonunda, dönüşüm sayısını takip etmek de benim fikrimdi, onay için teşekkürler (seninki benim fikrimden daha temiz)
@OlafM, teşekkürler! Not, eklenecek döngüleri yanlış hesapladım - 108 us yanlış birim; Kullanılacak sayı 13 * 128 = 1664 döngüdür.
OP'nin orijinal fikrine göre, `TCNT0 + = 26; 'yaparsanız ve _that_ operasyon taşarsa, kesinlikle taşma olayını kaçırabilirsiniz.
@EdgarBonet, doğru! Veri sayfasının uyardığı bir şey.
Benim fikrim yazdıktan sonra sahip olduğumdan, yani dönüşüm sayısının 0,1 ms katını çıkarmaktan bahsediyordum. Bu, dönüşümleri takip eden * 1664/16000 daha doğrudur.
@OlafM: Ön ölçekleyiciyi sıfırlamadıkça, ADC dönüşümü ADSC'den ADIF'e (1664 + 0 ile 127 arasında bazı bilinmeyen sayılar) CPU döngüleri alacaktır. C.f. veri sayfasının 24.4 bölümündeki zamanlama diyagramları.
Tüm "milis" çağrısını değiştirmek zorunda kalmak istemiyorsanız, aslında "milis" tarafından kullanılan değişkeni artırabilirsiniz. [Cevabımın altına] bakın (http://arduino.stackexchange.com/a/22997/2881).
@Gerben, teşekkürler; Cevabın sonunda bu yaklaşımı düzenledim
@jwpat7, "timer0_millis" 4 bayt uzunluğunda olduğundan, bir ISR'de güncellendiğinden, bunu atomik olarak güncellemeniz gerekir veya kodunuzun ortasında 4 baytı değiştiren timer0 kesmesi olursa garip şeyler olabilir. Kodumda bunun için kesintileri geçici olarak devre dışı bıraktım.
@Gerben, bunu belirttiğiniz için teşekkürler ... cevabı buna göre düzenledik
Edgar Bonet
2016-10-10 23:14:30 UTC
view on stackexchange narkive permalink

Nasıl yapacağımı gerçekten bilmediğim için, istediğini nasıl yapacağını söylemeyeceğim. Ama bunun yapılabileceğine dair içimden bir his var. Yalnızca gerçekten çok zor olurdu, bu yüzden şu soruyu getiriyor: Buna değer mi?

Tamamen fikirlere dayalı bir yanıt yayınlamaktan kaçınmak için, bazı kişiler:

  • Zamanlayıcı 0'ı tek CPU döngü çözünürlüğünde, yeni bir değer belirlediğinizde ön ölçekleyiciyi sıfırlayarak ayarlayabilirsiniz.
  • Çok hassas gecikmeler yapabilirsiniz. _delay_us () işlevi ile. Argüman bir derleme zamanı sabiti olmalıdır. _delay_us (0.125) , söylediği şeyi tam olarak yapar.
  • ADC saatinin ön ölçekleyicisine dikkat edin: siz de sıfırlamadıkça ADC zamanlamalarında bir miktar titremeye neden olacaktır. AFAIK, bu ön ölçekleyiciyi sıfırlamanın tek yolu, otomatik tetikleme modunda bazı tetikleyici kaynakları kullanarak ADC dönüşümünü başlatmaktır. Bu size bir zamanlayıcıya mal olabilir.
  • Taşma olayını kaçırırsanız, ilgili ISR'yi ( TIMER0_OVF_vect () , ISR (TIMER0_OVF_vect ); ). Kulağa aptalca geliyor, ancak geri dönüşte kesintileri zorla mümkün kıldığı tek uyarı ile işe yarıyor.

Tam olarak kaç tik kaybettiğinizi bilmek için, bir Birkaç akşam veri sayfasındaki zamanlamaları dikkatlice inceleyin veya kararlı bir frekans kaynağına göre kalibre etmeyi deneyebilirsiniz. İkincisini tavsiye ederim.

Gerben
2016-10-11 01:24:51 UTC
view on stackexchange narkive permalink

Veri sayfasını yanlış okuduğunuzu düşünüyorum. Dönüşüm 13.5 değil, 13 ADC döngüsü alır (birincisi hariç).

İkinci olarak; dönüşüm doğrudan değil, ADC saatinin bir sonraki yükselen kenarında başlar. Bu nedenle, ön ölçekleyici 128 olduğundan, bir sonraki ADC saatinin yükselen kenarından önce ek 127 ana saat döngüsü alabilir. Yani bu, ADC dönüşümünün 13 ile 13.9921875 ADC saat döngüsü sürdüğü anlamına gelir.

uC'nin CPU saatini yeniden başlatmasının ne kadar sürdüğünden de emin değilim. Yani burada "kaçırılan" daha fazla zamanım olabilir.

Kristal osilatörün başlangıçta o kadar da doğru olmadığını bilmelisiniz.

Aklımda, ADC gerçekten başladığında CPU saatinin duracağını düşündüm, bu nedenle "0'dan 127'ye kadar" gecikme döngüleri dikkate alınmadı. Bundan daha karmaşık olduğunu görüyorum.
Nick Gammon
2016-10-11 04:43:07 UTC
view on stackexchange narkive permalink

Bir dönüştürme başlatma süresi sabit değildir. Zamanlayıcılarla ilgili sayfamdan şunu görebiliriz:

Tip:”

Bir dönüşüm başlar. Dönüşüm, kodun istediği anda değil, ADC saatinin ön ucunda başlar. 128 ölçekleyici olması durumunda, donanımın bir sonraki ADC saat döngüsünü beklemesi gerektiğinden 127 ekstra (işlemci) saat döngüsü eklenebilir. Bu, dönüştürme süresine 7,938 µs ekleyebilir.

Ön ölçekleyici, ADEN'e 0 yazarak sıfırlanabilecek gibi görünüyor. Veri sayfasına göre:

Ön ölçekleyici, ADCSRA'da ADEN bitini ayarlayarak ADC'nin açıldığı andan itibaren saymaya başlar. Ön ölçekleyici, ADEN biti ayarlandığı sürece çalışmaya devam eder ve ADEN düşük olduğunda sürekli olarak sıfırlanır.

Bunu yaparsanız, o zaman olduğu gibi dönüşüm süresine eklersiniz. şimdi 13 yerine 25 ADC saat döngüsü alın.


  • Başkalarının da önerdiği gibi, eğer istediğiniz zaman doğru ise, o zaman yanıt harici bir saat çipi olabilir. Çip ve parçaların yaklaşık bir dolara gelmesi muhtemeldir.

  • Bir alternatif, harici bir ADC yongası almak ve böylece gürültü azaltma sorununu bir çipe taşımak olabilir. (umarız) bununla başa çıkmak için tasarlanmıştır.

  • Diğer bir olasılık, Zamanlayıcı 1'e harici bir saat girişi koymak ve bunu eşzamansız olarak çalıştırmak olabilir. Veri sayfasını okuduğumda, bu zamanlayıcı ADC uyku modunda çalışmaya devam edecek ve böylece bunu zamanlama için kullanabilirsiniz. (Bu, millis () ve micros () için yardımcı olmaz, ancak Timer 1'i kullananlara benzer bir şey yazabilirsiniz).

    Düzenle - Edgar Bonet'in işaret ettiği gibi dışarı, veri sayfasını yanlış okudum. Bunu yapabilen Timer 2'dir, Timer 1'i değil. Zamanlayıcıyı eşzamansız olarak çalıştıran pinler Uno'daki PDIP çipinde değildir ve Mega üzerindeki kart pinlerine çıkarılmaz.

Sadece Timer 2, en azından Uno ve Mega'da asenkron olarak çalışabilir. Ve bu kartlarda asenkron modu kullanamazsınız çünkü gerekli pinler (TOSC1 / TOSC2) mevcut değildir.
Hata! Veri sayfasını yanlış okuyun. Cevabımı düzelttim, teşekkürler.


Bu Soru-Cevap, otomatik olarak İngilizce dilinden çevrilmiştir.Orijinal içerik, dağıtıldığı cc by-sa 3.0 lisansı için teşekkür ettiğimiz stackexchange'ta mevcuttur.
Loading...