AVR- I2C (TWI) AHT10 Kullanımı Bölüm 1

 

 

Arduino ile ilgilendiğim zaman DS3231 kullanmıştım. Bunu hazır kütüphaneyle yapmıştım. Ondan sonra hiç kullanmadım haliyle öğrenme gereği duymadım. Sıcaklık sensörü araştırırken ucuz ve 20 bit çözünürlükle fiyatına göre iyi diyebileceğim AHT10 sensörü aldım. I2C ile haberleştiğinden ve artık hazır kütüphaneler kullanmak yerine öğrenme yöntemini seçtiğimden oturup incelemek zorunda kaldım. AVR de TWI olarak isimlendirilen birimin registerleri ve I2C hakkında internette yeterince bilgi var.  Ben bu bilgilerin çok gerekli olmadıkça tekrarını yapmayacağım.  İsmi ve tanımları yapılan registerleri tek tek anlatmayacağım. SCL SDA tanımı yapmayacağım bunlar için lütfen linkten veya başa bir kaynaktan bilgi sahibi olmanız gerekiyor. TWI  biriminin nasıl kullanıldığını, haberleştiğini ve son olarak TWI ile AHT10 sensörün kullanımını yazacağım. Açıkçası çok fazla benzer konu olduğundan yazıp yazmama konusunda tereddüt ettim ama kesme kullanarak yapılan örnek azlığından ve türkçe kaynak olması açısından yazmaya karar verdim. Umarım hatasız bir anlatımı başarabilirim. Başlamadan önce SDA ve SCL hatlarına uygun (4,7K) pull up dirençler muhakkak bağlı olmalıdır.  

TWI (I2C) Write

Başlatma

Datasheet rehberliğinde ilerleyeceğim ama AVR315 aplikasyon notlarından da faydalandığımı ve kendi kütüphanemi oluştururken bu notun kaynak kodlarından faydalandığımı belirteyim. Öncelikle birimin çalışması için bazı ayarlamalar yapmamız gerekiyor. Bu ayarlardan ilki TWI biriminin çalışma frekansını belirlemek olacaktır. MCU saat frekansına bağlı olarak verilen formülle SCL frekansını belirliyoruz.


 


Formülden TWBR registerine yazmamız gereken değeri buluyoruz. Genellikle SCL 100kHz veya hızlandırılmış olarak 400kHz frekansı kullanılmaktadır. Prescaler 1 olarak seçip bilinmezleri azaltıyoruz. Böylece formülden TWBR yi çektiğimizde TWBR=((F_CPU/F_SCL)-16)>>1; şekline dönüşüyor. (F_CPU 16000000Hz ve F_SCL 100000Hz)  Yine TWI birimini çalıştırmak için TWEN "1" yapılmalı ayrıca TWI kesmesi için TWIE de "1" yapılmalıdır. Bütün bunlar için oluşturduğum init fonksiyonu aşağıda görebilirsiniz ama ben adım adım gideceğim ve fonksiyonlara geçmeden tek tek ilerleyeceğim. Main içine bu ayarlar için yazacağımız kodlar bu şekilde olacak. Burada kesmeyi henüz devreye almayacağım.

	TWSR|=(0<<TWPS0)|(0<<TWPS1);//prescaler 1
	TWBR=((F_CPU/F_SCL)-16)>>1;//i2c scl frekans	
	TWCR=(1<<TWEN);

Bu kodlar ile TWI birimini başlatmış oluyoruz. I2C protokolünde işleyiş için bu şema gerekli:


Start

Görüldüğü gibi haberleşme için öncelikle START gereklidir. Start denilen SCL hattı "1" durumundayken SDA hattının "0" olmasıdır. TWI biriminin bunu yapması için TWCR TWI kontrol registerinde yer  alan TWSTA "1" (TWCR|=(1<<TWSTA);) yapılmalıdır.

	TWCR|=(1<<TWSTA);// twsta 1
	TWCR|= (1<<TWINT);
	while(!(TWCR & (1<<TWINT)));& (1<<TWINT)));

