- 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.
Interrupt servis rutin çok kısa
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.
Programın gövdesinde ise pwmout değerini 1000 - 2000 arasında artırıp azaltan ve servo çıkışı veren bir kod kullanıyoruz.
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...
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...