جلسه سی و یکم : پیدا کردن آدرس I2C توسط آردوینو

سلام
جلسه قبل در مورد پروتکل I2C یه کوچولو صحبت کردیم و جنبه های سخت افزاری و نرم افزاری اون رو مورد بررسی قرار دادیم . اگه بخوایم یه دوره مختصر بکنیم باید بگم که از لحاظ سخت افزاری پینهایی که اسمشون scl و sda هست بارزه مشخص قطعاتی خواهند بود که با پروتکل ارتباطی I2C کار میکنن. از لحاظ نرم افزاری هم برامون مهم بود که بدونیم آدرس I2C قطعه ای که داریم باهاش کار میکنیم چیه. در مورد این آدرس I2C هم یه کم حرف زدیم و گفتیم که درست مثل کد پستی خونه ها می مونه . برای این که داده صحیح و سلامت از فرستنده به گیرنده برسه لازمه که بدونه باید داده رو به کدوم آدرس پستی تحویل بده.
اما سوالی که پیش میاد اینه که برای یه قطعه چطوری باید تشخیص بدیم آدرس I2C اش چیه؟
جواب اینه که دو تا راه داره.
راه حل : بعضی قطعه ها و ماژول ها که میخریم پشتشون روی PCB شون نوشته که آدرسشون چیه. مثلا نوشته 0X4A این 4A که نوشته همون آدرس قطعه محسوب میشه. دقت کنید که ما همیشه با کد هگز آدرس I2C سر و کار داریم.
راه حل دوم :این راه حل زمانی به درد میخوره که هیچ جوری نتونیم آدرس I2C قطعه رو پیدا کنیم. نه سازنده اون رو در اختیارمون قرار بده ( هر از گاهی از این شیرین کاری ها میکنن تولید کننده ها ) و نه روی خود قطعه چیزی نوشته . کار بسیار بسیار ساده تر از چیزیه که فکرشو بکنید . برای این کار کافیه مراحل زیر رو طی کنید:
1 . اول از لحاظ سخت افزاری قطعه رو تمام و کمال به آردوینو وصل کنید یعنی :
VCC به 5 یا 3.3 ( بسته به ولتاژ کاری ماژول)
GND به GND
SCL به A4
SDA به A5
2. اپلود کد توسط نرم افزار Arduino IDE . به دلیل طولانی بودن کد اون رو توی انجمن قرار دادم.

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

همونطور که می بینید برای من دو تا آدرس I2C پیدا کرده . دلیل این موضوع این هست که من دو تا ماژول با پروتکل ارتباطی I2C به آردوینوم وصل کردم و الان هم هر دو شناخته شدن و هم آدرسشون معلوم شده . آدرس یکیشون IE هست و آدرس اون یکی 42 . تعجب نکنید بله میشه چند تا ماژول با ارتباط I2C رو با هم به یه آردوینو وصل کرد که در آینده در موردش پروژه خواهیم بست.

پایان جلسه اعلام میشه

جلسه سی ام : راه اندازی ماژول OLED با آردوینو

سلام
این جلسه و چند جلسه آینده قصد دارم در مورد پروتکل ارتباطی i2c صحبت کنم. سنسورها و محرک های زیادی هستن که با این پروتکل کار می کنن و بنابراین اطلاع از نحوه کار این پروتکل لازمه .

برای شروع از یه ماژول ساده شروع می کنیم : ماژول نمایشگر OLED . این ماژول رو می تونید از اینجا ببینید:

جلسه های اول همین وبلاگ در مورد یه نمایشگر دیگه هم صحبت کردیم . نمایشگر کاراکتری 1602 . و این دومین نمایشگری هست که قصد داریم روش تمرکز کنیم و راه بندازیم .
از ضرورت های استفاده از نمایشگر که قائدتا مجددا حرفی نمی زنم چرا که بر هیچکس پوشیده نیست توی پروژه ها چقدر می تونه کار رو زیبا کنه و البته کار رو راه بندازه .
یه جاهایی که دسترسی مون به کامپیوتر قطع میشه میتونیم طوری کد نویسی کنیم که از LCD برای دیباگ برناممون استفاده کنیم . اما این که OLED دیگه چه جور نمایشگریه و فرقش با LCD چی هست رو خیلی ریز و بازاریابانه از اینجا بخونید.

و اما مثل همیشه اول میریم سراغ ارتباطات سخت افزاری . قدم اول باید بریم اسم پینهای روی OLED رو بخونیم. اسم پینها به این صورت هست : GND VCC SCL SDA .
تکلیف GND و VCC که معلومه میمونه اون دو تا پین دیگه . یه سرچ کوچیک نشون میده که این دوتا پین مربوط به ارتباط I2C هستن .
سیم بندی ارتباط این نمایشگر با آردوینو به صورت زیر هست :

همونطور که میبینید SCL وصل شده به پین آنالوگ شماره 4 و پین SDA وصل شده به پین آنالوگ شماره 5 آردوینو . و اما سوال همیشگی : آیا میشه به جای A4 و A5 آردوینو از پینهای دیگه ای استفاده کرد ؟
جواب اینه که خیر. به جای این دو تا پین از هیچ دو پین دیگه ای نمیشه استفاده کرد. یعنی نمیتونی بری سمت پین های دیجیتال یا حتی بقیه پین های آنالوگ . توی هر برد آردوینویی اعم از UNO ، مگا ، DUE و …. پینهای مشخصی برای ارتباط I2C استفاده میشن که جدولشو میتونین اینجا ببینین:

چون ما برد پایه ای که تو وبلاگ استفاده می کنیم UNO هست بنابراین از پینهای آنالوگ شماره 4 و 5 استفاده می کنیم.

حالا باید بریم سراغ کد نویسی. هدفمون تو این پروژه این هست که اول یه پیغام سلام علیک چاپ کنه ، بعد از 0 تا 10 رو بشماره و بعد با یه خداحافظی خوشحالمون کنه .
برای این کار مطابق زیر کد نویسی می کنیم:

قسمت 1 : این قسمت مثه همیشه میام کتابخونه هایی که استفاده کردیم رو معرفی می کینم . اول کتابخونه Wire معرفی شده . یادتونه وقتی از پروتکل SPI استفاده میکردیم کتابخونه SPI رو اینکلود میکردیم . تو ارتباط I2C همیشه کتابخونه Wire رو اینکلود می کنیم. با استفاده از این کتابخونه میگیم پین شماره 4 و 5 آنالوگ آردوینو دارن به عنوان پین های ارتباط I2C استفاده میشن. دو تا کتابخونه دیگه ای هم که اینکلود کردیم و میتونین از اینجا دانلودشون کنین کتابخونه هایی هستن که تو این پروژه ازشون استفاده می کنیم .
با استفاده از این کتابخونه ها خیلی راحت تر می تونیم به OLED مون فرمان بدیم. یعنی به جای این که مستقیم با خود OLED بخوایم سر و کله بزنیم میریم سراغ دستورهایی که این کتابخونه ها دارن . واضح تر بگم ما با کتابخونه و دستورهای آدمیزادیش سر و کله میزنیم ، کتابخونه با OLED و تنظیمات غیر آدمیزادیش سر و کله میزنه. بنابراین تاکید میکنم به کتابخونه ها احترام بزارید.

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

قسمت 3 : مهم ترین بخش راه اندازی یه قطعه با ارتباط I2C همین قسمت هست. فک کنید یه پست چی یه نامه داره که باید به مقصد برسونه . مهم ترین بخش آدرس کد پستی هست . در واقع هر خونه با کد پستیش شناسایی میشه . اگر پست چی اون کد پستی رو نداشته باشه عملا نامه به مقصد نمیرسه . تمامی قطعات I2C هم درست مثه خونه ها یه آدرس دارن . داده ای که قراره از آردوینو به اون قطعه I2C منتقل بشه باید مشخص بشه کجا میره . در واقع ما باید به آردوینو بگیم اگه قراره واسه OLED ما دیتا بفرستی اون رو بفرست به فلان آدرس . این خط کد رو ببیند. دو تا ارگومان داره . آرگومان اولش رو بعدا توضیح میدم ، آرگومان دوم نوشته 0x3C . این 3C همون آدرس این قطعه I2C ما هست . دقت کنید که آدرس به شکل هگز نوشته شده . حالا این که این آدرس چطوری پیدا میشه و به دست میاد رو تو جلسه بعدی صحبت می کنیم.
اما در مورد آرگومان اول که نوشته SSD1306_SWITCHCAPVCC . یه قانون ثابت در مورد راه اندازی تمام LCD ها و OLED ها وجود داره و اون هم اینه که چیپ درایور اونها معیار راه اندازیشون هست. الان این SSD1306 که نوشته چیپ درایور این OLED ای هست که ما داریم باهاش پروژمون رو انجام میدیم. خیلی خیلی مهمه که بدونیم LCD یا OLED ای که میخوایم راهش بندازیم چیپ درایورش چی هست. تو بعضی موارد وقتی چیپ درایور عوض میشه کافیه یه تغییر کوچیک توی کد بدیم تا با درایور جدید هم سازگار بشه اما تو بعضی موارد انقدر کانفیگ و رجیسترهای داخلی چیپ های درایور متفاوته که کلا کتابخونه رو عوض می کنیم. دقت کنید که این begin ای که نوشته شده میره توی کتابخونه Adafruit_SSD1306 و یه سری دستورها رو مربوط به این چیپ اجرا می کنه.

