AVR- AC Dİmmer

 

Burada anlattığım tehlikeli bir işlem olduğundan dikkatli olmanız gerekir 220 V şakası yok.

Alternatif Akımda PWM?

Bu konu hakkında bir eğitim almadım sadece okuyup öğrendiklerimi kendime notlar olarak yazıyorum. Hatalarım yanlış öğrendiklerim olabilir. Düzeltmeme yardımcı olabilirsiniz. Daha önceki bir yazımda motor hızını kontrol etmek için PWM konusuna değinmiştim. Doğru akımda belirli aralıklarla açma kapama yaparak motor veya bir led diyotun ışığını ayarlayabiliyoruz. Alternatif akımda (AC) hem yön hem genlik değişken olduğundan bu şekilde açma kapama yaparsak muhtemelen kömürlerinde ark patlamaları olan bir motor veya aşırı titreyen bir ampulle karşılaşırdık. AC de bu yön değişimi yani şebekenin frekansı Türkiye'de 50Hz dir. Saniyede 50 defa değişmektedir.


Bu değişime rağmen DC gibi aç kapa yaptığımızda bu şekilde bir sonuç alırdık.

Şekilde görüldüğü gibi eğrinin altında kalan taralı alan değişken bu nedenle çıkış değer de değişken olacaktır. Bu haliyle çıkış voltajını düşürememiş oluruz. AC de aç kapa şeklinde sonuca ulaşmak için grafikte taralı alanın aynı olması gerekir. Bunu yapabilmenin yolu sinüs dalgasının sıfır geçişini yakalayıp açma işlemini buna göre yapmaktır.


Bu şekilde yaptığımızda her iki tarafta da eğrinin altında taralı alanlar eşit olur. Böylece çıkış değeri sabit ve ayarlanabilir olur. AC de transistörler gibi bu şekilde anahtarlama yapabilen Triyak denilen parçalar vardır. Triyak gate ucuna verilen akım ile iletime geçer fakat gate ucu sıfır olsa bile üstünde akım olduğu sürece iletken olmaya devam eder. Ancak sıfır geçişinde iletimi durdurur. Bu açma kapama işini triyak ile yaptığımızda grafik değişir.

Şekilde görüldüğü gibi biz sinyali "0" yapsak da AC taralı alan sıfır geçişine kadar devam eder. Bu durumda çıkıştaki voltajı ayarlamak için sıfır geçişini bilmek kadar bir sonraki sıfır geçişine kalan süreyi de bilmek önemli oluyor. Şebeke frekansı 50hz olduğuna göre 1 periyot 0,02 sn o da 20ms olacaktır. Bir periyotta iki sıfır geçiş olduğundan geçişler arası 10ms olur. Çıkış voltajını yarı yarıya (%50 duty) azaltmak istediğimiz zaman yapmamız gereken sıfır geçişi bulmak. Bir sonraki sıfır geçişi 10ms sonra olacağına göre 5ms beklemek. 5ms sonra triyakı iletime geçirmek olacaktır.


Triyak iletime geçince sıfır geçişi beklediğine göre Gate bacağına gönderdiğimiz sinyali uzun tutmamıza gerek yok. Aslında ilk iş olarak gate sinyalini "0" yapmak daha doğru olur. Teorik olarak ne yapmak gerekir kısmı buraya kadar. Artık bunları nasıl yapacağız kısmına geçiyorum.

PC814-Sıfır Geçiş Tespiti (Zero Cross Detection)

Bu işlem için çeşitli yöntemler var. Alternatif akımı diyotlar ile doğrultup transistörle veya izolasyon için optokuplörle yapabilirsiniz. Ben bunlara gerek kalmadan yapmak için PC814 kullandım. İçinde birbirine ters paralel bağlanmış iki LED diyot ve foto transistör bulunuyor. Bu sayede hem izolasyon hem de daha az komponentle çözüm getiriyor. PC814 datasheet anlatmayacağım.


