CubeMX ile I2C Projesi

Merhaba,

Benim de projelerimde aktif olarak kullandığım MPU6050 sensör ile ilgili forumda (PICproje.org) bir arkadaş soru sormuş. Bunun üzerine çalışırlığını denediğim, sadece HAL Kütüphanelerini kullanarak oluşturduğum projemi sizlere sunmak istedim. Umarım faydalı bir paylaşım olur. Lütfen soru ve yorumlarınızı eklemekten çekinmeyin.

Proje oluşturma aşaması, videolu anlatımı:

Projemizi oluşturduğumuza göre kullanacağımız değişkenleri tanımlayalım.

uint8_t TxBuffer[2];
uint8_t RxBuffer[7];
uint8_t DataBuffer[14];
int16_t DataBuffer16[7];
uint8_t MPU6050_INT_State = 0;
int16_t gyro_x_temp, gyro_y_temp, gyro_z_temp, accel_x_temp, accel_y_temp, accel_z_temp, temp_raw;
float temp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z;

Daha sonra kullanacağımız fonksiyonları tanımlayalım.

static void Error_Handler(void);
static void MPU6050_Extract_Readings(void);

Error_Handler() fonksiyonu herhangi bir hata tespitinde bulunmak istersek kullanabileceğimiz bir fonksiyon olacak. Oluşturduğum koşul (if) kontrollerinde hata tespit edersem bu fonksiyona yönlendirip programı sonsuz while döngüsünün içine sokuyorum. Bu geliştirme aşamasında ciddi manada yardımcı oluyor.

MPU6050_Extract_Readings() fonksiyonu ise sensörden gelen ham veriyi işleyip son haline dönüştürüyor.

Şimdi MPU6050’nin istediğimiz gibi çalışması için gerekli register’lara gerekli değerleri yazıyoruz. Bu değerlerin anlamlarını yanlarına yazmadım, sizler de modülün Register Haritasına bakarak kolayca ne işe yaradıklarını anlayabilirsiniz.

TxBuffer[0] = 0x80;
HAL_I2C_Mem_Write(&hi2c1, MPU6050_DEFAULT_ADDRESS, MPU6050_RA_PWR_MGMT_1, 1, TxBuffer, 1, 1000);
HAL_Delay(200);
TxBuffer[0] = 0x00;
HAL_I2C_Mem_Write(&hi2c1, MPU6050_DEFAULT_ADDRESS, MPU6050_RA_PWR_MGMT_1, 1, TxBuffer, 1, 1000);
HAL_Delay(200);
TxBuffer[0] = 0xF8;
HAL_I2C_Mem_Write(&hi2c1, MPU6050_DEFAULT_ADDRESS, MPU6050_RA_FIFO_EN, 1, TxBuffer, 1, 1000);
HAL_Delay(200);
TxBuffer[0] = 0x10;
HAL_I2C_Mem_Write(&hi2c1, MPU6050_DEFAULT_ADDRESS, MPU6050_RA_FIFO_EN, 1, TxBuffer, 1, 1000);
HAL_Delay(200);
HAL_I2C_Mem_Read(&hi2c1, MPU6050_DEFAULT_ADDRESS, MPU6050_RA_ACCEL_CONFIG, I2C_MEMADD_SIZE_8BIT, RxBuffer, 1, 1000);
HAL_Delay(200);
RxBuffer[0] |= 0x18;
HAL_I2C_Mem_Write(&hi2c1, MPU6050_DEFAULT_ADDRESS, MPU6050_RA_ACCEL_CONFIG, 1, RxBuffer, 1, 1000);
HAL_I2C_Mem_Read(&hi2c1, MPU6050_DEFAULT_ADDRESS, MPU6050_RA_INT_ENABLE, I2C_MEMADD_SIZE_8BIT, RxBuffer, 1, 1000);
HAL_Delay(200);
RxBuffer[0] |= 0x11;
HAL_I2C_Mem_Write(&hi2c1, MPU6050_DEFAULT_ADDRESS, MPU6050_RA_INT_ENABLE, 1, RxBuffer, 1, 1000);
HAL_I2C_Mem_Read(&hi2c1, MPU6050_DEFAULT_ADDRESS, MPU6050_RA_INT_PIN_CFG, I2C_MEMADD_SIZE_8BIT, RxBuffer, 1, 1000);
HAL_Delay(200);
RxBuffer[0] |= 0x30;
HAL_I2C_Mem_Write(&hi2c1, MPU6050_DEFAULT_ADDRESS, MPU6050_RA_INT_PIN_CFG, 1, RxBuffer, 1, 1000);
HAL_I2C_Mem_Read(&hi2c1, MPU6050_DEFAULT_ADDRESS, MPU6050_RA_CONFIG, I2C_MEMADD_SIZE_8BIT, RxBuffer, 1, 1000);
HAL_Delay(200);
RxBuffer[0] |= 0x06;
HAL_I2C_Mem_Write(&hi2c1, MPU6050_DEFAULT_ADDRESS, MPU6050_RA_CONFIG, 1, RxBuffer, 1, 1000);

