آموزش STM32 رجیستری : مبانی برنامه نویسی رجیستری
در این مقاله آموزش میبینید که برای برنامه نویسی رجیستری میکروکنترلرهای STM32 چطوری پوینتر میسازیم. ابتدا بررسی میکنیم که برای پروژه led چشمک زن با میکروکنترلر STM32F103C8 روی برد بلوپیل چه رجیسترهایی رو باید تغییر بدیم، سپس سه روش ساخت پوینتر برای دسترسی به رجیستر ها رو آموزش میبینید و سه بار پروژه led چشمک زن رو انجام میدیم. کد در نرم افزار STM32CubeIDE نوشته میشه و از پروگرمر ST-link v2 برای load کردن کد استفاده میکنیم.

این مقاله بخشی از ویدئوی آموزشی ” صفر تا صد برنامه نویسی با رجیسترها برای میکروکنترلر های STM32 ” هست که ویدئوی معرفی دوره جامع آموزش برنامه نویسی میکروکنترلر ARM با STM32 میباشد.
بخش اول : مقدمه
برنامه نویسی رجیستری میکروکنترلر های STM32 یعنی چی؟
ابتدا باید اجزا میکروکنترلر رو بشناسید و هدف از دسترسی به رجیسترهای میکروکنترلرهای STM32 رو بدونید
یک میکروکنترلر از چهار بخش اصلی تشکیل شده .
+ Cpu : که وظیفه ی اجرای کد رو به عهده داره
+ حافظه ها: مثل فلش برای ذخیره سازی کد و رم برای ذخیره سازی متغیرها
+ باس ها: برای جابجایی دیتا بین بخش های مختلف
+ پریفرال ها: مدار های الکترونیکی که برای انجام کار خاصی طراحی شده اند. مثلا هر پریفرال GPIO در میکروکنترلرهای stm32 در مورد تا 16 پین وظیفه تنظیم پین میکروکنترلر در حالت ورودی یا خروجی دیجیتال ، مشخص کردن وضعیت پین خروجی، اطلاع از وضعیت پین ورودی رو به عهده داره.

تنظیم پریفرال ها
فرض کنید میخواهیم یکی از پین های میکروکنترلر رو در حالت خروجی تنظیم کنیم چطور باید با پریفرال GPIO ارتباط برقرار کنیم و ازش بخواهیم تنظیمات مورد نیاز رو برای ما اعمال کنه؟
پریفرال GPIO هم مثل سایر پریفرال ها فضایی در حافظه مختص خورش داره که شامل تعدادی رجیستره، برای اینکه از پریفرال در راستای اهدافمون استفاده کنیم باید اعداد مشخصی رو در رجیستر های پریفرال بنویسیم. در میکروکنترلرهای STM32F1 رجیسترها فضای 16 یا 32 بیتی ( 2 یا 4 بایت ) در حافظه هستند.
بخش بزرگی از کاری که در برنامه نویسی میکروکنترلر ها انجام میدیم تنظیم پریفرال با تغییر محتوای رجیسترهای اونهاست. در پروژه هایی که با میکروکنترلر های STM32 انجام میدیم برای تغییر محتویات رجیسترها میتونیم از کتابخونه ها مثلا کتابخونه hal استفاده کنیم، توابع hal رو فراخوانی میکنیم و در اون توابع مقدار رجیسترها تغییر داده میشه ، همچنین میتونیم خودمون بدون استفاده از کتابخونه ها مستقیما مقدار رجیسترها رو تغییر بدیم، به این کار میگن برنامه نویسی با رجیسترها .
تمام توضیحات مربوط به امکانات پریفرال ها ، رجیستر های هر پریفرال و اینکه برای رسیدن به تنظیم دلخواه چه اعدادی رو باید در رجیستر ها بنویسیم در رفرنس منوال میکروکنترلر های STM32F103 آورده شده.
دانلود رفرنس منوال STM32F103
آموزشن STM32 ، پروژه Led چشمک زن با میکروکنترلر STM32F103C8 روی برد Blue pill
در این بخش برد بلوپیل رو برای انجام پروژه led چشمک زن بررسی میکنیم.
قراره برنامه نویسی با رجیستر ها برای میکروکنترلر های STM32 رو با مثال led چشمک زن روی برد blue pill آموزش ببینید. برد blue pill یک برد توسعه است که امکانات اولیه مورد نیاز برای راه اندازی میکروکنترلر STM32F103C8 رو برای ما فراهم میکنه . روی برد blue pill یک led سبز رنگ وجود داره که آند led به 3.3v و کاتد led با یک مقاومت به پین PC13 میکروکنترلر متصل شده. پس وقتی پین PC13 زمین بشه ، led روشن میشه.
دانلود شماتیک برد بلوپیل

