DERS 11: Stm32f4xx- HMC5983 I2C Uygulaması

Bu dersimizde STM32F4xx ile IIC (Inter Integrated Circuit) kullanımına giriş yapacağız. I2C protokol yapısını inceledikten sonra STM32F4xx discovery board ile HMC5983 magnetometer sensörü ile I2C hattı üzerinden sensör verilerini okuyup UART kanalı üzerinde okunan verileri bilgisayara göndereceğiz.

► I2C nedir?

► I2C nasıl kullanılır?

► HMC5983 hakkında genel bilgiler

► Discovery board – Sensör – Uart bağlantıları

I2C, düşük bant genişliğine sahip kısa mesafede haberleşmede kullanabileceğimiz 2 uçlu seri haberleşme protokolüdür. I2C protokolünde temel olarak bir master ve bir/birkaç slave cihaz bulunur. Birden fazla master cihazın bulunduğu I2C haberleşmesi de multimaster haberleşme olarak adlandırılır.

yapı

 

I2C hattında besleme hattı haricinde 2 hat vardır. Bunlardan birincisi haberleşmeyi başlatıp sonlandıran ve veri hattı olan SDA (Serial Data) ile veri senkronizasyonu için kullanılan SCL (Serial Clock) hatlarından oluşmaktadır.

I2C hattı, SDA hattının lojik high seviyeden lojik low seviyeye düşmesi ile başlar ve lojik low seviyeden lojik high seviyeye çıkmasıyla sonlanır. Ayrıca SDA hattının haberleşmeyi başlatabilmesi için SCL hattının da lojik high seviyesinde olması gereklidir.

start

SCL hattı lojik high seviyedeyken SDA hattı lojik low a çekilirse I2C haberleşmesi başlatılmış olur.

stop

SCL hattı lojik high seviyedeyken SDA hattı lojik high seviyeye çekilirse I2C haberleşmesi sonlandırılmış olur.

Start ve stop durumları haricinde multimaster I2C haberleşmesinde Repeated Start komutu da sıklıkla kullanılmaktadır. I2C haberleşmesinde 2 adet master cihaz olduğunu varsayalım. Birinci master cihaz start komutunu gönderdi ve start komutundan sonra gerekli adres bilgilerini gönderdi. Tüm bu işlem süresince I2C hattı birinci master cihaz tarafından kullanıldığından I2C hattı idle durumda olmayacaktır. Birinci cihaz stop komutunu göndermeden önce haberleşmede bir değişiklik yapmak isterse Repeated Start komutunu gönderir ve böylece haberleşme için sırada bekleyen 2. Master cihaz I2C hattını kullanamamış olur ve bu sayede 1. Master cihazın slave cihaz ile I2C haberleşmesi kopmamış olur. Multimaster olmayan durumlarda ise Repeated Start komutunu kullanmaya gerek yoktur. Repeated Start komutu ard arda gelen Stop + Start komutlarından oluşmaktadır.

repeatedStart

I2C haberleşmesinde start komutundan sonra SCL hattı high iken slave cihazdan veri okunur, SCL hattı lojik low iken slave cihaza veri gönderilir. Gönderilecek bit 1 ise SDA hattı lojik high seviyesine, gönderilecek bit 0 ise SDA hattı lojik low seviyeye çekilir. Bu işlem 8 kere tekrarlandığı zaman 8 bit (1 byte) veri alınmış veya gönderilmiş olur.

I2C haberleşmesinde verinin gönderildiğini veya verinin alındığını doğrulamak için ACK (acknowledges) mesajları gönderilir. I2C haberleşmesinde 1 Master cihaz ve birden fazla slave cihaz olduğunu varsayalım. Master cihaz herhangi bir slave cihaza erişmek için start komutundan sonra ilgili slave cihazın adresini gönderir. Aynı hatta bağlı olan slave cihazların tamamı bu mesajı alır ancak sadece bu mesaja sahip olan slave cihaz ACK mesajını göndererek iletişimin kurulduğunu master cihaza bildirir ve ACK mesajını alan master cihaz adres bilgisinden sonra hemen veri göndermeye başlar.

♦ I2C veri gönderme:

send

