24 Nisan 2019 Çarşamba

STM32F4 I2C Kullanımı Standart Peripheral Library (SPL) MPU6050 İvme ve Gyro Sensörü - GY-521

    I2C iletişim protokolünde veri aktarımı için SDA (Serial Data Line) ve SCL (Serial Clock) olmak üzere iki adet haberleşme hattı bulunur. SDA, cihazlar arasındaki veri aktarımının sağlandığı hattır. Bu hatta çift yönlü veri aktarımı olur. Hatta aktarılan verilerin senkronizasyonu, SCL hattı ile gerçekleştirilir. . SDA hattındaki veri iletimi, SCL sinyaline göre düzenlenir. SDA ve SCL hatları, pull-up dirençlerle VCC hattına bağlanmalıdır. I2C haberleşen cihazların GNDlerinin ortak olması  gerekmektedir.

    İletişim başlaması için ilk olarak  master cihazı slave cihaza start komutunu gönderir. Ardından adres bilgisini ve veri göndereciğini yada veri alacağını belirten bilgi datasını göndermesi gerekir. Master slave'e ait adresi göndermişse slave master'a ack bilgisini gönderir. Eğer veri gönderilecekse master ACK bilgisini aldıktan sonra 8 bitlik data gönderir ve slave cihazdan verinin alındığına dair bir ACK bilgisi bekler. Gönderilmesi gereken data bitene kadar data iletimi 8 bit data-ACK-8 bit data-ACK şeklinde gönderilir . En son alınan ACK mesajından sonra master  stop komutunu gönderir ve haberleşmeyi bitirir. Veri alımı yapılacak ise master slave adresini gönderdikten sonra veri alacağını slave'e gönderir slave adresi doğrulamak için ACK mesajını yollar ve ardından 8 bit datayı master'a yollar bu kez master slave'a  ACK masajını gönderir ve veri iletimi bu şekilde devam eder. Master veri alamazssa  slave'e NACK biti gönderir. Veri iletimi tamamlandığında master stop komutu gönderir ve haberleşmeyi bitirir.










    STM32F4 kartı ile MPU6050  İvme ve Gyro Sensörü I2C iletişiminde STM32F4 master MPU6050 ise slave cihaz olacak. MPU6050'nin adresi AD0 pinine göre 68 veya 69 olarak seçilebilir. AD0 Low olursa 0x68 Hıgh olursa 0x69 kullanılır. I2C konfigürasyon aşarları ayarlamaları şu şekildedir.



void init_I2C1(void){
 
 GPIO_InitTypeDef GPIO_InitStruct;
 I2C_InitTypeDef I2C_InitStruct;
 
 // enable APB1 clock bus for I2C1
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
 // enable AHB1 clock bus for SCL and SDA pins
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
 
 /*
  SCL on PB6 and SDA on PB7 
  SCL on PB8 and SDA on PB9
  */
 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PB6 and PB7
 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;   // set to alternate function
 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;  
 GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;   //set to open drain 
 GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;   // pull up resistors
 GPIO_Init(GPIOB, &GPIO_InitStruct);     
 
 GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1); // SCL
 GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1); // SDA
 
 // I2C1 
 I2C_InitStruct.I2C_ClockSpeed = 100000;   
 I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;   
 I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; 
 I2C_InitStruct.I2C_OwnAddress1 = 0x00;   // own address
 I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;  // enable acknowledge 
 I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; // 7 bit address length 
 I2C_Init(I2C1, &I2C_InitStruct);   
 
 // enable I2C1
 I2C_Cmd(I2C1, ENABLE);
}



 
Veri gönderme ve alma fonksiyonları bu şekildedir.





void write_i2c(I2C_TypeDef* I2Cx,uint8_t address, uint8_t data){


    // wait until I2C1 is not busy anymore
 while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));
 
  // I2C1 enable
 I2C_GenerateSTART(I2Cx, ENABLE);
   
 // wait for I2C1 EV5 --> Slave has acknowledged start condition
 while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
  
 // Send slave Address for write 
 I2C_Send7bitAddress(I2Cx, address, I2C_Direction_Transmitter);
 
 while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
 
 I2C_SendData(I2Cx, data);
 // wait for I2C1 EV8_2 --> byte has been transmitted
 while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
 
 I2C_GenerateSTOP(I2Cx, ENABLE);

}


