کار با واحد I2C در رزبری پای – {راه اندازی MPU6050}

مقدمه

در این جلسه قصد داریم در مورد واحد I2C که یکی از واحدهای پرکاربرد در رزبری پای است صحبت کنیم. برای این کار سنسور MPU6050 را به رزبری خود وصل میکنیم و با پروتکل I2C داده های آن را میخوانیم. ما قبلا در مورد برد رزبری پای و سنسور MPU605 چند جلسه منتشر کرده بودیم که لازم است قبل از خواندن این جلسه آنها رامطالعه کنید و دیگر به معرفی سنسور نمیپردازیم :‌

البته در صورتی که با سنسور های شتاب سنج و ژيروسکوپ نیز آشنایی ندارید حتما دو جلسه زیر را مطالعه کنید :

سخت افزار

اتصال ماژول MPU6050 به برد رزبری پای :‌

تذکر‌: همانطور که گفته شد در این جلسه به صورت مستقیم وارد مبحث راه اندازی سنسور می شویم و به معرفی خود سنسور و مشخصات آن نمیپردازیم. در صورت که دوست دارید سنسور را بهتر بشناسید حتما جلسات راه اندازی ماژول با آردوینو را که لینک آن در بالا معرفی شد مطالعه بفرمایید.

همانطور که میدانیم برای اتصال یک سنسور که با پروتکل I2C کار میکند صرفا به ۴ پایه نیاز داریم. یک پایه برای VCC و یک \ایه برای GND  و دو پایه مربوط به دیتا و کلاک که پایه های خود پروتکل I2C می باشد.

در جدول زیر پایه های اتصالی مربوط به رزبری پای و سنسور MPU6050 آورده شده است :

کار با واحد I2C در رزبری پای

در شکل زیر نیز میتوانید نمایی از اتصال رزبری پای به MPU6050 را مشاهده کنید‌:

کار با واحد I2C در رزبری پای

پس از اتصالات سخت افزاری به بخش نرم افزاری قضیه وارد می شویم :‌

نرم افزار

بخش اول : فعال کردن واحد I2C برد رزبری پای :

برای فعال کردن واحد های SPI و I2C در رزبری پای از بخش Menu قسمت Preferences  و  سپس گزینه Raspberry Pi Configuration را انتخاب کنید.

حال مشابه با شکل زیر از پنجره باز شده وارد قسمت Interfaces شوید و گزینه I2C را بر روی enable تنظیم کنید و بر روی OK کلیک کنید.

فعال کردن واحد I2C در رزبری پای

در صورتی که برای اعمال تغییرات  سیستم عامل نیاز به ریبوت شدن داشت آن را ریبوت کنید.

حال دوباره ترمینال را باز کنید و ابزارهای مورد نیاز برای کار با پروتکل I2C را بر روی‌ آن نصب کنید‌:

sudo apt-get install python-smbus
sudo apt-get install i2c-tools

در واقع به کمک خطوط بالا دو پکیج کمیکی smbus و i2c-tools را نصب میکنیم. نصب پکیج i2c-tools اجباری نمی باشد ولی پیشنهاد می شود که آن را نصب کنید. این پکیج در واقع دستگاه های I2C یا Smbus متصل به برد را اسکن می کند. اگر مطمین هستید که یک سنسور یا ماژول با پروتکل I2C به برد خود متصل کرده اید اما آدرس این سنسور و ماژول را نمی دانید پکیج i2c-tools در اینجا به کمک شما می آيد و به کمک آن میتوانید این آدرس را به سادگی پیدا کنید.

نکته :‌ نصب پکیج python-smbus الزامی می باشد. به کمک این پکیج میتوانید با زبان پایتون با پروتکل I2C برد اراتباط برقرار کنید.

پس از نصب این پکیج ها میخواهیم مطمین شویم که ماژول مان به درستی به برد متصل شده است یا خیر. برای این کار کافی است دستور زیر را در ترمینال وارد کنیم ‌:

sudo i2cdetect -y 1

نکته : در صورتی که از نسخه ۱ رزبری پای استفاده میکنید باید از دستور زیر استفاده کنید :‌

sudo i2cdetect -y 0

به کمک این دستورها یک اسکن کلی انجام می شود و چک می شود که چه دستگاه هایی از طریق I2C به برد متصل شده اند و آدرس این دستگاه ها در خروجی نمایش داده می شود. در صورتی که ماژول MPU605 به درستی به برد متصل شده باشد باید خروجی این دستور برای شما مشابه شکل زیر باشد‌:

pi@raspberrypi ~ $ sudo i2cdetect -y 1
 0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