NOT: Ben burada veya "OR" kullanarak işlem yaptım. Adım adım anlatmak için bu yöntemi seçiyorum. Normalde bitlerden "1" ve "0" olanları aynı anda işlemek adına "=" kullanılmıştır.  Kodda değişiklik yaparken buna dikkat edilmesi gerekiyor.

Bu durumda SDA (D0) "0" olduktan yaklaşık 5μs sonra SCL (D1) "0" oluyor ve Start gerçekleşiyor. TWI birimi start  sonrası TWINT kesme bayrağını "1" yapıyor ve TWI durum registeri TWSR de yer alan TWS7-TWS3 arası 5 bit ile bir durum kodu oluşturur. Start için bu 0x08 dir. Bu durum kodlarına erişmek için TWSR ilk üç biti görmezden gelmemiz gerekir. TWI birimi her adım için bir durum kodu oluşturur bunlara datasheet veya <util/twi.h> kütüphanesinden ulaşabilirsiniz. Durum kodları için aşağıdaki makroları tanımlarız.

#define I2C_STATUS_MASK	0xF8//tws7-tws3
#define I2C_STATUS	(TWSR&I2C_STATUS_MASK)

TWI birimi bana biraz karışık geldi bunun en belirgin nedeni alışık olduğumun dışında çalışan kesmesidir. Start veya daha sonra değineceğim her adım sonrası TWINT "1" yapılır. Normalde kesme rutini içinde  donanım otomatik olarak bu kesme bayrağını "0" yapar. TWI biriminde bunu elle "0" yapmak için TWINT e "1" yazmak gerekir. Bu yapılmazsa bir sonraki işlem başlatılmaz. Ayrıca "1" yapılsa bile burada while ile döngü şart. Bu döngü konusuna daha sonra tekrar değineceğim.

Adres

Start sonrası TWI biriminde gönderilen ve alınan veriler için kullanılan TWI data registeri TWDR ye adres ve komut bilgisini yazmamız gerekir. Slave (köle) aygıtın belli bir adresi olur ve 7 bitten oluşur. TWDR ye bu adres bir bit sola kaydırarak yazılır. TWDR ilk biti slave aygıta ne amaçla veri gönderdiğimizi gösteren (R/W) okuma veya yazma bildiren bittir.  Kısaca Sladr-RW olarak tanımlayacağım değişken üstünden kayıt işlemini yapıyorum. Sladr-RW ilk biti"0" olursa yazma "1" olursa okuma komutu olarak adresi kaydeder. 0x01 numaralı slave aygıta yazma görevi için Sladr-RW =0000'0010=0x02 olmalıdır. Okuma içinse Sladr-RW =0000'0011=0x03 olmalıdır.  TWDR=Sladr-RW; veya TWDR=0x02; şeklinde registere veriyi yazmamız gerekir. Bu adımdan sonra SCL saat sinyali başlar bunun için yine TWINT "1" TWCR|= (1<<TWINT); ve TWSTA "0" TWCR&=~(1<<TWSTA);  yapılmalıdır.

	TWDR=0x02;
	TWCR&=~(1<<TWSTA); //twsta 0
	TWCR|= (1<<TWINT);	
	while(!(TWCR & (1<<TWINT)));

Artık start sonrası SCL hattı sinyaline bağlı olarak SDA "1" veya "0" oldu böylece slave aygıta veriyi gönderdik. SDA hattı son kısımda tekrar "1" oldu. Bu bize slave aygıtın bir cevap vermediği veya öyle ayarlandığını gösterir. SCL nin 8 saat darbesi veriyi gönderir. 9. darbede eğer slave iletişim başarılı olmuşsa ve cevap vermesi için ayarlanmışsa SDA hattını "0" yapacaktır.  Buradan hazır bir kodu kullanarak çalıştım. Aldığım bir sensörün kontrolünü genelde hazır kodlarla test edip sonra datasheet çalışıp kullanıyorum. TWI içinde çalışan bir slave ihtiyacım oldu. Şimdi adresi 0x01 olan bir slave aygıta veri gönderelim.

