مقدمه
شاید برای شما سوال شدهباشه چرا اصلا رجیستری، مگر توابع cmsis و یا توابع Hal جوابگوی پروژههایمان نیستند و نمیتوانیم از توابع آماده استفادهکنیم؟ خب به این سوال هرکسی با هر اندازه تجربه میتواند پاسخدهد اما پاسخی که حداقل من را به خود قانع میکند این است که درسته ما توابع متنوعی را در اجرای پروژههایمان میتوانیم استفادهکنیم اما آیا در کنار اجرای پروژهها به درک عمیقی خواهیم رسید؟ و آیا نسبت به توابعی که دیگران آن را نگارش کردند اطمینانخاطر داریم؟ و… اکنون با این دید که میخواهیم درکی متفاوت و عمیق در زمینه میکروکنترلرهای آرم سری Cortex-m3 از خانوادهی بزرگ شرکت ST پیدا کنیم کار خود را شروع میکنیم. طبق روالما از ایجاد پروژه، کار خود را شروع میکنیم و سعی میکنیم یک پین را خروجی کرده و led ای را روشن و خاموش کنیم. برای کدنویسی میکروکنترلرهای ARM نرمافزارهای زیادی موجود است که انصافا باتوجهبه قدرت دیباگ نرمافزار کیل، من از نرمافزار کیل استفاده میکنم اما شما میتوانید از IAR ،eclips و … استفادهکنید، مهم اصول کارکردن است که باهم و در کنارهم یاد میگیریم. من درمورد نصبکردن نرمافزار کیل چیزی نمیگم چون دوستان لطفکردن در این سایت و فضای اینترنت مطالب زیادی رو تهیه و به اشتراک گذاشتند و اگر بازهم مشکل داشتید در آخر این پست کامنت کنید و درقالب پستی نو برای شما آموزشی خواهم گذاشت.
ایجاد پروژه
برای ایجاد پروژه کاملا طبق عکسهای زیر عمل خواهیم کرد:
1 – ایجاد پروژه جدید:
2 – مشخصکردن محل ذخیره فایل پروژه و تعیین نامی مناسب برای پروژه:
3 – انتخاب میکروکنترلر مورداستفاده(من از STM32f103ret6 استفاده میکنم) برای انجام پروژه:
4 – انتخاب امکانات اولیه برای کدنویسی و انتقال فایل startup به پروژه:
5 – اضافهکردن فایل main به پروژه:
خب بعد ایجاد پروژه، طبق معمول در همهی پروژهها به زبان C، تابع main را به پروژه خود اضافه میکنیم.
void main(int) { while(1) { } }
اما برای اینکه به امکانات میکروکنترلرهای سری M3 دسترسی داشتهباشیم باید فایل هدر میکروکنترلر را به پروژه اضافه کنیم.
# include "stm32f10x.h"
اما با اضافهکردن این هدر کار تمامشده نیست، بلکه باید وارد این هدر شده و میکروکنترلر موردنظر خود را از حالت کامنت خارجکرده تا از امکانات این میکروکنترلر بهرهمند شویم طبق عکسزیر:
اما اگر فایل stm32f10x.h را در پوشهی پروژه ذخیره نکنیم نمیتوانیم define STM32F10X_HD # را از حالت کامنت خارجکنیم.
حالا میریم سروقت اصل ماجرا. برای اینکه یک پین را on یا off کنیم اول باید یکسری توضیحات درمورد بعضی رجیسترها بدهم تا اصل ماجرا رو بفهمیم، بعد بریم سراغ پروژه. دانستن این نکته مهمه که شرکت ST علاقه دارد هر پورت خود را ۱۶بیتی تقسیم کند و این ۱۶بیت را به دو دسته ۸بیت کم ارزش و ۸بیت پرارزش تقسیم کند و برای کنترلشان نیز رجیستری را درنظر میگیرد. برای خروجیکردن هر پین میتوانیم از دو مدل آرایش مداری که بهصورت نرمافزاری انتخاب میشود استفادهکنیم (PushPull و یا OpenDrin) برای ورودیکردن هم طبق معمول حالتها PullUp ,PullDown ,Float و آنالوگ را پشتیبانی میکند. و یک حالت Alternate Function هم وجود دارد که بهموقع توضیحخواهمداد.
نکته بعدی هم اینکه در حالت خروجی ما میتوانیم حداکثر فرکانسی را که از پین موردنظرمون عبور کند را هم مشخصکنیم (۱۰Mhz ,۲Mhz ,۵۰Mhz)
خب میریم سروقت رجیستری که ورودی-خروجی را مشخص میکند. اگر یادتانباشد گفتم هر پورت به دو دسته ۸بیتی تقسیم میشود پس بهخاطر این دستهبندی ما از دو رجیستر برای تنظیم حالتهای ورودی–خروجی استفاده میکنیم که اسمشان CRL و CRH است؛ که CRL برای رجیسترهای ۸بیت کم ارزش و CRH برای رجیسترهای ۸بیت پر ارزش است.
رجیستر CRL:
همانطورکه در عکس بالا نمایشدادهشدهاست این رجیستر ۳۲بیتی است و برای تنظیم هر پین ما ۴بیت را دراختیار خواهیمداشت که بهصورت جدولزیر تنظیم میشود.
جدولبالا به ما این را میفهماند که اگر بخواهیم پین موردنظر را خروجی از نوع پوش-پول کنیم باید در ۴بیتی که دراختیار داریم، در دو بیت CNF عدد “۰۰” را نوشته و در دوبیت mode براساس سرعتی که میخواهیم، عدد موردنظر را قرار دهیم، که من میخواهم از سرعت ۵۰Mhz استفادهکنم، که میشود “۱۱” و اگر بخواهیم بهصورت هگز بنویسیم میشود 0x3 اما چگونه باید کد نوشت؟
مثلا من برای پورت A.0 میخواهم تنظیمات رو پیکربندی کنم:
GPIOA->CRL &= ~(0x0000000F); GPIOA->CRL |= 0x00000003;
حالا شاید برای شما سوالشدهباشه که چرا اول(0x0000000F)~ را با رجیستر CRL بهصورت AND بیتی نوشتم! جوابتون هم یک جملهست: چون درحالت اولیه تمام پینها، ورودی و از نوع شناور یا همان float هستند(باتوجهبه مقدار Reset value = 0x4444 4444) و ما در ابتدا باید بیتهای موردنظر را صفر کنیم، سپس در آنها بنویسیم. عبارت (0x0000000F)~ برابر است با 0xFFFFFFF0، یعنی وقتی این عبارت با رجیستر CRL بهصورت بیتی AND شود، ۴بیت اول رجیستر CRL صفر میشوند.
خب بعداز شناختاولیه نسبتبه رجیستر CRL(نگراننباشید در آینده آنقدر با این رجیستر سروکله خواهیمزد که شما قطعا بر این موضوع اشراف پیدا خواهید کرد) میریم سروقت مقداردهی به پین موردنظرمون.
برای روشن و خاموشکردن یک پین ما سه رجیستر کاملا مجزا داریم، رجیسترهای ODR ,BRR ,BSRR که هر سه تاشو توضیح میدم.
رجیستر ODR:
این رجیستر همانطورکه در عکسبالا مشخصاست یک رجیستر ۳۲بیتی است که فقط ۱۶بیت آن دردسترس است که هر بیت برای مقداردهی به هر پین منتاظر است.
از این رجیستر برای نوشتن مقدار صفر و یک در پین موردنظر استفاده میشود اما بهتر است فقط برای نوشتن مقدار یک از این رجیستراستفاده کنیم چون با هربار مقداردهی به این رجیستر اگر مقدار قبلی را OR با مقدار جدید نکنیم کل مقدار قبلی را پاک میکنه و مقدار جدید را رایت میکنه(جایگزینش BSRR است)
GPIOA->ODR |= (1<<0);
یک را شیفت به چپ میدهیم صفرتا =>یعنی بیت صفرم پورت Aرا یک کن
اما برای خاموشکردن این پین از رجیستر BRR استفاده میکنیم.
رجیستر BRR :
این رجیستر هم ۳۲بیتی است که ۱۶بیت آن دردسترس است که با نوشتن مقدار یک در این رجیستر پین موردنظر را صفر میکند(بههمینسادگی) این رجیستر برای صفرکردن پینهایی استفادهمیشه که ازطریق ODR یک شدن(البته در هرصورت برای صفرکردن استفاده میشه اما بیشترین کاربردش برای clear کردن پینی است که ازطریق ODR یک شده است)
GPIOA->BRR = (1<<0);
اما رجیستری که هم خودش میتونه پینی رو یک کنه و هم صفر کنه!
رجیستر BSRR:
این رجیستر ۳۲بیتی است که ۱۶بیت کم ارزش آن برای یک کردن پینها استفاده میشود و ۱۶بیت پر ارزش آن برای صفرکردن پینها مورداستفاده قرار میگیرد.
GPIOA->BSRR = (1<<0); Delay(100); GPIOA->BSRR = (1<<16); Delay();
کد بالا اول پین شماره صفر از پورت A را یک میکند و بعداز صرف زمانی خاص صفر میکند. نکته مهم و پایانی که اگر این نکته را در کدنویسی خود رعایتنکنید قطعا جوابی حاصل نخواهد شد.
طبق عکسبالا که طراحی هستههای ARM سری Cortex-m3 شرکت ST را نشان میدهد، هرکدام از پریفرالها(امکانات) این میکروکنترلر ازطریق باس(رابط)هایی با هستهی مرکزی در ارتباط هستند. حال اگر بخواهیم مثلا پریفرالهای مانند USART ,i2C ,TIMER ,GPIO و… را راهاندازی کنیم و استفاده داشتهباشیم باید باس متصلبه آن پریفرال را فعالکنیم تا(بهقولمعروف اول خون به اعضاء برسه و بعد استفادهکنیم) بتوانیم استفادهکنیم. خب برای فعالسازی کلاک GPIOها اول به عکسبالا نگاه میکنیم تا به کدام یک از باسها متصل هستند، اگر خوب نگاه کنیم میبینیم GPIOها به باس APB2 متصل هستند.
رجیستر APB2ENR:
این رجیستر عمل فراهمسازی کلاک رو برای پریفرالهای متصلبهخود فراهم میسازد.
این رجیستر هم ۳۲بیتی است که بعضی قسمتهای آن دردسترسما است که میتوانیم با یک کردن این قسمتها کلاک را برای پریفرال مورداستفادهمان فراهمسازیم.
RCC->APB2ENR |= (1<<2);
این کد یعنی بیت شماره دو که برای پورت A است را یک کن تا کلاکش مهیا شود.
کد نهایی:
با توضیحات بالا فکر میکنم همهی شما قطعا میتونید اولین پروژه کاملا رجیستری خودتون رو پیادهسازی کنید و درکی درست از برنامه نویسی میکروکنترلر ARM کسبکنید. در جلسه آینده باهم حالت ورودی IOها را بررسی خواهیمکرد و بعد سراغ وقفه خارجی خواهیم رفت.
منبع:سیسوگ