میکروکنترلر STM32F103 چهار تا پریفرال GPIO داره:
GPIOA برای پین های PA0 تا PA15
GPIOB برای پین های PB0 تا PB15
GPIOC برای پین های PC13, PC14, PC15
GPIOD برای پین های PD0 و PD1

برای تنظیم پین PC13 میکروکنترلر STM32F103 باید از پریفرال GPIOC استفاده کنیم و مقدار رجیسترهای این پریفرال رو برای رسیدن به هدفمون تغییر بدیم.
آموزش STM32 ، پریفرال GPIO در میکروکنترلر STM32F103
در این بخش بررسی میکنیم برای انجام پروژه led چشمک زن با STM32F103C8 چه بیت هایی رو در چه رجیسترهایی رو باید تغییر بدیم.
هر پریفرال GPIO هفت تا رجیستر 32 بیتی داره ، دو تا رجیستر اول به نام های CRH و CRL برای تنظیم پین ها در حالت ورودی یا خروجی هستند و همچنین نوع ورودی یا خروجی رو هم مشخص میکنند. رجیستر CRL برای تنظیم پین های 0 تا 7 و رجیستر CRH برای پین های 8 تا 15 هستند. در تصویر زیر register map یا نقشه رجیسترهای پریفرال GPIO میکروکنترلرهای STM32F1 رو میبینید که از صفحه ی 194 رفرنس منوال برداشته شده.

برای تنظیمات هر پین هم 4 بیت اختصاص داده شده. دو بیت به نام MODE و دو بیت به نام CNF . مثلا در رجیستر GPIOC_CRH بیت های MODE13 و CNF13 رو داریم که برای تنظیمات پین PC13 هستند.

برای تنظیم یک پین در حالت ورودی یا خروجی و تنظیم سرعت از بیت های MODE استفاده میشه. جهت تنظیم پین PC13 در حالت خروجی با کمترین سرعت مقدار MODE13 باید 10 باشه، یعنی بیت 20 صفر و بیت 21 باید یک بشه.
برای مشخص کردن حالت ورودی یا خروجی هم از بیت های CNF استفاده میشه ، با توجه به اینکه در مورد پین PC13 وقتی پین زمین بشه بار اکتیو هست، یعنی led روشن میشه ، خروجی push-pull یا open-drain هر دو کار میکنند. اینجا من انتخاب میکنم که خروجی رو open-drain تنظیم کنم پس باید مقدار 01 در بیت های CNF13 ثبت بشه، یعنی بیت 22 یک و بیت 23 باید صفر بشه.

قدم بعدی تغییر وضعیت پین هست ، که برای اینکار از رجیستر ODR استفاده میکنیم. بیت 13 رجیستر ODR از پریفرال GPIOC برای تعیین وضعیت خروجی پین PC13 اختصاص داره. اگر این بیت یک بشه پین PC13 هم 3.3v میشه و اگر بیت صفر بشه وضعیت پین هم 0v یا GND میشه.

آموزش STM32 فعالسازی کلاک پریفرال GPIOC
برای پروژه led چشمک زن مقدار یک رجیستر دیگه رو هم باید تغییر بدیم. همه ی پریفرال های میکروکنترلر برای عملکرد به کلاک نیاز دارند و در شروع کار کلاک تمام پریفرال ها قطعه و از هر پریفرالی بخواهیم استفاده کنیم قبل از تغییر رجیستر های اون پریفرال باید کلاکش رو وصل کنیم. کنترل کلاک میکروکنترلر به عهده ی پریفرال RCC هست. پریفرال RCC سه تا رجیستر داره برای قطع و وصل کردن کلاک سایر پریفرال ها. با توجه به اینکه باید از پریفرال GPIOC استفاده کنیم ، باید ابتدا کلاکش رو فعال کنیم. فعال کردن کلاک این پریفرال با یک کردن بیت IOPCEN ( بیت 4 ) در رجیستر RCC_APB2ENR انجام میشه.