Artık 9. darbede SDA "0" durumdadır, bu da slavin adres verisini aldığını ve cevap verdiğini gösterir. Bu cevaplar ACK veya NACK şeklinde olur. Bu durumlardan her ikisi için de TWI birimi birer durum kodu oluşturur. (adres ACK: 0x18, NACK:0x20)

Bu durum koduna göre sonraki adımımızı planlarız. Slave ACK aldığımızda okuma veya yazma işlemine devam ederiz. NACK durumunda ya terkar deneme ya da sonraki slave seçme gibi işlemler yapabiliriz. Yukarıdaki şemada 4. adıma geldik. Adres ve komut bildirdik ACK aldık. Sonraki adım veri yazma kısmına geçelim.

Data

Hazır kodlarda slave aygıta "a" ve "b" gönderdiğimizde   D13 (PB5) bağlı olan LED yanıp sönüyor. Bunun için göndereceğimiz ilk veri "a" olacaktır. Küçük "a" için Decimal "97" veya Hex "0x61" şeklinde TWDR yazıyoruz. Adres ve komut gönderimi bittiği için bu registere yazabiliriz. Tabi bunun için yukarıdaki gibi while ile TWINT kontrolü yapmamız şart. Bunu yapmazsak TWDR de yazma çakışması olur. TWINT "0" olduğunda TWCR de bulunan TWWC biti "1" olur. Bu durumda TWDR veri yazamayız. Bu nedenle döngü ile TWINT kontrolü şart. Kesme kullanırken döngü şart değil ama bunun da bir şartı var. Benim oluşturduğum bir diziyi gönderdiğim fonksiyonu arka arkaya arada bekleme veya başka işlem olmadan çağırmamak gerekiyor.

Bunun nedeni her adımda (start, adres, data vb.) kesme rutinine girip çıkmasıdır. Start sonrası kesme olur. Kesme içine girer ilgili işlem yapılır. TWINT "1" yapınca kesmeden çıkar. İşte bu çıkış ile sonraki fonksiyona veya satıra geçer. İlk fonksiyonda belleğe "abc" yazmış ikinci fonksiyonda "def" yazmış ve farklı bir slave aygıta göndermek istediğimde sadece ilk aygıta "abcdef" göndermiş oluyorum. Bunun nedeni ring buffer kullanmam ve ilgili işaretçileri sıfırlamamamdır. Sıfırladığımda ring olmasının anlamı kalmaz ve kesme içinde kullanmayı istemesem de doğru olmasa da burada kullandım. Bu sayede arka arkaya çok hızlı bir şekilde iletişim kurabildim. Böyle bir zorunluluk yoksa kesme ile TWI kullanımında while döngülerine ihtiyaç yoktur.

	TWDR=0x61;
	TWCR|= (1<<TWINT);
	while(!(TWCR & (1<<TWINT)));

Bu şekilde kodumuzu ekledikten sonra veriyi gönderiyoruz ve slave ACK yanıtını geliyor. Bu işlem sonunda durum kodu oluşturulur. (Data ACK:0x28, NACK:0x30)

SDA ve SCL hatları normalde "1" durumdadır. İletişim başında  "0" oldu adres ve veri gitti ama halen "0" durumdadır. Hattın bu şekilde kalmaması için Stop gereklidir. Hemen ardından bir iletişim daha başlatılacaksa Stop olmadan Start gelir buna tekrarlayan başlatma (Rep_Start) diyebiliriz. Bu durumdaki start için 0x10 kodu oluşturulur. Bazı aygıtlar yazma ve okuma arasında Stop beklerken bazıları ihtiyaç duymayabilir.

Stop

