Soru:
Arduino'da uçucu değişkenler nasıl doğru şekilde kullanılır?
daltonfury42
2015-12-17 00:00:53 UTC
view on stackexchange narkive permalink

Arduino Uno ile küçük bir proje yapıyordum. Diferansiyel tekerlek sisteminin ne kadar ileri gittiğini ölçmek için kodlayıcıları kullandığım için kesintiler içeriyordu. Robotum yalnızca ileri doğru hareket ediyor. Bu yüzden her kodlayıcıdan yalnızca tek bir kanal kullanıyorum. İşte iki kesme rutinim:

  ISR (INT0_vect) {encoderRPos = encoderRPos + 1; } ISR (INT1_vect) {encoderLPos = encoderLPos + 1;}  

encoderRPos ve encoderLPos değişkenleri uçucu tiptedir int . Herhangi bir kesme rutininde değişime uğrayan değişkenlerin uçucu tipte olması gerektiğini anlıyorum. Bu, kodun bu değişkenleri kullanan diğer bölümlerini her an değişebileceği konusunda uyarmak içindir.

Ancak kodumda olanlar biraz tuhaftı ve bunu açıklayamadım. Sol tekerleğin hareket ettiği mesafeyi şu şekilde hesaplıyorum:

  #define distancePerCount 0.056196868 float SR = distancePerCount * (encoderRPos - encoderRPosPrev); float SL = distancePerCount * (encoderLPos - encoderLPosPrev); encoderRPosPrev = encoderRPos; encoderLPosPrev = encoderLPos;  

Ancak aşağıdakini seri monitörümde yazdırdığımda bir anormallik fark ediyorum:

enter image description here

Üçüncü sütuna bakarsanız, (SL) değeri bir süreliğine çok yüksek. Bu tüm hesaplarımı altüst ediyor.

Aldığım SL'nin değerini (3682) alırsam elde edebileceğim tek ipucu, ki bu her zaman sabittir ve geri hesaplar (encodeLPos - encoderLPosPrev) unsigned int 'in maksimum değerine yakın olan 65519.66 değerini alacağım. Bu da (encoderLPos - encoderLPosPrev) 'nin bir taşmaya neden olduğu ve farkı alınan değerlerin ikisi de sadece 5000 civarında olduğu anlamına geliyor!

Ve ben bunu çözmeyi başardım. Şans eseri oldu. Kodu şu şekilde değiştirdim:

  static int encoderRPosPrev = 0; statik int kodlayıcıLPosPrev = 0; int diffL = (encoderLPos - encoderLPosPrev);
int diffR = (encoderRPos - encoderRPosPrev); float SR = distancePerCount * diffR; float SL = distancePerCount * diffL; encoderRPosPrev = encoderRPos; encoderLPosPrev = encoderLPos;  

Ne olduğunu anlayamıyorum. Uçucu değişkenlerle ilgili bilmem gereken bir şey var mı?

Güncelleme: Bir göz atmak isterseniz, kodun tamamı burada. Ve kabul edilen yanıtta önerilen şeye değiştirdikten sonra da çok iyi çalışıyor.

