Sim kumandası yapalım ...

Sim kumandası yapalım ...

Sümer abi, neredeyse 1 hafta bu işi çözecek bir kütüphane aradım ama bulamayınca maalesef kütüphanesiz yazmak zorunda kaldım. Ama fena mı olurdu tak diye kütüphaneyi ekleyip 10 dakikada kodu yazmak ...

Aşağıdaki de Arduino SIM Joystick ile ve uçakla yaptığım ilk test uçuşlarımdan biri, yanlış anlaşılma olmasın uçakçı olduğum yok sadece kullanılması çok bir yetenek gerektirmediği için uçak kullandım :D :D :D (uçakçı arkadaşları tenzih ederim, bu ifade nacizane, nükte konusundaki anlayışına güvendiğim sahibine ithafen yazıldı :) ) ...
Bu VIDEOYU görmek için izniniz yok. Giriş yap veya üye ol
 
Sim kumandası yapalım ...

Zafer SAHIN' Alıntı:
Ama fena mı olurdu tak diye kütüphaneyi ekleyip 10 dakikada kodu yazmak ...

Fena ötesi, yıvranççç olurdu. Elalemin kütüphanesini boşver. Kendi kodun candır!

Zafer SAHIN' Alıntı:
Aşağıdaki de uçak ile yaptığım ilk test uçuşlarımdan biri, yanlış anlaşılma olmasın uçakçı olduğum yok sadece kullanılması çok bir yetenek gerektirmediği için uçak kullandım :D :D :D (uçakçı arkadaşları tenzih ederim, bu ifade nacizane, nükte konusundaki anlayışına güvendiğim sahibine ithafen yazıldı :) ) ...

Ehhh hadi bakalım öyle olsun... :p

Koda gelince. ISR çağrılma periyodunu tam anlamadım ama ben bu görev için interrupt kullanmazdım doğrusu. Ama illa kullanacağım diyorsan (ki demişsin) bari ISR içinde eriştiğin ppm değişkenini baştan VOLATILE olarak tanımla. :D (Gerçi array olunca gerekli mi bilemedim).

NOT: Şöyle bir tartışma buldum.
 
Sim kumandası yapalım ...

Sümer Yamaner' Alıntı:
Koda gelince. ISR çağrılma periyodunu tam anlamadım ama ben bu görev için interrupt kullanmazdım doğrusu. Ama illa kullanacağım diyorsan (ki demişsin) bari ISR içinde eriştiğin ppm değişkenini baştan VOLATILE olarak tanımla. :D (Gerçi array olunca gerekli mi bilemedim).
Sümer abi, okuduğumu doğru olarak anlamışssam VOLATILE değişken kullanılmasının amacı şu;

ISR veya benzeri interrupt rutinlerinde, loop veya diğer kullanıcı rutinleri içerisine gönderilecek değişkenler, kullanıcının onayı ve isteği olmaksızın değişebilir. Çünkü interrupt rutinleri kullanıcıyı asla beklemez ve zaten amacı da budur. Interrupt rutinleri içerisindeki değişkenler, anlık olarak değişebilecekleri gözönünde tutularak VOLATILE olarak tanımlanır ve eğer loop ve benzeri kullanıcı rutinleri içerisine taşınacaklarsa, GLOBAL değişkenlere kopyalanır. Kullanıcı rutinlerinin içerisine bu şekilde taşınır ve beklenmedik bir anda bu değişkenlerin değerinin değişme riski ortadan kalkar.

Yukarıdaki kodda ise ben loop'a hiç bir şey göndermiyorum. Tüm olanlar ve yeni darbenin oluşturulması ISR içerisinde olup bitiyor. Ayrıca minimum PPM darbesi genişliği 300us olduğu için, hepi topu 2 - 3 tane" IF - ELSE" içeren, ISR içerisindeki kodun tamamlanmama gibi bir riski yok. Hoş tamamlanmıyor olsa bile, OCR1A ile belirtilmiş yeni ISR(TIMER1_COMPA_vect) çağırılmış olsa bile, bir önceki bitmeden yenisi çalışmıyor, birebir denedim(yanlışlıkla :D ).

İlk denemelerimden biri olan aşağıdaki kodun temel amacı, ISR'ı her 1 mikrosaniyede bir çağırmak ve if - else ile statik bir PPM oluşturmaktı, ama her bir ISR'ın tamamlanması 1 mikrosaniye değil 40 mikrosaniye sürdüğünden sonuç beklenenden 40 kat fazla çıkıyordu :D. Ama ISR, OCR'ler vasıtası ile 40 mikrosaniye üzerinde bekletiliecekse hiç bir sıkıntı olmuyor...