Bu noktadan sonra işlemci ana while döngüsünde (super loop) sürekli veri okuyup dönüştürme işlemi yapacak. Gelen verileri istediğiniz gibi kullanabilirsiniz.

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1) {
  while (MPU6050_INT_State == 1) {
    HAL_GPIO_WritePin(GPIOD, LD4_Green_Pin, GPIO_PIN_SET);
    //MPU6050_I2C_Mem_Read_Multi(MPU6050_DEFAULT_ADDRESS, MPU6050_RA_ACCEL_XOUT_H, DataBuffer, 14);
    HAL_I2C_Mem_Read( & hi2c1, MPU6050_DEFAULT_ADDRESS, MPU6050_RA_ACCEL_XOUT_H, I2C_MEMADD_SIZE_8BIT, DataBuffer, 14, 10000);
    for (int i = 0; i < 7; i++) {
      DataBuffer16[i] = (int16_t)(((uint16_t) DataBuffer[2 * i] << 8) | DataBuffer[2 * i + 1]);
    }
    MPU6050_Extract_Readings();
    MPU6050_INT_State = 0;
  }
  HAL_GPIO_WritePin(GPIOD, LD4_Green_Pin, GPIO_PIN_RESET);
}
/* USER CODE END WHILE */
Kesme oluştuğunda çalışacak fonksiyonumuz.
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
  if (GPIO_Pin == GPIO_PIN_5) {
    MPU6050_INT_State = 1;
  }
}

Hata ayıklama fonksiyonumuz. (Bu uygulamada hiç kullanılmıyor.)

static void Error_Handler(void) {
  while (1) {
    HAL_GPIO_TogglePin(GPIOD, LD5_Red_Pin);
    HAL_Delay(250);
  }
}

Ham verileri işlediğimiz fonksiyon. Bu fonksiyonda float sayılarla yapılan işlemlerin FPU (Floating Point Unit) ünitesi tarafından gerçekleştirilmesi için sayıların sonuna “F” harfi eklenmiştir. Aksi taktirde kayar noktalı sayılarla işlem yazılımsal olarak yapılacaktı ve bu da programı ciddi manada yavaşlatacaktı. Farklı işlemcilerle çalışan arkadaşlar bu konuya dikkat etsinler.

void MPU6050_Extract_Readings() {
  // Extract accel readings
  accel_x_temp = DataBuffer16[0];
  accel_x = (float) accel_x_temp / 2048.0 F;
  accel_y_temp = DataBuffer16[1];
  accel_y = (float) accel_y_temp / 2048.0 F;
  accel_z_temp = DataBuffer16[2];
  accel_z = (float) accel_z_temp / 2048.0 F;
  // Extract temperature readings
  temp_raw = DataBuffer16[3];
  temp = temp_raw / 340.0 F;
  // Extract gyro readings
  gyro_x_temp = DataBuffer16[4];
  gyro_x = (float) gyro_x_temp / 131.0 F;
  gyro_y_temp = DataBuffer16[5];
  gyro_y = (float) gyro_y_temp / 131.0 F;
  gyro_z_temp = DataBuffer16[6];
  gyro_z = (float) gyro_z_temp / 131.0 F;
  // Set accel offset
  accel_x += Accelx_offset;
  accel_y += Accely_offset;
  accel_z += Accelz_offset;
  // Set temp offset
  temp += Temp_offset;
  // Set gyro offset
  gyro_x += Gyrox_offset;
  gyro_y += Gyroy_offset;
  gyro_z += Gyroz_offset;
}

