AVR Programlama – I2C (TWI)

I2C Nedir?

Inter-Integrated Circuit , 1982 yılında Philips tarafından icat edilmiş senkron bir seri haberleşme protokolüdür. I2C haberleşmede sadece iki terminal bulunmaktadır. Bunlar, SDA ve SCL hatlarıdır ve bu hatlar birer pull-up direnç ile güç hattına bağlanır. SDA, veri iletimi için kulanılan hat iken SCL ise haberleşmede senkronizyonu sağlayan saat sinyalini gönderir. SDA hattı veri transferi için kullanılacağı için hem master hemde slave cihazlar tarafından kontrol edilirken SCL hattı sadece master cihaz tarafından kontrol edilmektedir.

I2C’nin diğer haberleşme protokollerine göre bazı avantaj ve dezavantajları vardır.Önce avantajları ile başlayalım; sadece iki veri hattı bulunması SPI haberleşmeye göre bir avantajdır ve aynı zamanda kendisi gibi iki terminal ile iletişim kuran UART dan da hızlıdır ve senkron yapıdadır. Dezavantajlarına gelecek olursak; pull-up dirençlerinden kaynaklı güç tüketimi yüksektir ve SPI haberleşme kadar hızlı değildir. Ayrıca veri iletimi için sadece bir hat kullanıldığı için half-dublex yapıdadır.

Yukarıda örnek bir I2C haberleşme görülmektedir. Şimdi parça parça haberleşmenin nasıl gerçekleştiğini inceleyelim.

Start Condition : Master cihaz tarafından SCL hattı 1 konumunda iken SDA hattının 0 konumuna getilirmesi ile haberleşme başlar.

Address Frame : Master cihaz tarafından 7 bitlik slave adrresine ek okuma veya yazma biti de dahil edilip 8 bitlik veri paketi SDA hattına yüklenir. Bu paketin son biti 0 ise bu veri yazılacağı 1 ise okunacağı anlamına gelir.

ACK/NACK : Veri hattı üzerindeki slave cihazlardan gönderilen adrese sahip olan tarafından ACK biti gönderilir.

Data Frame : Master tarafından ACK biti okunduktan sonra veri yazılacaksa yazılır veya veri okunacaksada bir sonraki ACK bitine kadar veri okunur.

Stop Condition : Master tarafından son byte okunup veya yazıdıktan sonra stop biti oluşturularak haberleşme durdurulur.

ATMEGA328P I2C(TWI) Yazmaçları

TWBR yazmacı kullanılarak SCL hattının frekansı belirlenir. Frekans hesaplaması aşağıdaki formül ile yapılır.

TWINT : Bu bayrak I2C birimi mevcut işini bitirtiği zaman donanım tarafından 1 konumuna getirilir

TWEA : ACK biti oluşturmak için kullanılan bittir. 1 konumuna getirildiğinde ACK biti oluşturulur.

TWSTA : START biti oluşturmak için kullanılan bittir. STAR biti oluşturmak için 1 yapılması gerekmektedir.

TWSTO : STOP biti oluşturmak için kullanılan bittir. 1 yapılarak STOP biti oluşturulur.

TWEN : I2C birimini aktif etmek için kullanılan bittir.

TWIE : Bu bit 1 yapıldığı zaman I2C kesmesi aktif edilmiş olur.

TWS(7:3) : I2C haberleşme ile ilgili durumları kontrol edebileceğimiz statü bitleridir. Ben kullanmayacağım için açıklamayacağım.

TWPS(1:0) : SCL frekansının belirlerken kullandığımız formüldeki Prescaler değerini belirlemek için kullanılan bitler.

TWDR : I2C haberleşmede veri gönderme ve veri alma işleminde kullandığımız veri yazmacı.

Ben ATMEGA328P yi master olarak kullanıp slave cihazlardan veri okuma yapacağım için sadece bu yazmaçları kullandım. Eğer mikrodenetleyicinizi slave olarak ayarlayacaksanız bakmanız gereken başka yazmaçlarda var.

I2C haberleşmede Örnek

Örnek uygulamada AT24C256 EEPROM entegresine veri yazıp daha sonra yazdığımız veriyi okuyacağım. EEPROM ve ROM çeşitleri hakkındaki ROM Nedir yazımı incelemenizi tavsiye ederim.