Kod:
int iTicks;
int PPM_PIN = 9;

void setup() {
  pinMode(PPM_PIN, OUTPUT);
  cli();//stop interrupts
//set timer1 interrupt at 1Hz
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  // set compare match register for 1Mhz increments, 8 prescaler
  OCR1A = 1;// = (16*10^6) / (8*1*10^6) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS11 bits for 1 prescaler
  TCCR1B |= (1 << CS11); 
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
  sei();//allow interrupts
}

void loop() {
  // put your main code here, to run repeatedly:

}

ISR(TIMER1_COMPA_vect){//timer1 interrupt 1MHz toggles pin 9

  if (iTicks==0){
    digitalWrite(PPM_PIN,LOW);
  }  else if (iTicks==300) {
    digitalWrite(PPM_PIN,HIGH);
  }  else if (iTicks==1500) {
    digitalWrite(PPM_PIN,LOW);
  }  else if (iTicks==1800) {
    digitalWrite(PPM_PIN,HIGH);
  }  else if (iTicks==3000) {
    digitalWrite(PPM_PIN,LOW);
  }  else if (iTicks==3300) {
    digitalWrite(PPM_PIN,HIGH);
  }  else if (iTicks==4000) {
    digitalWrite(PPM_PIN,LOW);
  }  else if (iTicks==4300) {
    digitalWrite(PPM_PIN,HIGH);
  }  else if (iTicks==22500) {
    iTicks=-1;
  }
  iTicks++;
}

Bu arada açıklamaları bu kadar uzun olarak ziyadesiyle kendime yapıyorum Sümer abi :). Acaba bir yerde yanlış bir şey yaptım mı diye tekrar kontrol edebilmek için :D :D .
 
Sim kumandası yapalım ...

Bence ISR ve değişkenler konusunda kafan biraz karışmış. :)

Arduino compiler kodu işlerken bazı optimizasyonlar yapıyor ve bazı değişkenleri RAM'a yazmak yerine sürekli registerlerde tutarak zaman kazanıyor. Bu da ISR içinde erişilen (RAM'den alınan) değerin hatalı olmasına yol açabiliyor. Bu nedenle eğer bir değişken ana program loop'u içinde değiştiriliyor ve bu değere ISR içinden erişiliyor ise değişkenin VOLATILE olarak tanımlanması gerekiyor.
Dikkat edersen ppm değerlerini içeren array loop içinde değiştiriliyor sürekli. Yani stickler ve butonlar okunup bu array içine yazılıyor. SOnra ISR bu değerleri darbeye çeviriyor yani loop içinde değişen değerleri ISR okumak durumunda.
Kural olarak bu array'i VOLATILE olarak tanımlamak gerekli.
Diğer yandan şöyle bir durum daha var ki sadece tamamlayıcı bilgi olarak yazıyorum. Bu kodla bir alakası yok.
Eğer bir değişken (byte olmadığı sürece) ISR içinde değiştirilip loop içinde okunuyorsa iş iyice karışıyor. Çünkü en basitinden bir int türü değişkeni ele alalım. İki byte uzunluğunda. Diyelim ki bu değişken ISR içinde değiştirildi. Ama öyle oldu ki, loop içinde bu değişkeni okurken ilk byte okundu ve o arada interrupt geldi. Loop ikinci byte'ı interrupt dönüşünde okudu ama o arada değişkenin tümü değişmiş olduğu için data tamamen karıştı. İşte bunun çaresi de "atomic read". Yani interruptları kısa süreliğine kapatıp değişkeni okuyup tekrar açıyoruz.
ISR'yi birer mikrosaniye aralıklarla çağırmak bence iyi bir fikir değil. Çünkü ISR çağrılması sırasında işlemcinin yaptığı bazı temel işler var ve bunlar belirli bir zaman alıyor. ISR ne kadar sık çağrılırsa zamansal hassasiyetin o kadar düşmesi riskini de göze almak gerekiyor.
Ayrıca statik PPM'den kastın nedir anlamadım. PPM sinyali statik olmaz bence. Çünkü sekiz kanalın darbe süreleri değişkendir. Dolayısıyla önemli olan frame süresini (22.5 mS ya da 20 mS) tutturmaktır.
Küçük bir hatırlatma: Bir interrupt geldiği anda sistem interruptları kapatır. Ta ki ISR'den dönünceye kadar. Eğer çok gerekliyse ISR başlangıcında interruptları enable edebilirsin.
Bir de genel uygulamalardan birisi ISR başlangıcında status registerin yedeklenmesidir. ISR çıkışında da restore edilir. Bunun açıklaması olarak da Atmel işlemcilerin interrupta giderken status registeri kaydetmiyor oluşu söylenir. Tam olarak gerekli midir bilmem ama eğer ben bir programda interrupt kullanıyorsam ISR x = SREG; diye başlar ve SREG = x; diye biter.