Son olarak sensörün Register Haritasını ve veri işleme fonksiyonumuzdaki offset değerlerini içeren header dosyasını buradan indirebilirsiniz.
Bu dosyayı main.c dosyanızın en üstünde projeye dahil ederseniz sorun yaşamazsınız.

Kolay gelsin.

CubeMX ile I2C Projesi’ için 31 yanıt

Add yours

    1. Tamamen MPU6050’nin yapısıyla ilgili. Elbette standart Tx ve Rx fonksiyonları kullanılabilirdi. Ancak biz doğrudan bir Register’a yazma ya da Register’dan okuma yaptığımız için HAL Kütüphanesinin bize sunduğu Mem_Read ve Mem_Write fonksiyonlarını kullanmayı tercih ettim.

      Diğer seçenekte ise önce Register adresini yazacaktık, daha sonra okuma yapacaktık. Seçim sizin.

      İyi günler, kolay gelsin.

      Beğen

      1. Anladım son bir soru daha sormak istiyorum. Yukarıda Tx ve Rx bufferlarına sürekli farklı adresler tanımlamışsınız bu adresleri register mapte bulamadım nedir onlar ?

        Liked by 1 kişi

      2. TxBuffer, Register’lara yazılacak değerin yüklendiği dizi.
        RxBuffer ise bazı Register’ların eski değerleri üzerinde değişiklik yapılıp tekrar yazılmak istendiğinde kullanılan okuma buffer’ı.

        Yani okuma varsa, RxBuffer’a okuyup, düzenleyip terkar Register’a yazıyoruz.
        Okuma yoksa, doğrudan değer atanacaksa TxBuffer’ı elle doldurup istediğimiz Register’a yazıyoruz.

        Kolay gelsin, iyi akşamlar.

        Beğen

  1. accel_x += Accelx_offset;
    accel_y += Accely_offset;
    accel_z += Accelz_offset;

    Şu bölümde sağ tarafta tanımladığımız değerleri değişken olarak atamamışız? Bunları yeni bir değişken olarak atarsak eğer başlangıç değeri olarak ne alacaklar? Şimdiden teşekkürler

    Beğen

    1. Merhaba.

      Yazının sonunda paylaştığım linkten indirebileceğiniz header dosyasında bahsettiğiniz değerler tanımlanmış. Sabit değerler oldukları için define etiketi ile tanımlandılar. Değişken olarak tanımlamak isterseniz de yine o dosyada değerler mevcut.

      İyi çalışmalar…

      Beğen

  2. Merhaba;
    STM32F4 ile MCP4725 DAC için I2C ile çoklu işlem yapan bir örnek paylaşabilir misiniz?
    1.DAC -> Sinus Dalga
    2.DAC-> Kare Dalga
    3.DAC-> Üçgen Dalga

    Beğen

    1. Maalesef bahsettiğiniz çipi ilk kez sizden duyuyorum. Elimde olsa uğraşmaya çalışırdım ancak, buradaki örnek bu çipi kullanmanıza yetecek kadar bilgi içeriyor.

      I2C hattınızda üç adet slave çip bulunsun istemişsiniz. Bu durumda her çipin adresinin farklı olması gerekiyor. Çipin sadece bir adres bacağı dışarıya verilmiş. Bu yüzden çiplerinizden en az birini farklı adresli olacak şekilde sipariş etmek zorundasınız. Toplamda 8 farklı kombinasyon mevcut, yani en fazla 8 çip aynı hatta kullanabilirsiniz.
      Buraya kadar tamamsa gerisi datasheet bölüm 6’da “Theory of Operation” kısmında anlatılmış. Takıldığınız bir nokta olursa picproje.org’da konu açıp yazının sonuna @Cemre. diye ekleme yapın, fırsat bulduğumda yardımcı olmaya çalışırım.

      İyi çalışmalar.

      Beğen

  3. Verdiğiniz kodları tam çalıştıramadım acaba bunları kod içinde hangi bölümleri yazıldığını söyleyebilir misiniz?

    Beğen

    1. Merhaba,

      Doğru anladıysam;
      İlk iki kod parçası değişken ve fonksiyon tanımlamalarını içeriyor. Bunları main.c dosyanızda “include” satırlarından hemen sonraya ekleyebilirsiniz.
      Bir sonraki uzun kod parçasını main fonksiyonunun içinde, while döngüsünden hemen önceye ekleyebilirsiniz.
      Bir sonraki zaten while döngüsünün içeriğini veriyor. Onu da sizdeki while’ın yerine ekleyebilirsiniz.
      Geriye kalan kod parçalarını ise main fonksiyonunun son süslü parantezinden sonra herhangi bir yere ekleyebilirsiniz. (Projeyi CubeMX ile oluşturduğunuzu varsayarsak /* User Code 4 */ tagleri arasına…)

      Beğen

      1. Dediğiniz gibi yaptım fakat hiçbir veri okuyamıyorum acaba size mail olarak projeyi atsam 10 dakikanızı ayırır mısınız ?

        Beğen

      2. Merhaba.
        Çalışmanın üzerinden o kadar zaman geçti ki, muhtemelen 10 dakikaya çözemeyeceğim. En iyisi şöyle yapalım, “picproje.org” forumda bir konu açın, açıkça kodunuzu paylaşın, @Cemre. diye mention atın, oradan bakmaya çalışalım. Bu şekilde birçok kişiye ulaşır sorunuz ve daha hızlı yanıt alırsınız.
        İyi çalışmalar.

        Beğen

  4. Ben de aynı kodları interrupt olmadan kullandım ama ilk veriyi okuduktan sonraki verileri okuyamıyorum. Ayrıca MPU6050’den veri okuyabilmek için öncelikle gerçekleştirilmesi gereken birkaç adım var mesela START ve RESTART sinyalleri gibi ama bu sinyallere dair hiçbirşey göremedim kodlarda ama ilk veriyi nasıl okudum onu da anlamadım.

    Beğen

  5. Tx ve Rx değerleri sürekli değişiyor. Tx önce 0x80 sonra 0x00 vermişsiniz ve bu değerler sürekli değişiyor. Rx de aynı şekilde bu değerleri neye göre verdiniz acaba ? Bir de Rx in önünde | işareti var o işaret ne için ? Kusura bakmayın çok soru soruyorum ama bu işlerde yeniyim ve öğrenmek istiyorum 🙂

    Beğen

    1. TX, register’a yazılacak değeri belirtiyor. MPU6050 için register haritasını incelerseniz hangi bitlere hangi değerler atanmış, hangi fonksiyonlar aktif veya pasif edilmiş göreceksiniz. Bu buradan anlatılamayacak kadar uzun bir konu maalesef. Ama basitçe misal PWR_MGMT register’ına önce 0x80 sonra 0x00 yazdığımızda, DEVICE_RESET bit’ini önce 1 sonra 0 yapmış oluyoruz. Bu işlem modülü uyandırmak için yapılıyor.

      RX ile yapılan | işleminin (OR (veya) işlemi) sebebi de şudur. Önce CONFIG register’ı okunur, gelen değer bir değişkene yazılır. Sonra 0x60 ile veya işlemine sokularak 5 ve 6. bit’i 1 yapılır. Daha sonra elde edilen değer tekrar CONFIG register’ına geri yazılır. Buradaki amaç CONFIG register’ındaki 5 ve 6. bit dışındaki bitlerin değerlerini olduğu gibi korumak istememizdir.

      Kolay gelsin.

      Beğen

  6. Merhaba ,rica etsem bu CCS C deki kodlara göre nasıl düzenleme yapmam gerektiğinden bahseder misiniz?Çoklu okuma nasıl yapılıyor?Birkaç gün önce Arm’a geçiş yaptım Hal kütüphanesini incelemeden önce sizden bilgi almam işimi kolaylaştıracaktır.
    I2C_MEMADD_SIZE_8BIT görevi nedir.

    HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);

    Kısaca üstteki fonksiyondan bahsedebilir misiniz? Timeout ne içindir neye göre belirlenir.
    Master_Receive ile arasındaki fark nedir? Mem_Read direkt olarak bir kaydedici içerisinden veriyi almayı mı sağlıyor?

    Blocking mode nedir?

    Teşekkürler.

    Düzenleme: Lütfen yorumlarda uzun kod parçaları paylaşmayınız. Bunun yerine pastebin.com gibi siteleri kullanabilirsiniz. Teşekkürler.

    Beğen

    1. I2C_MEMADD_SIZE_8BIT, aygıtın register haritasında adres genişliğini belirtiyor. İki seçenek var 8 bit ve 16 bit.

      Bahsettiğiniz platforma aşina değilim. Master_Receive nedir bilmiyorum.

      TimeOut değeri, okuma işlemi başladıktan sonra kaç milisaniye içerisinde tamamlanamazsa TimeOut hatası ile dönecek, bunu belirlemeye yarıyor. Cevap gelmiyorsa bir süre, hata döndürüyor.
      Blocking mode, Interrupt mode ve DMA mode HAL kütüphanesinde neredeyse tüm çevrebirim erişim fonksiyonları için geçerli. Bunları ayrıca araştırmanız sizin faydanıza olacaktır.

      Kolay gelsin.

      Beğen

  7. Şunuda ekleyecektim
    Adres 00011101 –> 0x1D / 000111010 –> 0x3A Yazma / 000111011 –> 0x3B Okuma
    Cihaz adresi olarak 0x1D mi kullanmalıyım yazma veya okuma yapacağım zaman fonksiyon son biti kendisi mi tamamlıyor?

    Beğen

    1. I2C cihazların adresleri 7 + 1 bit ile ifade ediliyor, zaten siz de bu durumdan bahsetmişsiniz. Cihaz datasheet’inde çoğunlukla 7bit formatta adres verilir. Misal MPU6050 için 0x68. Ancak bu kütüphane 8bit formatta adres girişine olanak tanıyor. Bu nedenle bu adresi bir kez sola kaydırıp kullanmak yeterli oluyor. Write veya Read fonksiyonu kullanıyor olmanız farketmiyor, yine gerekli düzenleme fonksiyon içerisinde yapılıyor.
      Kolay gelsin.

      Beğen

      1. 0x68 atama yapılıp sola kaydırıldığında D0 oluyor tamam ,
        0011101 atama yapıp kaydırırsam 3A bu durumda bunu kullanmam gerek,kodalarımda bunu kullanıyorum fakat cihazdan aldığım veriyi atadığım dizi elemanında 0 görünüyor sürekli hala çalıştıramadım örnek kodları atayım bir yazım yanlışımı yapıyorum acaba ilk başlarda bu şekilde takılabiliyorum yardımcı olursanız çok memnun olurum.Şu anda döngünün içerisinde takılıyorum

        Düzenleme: Lütfen yorumlarda uzun kod parçaları paylaşmayınız. Bunun yerine pastebin.com gibi siteleri kullanabilirsiniz. Teşekkürler.

        Beğen

  8. Hi all, I don’t know … // Set Accel
    accel_x + = Accelx_offset;
    accel_y + = Accely_offset;
    accel_z + = Accelz_offset;

    // Set
    tạm thời + = Temp_offset;

    // Set gyro
    gyro_x + = Gyrox_offset;
    gyro_y + = Gyroy_offset;
    gyro_z + = Gyroz_offset;
    Where were they define
    thanks !

    Beğen

    1. Merhaba,
      Bahsettiğiniz sensör ile ilgili bilgim yok maalesef. Ancak benzer olarak I2C donanımı üzerinden haberleşmek mümkün gözüküyor. Bu çalışmada izlenen adımlara benzer olarak ilerleyip, ilgili ürünün Datasheet dokümanından faydalanarak istediğiniz değerleri sensörden okuyabilmeniz gerekir. Burada yapacağınız işlemler kullanacağınız platformdan bağımsız olarak I2C protokolü ve kullanmak istediğiniz sensör ile ilgili olarak değişkenlik gösterir.
      İyi çalışmalar dilerim.

      Beğen

Yorum bırakın

WordPress.com'da bir web sitesi veya blog oluşturun

Yukarı ↑