Dikkat etmeyi gerektiren ilk şey AC tarafındaki dirençlerin değeri olacak. En az 47K 2W iki direnç (Rd) takmak gerekir. Daha aşağısı sıfır geçişi yakalamak için daha uygun olsa da ısınma ve PC814 ün zarar görmesi olasılığından riskli görüyorum. Dayanabileceği akım çok daha yüksek ama direnç yerine bir rezistans takmak  gibi bir durum olur. Direnç ne kadar düşük olursa LED o kadar geç söner ve sıfır geçiş noktasına o kadar yakın bir tespit yapılır. Ayrıca çıkışta kullanacağımız pull up (Rl) direnç ne kadar büyük olursa gecikme süresi (Tf) o kadar artar. Tf artışı da sıfır geçişe yaklaştırır. Tabi bunu söylerken alttaki grafikte görülen Input sinyalinin "0" olduğu zamanın gerçek sıfır geçişinin önünde olduğunu düşünerek söylüyorum. Düşük Rd direnciyle gerçek sıfır geçişe çok yaklaştığımız  zaman Rl direnci daha düşük olması doğru olacaktır. Benim kullandığım dirençlerle bir miktar erken sıfır tespiti olacaktır ve bunu yazılımla gidereceğiz.

Datasheet bağlantı şeması verilmiş benim uyguladığım şema bu şeklide. Bu bağlantı sonrası PD2 çıkışı grafiği aşağıdaki gibi olur. Bahsettiğim erken tespit ve gecikme de bu şekilde olacaktır.


Sıfır Geçiş Tespit Hatası

Giriş sıfıra yaklaşınca LED sönecek bu sayede foto transitör iletimi kesecektir. Pull up direnç ile PD2 çıkışı "1" olarak çıkış verecektir. Giriş negatif tarafta yükselirken diğer LED yanacak ve foto transistör iletime geçerek PD2 çıkışını "0" yapacaktır. PD2 her "1" oluşu sıfır geçişi olduğunu gösterecektir. Burada oluşan tespit hatasını yazılımda düzeltmemiz gerekecek. İki geçiş arası 10ms olarak tespitimizi yine yapacağız fakat bir miktar önce olacak bunu 500μs olduğunu düşünelim. Biz %100 duty ile çıkış vermek istiyorsak geçiş anında triyakı iletime geçirmeli gate bacağına "1" sinyalini göndermeliyiz.

Gate sinyalini hemen "0" yapmamız gerektiğini söylemiştim.  50μs sonra gate "0" yaptığımızı düşünelim. Bu durumda gerçekte diğer alternansa geçmeden triyakı iletime geçirmiş oluruz. Gate bacağını da "0" yaptığımızdan 450μs süresince çıkış ile %100 yerine çok daha küçük bir çıkış alırız. Aşağıdaki grafikte durumu "biraz abartarak" göstermeye çalıştım. Bunu engellemek için bir ofset süre eklemesi yapacağız.


Görüldüğü gibi sıfır geçiş tespiti yapıldığı an Gate "1" yapılır ve sonrasında "0" yapılırsa böyle bir durumla karşılaşırız. Hemen "0" yapmazsak çıkışı ayarlama şansımız hiç kalmaz. Yukarıdaki devreyi dikkatli bir şekilde kurup PD2 çıkışını bir Arduino Unonun D5 pinine bağlayarak linkteki frekans sayıcı ile kontrol edebilirsiniz. Her şey doğru yapıldıysa seri ekranda 100Hz olarak sonucu görürsünüz. Şebeke 50Hz ve bir periyotta yukarıda görüldüğü gibi iki sıfır geçişi olduğundan 100Hz olarak görmemiz gerekir.  Bu da sıfır geçişi tespit ettiğimizi ve bir sonraki aşama olan triyak iletim kısmına geçebileceğimizi gösterir.

MOC3020- Triyak kontrol

Triyakı kullanabilmek için gate bacağına çıkış sinyalini vermemiz gerekiyor. Burada mikro denetleyici direk bağlamak sağlıklı olmayacağından bu işi izole bir şekilde yapmak için MOC3020 kullanıyorum. Triyak çıkışlı bir optokuplör olan 3020 kolay bulunan, ucuz ve gerekli değerleri fazlasıyla karşıladığı için seçtim. Bağlantı şeması Datasheette verilmiş ve aynen uyguladım. Bu şema SSR rölelerin içinde kullanılanla aynıdır. Bu devre yerine SSR kullanabilirsiniz ama muhtemelen daha pahalıya gelir. Yük durumuna göre farklı koruma devreleri de eklenmiş ama bilmediğim ve öğrenmeme gerek olmadığını düşündüğüm için araştırmadan uygulamak mantıklı geldi. Bu tipik şemada da koruma için gerekli parçalar mevcut ve yük olarak bir motor bağlanabilir. C9 ve C10 400v R2 ve R4 1W yeterlidir.