Bu entegre ile uygulama yapmak istediğim zaman internette çok uzun süre araştırma yapmak zorunda kalmıştım ve çok farklı fonksiyonlara ve kütüphanelere rastladım fakat benim yapmak istediğim şey herhangi bir kütüphane kullanmaksızın entegreyi I2C ile kontrol etmekti. Çok uzun süre bunu başaramadım ve bunun tek sebebi datasheetini okumamış olmamdı. Çok uzun süreler boyunca internette araştırma yapacağıma datasheeti incelemiş ve okumuş olsam çok daha kısa sürede çalıştırabilirdim. Birazdan anlattığımda ne kadar basit olduğunu göreceksiniz.

Datasheet de Yukarıdaki gibi AT24C256 ya veri yazmak için yapmamız gereken örnek bir veri formatı verilmiş. Kodumuzu adım adım bu veri formatına uygun şekilde yazacağız.

Burada ise örnek bir veri okuma formatı gösterilmiş.

A0 ve A1 pinleri ground a bağlı olduğu için benim yazma adresim 0b10100000 yani 0xA0 ve okuma adresimde 0b10100001 yani 0xA1 olacaktır.

Şimdi EEPROM çipimizin bir adresine 8 bitlik bir veri yazalım ve daha sonra aynı adresteki veriyi okuyalım. I2C hattını standart olan 100 kHz frekansta çalıştıracağım.

#define get_bit(reg,bitnum) ((reg & (1<<bitnum))>>bitnum) // bayrak bitini kontrol etmek için       
                                                          // kullandığım makro

void TWI_Init (void)

{

    TWBR=0b01001000; //For 100 KHz SCL Frequancy
    TWCR|=(1<<TWEN);

}




void TWI_Start (void)

{

	TWCR= (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);

	while (get_bit(TWCR,TWINT)==0)

	{

	}

}




void TWI_Stop (void)

{

	TWCR=(1<<TWSTO)|(1<<TWEN)|(1<<TWINT);

}




void TWI_Write (char data)

{

	TWDR=data;

	TWCR= (1<<TWINT)|(1<<TWEN);

	while (get_bit(TWCR,TWINT)==0)

	{

	}

}




char TWI_Read_ack (void)

{

	TWCR=(1<<TWEN)|(1<<TWINT)|(1<<TWEA); /* Enable TWI, generation of ack */
	while(!(TWCR&(1<<TWINT)));	/* Wait until TWI finish its current job */
	return TWDR;

}

char TWI_Read_Nack(void)		/* I2C read nack function */
{
	TWCR=(1<<TWEN)|(1<<TWINT);	/* Enable TWI and clear interrupt flag */
	while(!(TWCR&(1<<TWINT)));	/* Wait until TWI finish its current job */
	return TWDR;		/* Return received data */
}

şimdi yukarıdaki fonksiyonları kullanarak entegremize uygun okuma ve yazma fonksiyonları yazalım

void EEPROM_Write (char data, uint16_t address)

{
uint8_t dataaddress[2];
dataaddress[0] = address >>8;
    /* buraya kadar olan kısımda 16 bitlik adress */
dataaddress[1] = address & 0x00FF;
  /*  iki adet 8 bitlik değişkene ayrılmıştır*/
TWI_Start();
                   /*   Start bitini oluştur*/
TWI_Write(0xA0);                /* EEPROM adresini yazma biti ile gönder*/
TWI_Write(dataaddress[0]); 
     /* veri adresinin yüksek öncelikli 8 bitini gönder */
     TWI_Write(dataaddress[1]);
      /* veri adresinin geri kalan 8 bitini gönder*/
TWI_Write(data);
                /* yazmak istediğin veriyi gönder*/
TWI_Stop();
                     /* haberleşmeyi durdur*/
}

void EEPROM_READP (uint16_t address , char *data)
{
uint8_t dataaddress[2];
dataaddress[0] = address >>8;
dataaddress[1] = address & 0x00FF;
    // adres değişkeni iki adet 8 bitlik değişkene ata
TWI_Start();
                          // Start bitini oluştur
TWI_Write(0xA0);
                      // EEPROM adresini yazma biti ile gönder
TWI_Write(dataaddress[0]);
            // veri adresinin yüksek öncelikli 8 bitini gönder
TWI_Write(dataaddress[1]);
            // veri adresinin geri kalan 8 bitini gönder
TWI_Start();
                          // tekrar start biti oluştur
TWI_Write(0xA1);
                       // EEPROM adresini okuma biti ile gönder
*ptr = TWI_Read_ack ();                // veriyi oku
TWI_Stop();
                            // stop biti oluştur

}

Bu fonksiyonları datasheet te yazan okuma ve yazma formatlarına göre hazırladım. Eğer siz farklı bir mikrodenetleyici kullanıyorsanız uygun I2C fonksiyonları ile aynı formatta bu çipe veri yazıp okuyabilirsiniz.

2 comments

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s