int read_i2c(I2C_TypeDef* I2Cx,uint8_t address)
{
 // wait until I2C1 is not busy anymore
 while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));
  
 // I2C1 enable
 I2C_GenerateSTART(I2Cx, ENABLE);
   
 // wait for I2C1 EV5 --> Slave has acknowledged start condition
 while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)); 
  
 // Send slave Address for read 
 I2C_Send7bitAddress(I2Cx, address, I2C_Direction_Receiver);
 
 
 while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
 
 I2C_AcknowledgeConfig(I2Cx, DISABLE);
 
 I2C_GenerateSTOP(I2Cx, ENABLE);
 
 // wait until one byte has been received
 while( !I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) );
 
 // read data from I2C data register 
 uint8_t data = I2C_ReceiveData(I2Cx);
 return data;

}


main fonksiyonu 



int main(void){
 
 init_I2C1(); // initialize I2C peripheral
 
 GPIO_InitTypeDef GPIO_InitStructure;
 
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);
 
 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;
 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12 |GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15 ;
 GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
 GPIO_Init(GPIOD,&GPIO_InitStructure);
 

 
 while(1){
  
   write_i2c(I2C1,ADDRESS<<1,0X6B); // PWR_MGMT_1 register 
 write_i2c(I2C1,ADDRESS<<1,0X00); // wakes up the MPU-6050

 write_i2c(I2C1,ADDRESS<<1,0X3B);//ACCEL_XOUT_H 
 xh=read_i2c(I2C1,ADDRESS<<1);
  
 write_i2c(I2C1,ADDRESS<<1,0x3C);//ACCEL_XOUT_L  
 xl=read_i2c(I2C1,ADDRESS<<1);
  
 write_i2c(I2C1,ADDRESS<<1,0x3D);//ACCEL_YOUT_H 
 yh=read_i2c(I2C1,ADDRESS<<1);
  
 write_i2c(I2C1,ADDRESS<<1,0x3E);//ACCEL_YOUT_L
 yl=read_i2c(I2C1,ADDRESS<<1);
 
  x  =((xh<<8 | xl));
  y  =((yh<<8)| yl);
   
 xm=map(x,-17000,17000,0,100);
 ym=map(y,-17000,17000,0,100);
   
   delay(10000);

  
             if(0<xm & xm<50)
        {
         GPIO_SetBits(GPIOD,GPIO_Pin_14);
       GPIO_ResetBits(GPIOD,GPIO_Pin_12);
        }
    
      if(50<=xm & xm<=100)
        {
  GPIO_SetBits(GPIOD, GPIO_Pin_12);
    GPIO_ResetBits(GPIOD, GPIO_Pin_14);
        }
   
      if(0<ym & ym<50)
               {
         GPIO_SetBits(GPIOD,  GPIO_Pin_13);
      GPIO_ResetBits(GPIOD,GPIO_Pin_15);
        }
      
      if(50<=ym & ym<100)
        {
         GPIO_SetBits(GPIOD, GPIO_Pin_15);
  GPIO_ResetBits(GPIOD,GPIO_Pin_13);
        }
   
          }
}






    Proje dosyasına buradan ulaşabilirsiniz. bu uygulamada MPU6050'ın konumuna göre STM32F4 üzerindeki ledler yanmaktadır. 














5 Nisan 2019 Cuma