تا اینجای کار میدونیم که برای پروژه led چشمک زن باید چه تغییراتی رو در کدوم رجیستر ها اعمال کنیم. چیزی که هنوز نمیدونیم آدرس این رجیستر هاست. در میکروکنترلرهای STM32F1 حافظه ی مربوط به پریفرال ها از آدرس 0x40000000 شروع میشه و به هر پریفرال 1kb حافظه اختصاص داده شده. هر پریفرال یک base address داره که آدرس اولین رجیستر اون پریفرال هست. Base address تمام پریفرال ها رو میتونید در نقشه حافظه در صفحه 34 دیتاشیت یا در جدول صفحه ی 50 رفرنس منوال ببینید.

برای بدست آورده آدرس هر رجیستر به base address پریفرال و همچنین آفست اون رجیستر نیاز داریم. آفست یعنی فاصله ی آدرس رجیستر از آدرس اولین رجیستر پریفرال. آفست هر رجیستر رو هم در ستون اول جدول رجیستر مپ هر پریفرال میتونید پیدا کنید. آدرس هر رجیستر با جمع base address پریفرال و آفست رجیستر بدست میاد.

هدف مقاله تا اینجا این بود که بفهید اطلاعات جدول زیر از کجا اومده، الان میدونیم در چه آدرس هایی چه تغییراتی رو باید اعمال کنیم. در قدم بعدی برنامه نویسی رو شروع میکنیم و به محتویات رجیسترهای پریفرال ها دسترسی پیدا میکنیم.

آموزش STM32 ، ساخت پروژه خالی در STM32CubeIDE برای STM32F103C8
برای انجام پروژه باید در نرم افزار STM32CubeIDE یک پروژه خالی برای STM32F103C8 بسازیم.
نرم افزار STM32CubeIDE رو باز کنید و در صفحه launcher ابتدا یک پوشه رو به عنوان workspace ( پوشه ای که یک یا چند پروژه در اون هست ) انتخاب کنید و سپس روی دکمه launch کلیک کنید.

در صفحه ی information center روی start new stm32 project کلیک کنید.

صفحه ی target selection ابتدا در بخش part number اسم میکروکنترلر رو مینویسیم ( STM32F103 ) در قدم دوم میکروکنترلر رو انتخاب کنید و سپس روی next کلیک کنید.

در پنجره STM32 Project ابتدا در بخش project name اسم پروژه رو تایپ کنید ، targeted Project type رو empty انتخاب کنید و روی finish کلیک کنید.

با موفقیت پروژه ای ساختیم که حداقل نیازمندی لود شدن در میکروکنترلر رو داره. از پوشه Src فایل main.c رو باز کنید و قسمت های اضافی رو پاک کنید. در تابع main قبل از حلقه ی بینهایت باید تنظیمات اولیه پریفرال ها رو اعمال کنیم.

