mpu6050 belirli aralıklarla veri almak yardım ?

Başlatan koray692, 19 Şubat 2016, 12:52:18

koray692

merhaba arkadaşlar ben i2c ile sürekli olarak veri alabiliyorum ancak gyro ile açı belirleyebilmem için belirli aralıklarla örneklemek istiyorum mpu6050 nin üzerinde bir int pini var onu nasıl kullancağımı anlayamadım register map ten yardımcı olursanız sevinirim... ayrıca şu yöntemleri de denedim ama çalışmadı stm32f4 kartında kesme oluşturup kesme fonksiyonu içinde i2c veri alma kodlarını yazdım...sürekli olarak aldığım verilerin birleştirilme gibi kısımlarını belirli sürelerle kesme fonksiyonu içinde yaptırmaya çalıştım...işe yaramadı :)

Cemre.

Kesme içerisinde uzun uzadıya dönüşümler yada haberleşme vs yapmak pek hoş olmaz malesef.
INT pini için yapılabilecek ayarların başında OpenDrain mi yoksa Push Pull mu çalışacak bunu seçmek geliyor. Bir de veri hazır olduğunda INT bacağı High mı Low mu olsun bunu seçmek gerekiyor.
İçerisinde INT geçen registerlar bu bacakla ilgili ayarları içeriyor.

Sonuç olarak INT,
sensörün ben ölçümü yaptım registerlara yazdım gel oku şunları demesine yarıyor.

koray692

Hmm ben o int pinini disardan tetikleyince sensorun verileri hazirlayip gonderdigini sanmistim...oyleyse en iyi yol nedir 10 ms de bir veri almanin sensorden bir yol buldum ama beni tatmin edecek kadar iyi bir yol degil acikcasi...sensorun kendi icinde boyle bir sistem varmi belli araliklarla veri gonderebilecegi bizim belirledigimiz...

görkem

 ben verileri 10 ms lik kesme  ile  almıştım ve çok düzgün çalışmıştı int bacağı kullanmadan.sadece kesmede veri al matematiksel işlem yapma yaparsan 10 ms lik süreyi aşarsın sapmalar oluşur ciddi oranda.ana döngünde fonksiyon oluştur hesaplat.

Cemre.

Datasheet'i karıştırırsanız 4 tip interrupt olduğunu ve bunların sadece ikisinin veri paketlerinin durumuyla ilgili olduğunu görürsünüz. Biri bahsettiğimiz veri hazır kesmesi, diğeri ise 1024byte'lık FIFO taşma kesmesi. Ancak bunların hiçbiri size zaman ayarlı okuma sağlamayacak.

Siz bir değişken tanımlayın bir de Timer kurun. Timer her 10ms.de bir taşma oluşturacak şekilde ayarlansın ve kesmenin içinde sadece tanımladığınız değişkene 1 değeri yüklensin.

Ana programda da bu değişkeni
While(mpuState)
{
  I2C okuma fonksiyonu;
  mpuState = 0;
}
  Dönüşüm vs fonksiyonları;

Şeklinde kontrol edin.

Bu sizin işinizi görür. Tabi burada while dışında kalan fonksiyonlarin vs harcayacağı süre sizin 10ms.yi biraz değiştirebilir orası ayrı. Ama geliştirilebilir.

Birde
While(mpuState && mpuINTstate)
Şeklinde kontrol etmek daha doğru olur.

Not.
mpuState herhangi bir değişken olabilir. mpuINTstate INT pinine bağladığınız işlemci pininin adresi olmalı.

görkem

kesmenin içinde veriyi çekmesi daha mantıklı olur. bu şekilde kullanışsız, işlemci başka işlerde yaparsa veriyi çekme kısmına gelene kadar zaten diğer veri gelmiş olur yeni kesme oluşur veriyi çekemeden. birde filtre olmadan açıdan performans alınamaz.

koray692