Öncelikle master cihaz start komutunu gönderir ve devamında ise haberleşmek istediği slave cihazın 7 bitlik adresini ve devamında da veri göndereceğini belirttiği W komutunu gönderir. Bu mesajlardan sonra eğer gönderilen slave adresi ile eşleşen bir slave cihaz var ise master cihaza ACK bilgisini gönderir. Master ACK bilgisini aldıktan sonra 8 bitlik datayı gönderir ve tekrar slave cihazdan verinin alındığına dair bir ACK mesajı bekler. Veri gönderme işlemi bu şekilde arka arkaya defalarca tekrarlanabilir. En son alınan ACK mesajından sonra ise master cihazın göndereceği bir mesajı kalmamış ise Stop komutunu gönderir ve haberleşmeyi bitirir.

Örneğin I2C haberleşmemizdeki slave cihazın adresi 0100111 olsun. Bu durumda cihazın 7 bitlik adresi hexadecimal karşılığı  0x27 ye denk gelmektedir. Bu 7 bitlik slave adresinin devamına yazma bitini ifade eden “0” biti eklenince ikili tabanda 01001110 baytı elde edilir ve bu baytın hexadecimal karşılığı 0x4E e denk gelmektedir. Start komutundan sonra slave cihaza 0x4E gönderildiğinde bu mesajı alan slave cihaz ACK bitini gönderir ve devamında ise master cihaz dataları byte byte göndermeye başlar.

♦ I2C veri okuma:

read

Öncelikle master cihaz start komutunu gönderir ve devamında ise haberleşmek istediği slave cihazın 7 bitlik adresini ve devamında da veri okuyacağını belirttiği R komutunu gönderir. Bu mesajlardan sonra eğer gönderilen slave adresi ile eşleşen bir slave cihaz var ise master cihaza ACK bilgisini gönderir. Slave cihaz, ACK bilgisinden sonra 8 bitlik datayı master cihaza  gönderir ve bu sefer master cihaz slave cihaza ACK gönderir. ACK mesajını alan slave cihaz 8 bitlik dataları göndermeye devam eder. (Master cihaz veriyi başarıyla alamaz ise NACK mesajı gönderir) Veri okuma işlemi tamamlandığı zaman master cihaz Stop komutunu göndererek haberleşmeyi bitirir.

 

Yukarıda yazılanları özetleyecek olursak;

▬ I2C haberleşmesini başlatmak için Master Cihaz Start komutunu göndermelidir.

▬ Master Cihaz Slave Cihaza veri yazacak ise Start komutundan sonra Slave Cihazın 7 bitlik adresini ve devamında da yazma işlemini belirttiği W komutu olan, dijital olarak 0 bitini göndermelidir. Bu işlemlerden sonra Master Cihaz, Slave cihazdan ACK mesajını beklemelidir.

▬ Master Cihaz Slave Cihazdan veri okuyacak ise Start komutundan sonra Slave Cihazın 7 bitlik adresini ve devamında da okuma işlemini belirttiği R komutu olan, dijital olarak 1 bitini göndermelidir. Bu işlemlerden sonra Master Cihaz Slave cihazdan ACK mesajını beklemelidir.

▬ Multi master yapıda bir haberleşme var ise Repeated Start komutu gözardı edilmemelidir.

▬ Haberleşmeyi sonlandırmak için ise Master Cihaz Stop komutunu göndermelidir.

 

STM32F4XX Discovery board ile haberleştireceğimiz HMC5983 sensörünü inceleyelim.

 

HMC5983, Honeywell firmasının üretmiş olduğu 3 eksen 12 bit çözünürlükteki dijital manyetometre sensörüdür.

hmc5983

HMC5983 manyetometre sensörü I2C ve SPI arayüzleri üzerinden kontrol edilebilmektedir ancak dersimizin konusu gereği I2C arayüzünü kullanacağız. Sensör datasheet dosyasından sensörün I2C Arayüz bilgilerine göz atıyoruz.

Sensör Datasheet dosyasından elde ettiğimiz bilgilere göre sensör I2C hattı data rate için 100 kbps, 400 kbps ve 3,4 mbps hızlarının desteklendiği bilgisini elde ediyoruz. Ayrıca sensörün yazma adresinin ( 7 bit slave adresi + 0) 0x3C ve okuma adresinin ise ( 7 bit slave adresi + 1) 0x3D olduğu bilgisini elde ediyoruz. Haberleşme ile ilgili diğer bilgileri de datasheette I2C communication protokol başlığından öğreniyoruz.IICspeed