بخش دوم : برنامه نویس رجیستری STM32
در بخش اول بررسی کردیم که برای پروژه led چشمک زن چه تغییراتی رو باید در رجیسترها اعمال کنیم ، میدونیم رجیسترهامون 32 بیتی هستند و آدرسشون رو هم داریم. در ادامه باید در زبان سی به محتوای آدرس ها دسترسی پیدا کنیم و مقدار رجیسترها رو تغییر بدیم. برای دسترسی به محتوایی که در آدرس ذخیره شده به پوینتر نیاز داریم.
پوینتر ها literal ( ثابت های عددی ) یا variable ( متغیر ) هایی هستند که مقدارشون آدرس جایی از حافظه است و هر پوینتر نوع و مقدار داره. چون آدرس رجیسترها قرار نیست تغییر کنه ، پوینتر ثابت میسازیم. برای ساخت پوینتر باید نوع و مقدارش رو بدونیم.
روش اول : دسترسی به رجیسترها بصورت 32 بیتی با ساخت پوینتر برای هر رجیستر برای میکروکنترلر های STM32
ساخت پوینتر برای هر رجیستر
فرض کنید میخواهیم پوینتری بسازیم برای دسترسی به رجیستر 32 بیتی RCC_APB2ENR. مقدار این پوینتر آدرس رجیستر RCC_APB2ENR خواهد بود یعنی 0x40021018 . برای ساخت پوینتر ثابت ابتدا مقدارش رو مینویسیم ، سپس در یک پرانتر قبل از مقدار باید نوعش رو بنویسیم ، به این کار میگن کست کردن . نوع پوینتر دو بخش داره، ابتدا نوع چیزی رو مینویسید که در آدرسی که مقدار پوینتر هست ذخیره شده. مثلا در در آدرس 0x40021018 یک رجیستر 32 بیتی وجود داره که نوعش میشه uint32_t ( یا unsigned int ) بخش دوم هم علامت استریسک یا dereference operator هست.

پوینتر رو ساختیم ، حالا برای اینکه با استفاده از این پوینتر به رجیستر RCC_APB2ENR دسترسی پیدا کنیم برای اینکه مقدارش رو بخونیم یا تغییر بدیم لازمه قبل از پوینتر علامت استریسک یا dereference operator بذاریم، در مثال اول عدد 10 در رجیستر ذخیره میشه و در مثال دوم مقدار رجیستر خونده میشه ، با 10 جمع میشه و در رجیستر ذخیره میشه.

در مورد رجیستر RCC_APB2ENR باید بیت چهارمش رو یک کنیم تا کلاک پریفرال GPIOC فعال بشه ، برای این منظور به ماسک بیت شماره چهار نیاز داریم که یک عدد 32 بیتی هست که فقط بیت چهارمش یکه. جهت ساخت این عدد از عملگر بیتی left shift استفاده میکنیم ، 1 رو left shift چهار میکنیم.
مقاله ی ” کاربرد عملگرهای بیتی در زبان سی ” رو مطالعه کنید.

وقتی میخواهیم بیتی رو در یک رجیستر یک کنیم باید رجیستر رو bitwise or assignment ماسک بیت کنیم و وقتی میخواهیم بیتی رو صفر کنیم رجیستر رو bitwise and assignment میکنیم با نات ماسک بیت . برای اینکه این عملیات ها رو بهتر درک کنید مقاله کاربرد عملگر های بیتی رو مطالعه کنید.

تا اینجا یادگرفتیم چطور باید پوینتر برای دسترسی به رجیستر رو بسازیم و چطور میتونیم بیت های دلخواهمون رو در یک رجیستر تغییر بدیم. تغییراتی که باید در رجیسترها اعمال بشه رو در قالب یک جدول در کامنت اول فایل main.c نوشتم.

دسترسی به رجیسترها با ساخت پوینتر اختصاصی برای هر رجیستر برای انجام پروژه ی led چشمک زن
در قدم اول برای فعالسازی کلاک پریفرال GPIOC باید بیت IOPCEN ( بیت 4 ) در رجیستر RCC_APB2ENR رو یک کنیم. قبلا یادگرفتیم که چطور با کست کردن آدرس رجیستر پوینتر رو بسازیم. برای دسترسی به محتوای رجیستر پشت پوینتر علامت استریسک میذاریم. برای یک کردن بیت چهار رجیستر رو با ماسک بیت چهار bitwise or assignment میکنیم.

در قدم بعدی برای اینکه پین PC13 در حالت خروجی با سرعت 2MHz تنظیم بشه باید بیت های MODE13 در رجیستر GPIOC_CRH رو 10 کنیم ، یعنی بیت 20 صفر و بیت 21 باید یک بشه. مشابه رجیستر RCC_APB2ENR برای دسترسی به محتوای رجیستر GPIOC_CRH هم پوینتر میسازیم ، آدرس رجیستر GPIOC_CRH رو مینویسیم و قبل مقدار پوینتر در یک پرانتز نوعش رو مشخص میکنیم. نوع این پوینتر هم مشابه قبلی * uint32_t هست. برای دسترسی به محتوای رجیستر GPIOC_CRH پشت پوینتر علامت استریسک میذاریم. جهت یک کردن بیت 21 رجیستر رو bitwise or assignment میکنیم با ماسک بیت 20 که میشه 20<<1 و برای صفر کردن بیت 20 رجیستر رو bitwise and assignment میکنیم با نات ماسک بیت 21 که میشه (21<<1)~ .