Datasheete göre dikkat etmemiz gereken LED için 15mA üst sınır. Bu akım değeri düştükçe bir miktar gecikme olmaktadır. Mikrosaniye  biriminden gecikme olduğundan çok önemli değil. Bu nedenle Rin direncini 470Ω olarak seçtim. En fazla 2μs gecikme olacaktır. Bu durumda Gate (PD4) sinyalini 2μs den uzun bir süre vermem yeterli olacaktır. Bir yanlış anlaşılma olmasın diye uyarı yapayım. Yukarıda Gate sinyalini "1" olarak verdim. Triyak sürerken MOC3020 kullandığım ve mikro denetleyiciyle kontrol edeceğim için ilgili pini (PD4) "0" yaparak triyakın (BT139) gate bacağını "1" yapmış oluyorum.

Dış Kesme Ayarları

Önceki konularda ve ayrıca bir dış kesme konusu olarak değindiğim için detaya girmiyorum. PC814 PD2 ye bağlı bunun için PD2 yi giriş yapıp yükselen kenarda kesme olacak şekilde ayarlıyorum. INT0 yani PD2 kesmesini açarak bu kısmı ayarlıyorum. Sıfır geçişi ile PD2 "1" olduğunda dış kesme oluşur. Bu kesme rutini içine bir sonraki adım olan beklemeyi gerçekleştirmek için zamanlayıcı başlatıyorum. Çıkışı ayarlamak için gereken bekleme süresi için OCR1A ya da değeri giriyorum.

Zamanlayıcı Ayarları (Timer1)

Sıfır geçiş sonrası çıkışı ayarlamak için gerekli olan Timer birimini ayarlayalım. Sıfır geçiş sonrası ne kadar çok bekleyip triyakı sürersem çıkış voltajını o kadar düşürürüm. 50Hz şebeke frekansında bir periyot 20ms bir alternans 10ms sürüyor demiştim. Buna göre bekleme süresinin teorik olarak 0 ile 10ms arasında olması gerekir. Daha önce farklı konularda farklı timer birimleri hakkında detaylı bilgi verdiğim için burada sadece olması gereken değerleri söyleyeceğim.  Ben timer1 kullandım farklı bir birimde kullanılabilir. 16MHz saat sinyali için 64 prescaler seçtim böylece 16.000.000/64 sonucunda 1 sn de TCNT toplam değeri 250000 olacaktır.

1ms de TCNT 250 ve 10ms de 2500 olacaktır. En fazla 10ms bekleyeceğimizi düşünürsek bu değer TCNT1 de taşmaya neden olmaz ve küsurlu sayılar çıkarmaz. 0 ile 10ms arası bekleme için TCNT değerleri 0 ile 2500 olacak. OCR1A ile istediğimiz bir değere gelince TCNT kendini sıfırladığı için CTC modu seçeceğim. İstediğim sürenin sonunda bir kesme oluşması için OCR1A kesmesini de açacağım. Kesme oluştuğunda bekleme sürem dolmuştur ve triyakı sürmem gerekir. Bunun için oluşan kesme rutini içinde ilk iş MOC3020 nin bağlı olduğu pini "0"  ve hemen tekrar "1" yapıyorum. Kesme rutini içinden çıkmadan tekrar zaman kesmesi olmaması için timer1 birimini durduruyorum. Bu önemli aksi halde kesme oluşur ve alakasız bir anda triyak iletime geçer.

ADC Ayarları

Çıkışı ayarlamak için gereken bekleme süresini potansiyometre ile ayarlamak istedim. Bunun için ADC kullanmam gerekiyor. Yine daha önce detaylı değindiğim bir konu olduğundan tekrar etmeyeceğim. Öncelikle referans olarak AVCC seçtim. ADC aktif edip prescaler 128 olarak ayarladım. ADC kesmesini açıp ADC başlangıç için otomatik tetiklemeyi seçtim. Otomatik tetikleyici kaynağı olarak da dış kesmeleri seçtim. Böylece her sıfır geçiş olduğunda ADC çevrimi başlayacak çevrim sonunda ADC kesmesi oluşacak. Bu kesme içinde, bulunan 0-1023 arasındaki değeri 0ms ile 10ms arsında bekleme yapmak için gerekli olan TCNT değerleri olan 0 ile 2500'e oranlayarak çevireceğim.