I2C haberleşme protokol bilgilerini öğrendikten sonra veri okuyup yazacağımız Register Adreslerine de sensör Datasheet dosyasından ulaşıyoruz.

registers

X-Y-Z eksenlerindeki verileri okumak için kullanacağımız registerlerin 0x03 – 0x08 adresleri arasında olduğunu görmekteyiz. Ayrıca her eksen verisinin 2 byte olduğunu da görmekteyiz.

Bu kadar bilgiden sonra kod yazmaya hazırız. Ancak kod yazmaya geçmeden önce Sensör – Discovery board I2C haberleşmesi için ve UART haberleşmesi için gerekli pinleri seçmemiz gerekmektedir. Bu konu hakkında daha detaylı bilgi için önceki derslere göz atabilirsiniz. Bu uygulamada I2C haberleşmesi için RB6-7 ve UART haberleşmesi için RA2 pinlerini seçeceğiz.

alternate

uart

I2C adında proje oluşturuyoruz. Önceki projelerden farklı olarak “Manage Run” kısmında I2C blogunu da seçiyoruz. (Bu kısım için önceki derslere göz atabilirsiniz.)

managerun

UART ve I2C kurulumlarını yaptıktan sonra kodumuzu aşağıdaki gibi yazabiliriz.

 

#include "stm32f4xx.h" // Device header
#include <stdio.h>    
#include <math.h>
 
#define OUT_X_H                         0x03
#define OUT_X_L                         0x04
#define OUT_Y_H                         0x07
#define OUT_Y_L                         0x08
#define OUT_Z_H                         0x05
#define OUT_Z_L                         0x06
#define HMC5983_ADDRESS                 0x1E
#define HMC5983_WRITE  			0x3C
#define HMC5983_READ  	         	0x3D
 
#define PI				3.14
 
uint8_t i2cdata,a1,a2;
float magX, magY, magZ, Derece;
 
uint8_t I2C_write(uint8_t devAddr, uint8_t regAddr, uint8_t val);
uint8_t I2C_readreg(uint8_t devAddr, uint8_t regAddr);
 
char str[50];
uint32_t i = 0;
 
void USART_Puts(USART_TypeDef* USARTx, volatile char *s)
{
	while(*s)
	{
		while(!(USARTx ->SR & 0x00000040)); // TX Buffer dolu ise bekle
		USART_SendData(USARTx, *s);
		*s++;
	}
}
 
void usart_ayar(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); // USART2 clock
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // A portu clock
 
		/* USART2 Ayarlari */
        GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_2;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_Init(GPIOA, & GPIO_InitStructure); 
 
	GPIO_PinAFConfig(GPIOA , GPIO_PinSource2,  GPIO_AF_USART2);
 
	USART_InitStructure.USART_BaudRate            = 9600; // baudrate
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode                = USART_Mode_Tx;
	USART_InitStructure.USART_Parity              = USART_Parity_No ;
	USART_InitStructure.USART_StopBits            = USART_StopBits_1;
	USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
	USART_Init(USART2,& USART_InitStructure);
	USART_Cmd(USART2, ENABLE);
}
 
void init_I2C1(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	I2C_InitTypeDef I2C_InitStruct;
 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
 
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, ENABLE);
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1, DISABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
 
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
 
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);	// SCL pini 
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1); // SDA pini
 
	I2C_InitStruct.I2C_ClockSpeed = 100000;    //i2c hizi normal modda                
	I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
	I2C_InitStruct.I2C_OwnAddress1 = 0x00;
	I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
	I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_Init(I2C1, &I2C_InitStruct);
 
	I2C_Cmd(I2C1, ENABLE);
}
 
 
uint8_t I2C_write(uint8_t devAddr, uint8_t regAddr, uint8_t val)
{
        while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY ));
 
        I2C_GenerateSTART(I2C1, ENABLE);
 
        while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT ));
 
        I2C_Send7bitAddress(I2C1, devAddr, I2C_Direction_Transmitter );
 
        while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ));
 
        I2C_SendData(I2C1, regAddr);
 
        while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING ));
 
        I2C_SendData(I2C1, val);
 
        while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING ));
 
        I2C_GenerateSTOP(I2C1, ENABLE);
}
 