Sorunuz, üçüncü çıktı sütununun ne olduğunu söylüyor ... diğer sütunlar nelerdir? Lütfen soruyu düzenleyin ve sütun başlıkları ekleyin
@jwpat7 Onları kasıtlı olarak kaldırdım çünkü bu sadece okuyucunun kafasını karıştıracaktır. Ancak soru zaten Majenko tarafından iyi yanıtlandı.
Parçacıklarınızdan detaylı cevaplar vermek zordur. `neden rastgele olmadığını, ancak kodu her çalıştırdığımda belirli bir zamanda olduğunu açıklayabilir misin? Ayrıca neden belirli bir değer veriyor? `` - Kodun tamamını görseydim muhtemelen bunu yapabilirdim. Bu arada şunu okuyun: http://www.gammon.com.au/interrupts
@NickGammon İşte gidin: http://paste.ubuntu.com/14085127/
`3683 / .056196868 = 65537` yani yanlış zamanda artmış gibi görünüyor, değil mi? Bu kodda bir kesmede birden çok kez değiştirilebilecek bir değişkene erişiyorsunuz, bu nedenle kesmeler kapalıyken yerel bir kopya almak çok daha güvenli olacaktır.
@NickGammon Evet, sonunda bunu yaptım.
Bir cevap:
Majenko
2015-12-17 00:08:37 UTC
view on stackexchange narkive permalink

Kritik bölümler hakkında bilgi edinmeniz gerekiyor.

Muhtemelen olan şey, değişkenlerin hesaplamaların ortasında kesme rutinleri tarafından değiştirilmesidir. 'Düzeltmeniz' uçucu değişkenlerle hesaplama yapmak için harcanan zamanı azaltır, böylece bir çarpışma olma olasılığını azaltır.

Yapmanız gereken, geçici değişkenleri bunun için devre dışı bırakılan kesintilerle yerel değişkenlere kopyalamak kısa süre.

  cli (); int l = encoderLpos; int r = encoderRpos; sei ();  

Çünkü Arduino 8 bitlik bir CPU 16 bitlik değerler üzerinde matematiksel işlemler gerçekleştirmek için birden fazla montaj talimatı alır. Kayan nokta, basit bir ekleme için birçok talimat kullanıldığında daha da kötüdür. Bölme ve çarpma çok daha fazla kullanır. Bir kesintinin, bu talimat listesi sırasında ateş etmek için bolca fırsatı vardır. Bunun gibi bir atama yaparak ve ardından hesaplamalarınızda yeni yerel değişkenleri kullanarak, uçucu değişkenlerle başa çıkmak için gereken talimatlar mutlak bir minimumda tutulur. Atama sırasında kesintileri kapatarak, değişkenleri kullanırken asla değiştirilemeyeceğinizi garanti edersiniz. Bu kod pasajına kritik bölüm denir.

Bu sadece durum olabilir. Merak ediyorum, bunun neden rastgele olmadığını ama kodu her çalıştırdığımda belirli bir zamanda olduğunu açıklayabilir misiniz? Ayrıca neden belirli bir değer veriyor?
İşte cli / sei'ye büyük bir referans. http://www.nongnu.org/avr-libc/user-manual/optimization.html#optim_code_reorder. Bellek engeli ile birlikte, yukarıdaki kodda uçucu beyana gerçekten ihtiyaç duyulmaz. İşte bu konuyla ilgili eğlenceli bir okuma. https://www.kernel.org/doc/Documentation/volatile-consaceful-harmful.txt
@MikaelPatel Nice, ancak MCU'lar için o kadar önemli değil. Bu durumda, derleyicinin kullanılmadığını düşündüğü durumları optimize etmesini önlemek için uçucu gereklidir (değer asla değişmez). Cli / sei, atomik WRT işlemini yürüten diğer tek iş parçacığı (kesmeler) yapmak için oradadır.
Kodu uçucu olan ve olmayan şekilde derlemeyi denediniz mi? Ancak kritik bölümle (cli / sei). Tartışmaya çalıştığım şey, bellek engeli kavramı ve bunun, değişkenleri geçici olarak bildirmek zorunda bırakılarak derleyiciden nasıl geçici erişim (ve doğru sıralama) sağladığıdır. Çoğu programcıya, bir ISR'de erişilen herhangi bir değişkenin geçici olarak beyan edilmesi gerektiği öğretilir, ancak bu hikayede çok daha fazlası var.
Derleyicinin cli () ve sei () 'nin ne yaptığı ve optimize edilmemesi gereken değişkenlerin optimizasyonu gibi şeyleri nasıl etkileyeceği konusunda fazla bir fikri olduğunu sanmıyorum. Tüm sei () ve cli () do, kendi yazmacındaki global kesme etkin bayrağını değiştirmektir. Kod akışı için hiçbir şey yapmazlar.
Sonuçta, sadece bir montaj talimatına eşlenirler: "# define sei () __asm__ __volatile__ (" sei ":::" bellek ")"
Derleyiciye aynı zamanda bir bellek engeli olduğunu söyleyen sihirli kelime "hafıza" dır. https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html
Bu, talimatların yeniden sıralanmasını durdurur. Şüpheli ölü kodun tamamen kaldırılmasıyla hiçbir ilgisi yoktur.
@MikaelPatel İşte uç bir örnek (uç nokta, bu yüzden gösterdikten sonra yaptığım optimizasyonu zorluyor): http://pastebin.com/u9bfiN4R
@Majenko Bu uç bir örnekti. Hem volatile hem de cli / sei'yi denediniz mi? Bir kontra örnek Cosa / RTT https://github.com/mikaelpatel/Cosa/blob/master/cores/cosa/Cosa/RTT.cpp'dir, uçucu yoktur. Bunun yerine tüm erişimler senkronize edilir.


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