قسمت 4 : یادتونه تو قسمت 3 گفتم begin یه سری دستور رو اجرا میکنه . یکی از اون دستورها اینه که لوگو ادافروت رو نشون میده . فک کنید طرف اومده یه کتابخونه نوشته ، دردسرای ما رو برای سر و کله زدن با زبون غیر آدمیزادی OLED از بین برده بعد از خودش یه رد و نشون به جا نزاره ؟ نمیشه که . به محض این که دستور begin تو بخش 3 اجرا میشه لوگو ادافروت نمایش داده میشه . ما با دستور clearDisplay میایم هر چیزی که رو صفحه هست رو پاک میکنیم. درست بعد از begin هم این کار رو می کنیم که چشممون به جمال لوگو روشن نشه . بعدشم یه تاخیر 100 میلی ثانیه میزاریم تا نمایشگر به یه پایداری برسه و برسیم سراغ کارهایی که خودمون میخواستیم انجام بدیم. دقت کنید این روشی که به کار بردیم تا لوگو رو نبینیم بی نهایت غیر اصولی و غیر حرفه ای هست. در واقع خیلی ساده و راحت می تونیم بریم تو کتابخونه و اون دستور رو پاک کنیم اما امان از تنبلی !

قسمت 5 : توی قسمت 5 اومدیم از تابعی استفاده کردیم که تو قسمت 9 معرفیش می کنیم . یه کلیت بخوام بگم اینه که ما خیلی جاها تو کدنویسی میخوایم یه سری عملیات رو تکرار کنیم . به عنوان مثال همین عملیات نوشتن توی OLED رو در نظر بگیرید . یه نوشتن ساده مراحل زیر رو داره :
1. تعیین کردن سایز نوشته
2. تعیین کردن رنگ نوشته
3. تعیین کردن نقطه شروع برای نوشتن
4. تعیین کردن متنی که قرار نمایش داده بشه
5. ارسال فرمان نمایش

شاید باورتون نشه ولی برای این که یه متن خشک و خالی روی یه نمایشگر نشون داده بشه باید 5 تا کار انجام بشه . یه راه حل باهوشانه !!! اینه که هر بار بخوایم چیزی رو چاپ کنیم هر 5 تا خط رو بنویسیم ( فک کنید برای چاپ چهار خط پشت سر هم باید 20 تا خط کد بزنیم ) و یه راه حل باحال اینه که بیایم یه تابع بنویسیم برای عملیات چاپ داده روی OLED . با این کار همون 4 خطی که بخوایم پشت سر هم بنویسیم رو میتونیم با 9 خط کد جمع کنیم حالا 20 خط کجا 9 خط کجا . منهای این که تعددخطوط کد نویسیمون کم میشه برنامه ای هم که نوشتیم بسیار تمیز و خوانا درمیاد.
ما هم برای این پروژمون از روش دوم یعنی تابع نویسی استفاده کردیم ( راه حل باهوشانه ماله باهوشا ) . یه تابع تعریف کردیم به اسم TextDisplay ( این اسم میتونست هر اسم دیگه ای هم باشه بعدا میگم ) که سه تا آرگومان ورودی داره . آرگومان اول دیتایی که هست که قراره روی نمایشگر چاپ بشه . آرگومان دوم شماره ستونی هست که میخوایم نوشتن دیتا از اون شروع بشه و آرگومان سوم سطری هست که میخوایم نوشتن دیتا از اون شروع بشه . دقت کنید که رنگ و سایز رو تو آرگومان ورودی تابع نیاوردیم که دلیلش رو تو قسمت 9 میگم. بعد از این دستور با همون دستور clearDisplay کل صفحه نمایشگر رو پاک می کنیم. شاید یه سوال به ذهنتون برسه که چاپ میکنیم بعد پاک میکنم ؟ با این کار که اصلا دیتایی نمی بینیم رو نمایشگر . این سوال رو تا پایان قسمت 9 نگه دارید.

قسمت 6 : توی این قسمت یه حلقه for داریم که 11 بار تکرار میشه. توی هر بار اجرای حلقه آردوینو یه مستطیل با لبه های گرد میکشه ( با استفاده از تابع RectangleShow که خودمون تعریفش می کنیم تو قسمت 10 ) بعد از اون شماره حلقه یا همون i رو با دستور TextDisplay چاپ می کنه  . دقت کنید تو قسمت 5 عبارت Hello از نقطه 0 و 0 نوشته میشه یعنی اینجا :