kesmede veri almayı denemiştim olmamıştı tekrar deneyeyim... peki kesme oluştuğunda sensor veri göndermeye hazır değilse ne olacak ? veri gelene kadar sistemi orda birşekilde bekleteyim dedim ancak bu seferde sensorden bir şekilde veri akışı durursa sistem orda saplanır kalır.Şuanda Cemre arkadaşın dediği gibi yaptım ancak bu en iyi yol değil biliyorum çünkü kod oraya gelene kadar gecikmeler olacak 10 ms süresi sürekli değişecek...tamamlayıcı filtre yaptım ancak nedense düzgün çalışmadı ve çok yavaş çalışıyor 168mhz de çalıştırıyorum işlemcimi...

Cemre.

Yazilimsal filtrelemeye bu modulde çokta ihtiyaç görmüyorum ben DLPF özelliğinin karşılaştırmalı grafikleri başka bir konuda da paylaşmıştım bakabilirsiniz. Biraz sağır kalacaktır sensor ancak yazilimsal filtreden iyidir bence.

Farklı bir bakış açısı geliştirebiliriz.
Ancak bu bakış açısı kesme içersinde uzun işlemler yapmak olmamalı. Hele hele 100-400kHz ile iletişim kuran bir hatta 14byte veri aktarilacaksa ve start stop rutinleri vs giriyorsa işin içine. ASM yazmadığımızı da düşünürsek.
Haksız mıyım?

görkem

#8
Kesmede program çakılmayacağı için bir problem yaşamassın ve 10ms de zaten sensör çoktan veriyi hazır eder haberleşme hızın max 400khz zaten 400 khz haberleşebiliyorsan verin zaten 10 ms lik kesmeye hazırlanmış olur.sen programını buraya koyarsan daha çok yardımcı olmaya çalışabiliriz. ben 10ms lik kesme kullanıp hem dlpf sensör filtresini hem complementary filitreyi kullandım performansı çok iyiydi.

Not= ben picle 20 mhz de çalışmıştım

koray692

#9
kodumun en son hali bu nerelerde hata var yada nasıl daha iyi olabilir bu arada bazen çalışmıyor nedenini tam anlayamadım mpu6050 ile iletişim kurarken çakılıyor...

  // LCD module connections
sbit LCD_RS at GPIOD_ODR.B2;
sbit LCD_EN at GPIOD_ODR.B3;
sbit LCD_D4 at GPIOD_ODR.B4;
sbit LCD_D5 at GPIOD_ODR.B5;
sbit LCD_D6 at GPIOD_ODR.B6;
sbit LCD_D7 at GPIOD_ODR.B7;
// End LCD module connections

//pb6=SLC   PB7=SDA  I2C1 ICIN

char register_of_accel=0,txt[20];
int data_[2],ACCx=0 ,ACCy=0  ,ACCz=0   ,TEMP=0  ,GYROx=0  , GYROy=0  ,GYROz=0;
double  G_ACC=0, G_ACCx=0, G_ACCy=0, G_ACCz=0 ,abc=0,ACI_ACCx=0, ACI_ACCy=0, ACI_ACCz=0 ,bolum=0,dt=0.01,a=0.95,aci=0;
int alinan_veri[15];
int c=0,kilit=0;



   tamamlayici_filitre()
   
   {
     
       aci= a*(aci+GYROz*dt) + ((1-a)*(ACI_ACCz)) ;
   
   
   }

  //Timer2 Prescaler :27; Preload = 59999; Actual Interrupt Time = 10 ms

//Place/Copy this part in declaration section
void InitTimer2(){
  RCC_APB1ENR.TIM2EN = 1;
  TIM2_CR1.CEN = 0;
  TIM2_PSC = 2;
  TIM2_ARR = 55999;
  NVIC_IntEnable(IVT_INT_TIM2);
  TIM2_DIER.UIE = 1;
  TIM2_CR1.CEN = 1;
}

void Timer2_interrupt() iv IVT_INT_TIM2 {
  TIM2_SR.UIF = 0;

  kilit=1;
}





