Servo sinyali üretilmesi konusunda yardıma ihtiyacın var... (Zafer Kardeşim...)

Sumer Yamaner

Moderator
Katılım
17 Eyl 2013
Mesajlar
9,084
Tepkime puanı
23,743
Yaş
62
Konum
İstanbul
Web sitesi
www.sumeryamaner.com
İlgi Alanı
Uçak
Arduino IDE'nin zamanlama işlevlerinin 4 mikrosaniye hassasiyetinde olması bugüne kadarki birçok uygulamamın tam olması gerektiği gibi çalışmadığını gösterdi. Tamam belki dışarıdan bir fark hissedilemiyor ama belli ki servo sinyal çözünürlüğü 1024 yerine 256 adımda kalıyor.
Bunun üzerine Arduino Uno kullanarak 1 hatta 0.5 mikrosaniye hassasiyette PWM darbesi üretmeye çalıştım ve başardım. Çünkü Atmega328 işlemcinin 16 bitlik Timer1'ini kullanınca bu iş çok kolay. Ama örneğin yükzek çözünürlükte çalışacak basit bir servo reverser için bir Atmega harcamak bana mantıklı elmiyor. En büyük aşkım olan Attiny85 bu tür işler için biçilmiş kaftan. Am onda da 16 bit timer yok.
Çözüm şu: 16 bit timer yerine 8 bit timer kullanıp, timer'ın kaç kez overflow olduğunu sayarak bir tür 8 bit 16 bit dönüşümü gerçekleştirmek.
Planım şöyle idi: Yeni bir devre ile uğraşmak yerine, Arduino Uno'daki 8 bir Timer2'yi kullanarak kodu test etmek ve istediğim kıvama gelirse daha sonra Attiny85'e aktarmak.
Kodun temel mantığı da şöyle: Timer'nin saat frekansını uygun bölücü ile :)coolxf: 2 MHz yapıyoruz. Sonra Timer2 Overflow Interrupt'ı aktifliyoruz. Her bir interrupt geldiğinde bir sayaç değişkenini bir artırıyoruz. Yani Timer2 255'e gelip bir sonraki adımda 0'a döndüğünde bir interrupt geliyor ve sayaç bir artırılıyor.

Kod:
const int cikis = 5; // Çıkış digital pin 5
volatile byte counterH;
unsigned int pwmout;
volatile byte pwmH;
volatile byte pwmL;

void setup()
{
  pinMode(cikis, OUTPUT);
  cli(); // Disable global interrupts
  TCCR0A = 0; // Her ihtimale karşı diğer tüm timer işlevlerini durduruyoruz
  TCCR0B = 0;
  TIMSK0 = 0;
  TCCR1A = 0;
  TCCR1B = 0;
  TIMSK1 = 0;

  TCCR2B = 2; // Timer2 prescaler 8
  TIMSK2 = 1; // Timer2 Overflow Interrupt enable
  TCNT2 = 0; // Reset Timer2
  sei(); //Enable global interrupts
}

Interrupt servis rutin çok kısa

Kod:
ISR(TIMER2_OVF_vect)
{
  counterH++;
}


Servo çıkış darbesini üretme mantığı da şu:
Diyelim ki 1500 mikrosaniyelik bir darbe üreteceğiz. 1500 / 256 = 5 (tam sayı olarak). Kalan da 220
Timer2 ve sayaç sıfırlandıktan sonra önce sayacın 5 olmasını bekliyoruz sonra da Timer''nin 220'ye kadar saymasını (yani küsuratı). Darbe süresini bu şekilde belirlemiş oluyoruz.

Kod:
void servoout()
{
  cli(); // Disable global interrupts
  TIMSK2 = 1; // Timer2 Overflow Interrupt enable
  pwmH = pwmout / 256;
  pwmL = pwmout - (pwmH * 256);
  TCNT2 = 0; // Reset Timer2
  counterH = 0;
  digitalWrite(cikis, HIGH);
  sei();
  while (counterH < pwmH)
  {

  }
  cli();
  if (pwmL > 4)
  {
    while (TCNT2 < pwmL)
    {
    }
    digitalWrite(cikis, LOW);
    counterH = 0;
    TCNT2 = 0;
    sei();
    while (counterH < 150)
    {
    }
  }
}