Stop durumunda SCL hattı "1" olduktan sonra SDA "1" olur. TWI birimi hattın kontrolünü bırakır. Başka bir ana (master) aygıt varsa iletişime geçebilir. Stop için TWCR de bulunan TWSTO "1" yapılmalıdır. Donanım daha sonra kendisi "0" yapar. TWINT  temizlendikten sonra stop koşulu yürütülür. Stop sonrası TWINT "1" yapılmaz.

	TWCR|= (1<<TWSTO);

SCL ve SDA hattı "1" oldu ve stop koşulu gerçekleşti. Buraya kadar olan kısmı tekrar edip "b" göndererek test edebilirim. Main fonksiyonu ve sonsuz döngü aşağıdaki gibi olacaktır. Ayrıca TWI durum (status) kodlarını görebileceğiniz şekilde hazırlanmış hali en altta paylaşıyorum. Yorum satırlarını açıp kapatarak işlem yapabilirsiniz. Veri yazmayı bu şekilde yaptıktan sonra sıra okumaya geldi. Benzer adımları daha kısa olarak anlatmak durumundayım.

int main(void){      
  ///başlatma
  TWSR|=(0<<TWPS0)|(0<<TWPS1);//prescaler 1
  TWBR=((F_CPU/F_SCL)-16)>>1;//i2c scl frekans  
  TWCR=(1<<TWEN);
  
    while (1){
    //start
    TWCR|=(1<<TWSTA);// twsta 1
    TWCR|= (1<<TWINT);
    while(!(TWCR & (1<<TWINT)));//sonraki adım için gerekli

    //adres
    TWDR=0x02;//0x01 adres ve write komutu
    TWCR&=~(1<<TWSTA); //twsta 0
    TWCR|= (1<<TWINT);
    while(!(TWCR & (1<<TWINT)));//burada kontrol edilmezse TWWC "1" olur.
    
    //data
    TWDR=0x61;// a
    TWCR|= (1<<TWINT);
    while(!(TWCR & (1<<TWINT)));//sonraki adım için gerekli

    //stop
    TWCR|= (1<<TWSTO);
    _delay_ms(100);

    //start
    TWCR|=(1<<TWSTA);// twsta 1
    TWCR|= (1<<TWINT);
    while(!(TWCR & (1<<TWINT)));//sonraki adım için gerekli

    //adres
    TWDR=0x02;//0x01 adres ve write komutu
    TWCR&=~(1<<TWSTA); //twsta 0
    TWCR|= (1<<TWINT);
    while(!(TWCR & (1<<TWINT)));//burada kontrol edilmezse TWWC "1" olur.
    
    //data
    TWDR=0x62;// b
    TWCR|= (1<<TWINT);
    while(!(TWCR & (1<<TWINT)));//sonraki adım için gerekli

    //stop
    TWCR|= (1<<TWSTO);
    _delay_ms(100);
    }
}


TWI (I2C) Read

Master aygıt okuma veya yazma sırasında aynı şekilde başlatılır bir farklılık yoktur. Slave için adres registeri TWAR a  seçilen adresin yazılması gerekir ama bu TWI slave konusuna girmeyeceğim. Slave tarafta veri istendiğinde "Merhaba" gönderecek şekilde ayarlanmıştır. Aşağıdaki şemada işleyiş görülüyor. Bu şemanın bir kısmını aldım tüm işleyiş ve durum kodları için datasheet e bakabilirsiniz. Yazma kısmında adres ve veri gönderimi sonrası ACK slave tarafından oluşturuluyordu. Okumada adres sonrası slave ACK cevabı verir devamında veriyi gönderir. Bundan sonra her veri gönderiminde eğer farklı ayarlanmamışsa master ACK oluşturur. Son alınan veri sonrası NACK ile iletişim sonlanır.



Gri renkli kısımlar master, beyaz olanlar slave aygıttan gelir. Şemada görünen HEX kodlar durum kodlarıdır. (S: start, SLA-R: adres ve okuma komutu, A: ACK, DATA: veri, P: stop ve üstü çizgili A ise NACK)

