Tiny AVR 1 Serisi - Event Sistem ve TCB Uygulama

 Küçük bir bilgisayar olan mikro kontrolcünün birçok çevre birimi vardır. Bu birimler genelde tüm işlemler için işlemciyi kullanır. Eski AVR ürünlerde olmayan Event (olay) sistemi bunu değiştiriyor. 

Event System

Event ile bir çevre birimi birden fazla çevre birimiyle işlemci harici bağlantı kurabiliyor. Bir olay oluştuğunda diğer çevre birimlerine işlem yapma şansı tanıyor. Bir buton ile zamanlayıcıyı çalıştırmak veya bir çıkışa bağlı ledi yakmak gibi hiç bir işlemci gücü harcamadan kod yazmadan işlem  yapılabiliyor. Bu bağlantılar kanal olarak adlandırılmış ve her olay başlatıcı bir kanala bağlanırken bu olaya göre işlem yapan kullanıcı (user) çevre birimleri bir den fazla olabiliyor. Bir buton olay oluşturucu (generator) tek bir kanala bağlanabiliyor. Bu butonun durumuna göre işlem yapmak için bir pin çıkışı, zamanlayıcı, ADC veya başka bir çevre birimi aynı anda kullanıcı olarak bağlanabiliyor.

Bu kanallar sistem saatine göre senkron veya asenkron olarak ayrılır. Sistem çevresel saati kullanır ama yapılandırmaya bağlı olarak uyku durumunda da çalışır. Sisteme bağlı olarak üç adat çıkış pini (EVOUTx) bulunur. Bu seride bu şekilde bir sınırlama vardır. Event out pinlerini kullanmak için PORTMUX.CTRLA registerindeki ilgili bite "1" yazmak gerekir. PORTMUX.CTRLA|=PORTMUX_EVOUT0_bm;

Donanım olarak çevre birimlerinden gelen olaylar dışında yazılımla da olay oluşturulabilir. Uyku modunda bu işlem geçersiz olur. Yazılımla olay oluşturmak için ASYNCSTROBE veya SYNCSTROBE registerine "1" yazmak yeterlidir.

Asenkron Olay Kanalları

Tabloda belirtildiği gibi asenkron olaylar için dört adet kanal bulunur. Bu kanallara yalnızca bazı çevre birimlerinin bağlanacağı gibi hepsine bağlanabilenlerde vardır. Örneğin PORTB de yer alan PIN7 sadece ASYNCCH1 e bağlanabilir. RTC taşma olayı ASYNCCH0-3 şeklinde hepsine bağlanabilir.  EVSYS.ASYNCCH1=EVSYS_ASYNCCH1_PORTB_PIN7_gc;//ASYNCCH1_PORTB_PIN7_gc=0x11 olarak tanımlı

Senkron Olay Kanalları

Asenkronda olduğu gibi senkron olaylar içinde bazı kanallar özeldir ve sadece o kanala bağlanabilir.

Asenkron Olay Kullanıcı Kanalları
Asenkron kullanıcıların bağlanabileceği kanallar tablodaki gibidir. Burada birden fazla kullanıcı aynı kanala bağlantı yapabilir. Asenkron kullanıcılar senkron olay oluşturuculara da bağlanabilir.
EVSYS.ASYNCUSER11=EVSYS_ASYNCUSER11_ASYNCCH1_gc;
EVSYS.ASYNCUSER9=EVSYS_ASYNCUSER9_ASYNCCH1_gc; 
Şeklinde 11 numaralı kullanıcı yani TCB1 birimi asenkron  kanal 1 e bağlanırken EVOUT1 yani 9 numaralı kullanıcı da asenkron kanal 1 e bağlanabilir. 

Senkron Olay Kullanıcı Kanalları

Senkron kullanıcı olarak TCA0 ve USART0 bulunuyor ve yalnızca senkron oluşturucu kanallara bağlanabiliyor.

PORTMUX

Bu seri hakkında yazmaya başladıktan sonra birkaç defa bahsettim ama içeriğine girmedim. Zaten oldukça basit bir yapı ama bu bölümde yapacağım uygulama için kullanmam gerekiyor.
Registereleri tablodaki gibi ve içeriğindeki bitler değiştirmek istediğimiz çevre birimlerine ait pinleri gösteriyor. Örneğin PORTMUX.CTRLD registerinde TCB1 bitine "1" yazarsak normalde TCB1 WO çıkışı alternatif pinle değişir.
Bu tabloda çevre birimlerine bağlı pinleri ve portmux ile değişebilecek alternatif pinleri görebiliriz. TCB1 WO PA3 olarak görünüyor. PORTMUX.CTRLD|=PORTMUX_TCB1_ALTERNATE_gc;
Şeklinde TCB1 e "1" yazarsak TCB1 WO PC4 olur.

Uygulama

İlk olarak bir buton olay oluşturucu olacak. Event sisteme bağlı bir çıkış ve TCB tek çekim modu kullanılarak olay sinyaline bağlı olarak çalışacak.
/*
 * attiny1614_event_tcb.c
 *
 * Created: 18.03.2024 17:17:24
 * Author : haluk
 */ 

#define F_CPU 333333// sigorta ayarlarıyla 16MHz/48
#include <avr/io.h>
#include <util/delay.h>