Programın gövdesinde ise pwmout değerini 1000 - 2000 arasında artırıp azaltan ve servo çıkışı veren bir kod kullanıyoruz.

Kod:
void loop()
{
  for (int n = 1000; n < 2000; n = n++)
  {
    pwmout = n;
    servoout();
  }
  for (int n = 2000; n > 1000; n = n--)
  {
    pwmout = n;
    servoout();
  }
}

Bu kodun yapması planlanan şey servoyu tek tık tek tık olarak bir uçtan diğer uca ve sonra da geriye döndürmek ve bunu sürekli yapmak.

Gelelim sorunumuza...

Evet çalışmasına çalışıyor ama şöyle bir şey oluyor. Servo tek tık tek tık yavaş yavaş bir tarafa dönerken bir yerde büyük bir adım atıyor ve sonra yine tek tık tek tık dönmeye devam ediyor. Birazdan yine büyük bir adım atıp normale dönüyor. Daha net anlatmam gerekirse, örneğin çıkış darbesi 1000, 1001, 1002, 1003, 1004, 1005 diye giderken bir anda 1100, 1101, 1102, 1103,... gibi devam etmeye başlıyor. Burada takılıp kaldım. Sorunu lokalize etmek için nasıl bir yol izlemem gerektiğini bile düşünemiyorum şu anda...
 
Servo sinyali üretilmesi konusunda yardıma ihtiyacın var... (Zafer Kardeşim...)

Sümer abi selamlar,

Kod:
if (pwmL > 4)
O kadar büyük atlamaları bilemeyeceğim ama yukarıdaki satır nedeni ile, "pwmout" değeri 256'nın tam katlarını aldığı zaman servo, pwmout ve pwmout+5 arasında hareket etmeyecektir. Servo, pwmout ve pwmout+5 arasında senkronizasyonunu kaybettiği(bu aralıkta hep HIGH gönderiliyor olacak) içinde beklenenden uzun bir atlama yapıyor olabilir. Bir de ek olarak, darbe peryodu(HIGH+LOW) sabit değil ama, doğrudan pwmout sayısı ile bağlantılı olduğundan dolayı da senkronizasyon sorunu yaşıyor olabilir mi ?

Ayrıca pwmL değerini OCR1A ile CTC modda saydırmak sanki daha kolay olabilirdi gibime geliyor. Sim kumandası konusunda PPM üretmek için kullandığım kodda bu yöntemi kullanmıştım. Frame süresi belirli bir PWM sinyali üretmek içinde kullanılabilir.
 
Servo sinyali üretilmesi konusunda yardıma ihtiyacın var... (Zafer Kardeşim...)

O satır aslında sonradan eklendi. Sebebi de eğer pwmL 0,1,2 gibi küçük bir değer ise oradaki geçiş sırasında sayacı kaçırma riski olabilir mi gibi bir düşünce idi. Ana kodda o yok ve aynı sorun var.
Compare interrupt ile de yapılabilir. Bu akşam bir de öyle deneyeyim bakalım ne olacak.
 
Servo sinyali üretilmesi konusunda yardıma ihtiyacın var... (Zafer Kardeşim...)

Bu arada bir başka tuhaflık daha var. Şimdi Arduino Uno 16 MHz'de çalışıyor. Prescaler olarak 8 dediğimizde her bir clock cycle 0.5 mikrosaniye oluyor. Yani ben eğer 1500 mikrosaniye sayacaksam 3000'e kadar saymam gerekli ama öyle olmuyor. 8 Prescaler ile sanki her clock 1 mikrosaniye gibi. Bunun bir açıklaması var mıdır acaba?
 