اما تو این قسمت شماره حلقه i که قراره چاپ بشه توی سطر 10 و ستون 55 نوشته میشه یعنی تقریبا وسط صفحه نمایش و اون مستطیلی که کشیدیم:

پایان هر حلقه هم صفحه کامل پاک میشه یه دونه به متغیر حلقه یعنی i اضافه میشه و دوباره مراحل قسمت 6 تکرار میشه. به این شکل اعداد 0 تا 10 نمایش داده میشن.

قسمت 7 : با دستور TextDisplay عبارت Good Bye توی سطر 5 وستون 30 نمایش داده میشه:

قسمت 8 : تمام این کدهایی که تا الان گفتیم تو تابع ستاپ نوشته شده بودن. علت این امر هم این بود که میخواستیم فقط یک بار تکرار بشن .
اگر هدفمون این بود که این پروسه مدام تکرار بشه باید تمام قسمت های 5 و 6 و 7 رو توی Loop می نوشتیم.

قسمت 9: اینجا در مورد طریقه نوشتن تابع TextDisplay صحبت می کنیم. 5 مرحله ای رو که لازم بود تا برای چاپ داده روی نمایشگر طی کنیم رو یادتون میاد؟
دقیقا عین همون 5 مرحله رو تو این تابع انجام میدیم :
1. تعیین کردن سایز نوشته : با استفاده از دستور setTextSize
2. تعیین کردن رنگ نوشته : با استفاده از دستور setTextColor
3. تعیین کردن نقطه شروع برای نوشتن : با استفاده از دستور setCursor
4. تعیین کردن متنی که قرار نمایش داده بشه :با استفاده از دستور println
5. ارسال فرمان نمایش :با استفاده از دستور display

یادتونه گفتم به این کتابخونه ها احترام بزارید. دلیلش رو الان باید متوجه شده باشید. برای این که به نمایشگر بگید سایزی که میخوام استفاده کنم 2 هست اگر قرار بود ما مستقیم به خود نمایشگر بگیم باید میرفتیم به رجیسترهای داخلیش دیتا میفرستادیم و دنگ و فنگ . الان خیلی شیک و بی دردسر داریم با دستور setTextSize و مشخص کردن سایز تو آرگومان این دستور کار خودمونو انجام میدیم.
و اما به اون تاخیر 1 ثانیه ای آخر تابع هم دقت کنید . یادتونه درست بعد از دستور TextDisplay از دستور clearDisplay برای پاک کردن استفاده می کردیم . سوالی که اونجا مطرح شد این بود که با این کار اصلا دیتایی می بینیم یا نه . جوای اینه که بله می بینیم. هر بار که تابع TextDisplay اجرا میشه بعداز دستور display یه تاخیر 1 ثانیه ای وجود داره همین تاخیر 1 ثانیه ای هست که ما میتونیم دیتایی رو که با دستور TextDisplay ارسال کردیم رو روی نمایشگر ببینیم. بعد از اون یک ثانیه هم با دستور clearDisplay  که نوشته بودیم صفحه پاک میشه.
این وسط فقط یک بحث باقی میمونه . چرا فقط متن قابل نمایش ، نقطه شروع و نقطه پایان رو به عنوان ورودی تابع گرفتیم و حق انتخابش رو به کد نویس دادیم؟ چرا مثلا یه int دیگه واسه سایز متن در نظر نگرفتیم و به عنوان آرگومان چهارم نذاشتیمش تو تابع ؟ باید بگم تابع نویسی سلیقه ایه . من مطمئن بودم که در تمام طول پروژه با سایز 2 کار می کنم بنابراین سایزم رو فیکس گذاشتم به جاش نقطه شروع و پایانمو به عنوان متغیر وردوی تابع TextDisplay در نظر گرفتم. ممکنه یکی دیگه بخواد تمام نوشته هاش رو از نقطه 0 و 0 بنویسه و در عوض دائم بخواد سایز رو عوض کنه خب اون تابعش رو یه شکل دیگه می نویسه . این وسط باید به یه چیز فقط دقت کنید. این که نوع متغیری که قصد دارید به عنوان ورودی تابع در نظر بگیرید رو درست انتخاب کنید.

قسمت 10 :با استفاده از این تابع و دستور drawRoundRect یه مستطیل با لبه های گرد کشیده میشه. باز هم از کتابخونه عزیز ادافروت متشکریم.
و بخش دوم این تابع هم با دستور display اون مستطیل رو روی صفحه نمایش نشون میده.
در انتها هم کد رو می تونید از این لینک بردارید و استفاده کنید.