آموزش قدمبه قدم راهاندازی +NRF24L01 با کتابخانه سازگار با انواع میکروکنترلرها و کامپایلرها
قبلاز اینکه قسمت بشه با ماژول +NRF24L01 کار کنم، خیلی از دوستان را دیدهبودم که با این ماژول درگیر بودند و توی راهاندازیش مشکل داشتند و مثل یک اپیدمی شده توی گروههای تلگرامی و فرومها. همانطورکه حدس میزنید وقتی که به ماژول+NRF24L01 نیاز پیدا کردم تا راهش بندازم و ازش استفادهکنم دقیقا همین بلا سر خودم هم اومد. هدفم از نوشتن این مطلب این هست که دیگه مشکلی توی راهاندازی ماژول +NRF24L01 نباشه و همه بهراحتی بتونن باهاش کار کنن و از قابلیتهای این کتابخونه تقریباً بطور کامل استفادهکنن. پس وقت را تلف نکنیم و بریم سراغ راهاندازی… با ماهمراه باشید.
ترتیب پایههای ماژول +NRF24L01
نکات
- در این کتابخانه از پین IRQ استفاده نمیشود.
- CSN همان SS در ارتباط SPI است و CE یک پین اضافه کنترلی برای ماژول +NRF24L01 میباشد.
- نحوه اتصال پایهها به میکروکنترلر در فایل c تعریف میشود که متعاقباً مفصل توضیح دادهخواهدشد.
- همانطورکه میدانید تغذیه ماژول ۳ولت هست اما بقیه پایههای ارتباطی ماژول(۵v tolerant) هستند. یعنی سطح لاجیک ۵ولت رو تحمل میکنه و میتونیم مستقیم به میکروکنترلرهای ۵ولتی، مثل بعضیاز مدلهای avr متصلکنیم.
توضیحات درمورد کتابخانه
این کتابخانه شاید بهینهترین نباشه اما احتمالاً portableترین هست. (نمیدونم فارسیش چیه) یعنی شما میتوانید با تغییرات کوچکی این کتابخانه را متناسب با میکروکنترلر خودتان شخصیسازی کنید. علت این صحبت هم این هست که شما با تغییردادن چندتا تابع (که به قسمت GPIO هر میکروکنترلر مربوط میشه) در فایل radioPinFunctions.c، میتوانید کتابخانه موردنظر را شخصیسازی کنید و هر پایهای از میکروکنترلر را به هرکدام از پایههای ماژول +NRF24L01 که خواستید وصلکنید و از این بابت جای هیچنگرانی نیست. این قسمت فقط یکم دانش زبان C میخواد و آشنایی با GPIO میکروکنترلر مربوطه. در این کتابخانه حالتهای Auto Acknowledgement و Auto Retransmission فعالشده است و تعداد دیتا باید یک اندازه ثابت باشه (static length payload mode). دو ویژگی ذکرشده از مهمترین قابلیتهای +NRF24L01 هستند که با آنها میتوانیم بفهمیم دیتا درست رسیده یا نه! همچنین اگر دیتا در مرحله ارسال ازدسترفت و درست به مقصد نرسید این چیپ میتواند خودش کار ارسال دوباره (Retransmision) رو انجام بدهد، که میتوانیم این قسمت را هم خودمان تنظیمکنیم که چه تعداد تلاش دوباره انجامبدهد و بین هرکدام از این تلاشها چه تاخیری ایجاد شود. البته تعداد تلاش دوباره روی ۱۵بار و تاخیر ۱۰۰۰میکروثانیه بهصورت خودکار تنظیمشدهاست، اما با تابع nrf24_RetransmissionConfig میتوانیم این مقادیر را تغییربدهیم. برای راهاندازی ماژول باید چهار فایل زیر در مسیر پروژه قرار بگیرد:
- nrf24.c
- nrf24.h
- nRF24L01.h
- radioPinFunctions.c
و در ابتدای کد برنامه باید دو فایل زیر ضمیمه شود:
#include <nrf24.c> #include <radioPinFunctions.c>
ممکناست کامپایلر یکسری Warning هم بدهد، آنقدر مهم نیستند، آنها را نادیده بگیرید.(علت اینکه فایلهای c. رو داریم ضمیمه میکنیم این هست که یهسری از توابعی که داخل فایل nrf24.c وجود دارند داخل فایل nrf24.h موجود نیست. حال اگر فایل h. رو ضمیمه کنیم، احتمالا کامپایلر کلی ارور خواهد داد؛ اگرچه در عمل فایلهای h. و c. فرقی باهم ندارند و فقط فایل h. یک نامگذاری دیگر است که تفاوت بین فایلهای کتابخانه و کد پروژه مشخص باشد) قبلاز اینکه برویم سراغ کدنویسی یه نکته رو بگم،
حتیالامکان اگر از هر تابعی داخل پروژه استفاده میکنید آنرا دستی ننویسید و بروید از هدر nrf24.h کپی و استفادهکنید، چون حروف کوچک و بزرگ داخل این توابع زیاد هست و ممکناست اشتباهات ناخواستهای رخ بدهد.
خب شروعکنیم به یادگرفتن توابع که ماشالله کم هم نیستن، ولی دست ما رو تقریباً واسه هرکاری باز میگذارند.
نحوه شخصیسازی فایل radioPinFunctions.c برای میکروکنترلر موردنظر
ابتدا ببینیم اصلا هدف و کاربرد این فایل چی هست؟! قرار است ما در این فایل به کتابخانه بفهمانیم که چه پایهای از ماژول به چه پایهای از میکروکنترلر وصل است، سپس بیایم ورودی-خروجی بودن اون پایهها رو تعیینکنیم و درنهایت نحوه صفر و یک کردن اون پایه رو به کتابخانه یاد بدهیم. علت اینکار هم این هست که کتابخونه بتونه با spi نرمافزاری با ماژول صحبت کند.(یعنی یه spi که ما خودمون با gpioها ساختیم و دیگه محدودیت کتابخونه به یک میکروکنترلر رو برداشتیم و میتوانیم با هر میکروکنترلری کار کنیم) در این قسمت همه مثالها و کدها براساس میکروکنترلر atmega8 ،avr است چراکه یک چیزعام باشد و همه بتوانند درکش کنند و ازش استفادهکنند. و شما باتوجهبه فهمی که از avr دارید و توضیحاتی که دادهشده، باید کدهای مربوطبه میکروکنترلر خودتان را جایگزین کنید. بریم سراغ کدنویسی… در خطوط اول فایل باید هدر میکروکنترلر مربوطه رو ضمیمه کنید تا برنامه رجیسترهای GPIO را بشناسد.
مثال
#include <mega8.h>
در ادامه سه تا define خواهید دید که کارشان مثل یک تابع هست که دوتا ورودی میگیرد و اونا رو توی یک عملیات جایگذاری میکند. (این قسمت برای کوتاهشدن کد استفادهشدهاست و شما ممکناست اصلا بهشنیازی نداشتهباشید و بستگیبه میکروکنترلرتون داره…)
- (set_bit(reg,bit: یک بیت از یک رجیستر رو ۱ میکند.
- (clr_bit(reg,bit: یک بیت از یک رجیستر رو ۰ میکند.
- (check_bit(reg,bit: مقدار یک بیت بخصوص از یک رجیستر را برمیگرداند. بقیه فایل دارای پنج تابع است که کاربرد و نحوه تغییرداد نشون را خدمت شما شرحخواهیمداد.
- ()nrf24_setupPins: نقش این تابع تعیین ورودی-خروجیبودن پایههای متصلبه میکروکنترلر از ماژول میباشد.
مثال
میخواهیم پایه x از یک پورت میکرکنترلر موردنظر را که به ce ماژول متصل است خروجی کنیم:
set_bit(Register,x);
که Register همان رجیستر مربوطبه تعیین خروجی میکروکنترلر موردنظر است.
مثال
پایه ce ماژول به پین ۱ از پورت B میکروکنترلر mega8 متصل است که باید خروجی شود، پس:
set_bit(DDRB,1);
مابقی را نیز بههمین شکل انجام میدهیم.
نکته: توجهداشتهباشید چه در سمت فرستنده چه در سمت گیرنده میکروکنترلر ما درحالت Master قرار دارد و ماژول Slave است، پس باید تمام پایههای متصلبه ماژول +NRF24L01، بهجز پایه MISO، در هردوطرف خروجی شوند.
تابع (nrf24_ce_digitalWrite(state برایمثال در این قسمت ما پایه ce را به پایه۱ پورت B میکروکنترلر atmega8 متصلکردهایم و تغییر آن به شکلزیر میشود:
void nrf24_ce_digitalWrite(unsigned char state) { if(state) { set_bit(PORTB,1); } else { clr_bit(PORTB,1); } }
همانطورکه مشاهده میکنید کار این تابع این است که از ورودی ۰یا ۱ میگیرد و اگر ۱ بود دستور set_bit و اگر ۰ بود دستور clr_bit را اجرا میکند. اما اینبار ورودی دستورات define شده، رجیستری است که مستقیماً سطح لاجیکی(Logic Level) پایهها را کنترل میکند. برای مابقی توابع همین عملیات تکرار میشود. برای تابع آخر هم باید مانند این مثال تغییر را اعمالکنیم. برایمثال اگر پایه miso ماژول به پین ۴پورت B متصل باشد، کد مربوطهبه شکلزیر خواهدشد:
unsigned char nrf24_miso_digitalRead() { return check_bit(PINB,4); }
خب کار شخصیسازی این فایل تمام شد، فایل را ذخیره میکنیم و دیگه کاری با فایلهای کتابخانه نداریم و میرویم سراغ یادگیری توابع و بکارگیریشون داخل کداصلی برنامه. توابع شامل چنددسته میشن که بهترتیب توضیحدادهمیشه :
پیکربندی (Configuration) ماژول +NRF24L01
برای پیکربندی اولیه ماژول +NRF24L01 سه تابع زیر تعریف میشود:
۱- ()nrf24_init این تابع باید اول از همه در تابع main فراخوانی شود و کارش تعیین ورودی-خروجی پایههای انتخاب شده از قبل و در کل، تخصیص پایههای میکروکنترلر به پایههای ماژول است.
۲- (nrf24_config(channel , payload_length , data_rate داخل این تابع کارهای زیادی انجام میشود، اما کارهایی که ما میتوانیم با ورودی تغییرشان بدهیم دو متغیر است، یکی انتخاب کانال فرکانسی هست؛ که میتوانیم بهجای channel از ۰ تا ۸۳ قراربدهیم و دیگری تعداد بایتهایی که میخواهیم در هر ارسال فرستادهشود؛ که میتوانیم بهجای payload_length از ۰ تا ۳۲ قرار بدهیم. بهجای data_rate باید یکیاز ۳عدد زیر قرارگیرد:
0 -> 1Mbit/s
1 -> 2Mbit/s 2 -> 250Kbit/s
توجه: این سه متغیر باید در هر دو طرف فرستنده و گیرنده یکی باشند.
مثال
void main(void) { nrf24_init(); nrf24_config(1,4,1); while(1) { } }
در این مثال کانال۱ انتخابشدهاست، یعنی فرکانس ۲.۴۰۱Ghz تعداد بایتهای ارسالی ۴عدد و سرعت ارسال ۲Mbit/s میباشد.
نکته: بعداز فراخوانی این تابع ماژول بهصورت خودکار بهحالت گیرنده میرود.
3- (nrf24_RetransmissionConfig( delay_us , trial این تابع برای تعیین تعداد تلاش دوباره برای ارسال و تاخیر بین هردو تلاش درحالتی که دیتا به مقصد نرسیدهاست یا خطا دارد کاربرد دارد. در این تابع مقدارتاخیر، بهجای delay_us فقط ضرایب خاصی از ۲۵۰ میتواند قرارگیرد. درغیر اینصورت رفتار ماژول قابل پیشبینی نیست.
این مقادیر تاخیر ازطریق فرمولزیر بهدست میآیند: delay = (۲۵۰*n)+۲۵۰ که n از ۰ تا ۱۵ مقدار میپذیرد . یعنی ضرایب ۲۵۰تا حداکثر عدد ۴۰۰۰ قابلفهم و کارکردن برای ماژول است. پس میتوانیم اعداد ۲۵۰، ۵۰۰، ۷۵۰، ۱۰۰۰، …، ۴۰۰۰ را در ورودی تابع قراردهیم. این تاخیر برحسب میکروثانیه میباشد. تعداد تلاش دوباره برای ارسال ۰تا ۱۵بار میباشد و بهجای trial قرار میگیرد. این تابع را خودم به کتابخونه اضافهکردم و دیدم بهدرد میخورد!
آدرسدهی
هر ماژول یک مک آدرس ۵بایتی مختصبه خود را دارد که ما آنرا به دلخواه انتخاب میکنیم و به ماژول اعلام میکنیم. یک چیزدیگر که باید به ماژول اعلام شود، این است که طرف مقابل چه مک آدرسی دارد. که اینها ازطریق دوتابع زیر انجام میشود:
nrf24_rx_address(rx_mac);
nrf24_tx_address(tx_mac);
این توابع باید در تابع main، بعداز توابع پیکربندی نوشتهشوند. اما چرا نوشتن این دو تابع مهم است؟ لطفا بهمثالزیر دقتکنید تا اشتباه من رو مرتکب نشوید و مثل من یک هفته وقتتان گرفتهنشود…
مثال:
کد در قسمت فرستنده
unsigned char rx_mac[5] = {0xE7,0xB2,0xE7,0xE7,0xE7}; unsigned char tx_mac[5] = {0xD7,0xD7,0xD7,0x12,0xD7}; nrf24_rx_address(rx_mac); nrf24_tx_address(tx_mac);
کد در قسمت گیرنده
unsigned char rx_mac[5] = {0xD7,0xD7,0xD7,0x12,0xD7}; unsigned char tx_mac[5] = {0xE7, 0xB2,0xE7,0xE7,0xE7}; nrf24_rx_address(rx_mac); nrf24_tx_address(tx_mac);
میبینید که [rx_mac[5 در قسمت فرستنده با [tx_mac[5 در قسمت گیرنده برابر است و [tx_mac[5 در قسمت فرستنده با [rx_mac[5 در قسمت گیرنده برابر است و مک آدرسها بهصورت ضربدری عوض شدهاند.
تنظیم فرستنده یا گیرنده
برای اینکار از دوتابع زیر استفاده میشود که نیازیبه توضیح ندارند.
- ()nrf24_powerUpRx: تنظیم بهعنوان گیرنده.
- ()nrf24_powerUpTx: تنظیم بهعنوان فرستنده.
بردن ماژول +NRF24L01 بهحالتخواب(Sleep Mode)
بااستفادهاز تابعزیر در زمانهایی که به ماژول نیاز نداریم میتوانیم مصرف توان ماژول را به حداقل برسانیم.
nrf24_powerDown();
ارسال داده(Transmit)
برای ارسال داده از تابعزیر استفاده میشود:
nrf24_send(data_array);
متغیر ورودی تابع در اینجا، برای مثالما data_array میباشد. در این تابع، برای فرستادن دیتا ابتدا ماژول بهصورتخودکار درحالت فرستنده قرار میگیرد (کد داخل این تابع به این شکل هست) و برای برقراری ارتباط باید ماژول دیگر درحالت گیرنده قرارگرفتهباشد و قسمت ( …. , …. , …. )nrf_config باید یکی باشند. برایمثال متغیر data_array هم بهشکلزیر تعریف میشود(با فرضاینکه payload_length در تابع nrf_config برابر با ۴ باشد):
unsigned char data_array[4]; data_array[0] = 0x00; data_array[1] = 0xAA; data_array[2] = 0x55; data_array[3] = 0xFF;
بعداز هر ارسال نباید دیتای دیگری را ارسالکرد و باید ابتدا از ارسال کامل دیتای قبلی اطمینان حاصل نمود برای اینکار از تابعزیر استفاده میشود: ()nrf24_isSending این تابع ورودی نمیگیرد و تاوقتیکه دیتا درحال ارسال است، ۱ را برمیگرداند و وقتی که ارسال تمامشد، ۰ را برمیگرداند. و معمولا قبلاز هر ارسال کد زیر قرار میگیرد:
while(nrf24_isSending());
بعداز هر ارسال پیشنهاد میشود از درست ارسالشدن آخرین دیتا اطمینان حاصلکنیم و روی آخرین ارسال پردازش انجامدهیم که با دوتابع زیر انجامپذیر است: ()nrf24_lastMessageStatus این تابع ورودی نمیگیرد و اگر ۰ را برگرداند یعنی دیتا درست ارسالشده و اگر ۱ را برگرداند یعنی ارسال دیتا به مشکل برخورده است. ()nrf24_retransmissionCount این تابع نیز ورودی نمیگیرد و تعداد تلاش دوباره برای فرستادن آخرین دیتا را برمیگرداند.
دریافت داده(Recieve)
چون در این کتابخانه از پین IRQ استفاده نمیشود، بنابراین برای دریافت داده باید پیوسته تابع زیر را بخوانیم تا از رسیدن دیتا مطلعشویم: ()nrf24_dataReady این تابع ورودی نمیگیرد و هروقت عدد ۱ را برگرداند یعنی در FIFO ماژول حداقل یک دیتا رسیده و باید خوانده شود.
نکته: اگر یک عملکرد بهینه در دریافت مدنظرتان است کمی درمورد پین IRQ مطالعهکنید و به کمک اینتراپت خارجی میکروکنترلر میتوانید کارهای جالبی انجام بدهید.
برای دریافت دیتا باید تابع روبرو فراخوانی شود:
nrf_getData(data_array);
چون در زبان C قابلیت return یک آرایه قراردادهنشدهاست ما از این ترفند و ورودی قراردادن آرایه در تابع استفادهکردیم. که متغیر data_array باید به شکلزیر تعریفشود(فرضبر این است که payload_length در تابع nrf_config برابر ۴ تنظیمشدهاست):
unsigned char data_array[4];
که دیتای اول در [data_array[0، دیتای دوم در [data_array[1 و… قرار میگیرند. با این توضیحات و توابعی که ذکرشد میتوانید کاملا ماژول را راهاندازی کنید. توابعی که در پایین تر ذکر میشود توابع دیگری هستند که در کتابخانه موجود است و ممکناست برحسب پروژه خود، به آنها نیاز پیدا کنید.
- nrf24_rxFifoEmpty: ورودی نمیگیرد و اگر FIFO دریافت خالی باشد ۱ را برمیگرداند.
- nrf24_payloadLength: ورودی نمیگیرد و تعداد بایت منتظر در FIFO را برمیگرداند.
- nrf24_getStatus: رجیستر Status در NRF را برمیگرداند.
- nrf24_configRegister: فقط یک بایت داده را در رجیستر موردنظر مینویسد.
- nrf24_readRegister: رجیستر موردنظر را از NRF را میخواند.
- nrf24_writeRegister: هر تعداد بایت داده به دلخواه در رجیستر موردنظر مینویسد.
نکات کلی
برای استفادهاز این ماژول ابتدا از میکروکنترلر mega ۸ استفادهکردم با کلاک ۸مگاهرتز. و سپس قرار شد که میکروکنترلر عوضشود و LPC1768 استفادهشود با کلاک ۱۰۰مگاهرتز، پس من به این فکر افتادم که سرعت Software spi رو چککنم تا از حد پشتیبانی nrf بیشتر نشود. تست من طبق شرایطزیر است: میکرو mega8 کلاک ۸مگاهرتز. کلاک تایمر-کانتر را بر ۸ تقسیمکردم تا محاسبات سادهتر شود و هر عدد کانتر نماینده یک میکروثانیه باشد. با یک کد ساده محاسبه شد که ارسال یک بایت داده با spi نرمافزاری ۱۵۰میکروثانیه طول میکشد. با یک محاسبه میتوان فهمید سرعت ارسال داده ۵۳.۳۳۳Kbit/s است پس با ۱۰برابرشدن کلاک GPIO باز هم از مرز ۱۰Mbit/s رد نمیشویم و این خبر خوبیست. خبربد این است که برای جاهایی که سرعت ارسال داده(data rate) بالا نیاز است، خوبنیست و بهدرد نمیخورد. پس بهترینکار این است که تابع spi_transfer در فایل nrf24.c را برای میکروکنترلر خودمان به spi سختافزاری تغییر بدیم تا دیگه این کتابخانه هیچمشکلی نداشتهباشد.(اینو دیگه میذارم بهعهده خودتون) فقط تنظیمات spi باید بهشکلزیر باشد:Sck idle state :low Sampling rising edge :idle to active Bit order :MSB First شرایط بالا Mode=۰، پروتکل spi را توصیف میکند. یک تست خاص هم برای بُرد ماژول انجامدادم گفتم بذارم شاید بهدرد کسی بخورد.
شرایط تست: فرستنده از نوع دارای pa+lna مشکی رنگ میباشد و گیرنده از نوع معمولی +NRF24L01 .
میکروکنترلر: ۸مگا در محیط بسته تا ۳لایه دیوار ارتباط برقرار است.(تا صدمترفاصله) اگر هردو از نوع pa+lna باشد تا ۱۰لایه دیوار نیز ارتباط برقرار است.
خب خستهنباشید امیدوارم ماژول +NRF24L01 را راهانداختهباشید و ازش بهخوبی استفادهکنید. اگر سوالی داشتید حتما کامنت بگذارید سعی میکنم همه سوالات را جواببدهم. سورس کامل را هم میتوانید از منبع(گیتهاب) و همچنین در زیر رایگان دانلود و استفادهنمایید. NRF24L01 Library-Example
منبع: سیسوگ
با سلام مهندس ببریان هستم میتونم تماس بگیرم خدمتتون در مورد پروژه راه اندازی NRF24l01
با سلام
راههای ارتباطی خدمت شما:
https://front.belec.ir/site/contact-us