aci_hesaplama()
{

// ivmelenme(g cinsinde) hesaplama        819-xy          z-1310.72

  G_ACCx = (double)( ((float)ACCx )/(16384.0f)   );              //     AFS_SEL=0   16,384 LSB/g DE
  G_ACCy = (double)( ((float)ACCy )/(16384.0f)   );              //     AFS_SEL=0   16,384 LSB/g DE
  G_ACCz = (double)( ((float)ACCz )/(16384.0f)   );              //     AFS_SEL=0   16,384 LSB/g DE
  abc=(G_ACCx*G_ACCx) + (G_ACCy*G_ACCy) + (G_ACCz*G_ACCz);

  G_ACC=(double) sqrt(abc);                                                // R^2 = Rx^2 + Ry^2 + Rz^2
  ACI_ACCx=  57.2958*acos(G_ACCx / G_ACC);                                 // Axr = arccos(Rx/R)
  ACI_ACCy= 57.2958* acos(G_ACCy / G_ACC);                                 // Ayr = arccos(Ry/R)
  ACI_ACCz=  57.2958*acos(G_ACCz / G_ACC);                                 // Azr = arccos(Rz/R)


}



void lcd_kurulumu()
{   
     
     // Lcd_Out(1,1,"txt");
     //Lcd_Cmd(_LCD_CLEAR);               // Clear display
  Lcd_Cmd(_LCD_CURSOR_OFF);               // Cursor off
  FloatToStr(aci, txt);
  Lcd_Out(1,1,txt);                 // Write text in first row
  delay_ms(1);

}

void ilk_sensor_ayarlarinin_yapilmasi()
{
         // 107 inci registera 001000000 bilgisi gönderilerek power managment ayarı yapıldı
         
         I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67);

         data_[0] = 107;
         data_[1] = 0b00100000;
         I2C1_Start();
         I2C1_Write(0x68,data_,2,END_MODE_STOP);
         delay_ms(5);
         
         data_[0] = 28;
         data_[1] = 0b00000000;
         I2C1_Start();
         I2C1_Write(0x68,data_,2,END_MODE_STOP);
         delay_ms(5);

         data_[0] = 26;
         data_[1] = 0b00000110;
         I2C1_Start();
         I2C1_Write(0x68,data_,2,END_MODE_STOP);
         delay_ms(5);



}

void verilerin_sensorden_alinmasi()

  {


     ACCx=0 ;ACCy=0  ;ACCz=0   ;TEMP=0  ;GYROx=0  ; GYROy=0  ;GYROz=0;alinan_veri[15]=0;

     for (register_of_accel=59 ;register_of_accel<73;register_of_accel++ )       // veriler sırasıyla alınıyor

      {

          data_[0] = register_of_accel;  // register başlangıç yeri
          I2C1_Start();        // issue I2C start signal
          I2C1_Write(0x68,data_,1,END_MODE_RESTART);
          I2C1_Read(0x68,data_ ,1,END_MODE_STOP);
         // delay_ms(5);
          alinan_veri[c]=data_[0];
          c=c+1;

       }
          c=0;



  }



void alinan_verilerin_birlestirilmesi()

   {
             // iki tane 8 bitlik veri arka arkaya konarak birleştiriliyor

            ACCx = (alinan_veri[0]<<8) + (alinan_veri[1]&0b11111111);
            ACCy = (alinan_veri[2]<<8) + (alinan_veri[3]&0b11111111);
            ACCz = (alinan_veri[4]<<8) + (alinan_veri[5]&0b11111111);
            TEMP = (alinan_veri[6]<<8) + (alinan_veri[7]&0b11111111);
            GYROx= (alinan_veri[8]<<8) + (alinan_veri[9]&0b11111111);
            GYROy = (alinan_veri[10]<<8) + (alinan_veri[11]&0b11111111);
            GYROz = (alinan_veri[12]<<8) + (alinan_veri[13]&0b11111111);

    }