Servo sinyali üretilmesi konusunda yardıma ihtiyacın var... (Zafer Kardeşim...)

Sümer Yamaner' Alıntı:
Bu arada bir başka tuhaflık daha var. Şimdi Arduino Uno 16 MHz'de çalışıyor. Prescaler olarak 8 dediğimizde her bir clock cycle 0.5 mikrosaniye oluyor. Yani ben eğer 1500 mikrosaniye sayacaksam 3000'e kadar saymam gerekli ama öyle olmuyor. 8 Prescaler ile sanki her clock 1 mikrosaniye gibi. Bunun bir açıklaması var mıdır acaba?
Sümer abi, senin UNO 8Mhz hızında çalışıp seni kandırıyordur . Şaka bir yana, 16Mhz de 8 prescaler ile test etmiştim, gayet normal çalışıyordu... Test etmek için kullandığın kodu gönderebilirmisin Sümer abi ?
 
Servo sinyali üretilmesi konusunda yardıma ihtiyacın var... (Zafer Kardeşim...)

Abicim binlerce kod denedim. Şu son kodda zamanlama tutuyor gibi.

Kod:
// Timer2 timing ayarları için test programı

const int out = 10; // Çıkış digital 10
volatile byte counter; // Timer overflow sayacı
unsigned int pwmout; // Çıkış darbe değeri
byte pwmH; // Çıkış darbe değerinin high byte'ı
byte pwmL; // Çıkış darbe değerinin low byte'ı

void setup() 
{
  pinMode(out, OUTPUT);
  cli();
  TCCR2A = 0;
  TCCR2B = 2; // Timer2 prescaler 8
  TIMSK2 = 1; // Timer2 Overflow Interrupt enabled
  sei();
}

void loop() 
{
  for(int n = 2000; n < 4000; n++)
  {
    pwmout = n;
    servoout();
  }
}

void servoout()
{
  pwmL = pwmout << 8; // çıkış darbe değerinin low byte'ı
  pwmH = pwmout >> 8; // çıkış darbe değerinin high byte'ı
  digitalWrite(out, HIGH); // Darbe ON
  TCNT2 = 0;
  counter = 0;
  while(counter <= pwmH) // Timer2 pwmH kadar overflow olmalı
  {
  }
  cli();
  TCNT2 = 0;
  counter = TCNT2;
  cli();
  while(counter <= pwmL) // Bu da küsurat süresi
  {
    counter = TCNT2;
  }
  digitalWrite(out, LOW); // Darbenin sonu
  sei();
  TCNT2 = 0;
  counter = 0;
  while(counter <= 7:coolxf: // Bu da yaklaşık 20 mS bekleme süresi
  {
  }
}


ISR(TIMER2_OVF_vect)
{
  counter++; // Timer2 her overflow oluşunda counter bir artırılıyor
}

Kodun çalışmasını gösteren kısa bir video koyacağım birazdan.
 
Servo sinyali üretilmesi konusunda yardıma ihtiyacın var... (Zafer Kardeşim...)

İşte böyle çalışıyor...

Bu VIDEOYU görmek için izniniz yok. Giriş yap veya üye ol
 
Servo sinyali üretilmesi konusunda yardıma ihtiyacın var... (Zafer Kardeşim...)

Sümer abi selamlar,

pwmL = pwmout << 8; // çıkış darbe değerinin low byte'ı
bu ifadenin istenilen değeri veriyor olup olmadığı konusunda yarın daha sakin kafa ile bakacağım ama aşağıdaki ile de deneyebilir misin ?
pwmL = pwmout % 256; // çıkış darbe değerinin low byte'ı

Bir de buralarda bir yerde sei() olması lazım ama onu da bulamadım :D
cli();
TCNT2 = 0;
counter = TCNT2;
cli();
 
Servo sinyali üretilmesi konusunda yardıma ihtiyacın var... (Zafer Kardeşim...)

O kadar kod oldu ki cli, sei birbirine girdi. :) Ona ben de bakacağım ama sorunun kaynağı değil çünkü sağa sola cli, sei serpiştirmekten iflahım kesildi. :)