int main(void){
  CPU_CCP=CCP_IOREG_gc;
  CLKCTRL_MCLKCTRLB=CLKCTRL_PDIV_48X_gc|CLKCTRL_PEN_bm;//ana saat/48
  PORTB.DIRCLR|=PIN7_bm;//pb7 giriş  
  PORTB.PIN7CTRL|=PORT_PULLUPEN_bm;// pull up açık
  EVSYS.ASYNCCH1=EVSYS_ASYNCCH1_PORTB_PIN7_gc;//PB7 asenkron kanal 1 de olay oluşturucu
  EVSYS.ASYNCUSER11=EVSYS_ASYNCUSER11_ASYNCCH1_gc;//TCB1 asenkron kullanıcı olarak asenkron kanal 1 e bağlı
  EVSYS.ASYNCUSER9=EVSYS_ASYNCUSER9_ASYNCCH1_gc;//EVOUT1 asenkron kullanıcı olarak asenkron kanal 1 e bağlı
  PORTMUX.CTRLA|=PORTMUX_EVOUT1_bm;//EVOUT 1 çıkış açık
  TCB1.CNT=0x00;//sayaç sıfırlandı
  TCB1.CCMP=0xffff;//top değer
  TCB1.CTRLB|=TCB_CCMPEN_bm|TCB_CNTMODE_SINGLE_gc;// eşleşme çıkış açık, tek çekim modu
  TCB1.EVCTRL|=TCB_FILTER_bm|/*TCB_EDGE_bm|*/TCB_CAPTEI_bm;//filtre açık, tek çekim düşen kenar,event açık
  TCB1.CTRLA|=TCB_CLKSEL_CLKDIV2_gc|TCB_ENABLE_bm;//ana saat/2,enable
  //main clock 333333Hz/2 TCB saati 166666Hz olur. Saniyedeki darbe sayısı 1666666 ise 16 bit CCMP en fazla
  //65535 olur 65535/166666=0,39 sn çıkış verir.  
    while (1) 
    {
    }
}

Giriş Yakalama Frekans ve PWM Ölçüm

Bu uygulamada yaklaşık 1 KHz PWM ölçümü yaptım. 2MHz e kadar ölçüm yapabildim. Logic analizörle arada ufak bir fark var. Bu fark iç osilatörün yetersizliği sanırım. 1 KHz frekansında CNT sayacı yeterli gelecektir. Bundan daha düşük bir frekans ile örneğin 50 Hz frekansta CNT taşacaktır. TCB1 saat kaynağı olarak yeterli prescalere sahip değil. Bu nedenle TCA kullanılmalıdır. Gerekli açıklamaları aşağıda yaptım.

/*
 * attiny1614_Event_PWM_Freq.c
 *
 * Created: 20.03.2024 20:51:33
 * Author : haluk
 */ 

#include <avr/io.h>
#include <stdio.h>
#include <util/atomic.h>
#include "tiny1_uart.h"
volatile uint16_t period=0;
volatile uint16_t high=0;
volatile uint16_t flag=0;
volatile double hsure=0;
volatile double sure=0;
volatile double oran=0;
char sonuc[256];
ISR(TCB1_INT_vect){
  if (flag<=100){//100 örnekten birini alıyoruz
    flag++;    
    TCB1.INTFLAGS=TCB_CAPT_bm;// kesme bayrağı temizlendi
  }  
}
int main(void){
  
  CPU_CCP=CCP_IOREG_gc;
  CLKCTRL_MCLKCTRLB=CLKCTRL_PDIV_6X_gc|(0<<CLKCTRL_PEN_bp);//20MHz    
  PORTB.DIRCLR=PIN1_bm;//PORTB1 giriş yapıldı
  EVSYS.ASYNCCH1=EVSYS_ASYNCCH1_PORTB_PIN1_gc;//pb1 asenkron olay oluşturucu kanal 1 e bağlandı
  EVSYS.ASYNCUSER11=EVSYS_ASYNCUSER11_ASYNCCH1_gc;//tcb1 asenkron kullanıcı olarak kanal 1 e bağlandı
  TCA0.SINGLE.CTRLA|=TCA_SINGLE_ENABLE_bm|TCA_SINGLE_CLKSEL_DIV8_gc;// tca başlatıldı
  TCB1.CTRLB|=TCB_CNTMODE_FRQPW_gc|TCB_ASYNC_bm;//frekans pwm ölçüm modu, asenkron giriş açık
  TCB1.EVCTRL|=TCB_CAPTEI_bm|TCB_FILTER_bm;// event giriş açık, filtre açık
  TCB1.INTCTRL=TCB_CAPT_bm;// kesme açık
  TCB1.CTRLA|=TCB_ENABLE_bm|TCB_CLKSEL_CLKDIV1_gc;// tcb saat kaynağı iç osilatör
  //TCB1.CTRLA|=TCB_ENABLE_bm|TCB_CLKSEL_CLKTCA_gc;// saat kaynağı olarak tca seçildi
  //düşük frekanslarda tca seçilmeli
  uart_basla(Bd115200);
    while (1)  {
    if (flag>100){      
      ATOMIC_BLOCK(ATOMIC_RESTORESTATE){// işlem sırasında kesme oluşmaması gerekiyor
        flag=0;
        period=TCB1.CNT;
        high=TCB1.CCMP;
        hsure=(double)high/20;//f_cpu 20000000 high/fcpu= sn*1000000=us cinsinden
        sure=(double)period/20;//f_cpu 20000000 period/fcpu= sn*1000000=us cinsinden
        //Fcpu değişirse formül değişir
        oran=(double)high/period;
        sprintf(sonuc,"per=%u high=%u\n",period,high);//ham cnt ve ccmp değerleri
        uart_dizi(sonuc);
        sprintf(sonuc,"persure=%.2fus, high sure=%.2fus, oran=%.2f\n",sure,hsure,oran);
        uart_dizi(sonuc);
      }      
    }
    }
}

Alt UART ile okunan değerler var. Üst logic analizörün okuduğu değerler.






Hiç yorum yok:

Yorum Gönder