uint8_t I2C_readreg(uint8_t devAddr, uint8_t regAddr)
{
        uint8_t reg;
        while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY ));
 
        I2C_GenerateSTART(I2C1, ENABLE);
 
        while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT ));
 
        I2C_Send7bitAddress(I2C1, devAddr, I2C_Direction_Transmitter );
 
        while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ));
 
        I2C_SendData(I2C1, regAddr);
 
        while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BTF ) == RESET);
 
        I2C_GenerateSTOP(I2C1, ENABLE);
 
        I2C_GenerateSTART(I2C1, ENABLE);
 
        while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT ));
 
        I2C_Send7bitAddress(I2C1, devAddr, I2C_Direction_Receiver );
 
        while (I2C_GetFlagStatus(I2C1, I2C_FLAG_ADDR ) == RESET);
 
	I2C_AcknowledgeConfig(I2C1, DISABLE);
 
	while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED ));
 
	reg = I2C_ReceiveData(I2C1);
 
        I2C_GenerateSTOP(I2C1, ENABLE);
 
        return reg;
}
 
void sensor_ayarla()
{
	// Power On Kurulumlari, Sensör Datasheet Sayfa 26
	I2C_write(HMC5983_WRITE, 0x00, 0x70); // 8-average, 15 Hz default, normal measurement, Sensör Datasheet sayfa 26
	I2C_write(HMC5983_WRITE, 0x01, 0xA0); // Gain=5, Sensör Datasheet sayfa 26
	I2C_write(HMC5983_WRITE, 0x02, 0x00); //Continuous-measurement mode, Sensör Datasheet sayfa 26
}
 
 
int main(void)
{
 
	SystemInit();
	init_I2C1();
	usart_ayar();
	sensor_ayarla();
 
	while (1)
		{
			a1 = I2C_readreg(HMC5983_READ,OUT_X_L);
			a2 = I2C_readreg(HMC5983_READ,OUT_X_H);
			magX =((a2<<8) | a1);
//			magX = magX * 0.00092; //Digital Resolution (G/LSb), Sensör Datasheet sayfa 16
 
 
			a1 = I2C_readreg(HMC5983_READ,OUT_Y_L);
			a2 = I2C_readreg(HMC5983_READ,OUT_Y_H);
			magY=((a2<<8) | a1);
//			magY = magY * 0.00092; //Digital Resolution (G/LSb), Sensör Datasheet sayfa 16
 
			a1 = I2C_readreg(HMC5983_READ,OUT_Z_L);
			a2 = I2C_readreg(HMC5983_READ,OUT_Z_H);
			magZ =((a2<<8) | a1);
//			magZ = magZ * 0.00092; //Digital Resolution (G/LSb), Sensör Datasheet sayfa 16
 
 
			/*
			* Hesaplanan magX, magY ve magZ raw datalari kullanilarak 
			* dünya manyetik alan acisi hesaplanmistir.
			*/
			if (magX > 0x07FF) magX = 0xFFFF - magX;
			if (magZ > 0x07FF) magZ = 0xFFFF - magZ;
			if (magY > 0x07FF) magY = 0xFFFF - magY;
 
			if (magY == 0 && magX > 0) Derece = 180.0;
			if (magY == 0 && magX <= 0) Derece = 0.0;
			if (magY > 0) Derece = 90 - atan(magX/magY) * 180 / PI;
			if (magY < 0) Derece = 270 - atan(magX/magY) * 180 / PI;
 
			sprintf(str,"Direction:%f **\n",Derece);
			USART_Puts(USART2,str);
	}
}

Seri port üzerinden herhangi bir terminal programı yardımızla UART kanalı üzerinden manyetik açı bilgimizi gözlemleyebiliriz.

data

*** Çalışmalarımıza devam ederken kütüphaneleri incelemeyi unutmayalayım.

IICkeil

 

►  Matematiksel hesaplamalar için linkteki dökümandan yararlanılmıştır.

https://aerocontent.honeywell.com/aero/common/documents/myaerospacecatalog-documents/Defense_Brochures-documents/Magnetic__Literature_Application_notes-documents/AN203_Compass_Heading_Using_Magnetometers.pdf

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir