Anasayfa > Yazılımlar > MMA8451Q Accelerometer Sensör Testi [ STM32F1 , C# , GDI+]

MMA8451Q Accelerometer Sensör Testi [ STM32F1 , C# , GDI+]

24 Ağustos 2013 Yorum Ekle Yorumlara git

Freescale’in MMA8451Q Accelerometer sensörünü kullanarak üç boyutlu bir test ortamı oluştudum.

Bilgisayar ortamında 3D oluşturmak için hazır GDI+ dll kütüpanesini C# yazılımına ekleyerek kullandım. Sensörün lehimlenmesi konusu biraz sıkıntılı oldu diyebilirim.

Elde basabildiğim en hassas ve küçük pcb çalışmam bu oldu. 🙂

 

 

Sensör sadece 3 Axis ölçüm yapıyor ve I2C haberleşiyor. Bu tarz sensörlerin dijital haberleşiyor olması çok iyi veriyi daha kolay elde ediyorsunuz. Gyro ve sıcaklık ölçmüyor olması eksi yönleri ancak üreticinin daha yeni sensörlerinde bu özellikler mevcut. Bende hali hazırda bekleyen USB den portable debug yapılabilen kolayca kullanılan STM32 Discovery bordu çekmeceden çıkarıp bu haberleşmede kullandım. Bilgisayar ile bağlantısını ise usart yoluyla yaptım burada size com portlu bilgisayar veya benim yaptığım gibi usb seri çevirici gerekecektir.

shema

Sensörün bağlantı şeması yukkarıda görülmektedir. SA0 pini adreslemede kullanılıyor aynı I2C hatı üzerinde 2 adet sensör okunabilir. SDA ve SCL pinleri I2C haberleşme uçlarıdır sensör pull-up barındırmıyor harici kullanıyorsunuz ve pinler open drain çalışıyor. IN1 ve INT2 pinleri sensörün kesme üretme pinleridir. Sensörde Pulse ve Jolt detection özellikleri mevcut bunlar bu pinlerde kesme oluşturabiliyorlar. Sallama ve tıklama ile şarkı değiştiren cep telefonlarına kullanılan teknoloji artık dijital olan tüm accel sensörlerde mevcut. Daha fazla ayrıntı için katalog bilgilerine buradan(pdf) ulaşabilirsiniz.

 

IMAG0509

Uygulamanın resimlerinde sensör kartını görülebilir. Bu küçük kartı oluşturmak beni oldukça zorladı diyebilirim. 4 adet yanyana panel yapıp baskıya girdim ve en iyi olanı kullandım diğer 3 ü ne oldu derseniz 2 tanesi iş görmez ancak 1 adet daha elimde hazır duruyor. 16 pin QFN kılıfdan 8 pin Dip kılıf header elde etmek çok büyük başarı benim için. Genelde bu tarz donanımları RF gibi zorunlulukları olmadıkça soketli yapmak işime geliyor 🙂

Kartın son hali bu şekilde eksen olarak board a paralel tutabileceğim ve I2C pinlerine yakın bir noktaya sensörü yerleştirdim ve harici pull-up dirençler ekledim.

form

Bilgisayar yazılım kısmında C# ile serialPort haberleşmesi yapılıyor. 8 byte lık veri paketi ilk byte’ı ‘S’ (0x53) karakteri ile başlar ardından 16 bit uzunlugunda X,Y,Z ölçüm değerleri gelir. Son byte CheckSum doğrulama byte’ı gelir. Checksum kontrolü yapılır. Veri doğruysa ekrandaki nesne yönlendirilir. Ayrıca “Okunan” bölümünde sensörden gelen ham verileri izleyebilirsiniz.

 

Uygulamanın STM32F100 kaynak kodlarındaki ana program döngüsü aşağıdadır;

int main(void)
{

		GPIO_InitTypeDef GPIO_InitStructure;
		RCC_ClocksTypeDef RCC_Clocks;
		USART_InitTypeDef USART_InitStructure;
		I2C_InitTypeDef  I2C_InitStructure;

		RCC_GetClocksFreq(&RCC_Clocks);

		if (SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000))
		{
			for (;;)
			{
			}
		}

		RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA 	|
							 RCC_APB2Periph_GPIOB 	|
							 RCC_APB2Periph_GPIOC 	|
							 RCC_APB2Periph_USART1  |
						         RCC_APB2Periph_AFIO, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    USART_InitStructure.USART_BaudRate = 9600;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

    USART_Init(USART1, &USART_InitStructure);

    USART_Cmd(USART1, ENABLE);

    RCC_APB1PeriphClockCmd(I2C_Clock_RCC_Accel, ENABLE);

    GPIO_InitStructure.GPIO_Pin = I2C_Pin_Accel_Scl|I2C_Pin_Accel_Sda;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_Init(I2C_Accel_Port, &GPIO_InitStructure);

    I2C_DeInit(I2C_Accel);

    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 =  Device_Adr_Accel;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_ClockSpeed = ClockSpeed_Accel;

    I2C_Init(I2C_Accel, &I2C_InitStructure);

    I2C_Cmd(I2C_Accel, ENABLE);

    I2C_AcknowledgeConfig(I2C_Accel, ENABLE);

    printf("MMA8451Q Test V1.0\r\n");

    Accel_Yaz(REG_DR,(uint8_t*)DR_4G,1);

    setActive(1);

while(1){

		DelayMs(50);

		GPIO_SetBits(GPIOC,GPIO_Pin_9);
	 	GPIO_SetBits(GPIOC,GPIO_Pin_8);

		X =getAccAxis(REG_OUT_X_MSB);
		Y =getAccAxis(REG_OUT_Y_MSB);
		Z =getAccAxis(REG_OUT_Z_MSB);

		sprintf((char *)Usart_Buffer,"S%c%c%c%c%c%c",X>>8,X,Y>>8,Y,Z>>8,Z);

		CheckSum=1;
		for(i=0;i<7;i++)
		  CheckSum+=Usart_Buffer[i];
		Usart_Buffer[7]=CheckSum;

		printf((char *)Usart_Buffer);

		GPIO_ResetBits(GPIOC,GPIO_Pin_9);
		GPIO_ResetBits(GPIOC,GPIO_Pin_8);

	}

}

Uygulamanın C# kaynak kodları da aşağıdadır;

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO.Ports;

namespace WindowsApplication1
{
    public partial class Form1 : Form
    {
        /*******************************************************************/
        YLScsDrawing.Drawing3d.Cuboid cub = new YLScsDrawing.Drawing3d.Cuboid(150, 50, 150);
        YLScsDrawing.Drawing3d.Camera cam = new YLScsDrawing.Drawing3d.Camera();
        Bitmap[] bmp = new Bitmap[6];
        byte[] Receive_Buffer = new byte[7];
        int cubeX = 0, cubeY = 0 , cubeZ = 0 ;
        int kont;
        int cubeX_m, cubeY_m;
        /*******************************************************************/
        public Form1()
        {
            InitializeComponent();
            this.Text = "MMA8451Q Test V1";
        }
        /*******************************************************************/
        private void button12_Click(object sender, EventArgs e)
        {
            cubeX += 5;

            YLScsDrawing.Drawing3d.Quaternion q = new YLScsDrawing.Drawing3d.Quaternion();
            q.FromAxisAngle(new YLScsDrawing.Drawing3d.Vector3d(1, 0, 0), 5 * Math.PI / 180.0);
            cub.RotateAt(cub.Center, q);
            Invalidate();
        }
        /*******************************************************************/
        private void button11_Click(object sender, EventArgs e)
        {
            cubeX -= 5;

            YLScsDrawing.Drawing3d.Quaternion q = new YLScsDrawing.Drawing3d.Quaternion();
            q.FromAxisAngle(new YLScsDrawing.Drawing3d.Vector3d(1, 0, 0), -5 * Math.PI / 180.0);
            cub.RotateAt(cub.Center, q);
            Invalidate();
        }
        /*******************************************************************/
        private void button10_Click(object sender, EventArgs e)
        {
            cubeY += 5;
            YLScsDrawing.Drawing3d.Quaternion q = new YLScsDrawing.Drawing3d.Quaternion();
            q.FromAxisAngle(new YLScsDrawing.Drawing3d.Vector3d(0, 1, 0), 5 * Math.PI / 180.0);
            cub.RotateAt(cub.Center, q);
            Invalidate();
        }
        /*******************************************************************/
        private void button9_Click(object sender, EventArgs e)
        {
            cubeY -= 5;
            YLScsDrawing.Drawing3d.Quaternion q = new YLScsDrawing.Drawing3d.Quaternion();
            q.FromAxisAngle(new YLScsDrawing.Drawing3d.Vector3d(0, 1, 0), -5 * Math.PI / 180.0);
            cub.RotateAt(cub.Center, q);
            Invalidate();
        }
        /*******************************************************************/
        private void button8_Click(object sender, EventArgs e)
        {
            cubeZ += 5;

            YLScsDrawing.Drawing3d.Quaternion q = new YLScsDrawing.Drawing3d.Quaternion();
            q.FromAxisAngle(new YLScsDrawing.Drawing3d.Vector3d(0, 0, 1), 5 * Math.PI / 180.0);
            cub.RotateAt(cub.Center, q);
            Invalidate();
        }
        /*******************************************************************/
        private void button7_Click(object sender, EventArgs e)
        {
            cubeZ -= 5;

            YLScsDrawing.Drawing3d.Quaternion q = new YLScsDrawing.Drawing3d.Quaternion();
            q.FromAxisAngle(new YLScsDrawing.Drawing3d.Vector3d(0, 0, 1), -5 * Math.PI / 180.0);
            cub.RotateAt(cub.Center, q);
            Invalidate();
        }
        /*******************************************************************/
        private void ResetButon_Click(object sender, EventArgs e)
        {

            cub = new YLScsDrawing.Drawing3d.Cuboid(150, 50, 150);
            cam = new YLScsDrawing.Drawing3d.Camera();
            cub.Center = new YLScsDrawing.Drawing3d.Point3d(400, 240, 0);
            cam.Location = new YLScsDrawing.Drawing3d.Point3d(400, 140, -400);

            bmp[0] = new Bitmap(global::WindowsApplication1.Properties.Resources.Yan1);
            bmp[1] = new Bitmap(global::WindowsApplication1.Properties.Resources.Alt);
            bmp[2] = new Bitmap(global::WindowsApplication1.Properties.Resources.Yan4);
            bmp[3] = new Bitmap(global::WindowsApplication1.Properties.Resources.Ust);
            bmp[4] = new Bitmap(global::WindowsApplication1.Properties.Resources.Yan3);
            bmp[5] = new Bitmap(global::WindowsApplication1.Properties.Resources.Yan2);

            cub.FaceImageArray = bmp;
            cub.DrawingLine = false;
            cub.DrawingImage = true;
            cub.FillingFace = true;

            Invalidate();
            cubeX = 0;
            cubeY = 0;
            cubeZ = 0 ;
            cubeX_m = 0;
            cubeY_m = 0;
        }
        /*******************************************************************/
        private void Form1_Load(object sender, EventArgs e)
        {
            timer1.Start();
            timer1.Interval = 100;
            comboBox1.Items.Clear();

            foreach (string portNames in SerialPort.GetPortNames())
                comboBox1.Items.Add(portNames);

            comboBox1.SelectedIndex = comboBox1.Items.Count - 1;

            cub.Center = new YLScsDrawing.Drawing3d.Point3d(400, 240, 0);

            cam.Location = new YLScsDrawing.Drawing3d.Point3d(400, 140, -400);

            bmp[0] = new Bitmap(global::WindowsApplication1.Properties.Resources.Yan1);
            bmp[1] = new Bitmap(global::WindowsApplication1.Properties.Resources.Alt);
            bmp[2] = new Bitmap(global::WindowsApplication1.Properties.Resources.Yan4);
            bmp[3] = new Bitmap(global::WindowsApplication1.Properties.Resources.Ust);
            bmp[4] = new Bitmap(global::WindowsApplication1.Properties.Resources.Yan3);
            bmp[5] = new Bitmap(global::WindowsApplication1.Properties.Resources.Yan2);

            cub.FaceImageArray = bmp;
            cub.DrawingLine = false;
            cub.DrawingImage = true;
            cub.FillingFace = true;

            Invalidate();

        }
        /*******************************************************************/
        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            cub.Draw(e.Graphics, cam);
        }
        /*******************************************************************/
        private void Baglan_Click(object sender, EventArgs e)
        {

            if (serialPort1.IsOpen)
                serialPort1.Close();

            serialPort1.PortName = comboBox1.SelectedItem.ToString();
            serialPort1.BaudRate = 9600;
            serialPort1.Parity = 0;
            serialPort1.DataBits = 8;

            serialPort1.Open();

            Durdur.Enabled = true;
            Baglan.Enabled = false;
            comboBox1.BackColor = Color.LawnGreen;

        }
        /*******************************************************************/
        private void Durdur_Click(object sender, EventArgs e)
        {

            if (serialPort1.IsOpen)
                serialPort1.Close();
            Durdur.Enabled = false;
            Baglan.Enabled = true;
            comboBox1.BackColor = Color.White;

        }
        /*******************************************************************/
        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            Thread.Sleep(10);
            int j;
            byte chk = 0;
            int bytes = serialPort1.BytesToRead;
            Receive_Buffer = new byte[bytes];
            serialPort1.Read(Receive_Buffer, 0, bytes);

            if (8 <= Receive_Buffer.Length && 'S' == Receive_Buffer[0])
            {

                chk = 1;
                for (j = 0; j < 7; j++)
                {
                    chk += Receive_Buffer[j];
                }

                if (chk == Receive_Buffer[7])
                {

                    cubeX = (int)(Receive_Buffer[1] * 256);
                    cubeX +=(int) Receive_Buffer[2];
                    kont = cubeX;
                    if (kont > 60000) kont -= (int)65535;
                    kont = kont / 45;
                    YLScsDrawing.Drawing3d.Quaternion q = new YLScsDrawing.Drawing3d.Quaternion();
                    q.FromAxisAngle(new YLScsDrawing.Drawing3d.Vector3d(1, 0, 0), (kont - cubeX_m) * Math.PI / 180.0);
                    cubeX_m = kont;
                    cub.RotateAt(cub.Center, q);

                    cubeY = (int)(Receive_Buffer[3] * 256);
                    cubeY += (int)Receive_Buffer[4];
                    kont = cubeY;
                    if (kont > 60000) kont -= (int)65535;
                    kont = kont / 45;
                    q.FromAxisAngle(new YLScsDrawing.Drawing3d.Vector3d(0, 0, 1), (kont - cubeY_m) * Math.PI / 180.0);
                    cubeY_m = kont;
                    cub.RotateAt(cub.Center, q);
                    cubeZ = (int)(Receive_Buffer[5] * 256);
                    cubeZ += (int)Receive_Buffer[6];

                    Invalidate();

                }
                serialPort1.DiscardInBuffer();

            }

        }
        /*******************************************************************/
        private void timer1_Tick(object sender, EventArgs e)
        {
             X_label.Text = cubeX.ToString();
             Y_label.Text = cubeY.ToString();
             Z_label.Text = cubeZ.ToString();
        }
        /*******************************************************************/
    }
}