که نشان می دهد آدرس دستگاه متصل شده به I2C برد 0x68 می باشد. (این عدد یک عدد هگزادسیمال می باشد. )

تذکر : در مورد آدرس این ماژول در جلسات پیش نیاز توضیح داده شده بود.

بخش دوم :‌ توضیح خط به خط کد مربوط به خواندن از MPU605‌‌:

حال وارد قسمت کد می شویم که بخش اصلی این آموزش است :‌

برای نوشتن کد ابتدا ویرایشگر متن Nano را به کمک دستور زیر باز میکنیم :‌

sudo nano gyro.py

حال در صفحه باز شده کد زیر را کپی کنید. در ادامه به صورت خط به خط در مورد این کد توضیح میدهیم ‌:

import smbus			#import SMBus module of I2C
from time import sleep          #import

#some MPU6050 Registers and their Address
PWR_MGMT_1   = 0x6B
SMPLRT_DIV   = 0x19
CONFIG       = 0x1A
GYRO_CONFIG  = 0x1B
INT_ENABLE   = 0x38
ACCEL_XOUT_H = 0x3B
ACCEL_YOUT_H = 0x3D
ACCEL_ZOUT_H = 0x3F
GYRO_XOUT_H  = 0x43
GYRO_YOUT_H  = 0x45
GYRO_ZOUT_H  = 0x47


def MPU_Init():
	#write to sample rate register
	bus.write_byte_data(Device_Address, SMPLRT_DIV, 7)
	
	#Write to power management register
	bus.write_byte_data(Device_Address, PWR_MGMT_1, 1)
	
	#Write to Configuration register
	bus.write_byte_data(Device_Address, CONFIG, 0)
	
	#Write to Gyro configuration register
	bus.write_byte_data(Device_Address, GYRO_CONFIG, 24)
	
	#Write to interrupt enable register
	bus.write_byte_data(Device_Address, INT_ENABLE, 1)

def read_raw_data(addr):
	#Accelero and Gyro value are 16-bit
        high = bus.read_byte_data(Device_Address, addr)
        low = bus.read_byte_data(Device_Address, addr+1)
    
        #concatenate higher and lower value
        value = ((high << 8) | low)
        
        #to get signed value from mpu6050
        if(value > 32768):
                value = value - 65536
        return value


bus = smbus.SMBus(1) 	# or bus = smbus.SMBus(0) for older version boards
Device_Address = 0x68   # MPU6050 device address

MPU_Init()

print (" Reading Data of Gyroscope and Accelerometer")

while True:
	
	#Read Accelerometer raw value
	acc_x = read_raw_data(ACCEL_XOUT_H)
	acc_y = read_raw_data(ACCEL_YOUT_H)
	acc_z = read_raw_data(ACCEL_ZOUT_H)
	
	#Read Gyroscope raw value
	gyro_x = read_raw_data(GYRO_XOUT_H)
	gyro_y = read_raw_data(GYRO_YOUT_H)
	gyro_z = read_raw_data(GYRO_ZOUT_H)
	
	#Full scale range +/- 250 degree/C as per sensitivity scale factor
	Ax = acc_x/16384.0
	Ay = acc_y/16384.0
	Az = acc_z/16384.0
	
	Gx = gyro_x/131.0
	Gy = gyro_y/131.0
	Gz = gyro_z/131.0
	

	print ("Gx=%.2f" %Gx, u'\u00b0'+ "/s", "\tGy=%.2f" %Gy, u'\u00b0'+ "/s", "\tGz=%.2f" %Gz, u'\u00b0'+ "/s", "\tAx=%.2f g" %Ax, "\tAy=%.2f g" %Ay, "\tAz=%.2f g" %Az) 	
	sleep(1)

حال وارد توضیح کد می شویم :‌

بخش اول‌: import کردن ماژول های مورد نیاز

import smbus
from time import sleep

در ابتدا ماژول های مورد نظر خود را به برنامه اضافه میکنیم. در خط اول ماژول smbus که آن را در مرحله اول نصب کرده ایم import میکنیم. از این ماژول برای ارتباط با پروتکل I2C استفاده می شود.

در خط دوم نیز ماژول time را وارد برنامه کرده ایم. چون میخواهیم بین خواندن های متوالی کمی وقفه بیندازیم.

بخش دوم ‌: تعریف یک سری متغیر برای آدرس دهی راحتتر

PWR_MGMT_1   = 0x6B
SMPLRT_DIV   = 0x19
CONFIG       = 0x1A
GYRO_CONFIG  = 0x1B
INT_ENABLE   = 0x38
ACCEL_XOUT_H = 0x3B
ACCEL_YOUT_H = 0x3D
ACCEL_ZOUT_H = 0x3F
GYRO_XOUT_H  = 0x43
GYRO_YOUT_H  = 0x45
GYRO_ZOUT_H  = 0x47

همانطور که مشاهده میکنید در اینجا متغیرهایی با اسامی مختلف تعریف کرده ایم که هر کدام یک مقدار هگزادسیمال به خود گرفته اند. این مقادیر هگزادسیمال در واقع آدرس های رجیسترهای می باشند که از register map مربوط به دیتاشیت استخراج شده اند. در واقع به کمک این اسم گذاری میخواستیم کمی برنامه مان خوانا تر شود. در ادامه تک تک این رجیسترها و وظایف آنها شرح داده شده است :‌

رجیستر PWR_MGMT_1 :

  • این رجیستر به کاربر اجازه می دهد تا حالت تغذیه ماژول و منبع کلاک آن را مشخص کند. علاوه بر‌ آن یک بیت برای ریست ماژول و یک بیت برای غیر فعال کردن سنسور دمای داخلی ماژول نیز در اختیار ما قرار می دهد.

رجیستر SMPRT_DIV  :

  • عبارت SMPRT_DIV مخفف کلمه Sample Rate Divider می باشد و به کمک آن Sample rate مشخص می شود. خروجی FIFO و رجیسترهای خروجی سنسور برمبنای همین Sample rate می باشد. رابطه بین Sample rate و Sample Rate Divider به صورت زیر می باشد:‌

که در فرمول بالا Gyroscope Output Rate در صورت غیرفعال بودن DLPF (فیلتر پایین گذر دیجیتال) برابر ۸ کیلوهرتز و در صورت فعال بودن آن ۱ کیلوهرتز می باشد.

رجیستر CONFIG :‌

  • به کمک این رجیستر میتوان یک فیلتر پایین گذر بر روی خروجی های شتاب سنج و ژیروسکوپ اعمال کنیم.

رجیستر‌ GYRO_CONFIG :

  • مهمترین وظیفه این رجیستر تعیین محدوده خروجی سنسور ژیروسکوپ می باشد. ما در مورد محدوده خروجی سنسور در جلسه مربوط به ژیروسکوپ توضیح داده ایم. در صورتی که با آن آشنا نیستید حتما به آن جلسه مراجعه کنید.

رجیستر INT_ENABLE‌ :

  • با این رجیستر میتوانیم وقفه های این ماژول را فعال کنیم. در مورد مقدار دهی این رجیستر در ادامه بیشتر صحبت کرده ایم.

رجیسترهای مربوط به خواندن دیتا از شتاب سنج :‌

  • همانطور که میدانیم شتاب سنج موجود بر روی MPU6050 یک شتاب سنج سه محوره است. دیتای هر کدام از این محورها در یک رجیستر ذخیره می شود. به همین خاطر سه رجیستر بالا برای خواندن دیتاهای شتاب  سنج مورد استفاده قرار گرفته اند.

رجیسترهای مربوط به خواندن دیتا از ژیروسکوپ :

  • مشابه با شتاب سنج  بر روی MPU6050 یک ژیروسکوپ سه محوره نیز وجود دارد که دیتای آن در سه رجیستر مختلف ذخیره می شود و ما به کمک این سه رجیستر میتوانیم خروجی خام  ژیروسکوپ را بدست آوریم.

تعریف آدرس و ساخت یک Instance از کلاس smbus :‌

در ادامه (بعد از تابع MPU_Init) : دو خط کد آورده شده است که اول تعریف یک Instance از کلاس smbus می باشد و خط دوم نیز تعریف یک ثابت برای آدرس دستگاه می باشد که همانطور که میدانیم آدرس این ماژول به صورت پیش فرض 0x68 می باشد.

سپس تابعی به نام MPU_Init‌ آورده شده است که در زیر به معرفی آن میپردازیم.

معرفی تابع MPU_Init‌:‌

پس از تعاریف اولیه یک تابع تعریف شده است که تنظیمات اولیه مربوط به ماژول MPU را انجام می دهد. این تابع را ببینید تا در ادامه به تفصیل در مورد آن صحبت کنیم :‌

def MPU_Init():
	#write to sample rate register
	bus.write_byte_data(Device_Address, SMPLRT_DIV, 7)
	
	#Write to power management register
	bus.write_byte_data(Device_Address, PWR_MGMT_1, 1)
	
	#Write to Configuration register
	bus.write_byte_data(Device_Address, CONFIG, 0)
	
	#Write to Gyro configuration register
	bus.write_byte_data(Device_Address, GYRO_CONFIG, 24)
	
	#Write to interrupt enable register
	bus.write_byte_data(Device_Address, INT_ENABLE, 1)