برای تنظیم پین PC13 در حالت خروجی Open drain باید بیت های CNF13 در رجیستر GPIOC_CRH رو 01 کنیم ، یعنی بیت 22 یک و بیت 23 باید صفر بشه. مشابه قبل تغییرات رو اعمال میکنیم.

تا الان اقدامات مورد نیاز برای تنظیم پین PC13 در حالت خروجی دیجیتال انجام شده. در قدم بعدی در حلقه ی بینهایت باید خروجی پین PC13 رو با یک تاخیر زمانی صفر و یک کنیم تا led چشمک زن بشه. برای این منظور یک تابع تاخیر با دو for تعریف میکنم تا بعد از صفر کردن و بعد از یک کردن پین این تابع رو فراخوانی کنیم.

برای اینکه led سبز رنگ روی برد Blue pill چشمک زن بشه باید پین PC13 رو در حلقه ی بینهایت متناوبا صفر و یک کنیم ، جهت یک کردن خروجی PC13 باید بیت 13 رجیستر GPIOC_ORD یک بشه و برای صفر کردن پین PC13 باید بیت 13 رجیستر GPIOC_ODR رو صفر کنیم. مشابه رجیستر های قبلی برای GPIOC_ODR هم پوینتر میسازیم و تغییرات رو در بیت 13 اعمال میکنیم. برای اینکه سرعت خاموش و روشن شدن led رو کم کنیم تا با چشمک زدن led رو با چشم ببینیم بعد از صفر و یک کردن باید تابع تاخیر رو فراخوانی کنیم.

پروژه led چشمک زن با روش اول برنامه نویسی رجیستری برای میکروکنترلر های STM32 به پایان رسید. در روش اول برنامه نویسی رجیستری برای هر رجیستر به صورت جداگانه پوینتر ساختیم و با این پوینتر ها به رجیسترها دسترسی پیدا کردیم و با عملگرهای بیتی مقدار بیت ها رو تغییر دادیم.

روش دوم : دسترسی به رجیسترها بصورت 32 بیتی در قالب اعضای استراکچر برای میکروکنترلرهای STM32
در نرم افزار STM32CubeIDE یک پروژه ی جدید به نام structure ایجاد و تابع delay رو هم اضافه کنید.

آموزش STM32 ، ساخت استراکچر قالب رجیسترهای پریفرال GPIO
روش قبلی دسترسی به رجیسترها برای هر رجیستر یک پوینتر درست کردیم، در روش دوم برای دسترسی به همه ی رجیسترهای یک پریفرال فقط به یک پوینتر نیاز داریم و برای هر پریفرال یک پوینتر طراحی میکنیم. پریفرال های GPIO رو در نظر بگیرید، این پریفرال 7 تا رجیستر 32 بیتی داره که پشت سر هم در حافظه قرار دارند، مشابه استراکچری که هفت ها عضو 32 بیتی داره.

پریفرال GPIOC رو در نظر بگیرید ،آدرس اولین رجیستر این پریفرال 0x40011000 و این پریفرال 7 تا رجیستر 32 بیتی داره که پشت سر هم در حافظه قرار دارند، مشابه استراکچری که هفت تا عضو 32 بیتی داره و در آدرس 0x40011000 ذخیره شده. با توجه به رجیستر مپ پریفرال های GPIO یک نوع متغیر استراکچر به نام GPIO_TypeDef قالب رجیسترهای پریفرال های GPIO طراحی میکنیم که هفت تا عضو 32 بیتی از نوع uint32_t داره و اعضای این استراکچر رو هم مثل رجیسترهای پریفرال های GPIO نامگذاری میکنیم.