Uygulamaya ait yazılım projelerini buradan indirebilirsiniz. Uygulamanın video görüntüsü aşağıdadır herkeze iyi çalışmalar.

Kategoriler: Yazılımlar Etiketler: ,
  1. gökhan
    1 Eylül 2014 00:56 | #1

    Merhaba hocam,
    Dikkatimi çekti, sadece ivme verileri ile theta(yaw) açısını nasıl elde ettiniz anlamadım. Normalde bu iş için magnetometre kullanarak yön bilgisi elde ediliyor. Gyro ile de başlangıç yönüne göre o anki konumu güncellemek mümkün, ama tek başına ivme ile nasıl oluyor bilmiyorum.
    Bir sorum daha var, ben opengl ve c++ kullanıyorum 3d için, c# ta daha basit ise ona geçebilirim. 3d modeli ne ile çizdiniz? Hazır bulduğumuz bir 3d modeli kodda değişiklik yapmadan kullanabiliyor muyuz?

  2. 1 Eylül 2014 10:52 | #2

    Gökhan haklısın magnetometer yok o sebeple şekil bir süre sonra olduğu yönden sapabiliyor. Özellikle 180 derece döndürdüğünüzde şekil ekseni sapıyordu.
    C# benim için her embedded yazılımcısının bilmesi gereken bir dili. Benim kullandığım nesne draw metodlarıyla çiziliyor yüzleri ise resimler ile kaplanmış biçimde. C# içerisinde GDI kütüpanesi ile resimler yeniden boyutlandırılıyor ve draw line eksenleri yeniden konumlandırılıyor. Aslında fazla üst seviyede çalışan bir kütüpane bu yüzden çok da hızlı değil. Amaca göre kullanılabilir.

  1. Şimdilik geri izleme yok