در واقع در این تابع تعدادی از رجیسترهایی که در بالا به معرفی آن پرداختیم ، مقداردهی شده اند.

برای مقدار دهی به رجیستر ها از متد write_byte_data در کلاس bus استفاده میکنیم. این متد سه پارامتر ورودی میگیرد:

  • پارامتر اول آدرس مربوط به دستگاه است که در اینجا 0x68 می باشد و ما‌ آن را به عنوان Device_Address قبلا تعریف کرده ایم.
  • پارامتر دوم آدرس مربوط به رجیستر است که ما قبلا این آدرس ها را قبلا برابر با یک سری عبارات قرار دادیم که این عبارات همان نام رجیسترها می باشند. بنابراین پارامتر دوم را برابر نام رجیسترها قرار میدهیم که در واقع آدرس آنهاست.
  • پارامتر سوم نیز مقداری که میخواهیم به آن رجیستر بنویسیم. در ادامه در مورد هر یک از مقادیر قرار داده شده به رجیسترها توضیحاتی ارا‌ئه شده است.

مقدار دهی به رجیستر SMPLRT_DIV :‌

مقدار مربوط به این رجیستر برابر ۷ قرار داده شده است. اگر به فرمولی که در هنگام معرفی این رجیستر آورده ایم دقت کنید خواهیم دید که در صورت غیر فعال بودن DLPG مقدار نرخ Gyroscope برابر ۸ کیلوهرتز می باشد. حال با قرار دادن ۷ برای این رجیستر مقدار Sample rate برابر ۱ کیلوهرتز می شود.

 

مقدار دهی به رجیستر PWR_MGMT_1 :‌

به این رجیستر مقدار ۱ نسبت داده شده است که به این معنی است که منبع کلاس ماژول ، کریستال ۸ مگاهرتز داخلی آن است.

برای کسب اطلاعات بیشتر در مورد این رجیستر میتوانید به Register map مربوط به این ماژول مراجعه کنید.

مقدار دهی به رجیستر CONFIG :‌

به این رجیستر مقدار صفر نسبت داده شده است و در واقع هیچ فیلتری بر روی خروجی های سنسورها قرار نگرفته است.

مقدار دهی به رجیستر GYRO_CONFIG :

همانطور که گفته شد از این رجیستر برای تعیین محدوده اندازه سنسور ژیروسکوپ استفاده می شود. به این رجیستر مقدار ۲۴ نسبت داده شده است که یعنی بیت ۴ و ۵ آن مقدار یک گرفته اند. با نگاهی به دیتاشیت این سنسور متوجه میشویم که وقتی این دو بیت ۱ باشند محدوده اندازه گیری بر روی ۲۰۰۰ درجه بر ثانیه تنظیم می شود.

مقدار دهی به رجیستر INT_ENABLE :

به این رجیستر مقدار ۱ نسبت داده شده است که یعنی بیت اول آن یک شده است. در واقع وقفه مربوط به آماده بودن اطلاعات ارسال شده است.

معرفی تابع read_raw_Data :

از این تابع برای خواندن اطلاعات از خروجی سنسور ها استفاده می شود : 

def read_raw_data(addr):
	#Accelero and Gyro value are 16-bit
        high = bus.read_byte_data(Device_Address, addr)
        low = bus.read_byte_data(Device_Address, addr+1)
    
        #concatenate higher and lower value
        value = ((high << 8) | low)
        
        #to get signed value from mpu6050
        if(value > 32768):
                value = value - 65536
        return value

به کمک متد read_byte_data میتوانیم داده های مورد نظر را از رجیسترهای مربوط به دیتا بخوانیم. این متد دو پارامتر در ورودی خود میگیرد. پارامتر اول که آدرس دستگاه است و پارامتر دوم نیز آدرس رجیستری است که میخواهیم دیتای مربوط به آن را بخوانیم.

نکته ای که باید به آن توجه کنیم این است که دیتاهای خروجی ژیروسکوپ و ۱۶ بیتی است ولی رجیسترهای مربوط به MPU6050 هشت بیتی هستند. به همین خاطر دیتای هر محور در دو رجیستر ذخیره می شود. رجیستر اول که آدرس آن را در بالا تعریف کرده ایم و رجیستر دوم نیز همان آدرس را دارد که با عدد ۱ جمع شده است.