در نظر بگیرید که یک استراکچر فرضی از نوع GPIO_TypeDef در آدرس 0x40011000 ذخیره شده. اگر به اعضای این استراکچر دسترسی پیدا کنیم مثل اینه که به رجیستر های پریفرال GPIOC دسترسی پیدا کردیم. اگر بخواهیم به رجیستر های پریفرال GPIOC در قالب اعضای این استراکچر فرضی دسترسی پیدا کنیم به پوینتر نیاز داریم .
آمورش STM32 ، ساخت پوینتر به استراکچر برای دسترسی به رجیسترهای پریفرال GPIOC
برای ساخت این پوینتر باید نوع و مقدارش رو بدونیم. مقدار این پوینتر آدرس استراکتراکچر فرضی خواهد بود که آدرس اولین رجیستر پریفرال GPIOC یعنی 0x40011000 هست. نوع پوینتر هم دو قسمت داره ، قسمت اول نوع متغیری که در نظر میگیریم در آدرسی کم مقدار پوینتر هست ذخیره شده و قسمت دوم هم علامت استریسک هست. الان ما میخواهیم فرض کنیم در آدرس 0x40011000 یک استراکچر از نوع GPIO_TypeDef هست ، پس نوع پوینتر میشه * GPIO_TypeDef .

میدونیم برای دسترسی به اعضای استراکچر باید بعد از اسم استراکچر عملگر دات ( . ) بذاریم ولی چطور با استفاده از پوینتر به استراکچر، به اعضا دسترسی پیدا کنیم.
برای دسترسی به محتوای آدرس پوینتر باید قبل از پوینتر عملگر استریسک رو قرار بدیم ، نکته ای که وجود داره اینه که برای استراکچر حتما استریسک پوینتر باید داخل پرانتز باشه ، بعد از پرانتر دات ( . ) میذاریم و اینجوری میتونیم با پوینتر به استراکچر به اعضاش دسترسی پیدا کنیم.
البته راه بهتری هم وجود داره و اون استفاده از عملگر پیکان یا arrow operator هست. اگر پوینتر به استراکچر رو داشته باشیم ، برای دسترسی به اعضای استراکچر کافیه بعد از پوینتر عملگر پیکان <- بذاریم.

با استفاده از یک definition میتونیم کدمون رو خواناتر کنیم. در کد عبارت GPIOC که اسم پریفرال هست رو ((GPIO_TypeDef *) 0x40011000) که پوینتر به رجیسترهای پریفرال هست define میکنم. یعنی هر جا در کد GPIOC رو بنویسی با پوینتر جایگزین میشه.


آموزش STM32 ، دسترسی به رجیسترهای پریفرال GPIOC درقالب اعضای استراکچر
در روش دوم برنامه نویسی رجیسترسی برای میکروکنترلر های STM32 ، قالب رجیسترهای پریفرال GPIO یک نوع متغیر استراکچر ساختیم چون به این نوع متغیر برای ساخت پوینتر به استراکچر فرضی نیاز داریم. با استفاده از پوینتر GPIOC می تونیم به اعضای استراکچر فرضی که در آدرس 0x40011000 ( آدرس اولین رجیستر پریفرال GPIOC ) ذخیره شده دسترسی پیدا کنیم و رجیستر های پریفرال GPIOC رو تغییر بدیم.
در پروژه ی led چشمک زن لازمه رجیستر های CRH و ODR از پریفرال GPIOC رو تغییر بدیم .برای دسترسی به رجیسترهای پریفرال GPIOC که اعضای استراکچر فرضی از نوع GPIO_TypeDef هستند کافیه بعد از پوینتر به استراکچر عملگر پیکان بذاریم ، اینطوری برای هفت ها رجیستر پریفرال GPIOC فقط به یک پوینتر نیاز داریم.

روش دوم برنامه نویسی با رجیستری برای STM32 به پایان رسید ، این روش رو برای رجیستر RCC_APB2ENR از پریفرال RCC اعمال نکردم ، اگر برای این رجیستر هم بخواهیم از روش دوم استفاده کنیم باید قالب رجیسترهای پریفرال RCC یک نوع متغیر استراکچر بسازیم.
لایه ی نرم افزاری CMSIS همین روش رو برای دسترسی به رجیسترهای پریفرال های ارائه میده و توابع HAL و سایر کتابخونه های مشابه هم از همین روش برای دسترسی به رجیسترهای پریفرال ها استفاده میکنند.