Kullandığım pwmout değişkeni unsigned integer. Yani 16 bitlik bir değer. Şimdiye kadar tüm kodlarda pwmH ve pwmL değerleri için şunu yapıyordum:
pwmH = pwmout / 256;
pwmL = pwmout - 256 * pwmH;

Bu sefer bir de bit shift ile deneyeyim dedim. Değer hiçbir zaman 32768'den büyük olmayacağı için en yüksek bit hiçbir zaman "1" olmaz. O nedenle;
pwmH = pwmout >> 8;
ifadesi doğru sonuç verecektir.
Ama...
pwmL = pwmout << 8;
ifadesinin istenilen sonucu vermeyeceği aşikar!!! :D
Belki;
pwmL = pwmout && B00001111;
ifadesi daha mantıklı olur. Çok teşekkürler...
 
Servo sinyali üretilmesi konusunda yardıma ihtiyacın var... (Zafer Kardeşim...)

Sonunda oldu...

Kod:
const int out = 10;
const int giris = 0;

unsigned int pwmin;
volatile unsigned int counter;
unsigned int pwmout;
unsigned int pwmH;
unsigned int pwmL;

void setup()
{
  pinMode(out, OUTPUT);
  cli();
  TCCR2A = 0;
  TCCR2B = 2; // Timer2 prescaler 8
  TIMSK2 = 1; // Timer2 Overflow Interrupt enabled
  sei();
}

void loop()
{
  pwmin = analogRead(giris);
  pwmout = 2000 + pwmin * 2;
  servoout();
}

void servoout()
{
  pwmL = pwmout & 255;
  pwmH = pwmout >> 8;
  digitalWrite(out, HIGH);
  TCNT2 = 0;
  counter = 0;
  while (counter <= pwmH)
  {
  }
  TCNT2 = 0;
  counter = 0;
    while (counter <= pwmL)
    {
      counter = TCNT2;
    }
  digitalWrite(out, LOW);
  TCNT2 = 0;
  counter = 0;
  while (counter <= 7:coolxf:
  {
  }
}


ISR(TIMER2_OVF_vect)
{
  counter++;
}
 
Servo sinyali üretilmesi konusunda yardıma ihtiyacın var... (Zafer Kardeşim...)

Yani bu demektir ki, çok basit işlevler (servo reverser, v tail mixer gibi uygulamalar için Attiny85 kullanılarak 1024 çözünürlüklü servo sinyali üretilebilecek.
 
Servo sinyali üretilmesi konusunda yardıma ihtiyacın var... (Zafer Kardeşim...)

Iste azmin ve sabırla denemelerin başarısı :halay:


Sent from my iPhone using Tapatalk
 
Servo sinyali üretilmesi konusunda yardıma ihtiyacın var... (Zafer Kardeşim...)

Atmel'e mail attım. Bir aylık Attiny85 üretimlerini kapattım! :lol:
 
Servo sinyali üretilmesi konusunda yardıma ihtiyacın var... (Zafer Kardeşim...)

Devam Sümer abi , gün gelecek sayende ülkemizde servo üretebilecek :thumbup: ...inşallah......... :rolleyes: Biz şimdiden sinyalinin nasıl üretildiğini görelim de...belki üretiriz.... %p
 
Servo sinyali üretilmesi konusunda yardıma ihtiyacın var... (Zafer Kardeşim...)

Yahu ne gerek var servo üretmeye. HobbyKing'de beş dolar! :D :D :D

Lise sondayken meslek tanıtımına gelen İTÜ Elektronik Müh. profesörü, "bizde neden yarı iletken üretilmiyor" soruma "ne gerek var, adamlar üretiyor biz de alıp kullanıyoruz" diyerek beni İTÜ'den ve mühendislikten soğutmuştu.