همانطور که میبینیم در این تابع ابتدا ۸ بیت بالا و سپس ۸ بیت پایین خوانده شده است و این دو ۸ بیت در کنار یکدیگر قرار گرفته اند و دیتای نهایی از خروجی هر محور را در اختیار ما قرار داده اند.

در بخش پایانی این کد نیز صرفا با یک if ساده دیتای گرفته شده را به یک دیتای علامت دار تبدیل کرده ایم تا اعداد بدست آمده بالاتر از ۳۲۷۶۸ که در واقع هنگامی اتفاق می افتد که در جهت مخالف حرکت کنیم با علامتی منفی نمایش داده شوند. این موضوع نتیجه را برای ما قابل درک تر می کند.

در نهایت نیز دیتای بدست آمده return شده اند.

توضیح حلقه while‌:

حلقه while که ساده ترین بخش کد نیز می باشد در واقع خواندن مداوم دیتا از رجیسترهای مربوط به داده است. این حلقه را ببینید‌:

while True:
	
	#Read Accelerometer raw value
	acc_x = read_raw_data(ACCEL_XOUT_H)
	acc_y = read_raw_data(ACCEL_YOUT_H)
	acc_z = read_raw_data(ACCEL_ZOUT_H)
	
	#Read Gyroscope raw value
	gyro_x = read_raw_data(GYRO_XOUT_H)
	gyro_y = read_raw_data(GYRO_YOUT_H)
	gyro_z = read_raw_data(GYRO_ZOUT_H)
	
	#Full scale range +/- 250 degree/C as per sensitivity scale factor
	Ax = acc_x/16384.0
	Ay = acc_y/16384.0
	Az = acc_z/16384.0
	
	Gx = gyro_x/16.4
	Gy = gyro_y/16.4
	Gz = gyro_z/16.4
	

	print ("Gx=%.2f" %Gx, u'\u00b0'+ "/s", "\tGy=%.2f" %Gy, u'\u00b0'+ "/s", "\tGz=%.2f" %Gz, u'\u00b0'+ "/s", "\tAx=%.2f g" %Ax, "\tAy=%.2f g" %Ay, "\tAz=%.2f g" %Az) 	
	sleep(1)

همانطور که مشاهده میکنید از تابع read_raw_Data استفاده کرده ایم تا داده های خام خود را از رجیسترها بخوانیم و سپس خروجی آن را در متغیرها ذخیره کرده ایم.

تنها نکته مربوط به این حلقه تقسیم هایی که در ادامه آورده شده است. داده های مربوط به شتاب سنج بر ۱۶۳۸۴ و داده های ژیروسکوپ بر ۱۳۱ تقسیم شده تا خروجی اصلی و نهایی مربوط به شتاب و سرعت زاویه ای در اختیار ما قرار بگیرد.‌ ( این که این اعداد از کجا آمده اند یکی از سوالات تمرین این مطلب است که در انتها به آن اشاره شده است. )

در نهایت نیز داده ها درخروجی نمایش داده شده اند و با دستور sleep بین هر دو خواندن متوالی ۱ ثانیه توقف اتفاق می افتد.

در نهایت اگر این کد را بر روی برد رزبری پای بریزیم در این صورت نتیجه نهایی به صورت زیر خواهد بود‌:

نتایج خروجی

که سه ستون اول داده های مربوط به ژیروسکوپ و سه ستون دوم داده های مربوط به شتاب سنج می باشد.

تمارین:

سوال ۱ :‌ تحقیق کنید که اعداد ۱۶۳۸۴ و ۱۶.۴ که داده های خام ژیروسکوپ و شتاب سنج بر آن ها تقسیم شده اند از کجا آمده اند ؟

راهنمایی : میتوانید از جلسه دوم راه اندازی MPU6050 با آردوینو استفاده کنید.

 

سوال ۲ :‌ محدوده ی اندازه گیری برای شتاب سنج در کد بالا تعیین نشده است. از دیتاشیت و Register map کمک بگیرید و این محدوده را تعیین کنید.

سوال ۳ :‌ از دیتاشیت و Register Map مربوط به این سنسور کمک بگیرید و برنامه را طوری تغییر دهید که محدوده اندازه گیری این سنسور ±16g شود.

سوال ۴ :‌ به کمک رجیسترهای TEMP_OUT_H و TEMP_OUT_L دمای داخلی سنسور را بخوانید و آن را در خروجی نمایش دهید.

در پایان مثل همیشه میتوانید ما را در تلگرام و یا اینستاگرام دنبال کنید:

اشتراک گذاری:
مطالب زیر را حتما بخوانید

دیدگاهتان را بنویسید