Kişisel görüşüme göre bu uygulamada interrupt kullanmak kafa karışıklığı dışında bir fayda sağlamayacak. Çünkü...
22.5 mS aralıklarla tekrarlanan bir PULSE TRAIN oluşturmamız gerekiyor. 8 kanal için hesaplarsak toplam azami 8 * (2000 + 300) yani 18400 mS boyunca darbe oluşturmamız gerekiyor. Geriye kalan en az 4.1 mS süresinde ise SYNC PULSE veriyoruz. Bu 4.1 mS süre stick konumlarına göre (22500 - 8 * (1000 + 300)) yani 12.1 mS'ye kadar çıkabiliyor.
Dört analog potansiyometre ve dört digital girişin okunması için 4.1 mS fazlasıyla yeterli. Hatta hem ölçüm doğruluğu açısından analog girişler birkaç kez okunabilir hem de trimler için ayrıca kullanılacak butonlar okunur. Yine de zaman kalır. Dolayısıyla frame süresini tutturabilmek için micros() kontrollü, interruptsuz bir loop içinden çıkış darbeleri üretilebilir. Tamamen kişisel fikrimdir...
Tekrar ellerine sağlık.
 
Sim kumandası yapalım ...


A variable should be declared volatile whenever its value can be changed by something beyond the control of the code section in which it appears, such as a concurrently executing thread. In the Arduino, the only place that this is likely to occur is in sections of code associated with interrupts, called an interrupt service routine.
Kodda "its value can be changed by something beyond the control of the code section" şeklinde bir olay olabilmesi için benim anladığım, ISR'da tanımlanıp loopda okunması gerekiyor. Çünkü zamanı geldiği anda ISR, loop'u keserek kendini çalıştırıyor. Ve değeri ISR içerisinden sürekli değiştiriyorsam, loop daki önceki satırda kullandığım değişken ile bir sonraki tutmaz hale geliyor. Ama production kodunda, loopda atadığım değerler gayet kararlı, değişmesi için bir neden yok ...

So would an array that is modified by an interrupt routine and read by the main program still need to be declared volatile?
Production kodda, ISR içinde değiştirilip ana programa giden hiç bir şey yok.

Statik PPM konusunda ise belirtmek istediğim husus, yukarıdaki 1Mhz'lik kodun pre pre production olduğu. Hiç bir sensörden(pot anahtar v.b.) okumadan kendim değerleri sabit(statik) vererek bir PPM kodu oluşturmayı denedim ve "nasıl patladı" diye anlatmak için verdiğim bir örnekti :D . Ve bu ilk pre pre production kodu sayesinde, atmel328'de bir ISR'ı çağırdığım zaman, 40 mikrosaniyeden fazla süren bir OCR değeri belirlemem gerektiğini biliyorum :D . Benzer şeyleri deneyecek arkadaşlar, boşu boşuna denemesinler :D . Kod ise sonradan evrilerek production haline geldi.

Senin belirttiğin şeklindeki versiyonunu da yazacağım Sümer abi, ama bir PPM kütüphanesi yazmak istiyorsam(kodumu kütüphane haline getireceğim :D ), ISR kullanmak kendi adıma daha makul gözüküyor.
 
Sim kumandası yapalım ...

micros() kullanarak oldu mu, oldu ... Daha mı az karışık ? Hemen hemen aynı. Ama ISR kullanılmasına kıyasla eksileri var...

- Bir çok CPU döngüsü, delayMicroseconds yüzünden kullanılamıyor. Led ve minik bir LCD gibi şeyleri kontrol etmek için delayMicroseconds tarafından harcanan bu döngüler kullanılabilir.

-
On 16 MHz Arduino boards (e.g. Duemilanove and Nano), this function has a resolution of four microseconds (i.e. the value returned is always a multiple of four). On 8 MHz Arduino boards (e.g. the LilyPad), this function has a resolution of eight microseconds.
1000 / 4 = 250 adımlık bir hassasiyet, başlangıç seviyesinde bir çok kullanıcı için pek farkına varılabilir bir durum olmasa da 1000 adım kullanabilecekken 250 adım kullanmak bir eksi...