Start

Slave aygıttan veri okumak için TWI birimini başlatıp yine Start koşulunu gerçekleştirmek gerekir. Yukarıda her biti adım adım gösterdiğim için burada tek seferde işlem yapılacak şekilde gösterip geçeceğim.

	TWCR=(1<<TWEN)|(1<<TWSTA)|(1<<TWINT);
	while(!(TWCR & (1<<TWINT)));

TWCR de yalnızca "1" olmasını istediğim bitler "1" diğerleri "0" yapıldı. Start için asıl koşul olan TWSTA "1" yapılarak ve TWINT "1" olması beklenerek işlem yapıldı. Yukarıdaki aynı görüntüyü tekrar paylaşmıyorum. Bu işlem sonucu yine bir durum kodu üretilir.(Start:0x08, Rep start: 0x10)

Adres

Slave adresi 0x01 olarak ayarlıydı. Okuma görevi için ilk bit "1" olacak. Adresi bir bit sola kaydırıp ilk biti "1" yaptığımız zaman Sladr-RW=0x03 olacaktır. TWDR=Sladr-RW; veya TWDR=0x03; olarak adresi ve komutu yazıyoruz. Sonrasında yine ilgili TWCR bitleri düzenleyip TWINT "1" olmasını bekliyoruz.

	TWDR=0x03;
	TWCR = (1<<TWINT) | (1<<TWEN);
	while(!(TWCR & (1<<TWINT)));

Bu işlem sonucu bu şeklide olur. gördüğünüz gibi 8. darbe de Read komutu için SDA "1" oldu. Bu işlem sonunda durum kodu oluşturulur. Master okuma komutu için adres ACK:0x40, NACK:0x48 dir.


Data

Adres ve okuma komutu sonrası ACK ile SDA hattına SCL darbelerine göre slave tarafından veri yazılır. TWI birimi bu veriyi TWDR registerine kaydeder. Bunu yapmak için adres sonrası TWINT "1" ve data=TWDR yapmamız gerekir. Buna ek olarak veri alınca ACK göndermek istersek TWEA da "1" yapılır aksi halde veriyi alsak da ACK yerine NACK koşulu oluşur. Bu işlem sonucu durum kodu oluşturulur. (Data ACK:0x50, NACK:0x58)

TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
//twea eklenmezse nack 
while(!(TWCR & (1<<TWINT)));		
data[0]=TWDR;

Veri alındı NACK oluşturuldu.

Veri alındı ACK oluşturldu.

"Merhaba" için ilk harf 'M' "0x4D" alındı bunu sonrasında kullanmak için oluşturduğum char data[7]; isimli dizinin ilk karakteri olarak kaydettim. Kalan altı harfi de bu şekilde almaya devam edeceğim. Bunun için yukarıdaki kod bu şekilde uzayacak.

	TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
	while(!(TWCR & (1<<TWINT)));		
	data[0]=TWDR;//M
	TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWEA);
	while(!(TWCR & (1<<TWINT)));	
	data[1]=TWDR;//e	 
	TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWEA);
	while(!(TWCR & (1<<TWINT)));
	data[2]=TWDR;//r
	TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWEA);
	while(!(TWCR & (1<<TWINT)));
	data[3]=TWDR;//h
	TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWEA);
	while(!(TWCR & (1<<TWINT)));
	data[4]=TWDR;//a
	TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWEA);
	while(!(TWCR & (1<<TWINT)));	
	data[5]=TWDR;//b
	TWCR = (1<<TWINT)|(1<<TWEN);//twea 0yapıldı.
	while(!(TWCR & (1<<TWINT)));		
	data[6]=TWDR;//a