روش سوم : دسترسی به بیت رجیسترها با بیت فیلد و یونیون برای میکروکنترلرهای STM32
تا حالا دو روش برنامه نویسی رجیستری برای STM32 ها رو بررسی کردیم، در روش اول برای هر رجیستر یک پوینتر ساختیم و در روش دوم به رجیسترهای در قالب اعضای استراکچر دسترسی پیدا کردیم. چیزی در این دو روش یکسان بود اینه که به رجیسترها بصورت 32 بیتی دسترسی پیدا میکنیم و باید از عملگرهای بیتی برای تغییر بیت رجیسترها استفاده کنیم. در روش سوم دیگه به عملگرهای بیتی نیازی نیست ، مستقیم به بیت ها دسترسی پیدا میکنیم و اعداد مورد نظر رو در اونها مینویسیم. قبل از اینکه به روش سوم بپردازیم باید راجع به بیت فیلد صحبت کنیم.
بیت فیلد
بیت فیلد ها اعضای استراکچر هستند که تعداد بیت هاشون رو خودمون مشخص میکنیم، برای تعریف عضو بیت فیلد در استراکچر ابتدا نوعش رو مینویسیم ، نوع بیت فیلد فقط میتونه اینتیجر باشه و علامت ( signed or un-signed ) و alignment رو برای بیت فیلد مشخص میکنه . در مورد نوع بیت فیلد خیلی نکته وجود داره ولی برای کاری که الان انجام میدیم نوع بیت فیلد همیشه uint32_t هست. بعد از نوع بیت فیلد با یک فاصله اسم بیت فیلد رو مینویسیم ، بعدش دو نقطه میذاریم و تعداد بیت های بیت فیلد رو مشخض میکنیم.
در مثال زیر عضو x و y به ترتیب 5 و 13 بیت دارند. در مثال زیر ابتدا نوع متغیر استراکچر bit_type تعریف شده که شامل دو عضو بیت فیلد هست سپس استراکچر mybit با نوع bit_type تعریف شده و اعضاش هم مقدار دهی اولیه شده اند ، عضو بیت مقدارش 3 شده و عضو y مقدارش 8 شده. در تابع main هم نشون دادم دسترسی به اعضایی که بیت فیلد هستند مشابه دسترسی به اعضای عادی استراکچر هست.

اگر استراکچر mybit دو عضو معمولی از نوع uint32_t داشت 8 بایت در حافظه رو اشغال میکرد ولی الان فقط چهار بایت یا 32 بیت رو اشغال میکنه که 14 بیتش هم استفاده نمیشه. در استراکچر mybit عضو x اولین عضو هست و 5 بیت داره ، 5 بیت کم ارزش به x اختصاص داده میشه یعنی بیت های 0 تا 4 و با توجه به اینکه عضو x پنج بیت داره فقط اعداد 0 تا 31 رو میتونیم در این عضو بریزیم. بعد از عضو x عضو y هست با 8 بیت و بیتهای 5 تا 17 به عضو y اختصاص داده میشه ، کم ارزش ترین بیت عضو y بیت 5 هست و میبینید که عدد 8 چطور در y ذخیره شده. بیت های 18 تا 31 هم خالی میمونند.

آموزش STM32 طراحی استراکچر بیت فیلد دار قالب بیت های رجیستر ODR
رجیستر ODR که برای تعیین وضعیت خروجی پین های ازش استفاده میشه رو در نظر بگیرید. قراره یک نوع استراکچر بیت فیلد دار قالب رجیستر ODR طراحی کنیم.