Kod:
#define CHANNELS 8        //set the number of chanels
#define ACTIVE_CH 6       //set the number of active channels, that has physical switch or knob v.b. ...
#define CENTER_VAL 1500   //set the default servo value
#define PPM_FRMLEN 22500  //set the PPM frame length in microseconds (1ms = 1000µs)
#define PPM_PLSLEN 300    //set the pulse length
#define ONSTATE 0         //set polarity of the pulses: 1 is positive, 0 is negative
#define SIGPIN 9          //set PPM signal output pin on the arduino

int ppm_val[CHANNELS];
int input_pins[CHANNELS] = {14,15,16,17,2,3,4,5};

unsigned long tPPMCurrent, tPPMPrev;

void setup(){
    tPPMCurrent = micros();
    tPPMPrev = tPPMCurrent;
    for (int i = 0;  i < CHANNELS; ++i) {
        if (i < 4) {
            pinMode(input_pins[i], INPUT);
            ppm_val[i] = map(analogRead(input_pins[i]), 0, 1023, 1000, 2000);
        } else {
            pinMode(input_pins[i], INPUT_PULLUP);
            ppm_val[i] = map(digitalRead(input_pins[i]), 0, 1, 1000, 2000);
        }
        if(i>(ACTIVE_CH - 1)) ppm_val[i]=CENTER_VAL; // Last 2 cahnnel is static ... If you have all 8 switches instead of 6, remove this line or adjust accordingly
    }
    pinMode(SIGPIN, OUTPUT);
    digitalWrite(SIGPIN, !ONSTATE);
}

void loop(){
    tPPMCurrent = micros();
    if (tPPMCurrent < tPPMPrev) tPPMPrev = tPPMCurrent; // Against micros() overflow after 70 munites...
    if (tPPMCurrent - tPPMPrev > PPM_FRMLEN) {
        tPPMPrev = tPPMCurrent;
        createPPMFrame();
    }
}

void createPPMFrame(){
    for (int j = 0;  j < CHANNELS; ++j) {
        digitalWrite(SIGPIN, ONSTATE);
        delayMicroseconds(PPM_PLSLEN);
        digitalWrite(SIGPIN, !ONSTATE);
        delayMicroseconds(ppm_val[j] - PPM_PLSLEN);
    }
    // SYNC FRAME
    digitalWrite(SIGPIN, ONSTATE);
    delayMicroseconds(PPM_PLSLEN);
    digitalWrite(SIGPIN, !ONSTATE);

    for (int i = 0;  i < CHANNELS; ++i) {
        if (i < 4) {
            analogRead(input_pins[i]);
            ppm_val[i] = map(analogRead(input_pins[i]), 0, 1023, 1000, 2000);
        } else {
            ppm_val[i] = map(digitalRead(input_pins[i]), 0, 1, 1000, 2000);
        }
        if(i>(ACTIVE_CH - 1)) ppm_val[i]=CENTER_VAL; // Last 2 cahnnel is static ... If you have all 8 switches instead of 6, remove this line or adjust accordingly
    }
}
 
Sim kumandası yapalım ...

Öncelikle delayMicroseconds() komutunun düşük çözünürlüğü gerçekten de ciddi bir sıkıntı. Tamamen haklısın. Ama belki bir while() içinde micros() takibi ile daha iyi bir sonuç alınabilir. Bundan emin değilim.
Volatile konusunda tam tersini anlıyorsun sanki. :)
Değişken ana loop içinde değiştirilip ISR içinde okunuyor. Değil mi? İşte VOLATILE tam da bu durumda gerekli.
Eğer değişken ISR içinde değiştirilip ana loop içinde okunuyor olsaydı o zaman "atomic read" gerekecekti.
 
Zafer SAHIN' Alıntı:
....Aşağıdaki de Arduino SIM Joystick ile ve uçakla yaptığım ilk test uçuşlarımdan biri, yanlış anlaşılma olmasın uçakçı olduğum yok sadece kullanılması çok bir yetenek gerektirmediği için uçak kullandım :D :D :D (uçakçı arkadaşları tenzih ederim, bu ifade nacizane, nükte konusundaki anlayışına güvendiğim sahibine ithafen yazıldı :) ) ...
Bu kadarını gördük ya, bu iş tamam bitmiştir artık :bravo: :halay: :yuppi:

Testler sırasında uçurması en kolay modeli yani uçağı seçmek kadar doğal bir şey olmaz zaten Zafer Hocam sen rahat ol :laugh: :D Netice de burada kodları kontrol ediyorsun. dolayısıyla bırakınca kendiliğinden de uçabilen bir model tercih etmek bence gayet akıllıca olmuş :lollol: :lol:
 
Sim kumandası yapalım ...

