رجیستر یک اندازه ی 8 ، 16 یا 32 بیتی با یک نشانی (آدرس) شناخته شده هست. با اندازه ای که دربیت های رجیسترها مینویسیم، گزینش میکنیم بخشی از میکروکنترلر چجوری کار کنه. در این نوشته روش های گوناگون دسترسی به داده گاه (حافظه) در زبان سی رو آموزش میبینید.
پیشنیاز:
مقاله کاربرد عملگرهای بیتی برای تغییر بیتها
پهرست
– برنامه نویسی رجیستری STM32 چیست؟
– ساخت نشانگر (pointer) برای هر رجیستر
– دسترسی به رجیستر در چهارچوب زیر گروه های (اعضای) استراکچر پنداره ای
– دسترسی به بیت رجیسترها با بیت فیلد
ویدئوی آموزش مبانی برنامه نویسی رجیستری STM32 در کانال یوتوب pointer-x
ساخت نشانگر (پوینتر) برای هر رجیستر
پندارید یک رجیستر 16 بیتی ( از گونه uint16_t ) در نشانی 0x40000000 هست. چجوری میشه به اندازه این رجیستر دسترسی داشت؟
به نشانگری (پوینتری) با اندازه 0x40000000 و از گونه *uint16_t نیاز داریم. چرا؟
- اندازه نشانگر = 0x40000000 = نشانی رجیستر ، چون میخواهیم به این نشانی دسترسی پیدا کنیم.
- گونه نشانگر = *uint16_t ، چون گونه رجیستری که میخواهیم به آن دسترسی یابیم uint16_t است.
هنگامی که نشانگر ( pointer ) به رجیستر را داریم، چگونه آنرا برای دسترسی به رجیستر بکار بگیریم؟
در این نمونه نام نشانگر p هست، با گذاشتن استریسک * پیش از نشانگر (پوینتر) به درونمایه ی نشانی دسترسی میابیم.
ساخت نشانگر پویاچنده (پوینتر متغیر)
برای شناسش نشانگر (تعریف پوینتر)، باید استریسک – پوینتر ( p* ) شناسش بشه. چرا؟
خب کامپایر همیشه نشانگر ها رو میشناسه، اندازه های 32 بیتی پیشا (مثبت) هستند. اونی که کامپایلر نمیدونه اینه که اون درونمایه ای در داده گاه (محتوایی در حافظه)ت میخواهی بهش دسترسی پیدا کنی از چه گونه ای هست.
گونه p* ، گونه اون چیزی در داده گاه (حافظه) است که برای دسترسی بهش نشانگر (پوینتر) میسازیم. برای نمونه اینجا میخواهیم به رجیستر 16 بیتی دسترسی داشته باشیم پس گونه p* هست uint16_t . همانجور که در شناسش پویاچنده (تعریف متغیر)، پیش از نام اون، گونه اون نوشته میشه. گونه p* هم پیش از نامش نوشته شده که uint16_t هست.
شناسش (تعریف) p* ، شناسش خود p هم هست (همزمان شناسش دو چیزه). گونه پوینتر (نشانگر) هم در شناسش نشانگر، پیش از اسمش نوشته شده است.
ساخت نشانگر پایاچنده (پوینتر ثابت)
برای ساخت نشانگر پایاچنده، باید گونه نشانگر رو پیش از اندازه نشانگر در پرانتز نوشت. به نوشتن گونه یک اندازه پیش از اون در پرانتر، میگن type casting یا گونه گذاری.
روش یکم دسترسی به رجیستر ها

دسترسی به رجیستر در چهارچوب زیرگروه های (اعضای) استراکچر پنداره ای (فرضی)

روش بهتری هم هست. بجای اینکه هر رجیستر رو یک 32 بیتی جدا ببینیم، همه ی رجیسترهای یک پیرامدار رو، زیرگروه های (اعضای) یک استراکچر پنداره ای (فرضی) ببینیم. پیرامدار GPIOC هفت رجیستر 32 بیتی داره که از نشانی 0x40011000 آغاز شدن. پندارید یک استراکچر که 7 عضو 32 بیتی داره در نشانی 0x40011000 هست.

یک گونه استراکچر به نام GPIO_TypeDef رو هماهنگ با رجیسترهای پیرامدار GPIO میسازیم و با بکارگیری از این گونه، یک نشانگر میسازیم برای دسترسی به استراکچر پنداره ای از نوع GPIO_TypeDef که در نشانی 0x40011000 هست.
برای دسترسی به رجیسترهای پیرامدار GPIOC که زیرگروه های (اعضای) استراکچر پنداره ای هستند، پس از نشانگر به استراکچر، پیکان میذاریم (دیگه به استریسک نیازی نیست).
برای خواناتر شدن برنامه، میتونیم نشانگر به استراکچر رو نام دهی define کنیم.
این روش CMSIS استاندارد هست برای دسترسی به رجیسترها ، در فایل CMSIS device این چیزها وجود داره که بتونیم به رجیسترها دسترسی داشته باشیم و برنامه نویسی رجیستری انجام بدیم.
- نام دهی (#define کردن با دستورات پیش پردازشگر) نشانی نخستین رجیستر پیرامدار ها ( base address ).
- شناسش (تعریف) گونه استراکچر هماهنگ رجیسترهای هر پیرامدار.
- ساخت و نام دهی (define#) نشانگر به استراکچر با بکارگیری گونه استراکچر که در گام 2 شناسش کردیم.
نوشتن و خواندن در رجیسترها با کننده های (عملگر) بیتی و روبند (ماسک) بیت
در دو روش پیشین برای دسترسی به رجیسترها، گونه خود رجیستر همیشه اینتیجر 32 بیتی بوده و برای نوشتن و خواندن در رجیستر ها به دو چیز نیاز است.
- کننده های بیتی : =| و =& .
- روبند بیت: یک شماره ی هم درازا با رجیستر که تنها بیت هایی که میخواهیم بخوانیم یا بنویسیم در آن یک هستند.

میخواهیم با روش های پیشین دسترسی به رجیستر، بیت های MODE1 در رجیستر CRL از پیرامدار GPIOC رو 0b01 کنیم و بیت 5 هیچا(0) و بیت 4 یک بشه.
- در گام یکم هر دو بیت هیچا میشن و برای این کار به روبند (ماسک) بیت های MODE1 نیاز هست.
- در گام دوم بیت 4 باید یک بشه که برای این کار به روبند(ماسک) بیت 4 نیاز هست.
در روش سوم دسترسی به رجیستر ها که در بخش آینده میگم، به کننده های بیتی و روبند (ماسک) بیت نیازی نیست و یکراست با کننده ی گمارش ( assignment =) میتونیم در بیت فیلد ها بنویسیم.
دسترسی به بیت رجیستر ها با بیت فیلد
رجیستر CRL، به شمار 16 بیت فیلد 2 بیتی داره که نخست MODE0 و پایانی CNF7 هست. بر پایه ی بیت های این رجیستر یک گونه استراکچر شناسش (تعریف) میشه به نام CRL_type به درازای 32 بیت (هم درازا با رجیستر CRL که 32 بیت داره) که زیرگروه هاش (اعضاش) بیت فیلد هایی هم ساختار با بیت های رجیستر CRL هستند.
اکنون بپندارید در نشانی رجیستر CRL، بجای یک uint32_t، یک استراکچر از گونه CRL_Type هست. در استراکچری که در گام پیشین، برای رجیستر های پریفرال GPIO ساخته بودیم، گونه زیرگروه CRL رو از uint32_t به CRL_type دگرگون میکنم .
یادآوری : نه استراکچری از گونه ی GPIO_TypeDef و نه استراکچری از گونه CRL_type شناسش شده، تنها گونه های این استراکچر ها برپایه ی ساختار رجیسترهای پیرامدار GPIO شناسش (تعریف) شده. یک چیزهایی از پیش (قبل) در داده گاه (حافظه) بوده و ما برای دسترسی بهش باید نشانگر (پوینتر) میساختیم. برای ساخت نشانگر به گونه استراکچر نیاز داشتیم و گونه استراکچر درست کردیم.
همچون گام های پیشین، برای دسترسی به زیرگروه های این استراکچر پنداره ای (فرضی ) از گونه GPIO_TypeDef نشانگری میسازیم:
((GPIO_TypeDef *) 0x40011000)
و سپس این نشانگر رو با کننده ی #define نام دهی میکنیم و نام GPIOC رو بهش میدیم.
با دسترسی به زیرگروه های استراکچر CRL که خودش زیرگروه استراکچری از گونه GPIO_TypeDef هست، میتونیم سرراست به بیت های رجیستر CRL دسترسی پیدا کنیم و بی نیاز از کننده های بیتی و ماسک بیت در این بیت ها بنویسیم.
میخواستیم در رجیستر CRL بیت 4 یک و بیت 5 هیچا (صفر) بشه، پس میخواستیم در بیت های MODE1 چنده (مقدار) یک نوشته بشه. اکنون میتوانیم با کننده ی گمارش یا = ،چنده (مقدار) یک رو در این دو بیت بنویسیم و به روبند (ماسک) بیت و کننده های بیتی هم نیازی نیست.
به 32 بیتی ای که در آدرس 0x4001000 هست، یک بار با این دیدگاه که گونه آن uint32_t است و یک بار هم با این دیدگاه که گونه آن CRL_Type است ، دسترسی پیدا کردیم.
اگر بخواهیم همزمان دو جور دسترسی را داشته باشیم به یونیون نیاز هست. برای یادگیری دنباله ی داستان آموزش نماوایی (ویدئویی) را ببینید. (رایگان هست).
دسترسی به بیت رجیسترها با بیت فیلد
در دو روش پیشین برای دسترسی به رجیسترها، گونه خود رجیستر همیشه اینتیجر 32 بیتی بوده و برای تغییر بیت ها چند نیازمندی وجود داره:
- فرایند صفر کردن و یک کردن بیت ها متفاوت هست.
- به ماسک بیت نیاز هست.

فرض کنید با روش قبلی قراره بیت های MODE1 در رجیستر CRL از پریفرال GPIOC رو 0b01 کنیم. یعنی قراره بیت 5 صفر و بیت 4 یک بشه. در قدم اول هر دو بیت صفر میشن و به ماسک بیت های MODE1 نیاز هست. در قدم دوم بیت 4 باید یک بشه که ماسک بیت 4 نیاز هست.
در روش سوم به عملگرهای بیتی و ماسک بیت نیازی نیست و مستقیما با عملگر = میتونیم بیت فیلد هارو تغییر بدیم. رجیستر CRL، تعداد 16 بیت فیلد 2 بیتی داره که اولی MODE0 و آخری CNF7 هست. مطابق بیت های این رجیستر یک نوع متغیر استراکچر تعریف میشه که اعضاش بیت فیلد هستند.
حالا فرض میکنیم در آدرس رجیستر CRL، بجای اینتیچر 32 بیتی، یک استراکچر از نوع CRL_Type وجود داره که البته 32 بیتی هست. در استراکچری که در مرحله قبل قالب رجیستر های پریفرال GPIO طراحی شده بود، نوع رجیستر CRL رو از uint32_t به CRL_type تغییر میدم .
با دسترسی به اعضای این استراکچر به بیت فیلد های رجیستر دسترسی مستقیم پیدا میکنیم. قرار بود بیت 4 یک و بیت 5 صفر بشه، یعنی در بیت های MODE1 عدد یک نوشته بشه. در یک مرحله و با عملگر = میشه عدد یک رو در این دو بیت نوشت و به ماسک بیت و عملگر های بیتی نیازی نیست.
به مقدار 32 بیتی بدون علامتی که در آدرس 0x4001000 هست، یک بار بصورت اینتیجر و یک بار استراکچر دسترسی پیدا کردیم. اگر بخواهیم همزمان قابلیت هر دو دسترسی رو داشته باشیم به یونیون نیاز هست. توضیحات مربوط به این قسمت رو در ویدئو ببینید.
لایه ی نرم افزاری CMSIS و بیت فیلد
لایه ی نرم افزاری CMSIS استاندارد که کتابخونه hal بکار گرفتتش، بیت فیلد نداره و دسترسی به رجیسترها 32 بیتی هست. ولی شرکت آرم که CMSIS استاندارد رو ساخته، فایل های دیگری هم ساخته که دسترسی به بیت فیلدها رو هم فراهم میکنند.
چرا با اینکه با دسترسی به بیت فیلد برنامه نویسی آسانتر میشه، کتابخونه HAL بکار نمیگیره و کمتر کسی میدونه هیچین چیزی هم هست؟
چون برنامه ای که ما به زبان سی مینویسیم، باید اسمبلی بشه، کی اسمبلی میکنه؟ کامپایلر، دشواری اینجاست که کامپایلر های گوناگون، بیت فیلد رو در اسمبلی با شیوه های گوناگون پیاده سازی میکنند، و این دردسر ساز هست.
آیا خوبه با بیت فیلد در رجیستر ها بنویسیم؟ نه، چون در همه ی برنامه های ما هم کتابخونه HAL بکار میره و هم دسترسی یکراست به رجیسترها داریم، و ما باید همان راهی رو برای دسترسی به رجیستر ها بکار بگیریم که کتابخونه HAL بکار میگیره. اگر بخواهیم کتابخونه HAL در برنامه باشه و همزمان دسترسی به بیت فیلد هم داشته باشیم، اینجوری باید دو گونه CMSIS در برنامه داشته باشیم ، که بسیار دردسر ساز هست و بیشتر از سود، زیان داره.
چجوری فایل های CMSIS بیت فیلد دار بسازیم؟ پیشتر راهش رو میدونستم و یک نماوای آموزشی هم ساختم، که راهش رو گفتم. ولی با بروزرسانی های نرم افزار keil اون راه ها دیگر کار نمیکنه. ولی فایلهای بیت فیلد دار رو میتونید بارگیری کنید و آزمایش کنید.