السن

آموزش STM32 | دسترسی به رجیسترها در برنامه نویسی رجیستری

رجیستر یک اندازه ی 8 ، 16 یا 32 بیتی با یک نشانی (آدرس) شناخته شده هست. با اندازه ای که دربیت های رجیسترها مینویسیم، گزینش میکنیم بخشی از میکروکنترلر چجوری کار کنه. در این نوشته روش های گوناگون دسترسی به داده گاه (حافظه) در زبان سی رو آموزش میبینید.

ساخت نشانگر (پوینتر) 

پندارید یک رجیستر 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 هم هست (همزمان شناسش دو چیزه).

در زبان C ، در شناسش هر چیز ، گونه اون پیش از نامش نوشته میشه.

گونه پوینتر به نام p هم  پیش از نامش نوشته شده است.

 

ساخت نشانگر پایا (پوینتر ثابت)

برای ساخت نشانگر پایا ( ثابت ) ، باید گونه نشانگر رو پیش از اندازه نشانگر در پرانتز نوشت. به نوشتن گونه یک اندازه پیش از اون در پرانتر، میگن type casting یا گونه گذاری.

 

ساخت نشانگر (پوینتر) برای هر رجیستر

 
برای دسترسی به رجیستر 16 بیتی در نشانی 0x40000000 بایستی یک نشانگر پایا (پوینتر ثابت) بسازیم و پیش از اون استریسک بذاریم. 
 

 
 
میتونیم استریسک-نشانگر رو در آغاز برنامه با بکارگیری دستور define نام دهی کنیم که برنامه خوانا تر باشه. پندارید نام این رجیستر 16 بیتی هست CR1 از پیرارونگ تایمر 2 (پریفرال TIM2) ، نام TIM2_CR1 را به استریک-نشانگر میدهیم و هر جایی در برنامه این نام را بکار ببریم ، پیش پردازشگر (preprocessor) آنرا با استریک-نشانگر جایگزین میکند.
 
 

 
میشه نشانگر پویا (پوینتر متغیر) هم بکار برد، باید در آغاز شناسش و اندازه دهی بشه. 
 

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

پیرارونگ (مدار پیرامونی یا پریفرال) GPIOC ،هفت رجیستر 32 بیتی داره که پشت سرهم در یادگاه (حافظه) چیده شده اند . رجیستر CRL یکمین رجیستره که در نشانی 0x40011000 هست. برای دسترسی به GPIOC_CRL باید نشانگر زیر رو بسازیم.
 



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

 

یک گونه استراکچر به نام GPIO_TypeDef رو هماهنگ با رجیسترهای پیرارونگ GPIO میسازیم و با بکارگیری این گونه، یک نشانگر میسازیم برای دسترسی به استراکچر پنداره ای از نوع GPIO_TypeDef که در نشانی 0x40011000 هست. 

ساخت پوینتر به استراکچر با کست کردن آدرس

برای دسترسی به رجیسترهای پیرارونگ GPIOC که زیرگروه های (اعضای) استراکچر پنداره ای هستند، پس از نشانگر به استراکچر، پیکان میذاریم (دیگه به استریسک نیازی نیست).

 

برای خواناتر شدن برنامه، میتونیم نشانگر به استراکچر رو نام دهی define کنیم.

 

این روش CMSIS استاندارد برای دسترسی به رجیسترها است . در فایل CMSIS device این چیزها وجود داره که بتونیم به رجیسترها دسترسی داشته باشیم و برنامه نویسی رجیستری انجام بدیم.

  1. نام دهی (#define کردن با دستورات پیش پردازشگر) نشانی نخستین رجیستر پیرارونگ ها ( base address ). 
  2. شناسش (تعریف) گونه استراکچر هماهنگ رجیسترهای هر پیرارونگ.
  3. ساخت و نام دهی (define#) نشانگر به استراکچر با بکارگیری گونه استراکچر که در گام 2 شناسش کردیم.

نوشتن و خواندن در رجیسترها با کننده های (عملگر) بیتی و روبند (ماسک) بیت

در دو روش پیشین برای دسترسی به رجیسترها، گونه خود رجیستر همیشه اینتیجر 32 بیتی بوده و برای نوشتن و خواندن در رجیستر ها به دو چیز نیاز است.

  • عملگر های بیتی : =| و =& .
  • روبند بیت ( ماسک بیت ) : یک شماره ی هم درازا با رجیستر که تنها بیت هایی که میخواهیم بخوانیم یا بنویسیم در آن یک هستند.

نوشته ی کاربرد عملگر های بیتی در تغییر بیت ها

 

 

میخواهیم با روش های پیشین دسترسی به رجیستر، بیت های MODE1 در رجیستر CRL از پیرارونگ GPIOC رو 0b01 کنیم و بیت 5 هیچا(0) و بیت 4 یک بشه.

  1. در گام یکم هر دو بیت هیچا میشن و برای این کار به روبند (ماسک) بیت های MODE1 نیاز هست.
  2. در گام دوم بیت 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 است ، دسترسی پیدا کردیم.

اگر بخواهیم همزمان دو جور دسترسی را داشته باشیم به یونیون نیاز هست. برای یادگیری دنباله ی داستان آموزش نماوایی (ویدئویی) را ببینید. (رایگان هست).

لایه ی نرم افزاری CMSIS و بیت فیلد

لایه ی نرم افزاری CMSIS استاندارد که کتابخونه hal بکار گرفتتش، بیت فیلد نداره و دسترسی به رجیسترها 32 بیتی هست. ولی شرکت آرم که CMSIS استاندارد رو ساخته، فایل های دیگری هم ساخته که دسترسی به بیت فیلدها رو هم فراهم میکنند.

چرا با اینکه با دسترسی به بیت فیلد برنامه نویسی آسانتر میشه، کتابخونه HAL بکار نمیگیره و کمتر کسی میدونه هیچین چیزی هم هست؟

چون برنامه ای که ما به زبان سی مینویسیم، باید اسمبلی بشه، کی اسمبلی میکنه؟ کامپایلر، دشواری اینجاست که کامپایلر های گوناگون، بیت فیلد رو در اسمبلی با شیوه های گوناگون پیاده سازی میکنند، و این دردسر ساز هست.

آیا خوبه با بیت فیلد در رجیستر ها بنویسیم؟ نه، چون در همه ی برنامه های ما هم کتابخونه HAL بکار میره و هم دسترسی یکراست به رجیسترها داریم، و ما باید همان راهی رو برای دسترسی به رجیستر ها بکار بگیریم که کتابخونه HAL بکار میگیره. اگر بخواهیم کتابخونه HAL در برنامه باشه و همزمان دسترسی به بیت فیلد هم داشته باشیم، اینجوری باید دو گونه CMSIS در برنامه داشته باشیم ، که بسیار دردسر ساز هست و بیشتر از سود، زیان داره.

چجوری فایل های CMSIS بیت فیلد دار بسازیم؟ پیشتر راهش رو میدونستم و یک نماوای آموزشی هم ساختم، که راهش رو گفتم. ولی با بروزرسانی های نرم افزار keil اون راه ها دیگر کار نمیکنه. ولی فایلهای بیت فیلد دار رو میتونید بارگیری کنید و آزمایش کنید. 

  • نشانی = آدرس .
  • نشانگر = پوینتر .
  • شناسش = تعریف . 
  • نام دهی = #define . 
  • پایا = ثابت .
  • پویا = متغیر . 
  • گونه گذاری = type casting .
  • یادگاه = حافظه . 

دیدگاه‌ خود را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

پیمایش به بالا