Her adımdan sonra TWINT "1" yapıldı beraberinde TWEN ve TWEA da "0" olmaması için koda eklendi. Son karakter 'a' alınmadan  önceki adımda TWEA "0" olması için yazılmadı. Bu sayede son karakteri alınca NACK koşulu oluştu. Bu kullandığınız yere bağlı olarak değişebilir. Bu çalışma için tüm karakterleri NACK ile alabilirdik. Bu ACk-NACK durumu iletişimin devamı için gereken kararları vermemizi sağlayacaktır. Aşağıda tüm karakterlerin alınmış hali vardır. ASCII seçeneğiyle karakterler üstte görünüyor.


Stop

Okuma işleminin de son adımı olarak stop koşulu gerekiyor. Yapılan işlem farklı değildir. Tüm okuma işlemleri birlikte bu şekilde olur.

int main(void)
{    
  //başlatma 
  TWSR|=(0<<TWPS0)|(0<<TWPS1);//prescaler 1
  TWBR=((F_CPU/F_SCL)-16)>>1;//i2c scl frekans  
  TWCR=(1<<TWEN);  
    while (1){    
    //slave okuma "Merhaba"
    //start
    TWCR=(1<<TWEN)|(1<<TWSTA)|(1<<TWINT);
    while(!(TWCR & (1<<TWINT)));
    //sprintf(str_data,"twsta %x\n",(TWSR&0xf8));//TWI status
    //uart_dizi(str_data);
    //adres
    TWDR=0x03;
    TWCR = (1<<TWINT) | (1<<TWEN);
    while(!(TWCR & (1<<TWINT)));
    //sprintf(str_data,"twsta %x\n",(TWSR&0xf8));//TWI status
    //uart_dizi(str_data);
    //data
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
    while(!(TWCR & (1<<TWINT)));
    data[0]=TWDR;
    //sprintf(str_data,"twsta %x\n",(TWSR&0xf8));//TWI status
    //uart_dizi(str_data);
    TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWEA);
    while(!(TWCR & (1<<TWINT)));
    data[1]=TWDR;
    TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWEA);
    while(!(TWCR & (1<<TWINT)));
    data[2]=TWDR;
    TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWEA);
    while(!(TWCR & (1<<TWINT)));
    data[3]=TWDR;
    TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWEA);
    while(!(TWCR & (1<<TWINT)));
    data[4]=TWDR;
    TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWEA);
    while(!(TWCR & (1<<TWINT)));
    data[5]=TWDR;
    TWCR = (1<<TWINT) | (1<<TWEN);//twea 0 yapıldı son karakter nack
    while(!(TWCR & (1<<TWINT)));
    data[6]=TWDR;
    //sprintf(str_data,"twsta %x\n",(TWSR&0xf8));//TWI status
    //uart_dizi(str_data);
    //stop
    TWCR=(1<<TWEN)|(1<<TWSTO)|(1<<TWINT)|(1<<TWIE);
    uart_dizi(data);
    uart_gonder('\n');
    _delay_ms(500);
    }
}


Buraya kadar olan bölümde TWI birimini veri yazma ve okuma için nasıl kullanacağımızı öğrenmiş olduk. Bundan sonraki bölümde bu işlemleri kesme kullanarak nasıl yapacağımıza ve AHT10 kullanımına değineceğim.

/*
 * aht10_i2c.c
 *
 * Created: 24.01.2022 00:56:31
 * Author : haluk
 */ 
#define F_CPU 16000000UL
#define F_SCL 100000L
//#include <xc.h>
#include <avr/io.h>
#include <util/twi.h>
#include "uart.h"
#include "i2c_master.h"
#include <util/delay.h>
#include <stdio.h>

char str_data[20];
char data[7];