void main()
{
lcd_Init();
ilk_sensor_ayarlarinin_yapilmasi();
InitTimer2() ;

while(1)
{

  lcd_kurulumu();

 
  if(kilit==1)
  {
         verilerin_sensorden_alinmasi();
         alinan_verilerin_birlestirilmesi();
         kilit=0;
  }
 

   tamamlayici_filitre();
 
}



}

mesaj birleştirme:: 24 Şubat 2016, 02:46:58

açı hesaplama fonksiyonunda orada kare alma işlemleri var "-" değerler + oluyor orda tekrar işlem yapmam gerekiyor değilmi eksi yönde ise ivme - ile çarpmam gibi..birde açı hesaplama fonksiyonu içinde mesela  G_ACCx i bulurken g-zero output u işin içine sokmadım çünkü kullandığım zaman çok saçma sapan sonuçlar ortaya çıktı

mesaj birleştirme:: 23 Şubat 2016, 14:32:57

verileri alma fonksiyonlarını şimdi kesme içine koydum yine çalışmadı

koray692

bu kodda interrup içinde veri almıyor...

// LCD module connections
sbit LCD_RS at GPIOD_ODR.B2;
sbit LCD_EN at GPIOD_ODR.B3;
sbit LCD_D4 at GPIOD_ODR.B4;
sbit LCD_D5 at GPIOD_ODR.B5;
sbit LCD_D6 at GPIOD_ODR.B6;
sbit LCD_D7 at GPIOD_ODR.B7;
// End LCD module connections

//pb6=SLC   PB7=SDA  I2C1 ICIN
char register_of_accel=0;
char txt[20];
int data_[2],ACCx=0 ,ACCy=0  ,ACCz=0   ,TEMP=0  ,GYROx=0  , GYROy=0  ,GYROz=0;
double  G_ACC=0, G_ACCx=0, G_ACCy=0, G_ACCz=0 ,abc=0,ACI_ACCx=0, ACI_ACCy=0, ACI_ACCz=0 ,bolum;
int alinan_veri[15];
int c=0;



void lcd_kurulumu()
{
  lcd_Init();                        // Initialize LCD

  Lcd_Cmd(_LCD_CLEAR);               // Clear display
// Lcd_Cmd(_LCD_CURSOR_OFF);          // Cursor off

FloatToStr(ACI_ACCz, txt);

  Lcd_Out(1,1,txt);                 // Write text in first row



}

//Timer2 Prescaler :27; Preload = 59999; Actual Interrupt Time = 10 ms

//Place/Copy this part in declaration section

void InitTimer2(){
  RCC_APB1ENR.TIM2EN = 1;
  TIM2_CR1.CEN = 0;
  TIM2_PSC = 2;
  TIM2_ARR = 55999;
  NVIC_IntEnable(IVT_INT_TIM2);
  TIM2_DIER.UIE = 1;
  TIM2_CR1.CEN = 1;


}

void Timer2_interrupt() iv IVT_INT_TIM2

{
     TIM2_SR.UIF = 0;
 
     ACCx=0 ;ACCy=0  ;ACCz=0   ;TEMP=0  ;GYROx=0  ; GYROy=0  ;GYROz=0;alinan_veri[15]=0;

     for (register_of_accel=59 ;register_of_accel<73;register_of_accel++ )       // veriler sırasıyla alınıyor

        {

          data_[0] = register_of_accel;  // register başlangıç yeri
          I2C1_Start();        // issue I2C start signal
          I2C1_Write(0x68,data_,1,END_MODE_RESTART);
          I2C1_Read(0x68,data_ ,1,END_MODE_STOP);
         // delay_ms(5);
          alinan_veri[c]=data_[0];
          c=c+1;

         }
       
         c=0;

}