از کم ارزش ترین بیت رجیستر ODR شروع میکنیم و اعضای بیت فیلد رو مینویسیم ، اولین بیت ODR به نام ODR0 یک بیت داره ، اولین عضو استراکچر هم یک بیت فیلد هست به نام ODR0 که یک بیت داره. اعضای ODR1 تا ODR15 هم همشون یک بیت دارند ، از بیت 16 تا بیت 31 بیت های reserved هستند که استفاده نمیشن ، برای بیت های reserved هم یک عضو بیت فیلد مینویسیم با 16 بیت ولی اسمی براش نمینویسیم ، به اعضای بدون اسم میگن padding که برای پر کردن جاهای خالی استفاده میشن.
الان یک نوع متغیر استراکچر به نام ODR_T تعریف کردم که اندازش 32 بیت هست ، مشابه رجیستر ODR و اعضای بیت فیلد این نوع استراکچر هم مطابق بیت های رجیستر ODR طراحی شدن.

آموزش STM32 ، دسترسی به بیت های رجیستر بدون نیاز به ماسک بیت و عملگرهای بیتی
یک پروژه جدید بسازید و تمام محتویات فایل main.c پروژه قبلی رو درش کپی کنید. تعریف نوع متغیر ODR_T رو هم قبل از تعریف نوع متغیر GPIO_TypeDef اضافه کنید. قراره روش سوم برنامه نویسی رجیستر ها رو فقط برای رجیستر ODR اعمال کنیم.

در نوع GPIO_TypeDef عضو ODR از نوع uint32_t هست ، نوع عضو ODR رو از uint32_t به ODR_T تغییر میدیم. دقت کنید که ODR_T و uint32_t هر دو 32 بیتی هستند و اندازشون 4 بایته و اعضای استراکچر ODR_T رو هم مطابق بیت های رجیستر ODR طراحی کردیم.

قبلا عضو ODR از نوع uint32_t بود، الان خود ODR یک استراکچر از نوع ODR_T هست ، بعد از ODR عملگر دات میذاریم تا به اعضاش دسترسی پیدا کنیم ، اعضای ODR بیت های رجیستر ODR هستند. به عضو ODR13 دسترسی پیدا میکنیم و برای یک کردن این بیت کافیه عدد یک رو در اون بنویسیم و برای صفر کردن بیت ODR13 کافیه عدد صفر رو در اون بنویسیم و دیگه به عملگرهای بیتی و ماسک بیت نیازی نیست.

آموزش STM32 دسترسی به بیت های رجیستر و دسترسی 32 بیتی همزمان با یونیون
با تغییراتی که ایجاد کردیم عضو ODR یک استراکچر شده و دیگه نمیتونیم مثل قبل به رجیستر ODR بصورت 32 بیتی دسترسی پیدا کنیم. هدف اینه که بتونیم به ODR هم بصورت استراکچر و هم بصورت 32 بیتی دسترسی پیدا کنیم برای این منظور باید یک نوع یونیون طراحی کنیم.
یونیون مشابه استراکچر یک نوع متغیر هست که اعضاش انواع دیگه ی متغیر هستند ، فرق یونیون با استراکچر اینه که تمام اعضای یونیون یک آدرس دارند پس وقتی میخواهیم به یک آدرس به دو شکل دسترسی پیدا کنیم از یونیون استفاده میکنیم. یک نوع یونیون به نام ODR_UNION_T تعریف کردم که دو عضو داره. عضو reg از نوع uint32_t و عضو bit از نوع ODR_T.

حالا نوع عضو ODR در نوع استراکچر GPIO_TypeDef رو به ODR_UNION_T تغییر میدیم.

عضو ODR یک یونیون هست، برای دسترسی به اعضای یونیون بعد از اسمش دات میذاریم. این یونیون دو عضو به نام های bit و reg داره. اگر بخواهیم به رجیستر ODR بصورت 32 بیتی دسترسی پیدا کنیم از عضو reg که نوعش uint32_t هست ، استفاده میکنیم. اگر بخواهیم به بیت های رجیستر ODR دسترسی پیدا کنیم از عضو بیت استفاده میکنیم که نوعش ODR_T هست که یک نوع استراکچر با اندازه ی 4 بایته که با توجه به بیت های رجیستر ODR طراحی کردیم. عضو بیت خودش یک استراکچره و برای دسترسی به اعضای این استراکچر که بیت های رجیستر ODR هستند بعدش دات میذاریم و میتونیم به بیتها دسترسی پیدا کنیم.

0 Comments