int main(void){  
    uart_basla(115200);     
  ///fonksiyon dışı başlatma a ve b gönderimi için 
  TWSR|=(0<<TWPS0)|(0<<TWPS1);//prescaler 1
  TWBR=((F_CPU/F_SCL)-16)>>1;//i2c scl frekans  
  TWCR=(1<<TWEN);  
    while (1){
        
    /// a ve b gönderimi
    //start    
    TWCR=(1<<TWEN)|(1<<TWSTA)|(1<<TWINT);
    while(!(TWCR & (1<<TWINT)));//sonraki adım için gerekli
    //sprintf(str_data,"twsta %x\n",(TWSR&0xf8));//TWI status
    //uart_dizi(str_data);//bu satırları açarsanız status kodlarını görebilirsiniz.
    //adres
    TWDR=0x02;//0x01 adres ve write komutu    
    TWCR=(1<<TWINT)|(1<<TWEN);
    while(!(TWCR & (1<<TWINT)));//burada kontrol edilmezse TWWC "1" olur.
    //sprintf(str_data,"twsta %x\n",(TWSR&0xf8));//TWI status
    //uart_dizi(str_data);
    //data
    TWDR=0x61;    
    TWCR=(1<<TWINT)|(1<<TWEN);
    while(!(TWCR & (1<<TWINT)));//sonraki adım için gerekli
    //sprintf(str_data,"twsta %x\n",(TWSR&0xf8));//TWI status
    //uart_dizi(str_data);
    //stop    
    TWCR=(1<<TWEN)|(1<<TWSTO)|(1<<TWINT);
    _delay_ms(500);
    //start    
    TWCR=(1<<TWEN)|(1<<TWSTA)|(1<<TWINT);
    while(!(TWCR & (1<<TWINT)));//sonraki adım için gerekli
    //sprintf(str_data,"twsta %x\n",(TWSR&0xf8));//TWI status
    //uart_dizi(str_data);
    //adres
    TWDR=0x02;//0x01 adres ve write komutu    
    TWCR=(1<<TWINT)|(1<<TWEN);
    while(!(TWCR & (1<<TWINT)));//burada kontrol edilmezse TWWC "1" olur.
    //sprintf(str_data,"twsta %x\n",(TWSR&0xf8));//TWI status
    //uart_dizi(str_data);
    //data
    TWDR=0x62;    
    TWCR=(1<<TWINT)|(1<<TWEN);
    while(!(TWCR & (1<<TWINT)));//sonraki adım için gerekli
    //sprintf(str_data,"twsta %x\n",(TWSR&0xf8));//TWI status
    //uart_dizi(str_data);
    //stop    
    TWCR=(1<<TWEN)|(1<<TWSTO)|(1<<TWINT);
    _delay_ms(500);
    
    //slave okuma "Merhaba"
    //start
    TWCR=(1<<TWEN)|(1<<TWSTA)|(1<<TWINT);
    while(!(TWCR & (1<<TWINT)));
    //sprintf(str_data,"twsta %x\n",(TWSR&0xf8));//TWI status
    //uart_dizi(str_data);
    //adres
    TWDR=0x03;
    TWCR = (1<<TWINT) | (1<<TWEN);
    while(!(TWCR & (1<<TWINT)));
    //sprintf(str_data,"twsta %x\n",(TWSR&0xf8));//TWI status
    //uart_dizi(str_data);
    //data
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
    while(!(TWCR & (1<<TWINT)));
    data[0]=TWDR;
    //sprintf(str_data,"twsta %x\n",(TWSR&0xf8));//TWI status
    //uart_dizi(str_data);
    TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWEA);
    while(!(TWCR & (1<<TWINT)));
    data[1]=TWDR;
    TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWEA);
    while(!(TWCR & (1<<TWINT)));
    data[2]=TWDR;
    TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWEA);
    while(!(TWCR & (1<<TWINT)));
    data[3]=TWDR;
    TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWEA);
    while(!(TWCR & (1<<TWINT)));
    data[4]=TWDR;
    TWCR = (1<<TWINT) | (1<<TWEN)| (1<<TWEA);
    while(!(TWCR & (1<<TWINT)));
    data[5]=TWDR;
    TWCR = (1<<TWINT) | (1<<TWEN);//twea 0 yapıldı son karakter nack
    while(!(TWCR & (1<<TWINT)));
    data[6]=TWDR;
    //sprintf(str_data,"twsta %x\n",(TWSR&0xf8));//TWI status
    //uart_dizi(str_data);
    //stop
    TWCR=(1<<TWEN)|(1<<TWSTO)|(1<<TWINT);
    uart_dizi(data);
    uart_gonder('\n');
    _delay_ms(500);
    }
}