Koda bakınca göreceğiniz gibi kullandığım Map fonksiyonu bu oranlama işlemini yapıyor. Orada 0 ile 2500 yerine 530 ile 2600 e oranlıyor. Buradaki 530 (132,5μs) bende ki donanımın yapmış olduğu erken tespitin ofsetidir. Neden 2600 de 2630 değil derseniz başta önemli olan 30 TCNT darbesi 7,5μs gibi küçük bir değer olduğundan sonda önemsiz oluyor. Ayrıca diğer alternansa taşma riskini de yok ediyor.

Tüm bu ayarları aşağıda kodlarda görebilirsiniz. Umarım faydalı bir yazı olur.


 * main.c
 *
 * Created: 4/4/2021 11:00:14 AM
 *  Author: haluk
 */ 
#define F_CPU 16000000UL
#include <xc.h>
//#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
//#include "uart.h"
#define MOC_PORT        PORTD
#define MOC_DDR            DDRD
#define MOC_PIN            PORTD4
#define TRI_DIR            MOC_DDR|=(1<<MOC_PIN)
#define TRI_ON            MOC_PORT&=~(1<<MOC_PIN)
#define TRI_OFF            MOC_PORT|=(1<<MOC_PIN)
#define  oranH 0.9
#define  oranL 0.1
volatile uint16_t adc=0;
volatile float sonuc=0;
volatile uint16_t dimmer=2500;
//char sadc[20];
uint32_t mapint( uint32_t x,uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max);
void timer1Con();
void irqZeroCross();
void adc_basla();
uint16_t adcOku(uint8_t kanal);
ISR(INT0_vect){
    TCCR1B|=(1<<WGM12)|(1<<CS10)|(1<<CS11);
    OCR1A=dimmer;
}
ISR (TIMER1_COMPA_vect){
    TRI_ON;
    _delay_us(10);
    TRI_OFF;
    TCCR1B=0x00;
}
ISR (ADC_vect){
    adc=ADCW;
    sonuc=(float)((sonuc)*oranH)+(float)(adc*oranL);    
    dimmer=mapint((uint16_t)sonuc,0,1023,530,2600);
}

int main(void){
    DDRB|=(1<<5);
    TRI_DIR;
    irqZeroCross();
    timer1Con();
    //uart_basla(115200);
    adc_basla();
    while(1)
    {
        /*sprintf(sadc,"%d--%d--%.2f\n",dimmer,adc,sonuc);        
        uart_dizi(sadc);        
        _delay_ms(100);*/               
    }
}
void irqZeroCross(){
    DDRD&=~(1<<PORTD2);//pd2 giriş yapıldı
    //PORTD|=(1<<PORTD2);//pd2 dahili pull-up
    EICRA|=(1<<ISC01)|(1<<ISC00);//pd2 yükselen kenar
    EIMSK|=(1<<INT0);//pd2
    sei();// tüm kesmeler açık
}
void timer1Con(){
    TCCR1B|=(1<<WGM12)|(1<<CS10)|(1<<CS11);// ctc mode prescaler 64 1sn 250000 
    TIMSK1|=(1<<OCIE1A);//ocra eşleşme kesmesi açık
    OCR1A=249;//1ms de kesme
    sei();// tüm kesmeler açık
}
uint32_t mapint( uint32_t x,uint32_t in_min, uint32_t in_max, uint32_t out_min, uint32_t out_max){
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

void adc_basla(){
    cli();
    ADMUX=(1<<REFS0);
    ADCSRA=(1<<ADEN)|(1<<ADPS0)|(1<<ADPS1)|(1<<ADPS2)|(1<<ADIE)|(1<<ADATE);//oto triger
    ADCSRB |=(1<<ADTS1);//dış kesme adc triger
    ADCSRA |=(1<<ADSC);
    sei();
}
 

Hiç yorum yok:

Yorum Gönder