Canım kardeşim, önce söylediklerine bi bak...
"Model" diyorsun. Model ne demek?
* Manken, moda amacıyla poz veren ve çeşitli ürünleri sergileyen kişi
* Maket, bir nesnenin ölçekli bir kopyası veya prototipi
* Model (soyut), önceden haber veren formülün bir yaratımında kullanılan kavramsal veya soyut obje

Ancak, gerçeğinin kopyası olan bir şey "model" olarak tanımlanabilir. Havada bir şeyler yaptırdığınız ve tamamen hatalı olarak "helikopter" diye nitelendirdiğiniz şeyler "model" olmuyo! :p
Ayrıca...
Seksen beş eksene cayro, stabilizasyon sistemi cart curt takmadan uçursanıza şu vantilatörlerinizi sıkıyorsa... Sizin o nesnelere taktığınız elektronik donanımın onda birini ben uçağa taksam havalandırıp çay içmeye giderim! :p
 
Sim kumandası yapalım ...

Abicim sen de gayet iyi biliyorsun ki model helilerimizin calisma prensibi gercek (full scale) helilerle aynidir. İlla tipi de benzesin, olcekli olsun diyorsan scale heliler de var. Gercek helilerin 3D hareketlerini yapamamasi ise tamamen guc/agirlik orani, arti o kutleye dayanacak kadar saglam ve hafif bir malzeme olmamasindandir. Ayrica o g'lere dayanacak bir insan evladi da yoktur zannedersem.

Gyro olayina gelince, herhangi bir mekanik/elektronik stabilizasyon olmadan heli heli olmaz ki. Ucak kadar kisitli hareket kabiliyeti olsaydi tabi ki hicbir sisteme gerek kalmazdi. Sadece ileri gidebilen ve kendi stabilizatorlerini kendi uzerinde tasiyan bir aracla, her eksende hareket edebilen bir araci bu acidan karsilastiramazsin
Ayrica hicbir gyroya ihtiyac duymadan kumandayi birakip eldiven giyen, cay icen, tokalasip opusen, yanindaki arkadasina el sakasi yapabilen ucakci arkadaslar var, cok buyuk bisey degil yani [emoji1]
 
Sim kumandası yapalım ...

Sümer Yamaner' Alıntı:
...Sizin o nesnelere taktığınız elektronik donanımın onda birini ben uçağa taksam havalandırıp çay içmeye giderim! :p
Sümer abi uçakçı arkadaşlar bize farkettirmeden çılgın para harcıyorlar bu durumda. Çoğu zaten uçağı havalandırdıktan sonra çay içmeye gitmek için ortamı kollar durumda :lol: :lol: :lol:
 
Sim kumandası yapalım ...

Bu arada güzelim kumanda konusunun içine de ettiniz siz vantilatörcüler (ben değil!!!). Bi susun bakiim Zafer neler yapacak... :D :D :D
 
Sim kumandası yapalım ...

Bütün hayallerim yıkıldı! :(

Sadece delayMicroseconds() işlevi değil, tüm zamanlamalı işlevler sorunlu Arduino'da. Örneğin micros() işlevinde de çözünürlük 4 mikrosaniye. Ama hepsinden kötüsü, pulseIn() işlevinde de öyle!!! Artık Arduino IDE çöpe gidebilir. Registerlere doğrudan müdahale şart.

Zafer kardeşime bir sorum var. analogRead() işlevi ile interrupt ilişkisi nasıl biliyor musun? Yani bir ADC çevriminde gelen interrupt request'in işleme zamanlaması falan ve bunun analog okumaya etkileri var mı?
 
Sim kumandası yapalım ...

Sümer Yamaner' Alıntı:
Sadece delayMicroseconds() işlevi değil, tüm zamanlamalı işlevler sorunlu Arduino'da. Örneğin micros() işlevinde de çözünürlük 4 mikrosaniye. Ama hepsinden kötüsü, pulseIn() işlevinde de öyle!!! Artık Arduino IDE çöpe gidebilir. Registerlere doğrudan müdahale şart.
Sümer abi senin projelerin zaten çoktan "hobi sınıfı" projeler olmaktan çıkmıştı, bu lafınla da tescilledin :D .

Sümer Yamaner' Alıntı:
Zafer kardeşime bir sorum var. analogRead() işlevi ile interrupt ilişkisi nasıl biliyor musun? Yani bir ADC çevriminde gelen interrupt request'in işleme zamanlaması falan ve bunun analog okumaya etkileri var mı?
Maalesef, analogRead() işlevinin "blocking" olarak 100 mikrosaniye sürdüğü haricinde bir bilgiye sahip değilim.