aci_hesaplama()
{

// ivmelenme(g cinsinde) hesaplama        819-xy          z-1310.72

  G_ACCx = (double)( ((float)ACCx )/(16384.0f)   );              //     AFS_SEL=0   16,384 LSB/g DE
  G_ACCy = (double)( ((float)ACCy )/(16384.0f)   );               //     AFS_SEL=0   16,384 LSB/g DE
  G_ACCz = (double)( ((float)ACCz )/(16384.0f)   );              //     AFS_SEL=0   16,384 LSB/g DE
  abc=(G_ACCx*G_ACCx) + (G_ACCy*G_ACCy) + (G_ACCz*G_ACCz);

  G_ACC=(double) sqrt(abc);         // R^2 = Rx^2 + Ry^2 + Rz^2
  ACI_ACCx=  57.2958*acos(G_ACCx / G_ACC);                                 // Axr = arccos(Rx/R)
  ACI_ACCy= 57.2958* acos(G_ACCy / G_ACC);                                 // Ayr = arccos(Ry/R)
  ACI_ACCz=  57.2958*acos(G_ACCz / G_ACC);                                 // Azr = arccos(Rz/R)


}



void ilk_sensor_ayarlarinin_yapilmasi()
{
         // 107 inci registera 001000000 bilgisi gönderilerek power managment ayarı yapıldı

         I2C1_Init();
         data_[0] = 107;
         data_[1] = 0b00100000;
         I2C1_Start();
         I2C1_Write(0x68,data_,2,END_MODE_STOP);
         delay_ms(5);

         data_[0] = 28;
         data_[1] = 0b00000000;
         I2C1_Start();
         I2C1_Write(0x68,data_,2,END_MODE_STOP);
         delay_ms(5);

         data_[0] = 26;
         data_[1] = 0b00000110;
         I2C1_Start();
         I2C1_Write(0x68,data_,2,END_MODE_STOP);
         delay_ms(5);



}

/*void verilerin_sensorden_alinmasi()

  {





  }*/


void alinan_verilerin_birlestirilmesi()

   {
             // iki tane 8 bitlik veri arka arkaya konarak birleştiriliyor

            ACCx = (alinan_veri[0]<<8) + (alinan_veri[1]&0b11111111);
            ACCy = (alinan_veri[2]<<8) + (alinan_veri[3]&0b11111111);
            ACCz = (alinan_veri[4]<<8) + (alinan_veri[5]&0b11111111);
            TEMP = (alinan_veri[6]<<8) + (alinan_veri[7]&0b11111111);
            GYROx= (alinan_veri[8]<<8) + (alinan_veri[9]&0b11111111);
            GYROy = (alinan_veri[10]<<8) + (alinan_veri[11]&0b11111111);
            GYROz = (alinan_veri[12]<<8) + (alinan_veri[13]&0b11111111);

    }


void main()
{
ilk_sensor_ayarlarinin_yapilmasi();
while(1)
{
lcd_kurulumu() ;
  InitTimer2();                         // her 10 ms yede bir kesme oluşturarak mpu6050 den okuma yapacak
// verilerin_sensorden_alinmasi();
  alinan_verilerin_birlestirilmesi();
  aci_hesaplama();
}



}

görkem

 veri okuma fonksiyonu
int16 mpu6050_read(int okunacak_adres){
         int sonuc;
         i2c_start();
         i2c_write(0xD0); // yazma datası
         i2c_write(okunacak_adres); //x koordinatının verisinin bulunduğu adres
         i2c_start();
         i2c_write(0xD1); //okuma datası
         sonuc=i2c_read(0); 
         i2c_stop();
         return sonuc;
}

 
kullandığın derleyiciye göre uyarlayıp bu şekilde bir fonksiyon oluşturup, kesme içerisinde bu fonksiyonu çağır. mpu6050_read(0x3B); gibi

birde kesmede bayrağını en son resetle girerken değil. mpu6050 nin init kodlarındada sıkıntı olabilir çakılmasında.benim kullandığım ayarlar

PWR_MGMT_1,  0x00

I2CMASTER_CONTROL, 0x0D

SMPRT_DIV, 0x07

CONFIG_R,    0x01

GYRO_CONFIG, 0x00

ACCEL_CONFIG, 0x00