تا به امروز آموزش های متفاوتی با استفاده ماژول ژایرو MPU6050 منتشر شده، در این آموزش قصد داریم با استفاده از ماژول پرکاربرد MPU6050 و همچنین نمایشگر Oled، یک مدل سه بعدی را کنترل کنیم با استفاده از تغییر مقادیر x, y که از ماژول ژایرو بدست میآید. در این آموزش از برد آردوینو نانو به عنوان برد راهانداز استفاده خواهیم کرد. برای آموزش های بیشتر به CiferTech مراجعه کنید، همچنین برای حمایت از من حتما پیج من در اینستاگرام را دنبال کنید. ^-^
ماژول MPU6050
حالا زمان این رسیده با سخت افزار اصلی این آموزش، یعنی MPU6050 بیشتر آشنا بشیم. Mpu6050 دارای یک شتاب سنج سه محوره و یک ژیروسکوپ سه محوره MEMS میباشد. دقت ماژول مبدل آنالوگ به دیجیتال ۱۲ بیت بوده و میتواند مقادیر X ، Y ، Z را در یک زمان اندازه گیری کند. بافر این ماژول از نوع FIFO با ظرفیت ۱۰۲۴ بایت میباشد. این ماژول به عنوان Slave به آردوینو توسط پایههای SCL و SDA متصل میشود. با استفاده از پردازشگر دیجیتال، ۴ خروجی برای دستیابی به دقت قابل توجه، در حرکتهای کند و سریع در سنسور فراهم کرده است. با این روش کاربر میتواند بازه اندازه گیری را متناسب با شتاب یا سرعت تغییر دهد. به همین منظور در قسمت ژیروسکوپ سنسور چهار بازه ۲۵۰±، ۵۰۰±، ۱۰۰۰± و ۲۰۰۰± درجه بر ثانیه و در قسمت شتاب سنج سنسور چهار بازه ۲g، ±۴g، ±۸g، ±۱۶g± برای کاربر قابل انتخاب خواهد بود. توجه داشته باشید که ولتاژ کاری این ماژول ۳ الی ۵ ولت میباشد.
برد آردوینو (Arduino)
مجموعه برد های آردوینو از جمله برد های توسعه پرطرفدار بین مهندسین امبدد هستند که در مدل های مختلفی از جمله Micro , proMini , Nano , Uno و همچنین Mega قابل تهیه هستند، هسته مرکزی این برد های محبوب از سری AtMega328 می باشد. آردوینو پلتفرم سختافزاری و نرمافزاری متنباز است. همان طور که قبل تر اشاره کردیم، پلتفرم آردوینو شامل یک میکروکنترلر تکبردی متنباز است که قسمت سختافزار آردوینو را تشکیل میدهد. علاوه بر این، پلتفرم آردوینو یک نرمافزار آردوینو IDE که به منظور برنامهنویسی برای بردهای آردوینو طراحی شدهاست و یک بوت لودر نرمافزاری که بر روی میکروکنترلر بارگذاری میشود را در بر میگیرد.
ماژول نمایشگر Oled 1306
معمولا در پروژه های اینترنت اشیا و دیگر پروژه های امبدد از برای نمایش متن و مقادیر مختلف از نمایشگر های Oled استفاده می شود، این ماژول ها بدر انواع مختلق بسته به نوع ذرایور اندازه یافت می شوند که یکی از پرطرفدار ترین آن ها SSD1306 می باشد، این نوع از Oled ها معمولا در اندازه های ۰٫۹۶ و ۱٫۳ اینچ ساخته می شوند همچنین پروتکل ارتباطی Oled ها I2C می باشد.
توضیح تکمیلی
در این آموزش اقدام به کنترل یک مکعب خواهیم کرد، در کد پروژه مکعب را طراحی می کنیم، به این صورت که زوایای مختلف را در کد تعریف و در شرط های برنامه به ازای تغییر مقادیر x, y تغییرات ظاهری مکعب را نیز مشخص خواهیم کرد. و در نهایت شکل معکب را در نمایشگر Oled چاپ می کنیم و تغییر زاویه ماژول MPU شاهد تغییر زوایای مکعب خواهیم بود.
وسایل موردنیاز
ماژول MPU6050
برد آروینو نانو (Arduino nano)
نمایشگر Oled 0.96
نصب کتابخانه مورد نیاز
ابتدا در نرم افزار Arduino IDE اقدام به نصب کتابخانه مورد نیاز خواهیمکرد. مراحل زیر را دنبال کنید:
- این مسیر را دنبال کنید Sketch > Include Library > Manage Libraries
- کلمه Adafruit SSD1306 را جستجو کنید.
- کتابخانه را نصب کنید.
- سپس کلمه “GFX” را جستجو کنید و آن را نصب کنید.
تست و راهاندازی
در اولین مرحل اتصالات را طبق جدول و شماتیک زیر برقرار کنید، برای راهاندازی قطعات استفاده شده در این آموزش از پروتکل I2C استفاده میکنیم، به این صورت که پین های SCL, SDA به پین های A4, A5 متصل خواهند شد.
خب بعد از برقراری اتصالات، نوبت به کد پروژه میرسد. در ادامه باهم قسمت های مهمی از این کد را بررسی خواهیم کرد. در چند خط اول کتابخانه های مورد نیاز را معرفی میکنیم.
#include <SPI.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#include <Wire.h>
در این چند خط راهاندازی های اولیه برای دو ماژول استفاده شده را انجام خواهیم داد، برای مثال مشخص کردن I2C آدرس ها برای هر ماژول و همچنین متغیر های اولیه.
const int MPU=0x68;
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
#define OLED_address 0x3c
اینجا دو متغیر تعریف کردهایم که وظیفه آن ها پردازش خطوط مکعب است،این دلیل که در حرکت مکعب خطوط قبلی باید پاک، و خطوط جدید چاپ شوند.
int LinestoRender;
int OldLinestoRender;
کد کامل پروژه.
#include <SPI.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#include <Wire.h>
const int MPU=0x68;
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
#define OLED_address 0x3c
float xx,xy,xz;
float yx,yy,yz;
float zx,zy,zz;
float fact;
int Xan,Yan;
int Xoff;
int Yoff;
int Zoff;
struct Point3d
{
int x;
int y;
int z;
};
struct Point2d
{
int x;
int y;
};
int LinestoRender;
int OldLinestoRender;
struct Line3d
{
Point3d p0;
Point3d p1;
};
struct Line2d
{
Point2d p0;
Point2d p1;
};
Line3d Lines[12]; //تعداد خطوط قابل پردازش
Line2d Render[12];
Line2d ORender[12];
void SetVars(void)
{
float Xan2,Yan2,Zan2;
float s1,s2,s3,c1,c2,c3;
Xan2 = Xan / fact; // تبدیل مقادیر زاویه
Yan2 = Yan / fact;
s1 = sin(Yan2);
s2 = sin(Xan2);
c1 = cos(Yan2);
c2 = cos(Xan2);
xx = c1;
xy = 0;
xz = -s1;
yx = (s1 * s2);
yy = c2;
yz = (c1 * s2);
zx = (s1 * c2);
zy = -s2;
zz = (c1 * c2);
}
void ProcessLine(struct Line2d *ret,struct Line3d vec)
{
float zvt1;
int xv1,yv1,zv1;
float zvt2;
int xv2,yv2,zv2;
int rx1,ry1;
int rx2,ry2;
int x1;
int y1;
int z1;
int x2;
int y2;
int z2;
int Ok;
x1=vec.p0.x;
y1=vec.p0.y;
z1=vec.p0.z;
x2=vec.p1.x;
y2=vec.p1.y;
z2=vec.p1.z;
Ok=0; // بطور پیشفرض 0
xv1 = (x1 * xx) + (y1 * xy) + (z1 * xz);
yv1 = (x1 * yx) + (y1 * yy) + (z1 * yz);
zv1 = (x1 * zx) + (y1 * zy) + (z1 * zz);
zvt1 = zv1 - Zoff;
if( zvt1 < -5){
rx1 = 256 * (xv1 / zvt1) + Xoff;
ry1 = 256 * (yv1 / zvt1) + Yoff;
Ok=1; // ok we are alright for point 1.
}
xv2 = (x2 * xx) + (y2 * xy) + (z2 * xz);
yv2 = (x2 * yx) + (y2 * yy) + (z2 * yz);
zv2 = (x2 * zx) + (y2 * zy) + (z2 * zz);
zvt2 = zv2 - Zoff;
if( zvt2 < -5){
rx2 = 256 * (xv2 / zvt2) + Xoff;
ry2 = 256 * (yv2 / zvt2) + Yoff;
} else
{
Ok=0;
}
if(Ok==1){
ret->p0.x=rx1;
ret->p0.y=ry1;
ret->p1.x=rx2;
ret->p1.y=ry2;
}
}
void setup() {
Wire.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3c);
display.clearDisplay(); // پاک کردن صفحه
Wire.begin();
fact = 180 / 3.14159265358979323846264338327950; // برای تبدیل زاویه به رادیان
Xoff = 60; // محل قرار گیری مکعب در محور x
Yoff = 15; // محل قرار گیری مکعب در محور y
Zoff = 1300; //اندازه مکعب
// Front Face.
Lines[0].p0.x=-50;
Lines[0].p0.y=-50;
Lines[0].p0.z=50;
Lines[0].p1.x=50;
Lines[0].p1.y=-50;
Lines[0].p1.z=50;
Lines[1].p0.x=50;
Lines[1].p0.y=-50;
Lines[1].p0.z=50;
Lines[1].p1.x=50;
Lines[1].p1.y=50;
Lines[1].p1.z=50;
Lines[2].p0.x=50;
Lines[2].p0.y=50;
Lines[2].p0.z=50;
Lines[2].p1.x=-50;
Lines[2].p1.y=50;
Lines[2].p1.z=50;
Lines[3].p0.x=-50;
Lines[3].p0.y=50;
Lines[3].p0.z=50;
Lines[3].p1.x=-50;
Lines[3].p1.y=-50;
Lines[3].p1.z=50;
//back face.
Lines[4].p0.x=-50;
Lines[4].p0.y=-50;
Lines[4].p0.z=-50;
Lines[4].p1.x=50;
Lines[4].p1.y=-50;
Lines[4].p1.z=-50;
Lines[5].p0.x=50;
Lines[5].p0.y=-50;
Lines[5].p0.z=-50;
Lines[5].p1.x=50;
Lines[5].p1.y=50;
Lines[5].p1.z=-50;
Lines[6].p0.x=50;
Lines[6].p0.y=50;
Lines[6].p0.z=-50;
Lines[6].p1.x=-50;
Lines[6].p1.y=50;
Lines[6].p1.z=-50;
Lines[7].p0.x=-50;
Lines[7].p0.y=50;
Lines[7].p0.z=-50;
Lines[7].p1.x=-50;
Lines[7].p1.y=-50;
Lines[7].p1.z=-50;
// چهار گوشه مکعب.
Lines[8].p0.x=-50;
Lines[8].p0.y=-50;
Lines[8].p0.z=50;
Lines[8].p1.x=-50;
Lines[8].p1.y=-50;
Lines[8].p1.z=-50;
Lines[9].p0.x=50;
Lines[9].p0.y=-50;
Lines[9].p0.z=50;
Lines[9].p1.x=50;
Lines[9].p1.y=-50;
Lines[9].p1.z=-50;
Lines[10].p0.x=-50;
Lines[10].p0.y=50;
Lines[10].p0.z=50;
Lines[10].p1.x=-50;
Lines[10].p1.y=50;
Lines[10].p1.z=-50;
Lines[11].p0.x=50;
Lines[11].p0.y=50;
Lines[11].p0.z=50;
Lines[11].p1.x=50;
Lines[11].p1.y=50;
Lines[11].p1.z=-50;
LinestoRender=12;
OldLinestoRender=LinestoRender;
Wire.beginTransmission(MPU);
Wire.write(0x6B);
Wire.write(0); // 0 برای بیدار کردن ماژول
Wire.endTransmission(true);
}
/***********************************************************************************************************************************/
void RenderImage( void)
{
for (int i=0; i<OldLinestoRender; i++ )
{
display.drawLine(ORender[i].p0.x,ORender[i].p0.y,ORender[i].p1.x,ORender[i].p1.y, BLACK); // خطوط قدیمی
}
for (int i=0; i<LinestoRender; i++ )
{
display.drawLine(Render[i].p0.x,Render[i].p0.y,Render[i].p1.x,Render[i].p1.y, WHITE);
}
OldLinestoRender=LinestoRender;
Wire.beginTransmission(MPU);
Wire.write(0x3B);
Wire.endTransmission(true);
Wire.requestFrom(MPU,14,true);
AcX=Wire.read()<<8|Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
AcY=Wire.read()<<8|Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
AcZ=Wire.read()<<8|Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
Tmp=Wire.read()<<8|Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
GyX=Wire.read()<<8|Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
GyY=Wire.read()<<8|Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
GyZ=Wire.read()<<8|Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
//delay(10);
}
void loop() {
display.display();
display.clearDisplay();
//برای چرخش مکعب
int xOut=0;
int yOut=0;
xOut = map(AcX,-17000,17000,-50,50);
yOut = map(AcY,-17000,17000,-50,50);
Xan+=xOut;
Yan+=yOut;
Yan=Yan % 360;
Xan=Xan % 360;
SetVars();
for(int i=0; i<LinestoRender ; i++)
{
ORender[i]=Render[i];
ProcessLine(&Render[i],Lines[i]);
}
RenderImage();
}
جمع بندی
در این آموزش با استفاده از ماژول MPU6050 و همچنین برد آردوینو نانو و نمایشگر Oled توانستیم با استفاده از مقادیر بدست آمده از ماژول ژایرو یک مکعب 3D را نسبت تغییرات در این مقادیر حرکت دهیم.
- تنظیمات منوی tools در نرم افزار Arduino IDE نیز مانند تصویر زیر است.