STM32F4 USART Örneği Standart Peripheral Library(SPL)


     USART (Universal Asynchronous Receiver Transmitter)  senkron yada asenkron olarak veri gönderimi ve alımını sağlayan bir seri iletişim.  USART ile 3 – 9 arası bit uzunluğunda veri taşır. STM32F4’te 8 veya 9 bit uzunlukta veri taşınır. senkron modda veri aktarımı için clock hattına ihtiyaç duyar. Asenkron modda ise herhangi bir clock’a ihtiyaç duymadan veri aktarımı sağlanabilir. Veri aktarımı için veri aktarım hızı yani baud rate belirlenmesi gerekir. Baud rate saniyede gönderilen bit sayısı demektir ve genel olarak 1200, 2400, 4800, 19200, 38400, 57600 ve115200 değerleri kullanılır. Alıcı ve vericinin  aynı baud rate değerlerine sahip olmaları gerekmektedir.  
     Bilgisayara USART ile veri iletmek ve almak için usb-ttl dönüştürücüsü gerekir. Bağlantı yaparken Rx-Tx, Tx-Rx şeklinde yapılır. Eğer stm32f4 usb ile bilgisayara bağlıysa usb-ttl dönüştürücünün diğer pinlerini bağlamaya gerek yoktur. USART için kullanılabilecek portlar aşağıdaki tablodaki gibidir. Bu uygulamada PA2 (Tx )ve PA3(Rx) pinleri kullanılacak. 

      STM32F4’te usart ayarları stm32f4xx_usart.h kütüphanesinde bulunan USART_InitTypeDef structı kullanılarak yapılır. Bu struct ile baudrate, data uzunluğu, datanın gönderileceği yada alınacağı gibi temel ayarları yapılır.






// USART2
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART2, &USART_InitStruct);
USART_Cmd(USART2, ENABLE);

  Rx ve Tx olarak kullanıcağımız pinlerin tanımlamalarını yapıyoruz.  


GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);//PA2->Tx
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);//PA3->Rx

//GPIOA
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStruct);


Bilgisayardan STM32F4’e veri göndermek için kullanılacak fonksiyonu yazıyoruz.



uint16_t USART_GetChar()
{

    while (!USART_GetFlagStatus(USART2, USART_FLAG_RXNE));

    return USART_ReceiveData(USART2);
}




STM32F4’ten bilgisayara veri gönderimini sağlayacak fonksiyonları yazıyoruz. 



void USART_PutChar(char c)
{

   while (!USART_GetFlagStatus(USART2, USART_FLAG_TXE));

   USART_SendData(USART2, c);
}

void USART_PutString(char *s)
{
  // stringi gönder
   while (*s)
   {
        USART_PutChar(*s++);
   }
}




Fonksiyonları ve tanımlamaları main.c dosyasında birleştirerek örnek programı yazıyoruz. Program da bilgisayardan STM32F4'e H karakteri gönderilirse PE7 pini high olacak ve led yanacak, L karakteri gönderilirse PE7 pini low olacak, S karakteri gönderilirse STM32F4'ten bilgisayara Hello World stringi gelecek ve ekrana yazılacak.



#include "stm32f4xx.h"


uint16_t USART_GetChar()
{
    // Data alinana kadar bekle
    while (!USART_GetFlagStatus(USART2, USART_FLAG_RXNE));
    // Alinan datayi oku
    return USART_ReceiveData(USART2);
}


void USART_PutChar(char c)
{
    // Data registeri bosalana kadar bekle 
    while (!USART_GetFlagStatus(USART2, USART_FLAG_TXE));
    // USART1 ile c datasini gönder
    USART_SendData(USART2, c);
}

void USART_PutString(char *s)
{
    // stringi gönder
    while (*s)
    {
         USART_PutChar(*s++);
    }
}



int main(void)
{
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);


    GPIO_InitTypeDef GPIO_InitDef;
    GPIO_InitDef.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitDef.GPIO_OType = GPIO_OType_PP;
    GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOE, &GPIO_InitDef);


    GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);//PA2->Tx
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);//PA3->Rx

    //GPIOA
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // USART2
    USART_InitTypeDef USART_InitStruct;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_InitStruct.USART_BaudRate = 115200;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_Init(USART2, &USART_InitStruct);
    USART_Cmd(USART2, ENABLE);



    while (1)
    {


        uint16_t data = USART_GetChar(); //bilgisayardan input al 

        if (data == 'H')//H karakteri gelirse PD13 High olsun
        {
            GPIO_SetBits(GPIOE, GPIO_Pin_8);
        }
        else if (data == 'L')//L karakteri gelirse PD13 Low olsun 
        {
            GPIO_ResetBits(GPIOE, GPIO_Pin_8);
        }
        else if (data == 'S')//L karakteri gelirse PD13 Low olsun 
        {
            USART_PutString("Hello, World\n");
        } 
 
     }
}


Programın örnek videosu aşağıdadır.  Sorularınızı yorum olarak belirtirtebilirsiniz.