/*
 * uart.c
 *
 * Created: 9.01.2021 15:26:37
 *  Author: haluk
 */ 
#include "uart.h"

static volatile uint8_t rx_bas=0,rx_son=0,tx_bas=0,tx_son=0;
static volatile uint8_t rx_ring[UART_Rx_Boyut];
static volatile uint8_t tx_ring[UART_Tx_Boyut];
static volatile uint8_t uaflag=0;

ISR (USART_RX_vect){
  uint8_t gelen=UDR0;
  rx_bas=(rx_bas+1) & UART_Rx_Mask;
  rx_ring[rx_bas]=gelen;
}
ISR (USART_UDRE_vect){  //uart veri gönderim kesmesi
  tx_son=(tx_son+1)&UART_Tx_Mask;
  UDR0=tx_ring[tx_son];
  if (tx_son==tx_bas)
  UART_Bos_Off;
}
ISR(USART_TX_vect){
  
}
void uart_basla(uint32_t baud){
  cli();
  uint16_t baudRate=0;
  baudRate=(F_CPU/baud/16)-1;
  if (baud>=115200){//115200 ve üstünde U2X 1 yapılıyor.
    baudRate=(F_CPU/baud/8)-1;
    UCSR0A|=(1<<U2X0);
  }
  UBRR0H=(baudRate>>8);
  UBRR0L=baudRate;
  UCSR0B|=(1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<TXCIE0);
  UCSR0C|=(1<<UCSZ01)|(1<<UCSZ00);
  sei();
}
uint8_t uart_oku(){
  rx_son=(rx_son+1) & UART_Rx_Mask;
  return rx_ring[rx_son];
}
void uart_gonder(uint8_t uData){
  tx_bas=(tx_bas+1)&UART_Tx_Mask;
  tx_ring[tx_bas]=uData;
  UART_Bos_On;
}
void uart_dizi(const char *str){
  while(*str){
    tx_bas=(tx_bas+1)&UART_Tx_Mask;
    tx_ring[tx_bas]=*str++;
  }
  UART_Bos_On;
}
uint8_t uart_gelen(){
  if (rx_son==rx_bas)  return 0;
  return 1;
}
void uart_dizi_al(char *stri){
  unsigned char gdata=0;
  uint8_t poz=0;
  do
  {  gdata=uart_oku();
    stri[poz]=gdata;
    poz++;
  } while (!(rx_bas==rx_son));
  stri[poz]='\0';
}


/*
 * uart.h
 *
 * Created: 9.01.2021 15:26:29
 *  Author: haluk
 */ 
#ifndef UART_H_
#define UART_H_


#define F_CPU 16000000UL

//#include <xc.h>
#include <avr/io.h>
#include <avr/interrupt.h>

/////////////////////////////////
#define UART_Rx_Boyut 128
#define UART_Tx_Boyut 128
#define UART_Rx_Mask (UART_Rx_Boyut-1)
#define UART_Tx_Mask (UART_Tx_Boyut-1)
#define UART_Bos_On UCSR0B|=(1<<UDRIE0)// uart UDR register boş kesmesi
#define UART_Bos_Off UCSR0B&=~(1<<UDRIE0)// uart UDR register boş kesmesi
////////////////////////////////////////////////////
void uart_basla(uint32_t baud);
uint8_t uart_oku();
void uart_gonder(uint8_t uData);
void uart_dizi(const char *str);
uint8_t uart_gelen();
void uart_dizi_al( char *stri);

#endif

 

 

Hiç yorum yok:

Yorum Gönder