آموزش STM32 با توابع HAL, توصیه شده

پایه های ورودی و خروجی GPIO در STM32 | قسمت پنجم آموزش STM32 با توابع HAL

GPIO

در قسمت‌های پیشین سری آموزش STM32 با توابع HAL، با محیط نرم‌افزار STM32CubeIDE، پیکربندی پروژه و نحوه دیباگ آشنا شدیم. در این قسمت می‌خواهیم اولین پروژه عملی سری آموزش را انجام دهیم و به سراغ کد نویسی و استفاده از توابع HAL برویم. در ابتدا در مورد پایه‌های ورودی خروجی میکرو و جزییات سخت‌افزار GPIO در STM32 صبحت می‌کنیم و سپس به سراغ استفاده از این پایه‌ها در برنامه و تنظیم پایه‌ها به‌عنوان خروجی و یا ورودی برای ارسال یا دریافت فرمان، استفاده خواهیم کرد. با سیسوگ همراه باشی

 

پایه‌های ورودی خروجی همه منظوره (GPIO) در STM32

در میکروکنترلرهای STM32  برای هر پورت همه‌منظوره ورودی/خروجی (I/O)، دو رجیستر تنظیم 32 بیتی، یک رجیستر 32 بیتی به‌منظور Set/Reset کردن، یک رجیستر 16 بیتی برای Reset کردن و همچنین یک رجیستر قفل 32 بیتی، وجود دارد. هرکدام از پورت‌های I/O، به‌طور مستقل قابل‌برنامه‌ریزی هستند. بااین‌حال دسترسی به این پورت‌ها تنها به‌صورت یک کلمه کامل (32 بیتی) امکان‌پذیر است. بدین معنی که نمی‌توان تنها به نصف کلمه یا یک بایت از آن دسترسی داشت. رجیسترهای Set/Reset برای هر پورت، امکان دسترسی خواندن/تغییر رجیسترهای GPIO به‌صورت Atomic را می‌دهد، به این معنی که خطر رخ دادن وقفه در بازه زمانی دسترسی و تغییر رجیسترها وجود نخواهد داشت.

در شکل زیر دیاگرام ساختار سخت‌افزار نمونه یک پایه GPIO نشان داده‌شده است. در این شکل اجزایی از قبیل دیودهای محافظ، مدارهای pull-up،pull-down،‌ push-pull، مدارهای فعال‌سازی/غیر فعال‌سازی حالت خروجی (برای انتخاب ببین ورودی یا خروجی بودن) پایه و همچنین مدار Schmitt-trigger ورودی دیجیتال و آنالوگ، وجود دارند.

 

پایه‌های ورودی خروجی

 

ولتاژ کاری برای اکثر پایه‌های GPIO، ‌3.3v است. توصیه‌شده است که در حالت پیش‌فرض همین ولتاژ کاری را برای پایه‌ها در نظر بگیریم، مگر اینکه در دیتاشیت میکروکنترلر قابلیت تحمل ولتاژ 5v، برای پایه‌ی خاصی قیدشده باشد. در غیر این صورت در صورت اعمال این ولتاژ به پایه‌ها، امکان آسیب دیدن میکروکنترلر وجود دارد. بنابراین کنترل ولتاژ اعمال‌شده به پایه‌های ورودی اهمیت زیادی دارد.

 

جدول ولتاژ ها

 

علاوه بر محدودیت‌های ولتاژ، برای جریان نیز حد مشخصی تعریف‌شده است. حداکثر جریانی که از پایه‌های خروجی کشیده می‌شود یا به آن‌ها هدایت می‌شود با توجه به دیتاشیت تعریف می‌شود. معمولاً میزان این جریان، نباید از 25mA تجاوز کند. پس این پارامتر نیز حتماً باید در دیتاشیت میکروکنترلر چک شود و در طراحی و کاربرد لحاظ گردد.

 

جدول حد جریان ها

 

سرعت پایه‌های GPIO در STM32

برای پایه‌های GPIO در stm32 بسته به ورودی یا خروجی بودن آن‌ها، پارامتر سرعت نیز تعریف می‌شود.

 

سرعت پایه GPIO در حالت ورودی

برای هر پایه GPIO، زمانی که در حالت ورودی تنظیم‌شده باشد، اطلاعات حاضر روی پایه، با هر کلاک بأس (APB2) نمونه‌برداری شده و در Input Data Register نوشته خواهد شد. پس برای پایه‌ها ورودی، سرعت نمونه‌برداری، به‌وسیله سرعت کلاک بأس APB2 تعیین می‌شود.

 

سرعت پایه GPIO در حالت خروجی

در حالتی که یک پایه GPIO، به‌عنوان خروجی تنظیم‌شده باشد، امکان انتخاب سرعت آن وجود دارد. این تنظیم سرعت، به‌وسیله تغییر مقدار بیت‌های مربوطه در رجیستر تنظیمات GPIO، صورت می‌گیرد. در جدول زیر حالت‌های مختلف پین‌ها و همچنین سرعت‌های قابل انتخاب برای پایه‌های خروجی در میکروکنترلر STM32F103C8، نشان داده‌شده است.

 

 

در میکروکنترلرهای دیگر، ممکن است سرعت‌های متفاوتی برای پایه‌های خروجی، قابل تنظیم باشد. به‌عنوان‌مثال در میکروکنترلر STM32L432KC، با استفاده از رجیستر کنترل سرعت، می‌توان سرعت پایه‌های خروجی را در 4 حالت سرعت‌پایین، سرعت متوسط، سرعت‌بالا و سرعت خیلی بالا تنظیم نمود. این 4 حالت سرعت بسته به پارامترهایی نظیر مقدار VDDio و مجموع ظرفیت خازنی موجود در پایه موردنظر، محدوده‌های فرکانسی زیر هستند:

سرعت پایین: حدود 10MHz

سرعت متوسط: حدود 50MHz

سرعت بالا: حدود 100MHz

سرعت خیلی بالا: حدود 180MHz

 

انجام عملیات بیتی Atomic روی پایه‌های GPIO

در ابتدای متن اشاره شد که عملیات Atomic این امکان را به وجود می‌آورند که بدون نیاز به غیرفعال کردن وقفه‌ها، مقدار بیت‌های رجیسترها را صفر یا یک کنیم. به‌عنوان‌مثال به‌جای مقداردهی به رجیستر  GPIOx_ODR برای تغییر وضعیت پایه‌ها، می‌توان با یک دسترسی Atomic به APB2، وضعیت یک یا چند بیت را تغییر داد. این عمل از طریق نوشتن مقدار 1 منطقی در بیت موردنظر در رجیستر Set/Reset یعنی GPIOx_BSRR یا رجیستر Reset یعنی GPIOx_BRR صورت می‌گیرد.

استفاده از وقفه خارجی GPIO در STM32

برای همه پورت‌های GPIO، قابلیت وقفه خارجی وجود دارد. برای استفاده از این قابلیت، پورت موردنظر، در حالت ورودی تنظیم می‌شود. در بخش‌های بعدی بیشتر در مورد وقفه خارجی GPIO صحبت خواهیم کرد و کاربرد آن را به‌صورت عملی در یک پروژه نشان خواهیم داد.

انتخاب پایه‌های دستگاه‌های جانبی (Peripheral Pin Select)

در میکروکنترلرهای STM32، به‌منظور استفاده بهینه از تعداد پایه‌ها در پکیج‌های متفاوت و همچنین بهره‌گیری از قابلیت‌های دستگاه‌های جانبی مختلف میکروکنترلر، امکان نگاشت یا Remap کردن پایه‌ها وجود دارد. بدین‌صورت که برای هر پایه مالتی پلکسری وجود دارد که از میان چندین کارکرد و Peripheral مختلف، عملکرد موردنظر را برای پایه انتخاب کرد. به این طریق و با برنامه‌ریزی رجیستر مربوطه، امکان این وجود دارد که عملکرد بعضی Peripheral ها را روی پایه‌های متفاوتی نگاشت کرد. مزیت این امر در کاربردهایی است که می‌خواهیم با توجه به پکیج و پایه‌های در دسترس طراحی خاصی انجام دهیم و یا از دستگاه‌های جانبی به‌خصوصی بهره بگیریم، بدین‌وسیله ممکن است مسیر کشی بسیار راحت‌تر صورت گیرد.. همچنین درصورتی‌که یک PCB با میکروی متفاوتی طراحی‌شده باشد و بخواهیم میکروکنترلر مورداستفاده را تغییر بدهیم، به این طریق ممکن است بتوانیم تغییرات موردنیاز در PCB را به حداقل برسانیم. مزیت مهم‌تر این قابلیت درجایی است که بخواهیم مثلاً مسیر یک سیگنال پرسرعت را جوری تغییر بدهیم که در معرض نویز کمتری قرار بگیرد، که به‌وسیله قابلیت توضیح داده‌شده، می‌توان مسیر مناسب‌تر برای سیگنال موردنظر، ایجاد کرد.

 

مکانیزم قفل GPIO در STM32

در ابتدای متن اشاره شد که در میکروکنترلرهای STM32، برای هر پورت معمولاً یک رجیستر 32 بیتی قفل نیز وجود دارد. مکانیزم قفل درجایی که کاربرد دارد که بخواهیم تنظیمات پایه‌های IO را “قفل” کنیم. یعنی اینکه در صورت اعمال آن، تنظیمات مربوط به پایه موردنظر تا انجام ریست بعدی، قابل‌تغییر و دست‌کاری نخواهد بود.

 

تنظیمات GPIO

پورت‌های همه‌منظوره در میکروکنترلر مطابق با مشخصات درج‌شده در دیتاشیت میکروکنترلر، به‌صورت مستقل و در حالت‌های مختلفی قابل تنظیم‌اند که در ادامه هرکدام از این حالت‌ها را معرفی می‌کنیم.

تنظیم پایه به عنوان خروجی
  • خروجی به صورت Open-Drain
  • خروجی به صورت Push-Pull
تنظیم پایه به عنوان ورودی
  • ورودی مدار باز یا فلوت (Hi-Z)
  • ورودی در حالت Pull-Up
  • ورودی در حالت Pull-Down
تنظیم در حالت عملکرد ثانویه (Alernate Function)
  • عملکرد ثانویه در حالت Push-Pull
  • عملکرد ثانویه در حالت Open-Drain
تنظیم در حالت آنالوگ
GPIO در stm32
حالت‌های active low و active high ‎  (‏Pull-up و Pull-down) برای پایه‌‎ها در حالت ورودی و خروجی.

جمع بندی نکات GPIO

  • همان‌طور که اشاره شد، همه‌ی پایه‌های GPIO تحمل ولتاژ 5v را ندارند و اکثر پایه‌ها تنها تا 3.3v را تحمل می‌کنند.
  • هر پورت GPIO را که بخواهیم در هرکدام از حالت‌های کاری مورداستفاده قرار دهیم، ضروری است که ابتدا کلاک آن را فعال کنیم.
  • گفته شد که برای پایه‌هایی که در حالت خروجی تنظیم‌شده باشند، سرعت تغییرات یا سوییچ، قابل تنظیم است. این سرعت معمولاً در سه حالت سرعت‌پایین، سرعت متوسط و سرعت‌بالا قابل تنظیم است.
  • برای پایه‌های ورودی اشاره شد سرعت بأس APB2، تعیین‌کننده نرخ نمونه‌برداری تمامی پایه‌های GPIO تنظیم‌شده در حالت ورودی است.
  • برای پایه GPIO، امکان قطع کردن (یا High impedance) کردن پایه وجود دارد. بدین منظور، پایه به‌عنوان ورودی و در حالت Hi-Z تنظیم می‌شود.
  • هنگامی‌که در یک پروژه می‌خواهیم تنظیمات پایه‌های GPIO، بعد از راه‌اندازی سیستم، قابل‌تغییر نباشد، می‌توان با استفاده از مکانیزم قفل، تنظیمات پایه‌های موردنظر را ثابت نگه داشت.
  • در مورد وقفه خارجی، گفته شد که برای تمامی پورت‌ها و پایه‌های GPIO، این نوع وقفه از بخش EXTI، قابل فعال‌سازی است.

API های GPIO در HAL

APIهای HAL اصلی و پراستفاده‌ای که برای GPIO تعریف شده‌اند، عبارت‌اند از:

• HAL_GPIO_Init() / HAL_GPIO_DeInit()
• HAL_GPIO_ReadPin() / HAL_GPIO_WritePin()
• HAL_GPIO_TogglePin ()

این API ها به ترتیب برای راه‌اندازی و غیر فعال‌سازی، خواندن ورودی و نوشتن در خروجی، و معکوس کردن حالت پین (از یک به صفر و از صفر به یک)، کاربرد دارند.

اکنون‌که با نکات اصلی GPIO آشنا شدیم، می‌خواهیم به چگونگی تنظیم پایه‌ها در حالت ورودی و خروجی و استفاده از آن‌ها بپردازیم. پس در ادامه کار، دو پروژه ساده را باهم توسعه می‌دهیم تا کاربرد هر یک از حالت‌های ورودی و خروجی پایه‌های GPIO را در عمل، مشاهده کنیم.

تنظیم پایه GPIO در حالت خروجی و ارسال سیگنال دیجیتال

در این بخش می‌خواهیم یک پایه GPIO در stm32 را در حالت خروجی تنظیم کنیم و سپس به‌وسیله صفر و یک کردن ولتاژ آن، یک LED را خاموش و روشن کنیم.

ایجاد پروژه GPIO Output

مانند روالی که برای ایجاد و پیکربندی پروژه گفتیم، یک پروژه جدید ایجاد می‌کنیم و تنظیمات دیباگ و کلاک آن را مطابق تصاویر زیر انجام می‌دهیم:

همان‌طور که در شکل‌ها نشان داده‌شده است، دیباگ در حالت Serial wire و کلاک سیستم در حالت کریستال تنظیم‌شده‌اند. اکنون یک پایه دلخواه را در حالت خروجی تنظیم می‌کنیم. ما برای این منظور پایه PC13 را انتخاب می‌کنیم که به LED روی بورد متصل است.
GPIO در stm32
سپس از تب Clock Configuration، کلاک سیستم را روی 72MHz تنظیم می‌کنیم؛
تنظیمات پروژه
اکنون وارد فایل main.c می‌شویم تا کد پروژه را بنویسیم.

نوشتن کد پروژه برای کنترل پایه خروجی

در کتابخانه HAL، تابع تعریف‌شده برای تغییر پایه‌های خروجی، GPIO_WritePin نام دارد که در داکیومنت کتابخانه به‌صورت زیر تعریف‌شده است:

 

کنترل پایه خروجی

 

همان‌طور که می‌بینیم، این تابع سه پارامتر ورودی دارد. ورودی اول پورت GPIO مورداستفاده، ورودی دوم پین موردنظر و ورودی سوم حالتی (منطق صفر یا یک) است که می‌خواهیم به آن پایه اعمال کنیم.

اکنون از این دستور برای صفر و یک کردن پایه متصل به LED، یعنی PC13 استفاده می‌کنیم. در تابع int main(void) و قبل از حلقه (1) while کد زیر را می‌نویسیم:

در این کد، ابتدا ولتاژ پایه PC13 را صفر کرده‌ایم. زیرا LED متصل به این پایه به‌صورت Active-low و با سیگنال صفر روشن می‌شود. سپس بعد از 1.5 ثانیه ولتاژ پایه را 1 و درنتیجه LED را خاموش می‌کنیم. بعد از نوشتن کد، از پروژه Build می‌گیریم و سپس برنامه خروجی را روی میکرو Download می‌کنیم. بدین منظور کلید Run (پایه های ورودی و خروجی GPIO در STM32 | قسمت پنجم آموزش STM32 با توابع HAL) را می‌زنیم یا اینکه از منوی Run، گزینه Run را انتخاب می‌کنیم.

درصورتی‌که همه مراحل گفته‌شده را به‌درستی طی کرده باشیم، مشاهده می‌کنیم که پس از ریست شدن و شروع کار میکروکنت، LED موجود روی بورد برای مدت کوتاهی (1.5 ثانیه) روشن و پس‌ازآن خاموش می‌شود.

 

تنظیم پایه GPIO در حالت ورودی و دریافت سیگنال دیجیتال

در این قسمت قصد داریم یک پایه GPIO دیگر را در حالت ورودی تنظیم کنیم و سپس به‌وسیله‌ی این پایه ورودی یک سیگنال کنترلی دریافت کنیم. سپس بر اساس مقدار این سیگنال کنترلی مشخص می‌کنیم که LED خاموش و یا روشن باشد. به عبارت ساده‌تر در این پروژه می‌خواهیم به‌وسیله یک کلید، LED را خاموش و روشن کنیم.

 

تنظیم پروژه GPIO Input

به‌جای تشکیل یک پروژه جدید، می‌توانیم همان پروژه قبلی را با اعمال تغییراتی برای اضافه شدن ورودی، تنظیم کنیم. مانند شکل زیر یک پایه دیگر GPIO (مثلاً PB12) را در حالت ورودی و به‌صورت Pull-up قرار می‌دهیم:

 

GPIO در stm32

 

پس از تنظیم پایه ورودی، مجددا به سراغ کد میرویم.

 

نوشتن کد پروژه برای خواندن پایه ورودی

در کتابخانه HAL، تابع تعریف‌شده برای تغییر پایه‌های خروجی، GPIO_ReadPin نام دارد که در داکیومنت کتابخانه به‌صورت زیر تعریف‌شده است:

 

GPIO_WritePin

 

همان‌طور که می‌بینیم، این تابع دو پارامتر ورودی دارد. ورودی اول پورت GPIO مورداستفاده و ورودی دوم پین موردنظر است.

اکنون از این دستور خواندن وضعیت کلید متصل شده به پایه ورودی، یعنی PB12 استفاده می‌کنیم. در تابع int main(void) و در بدنه حلقه (1) while کد زیر را می‌نویسیم:

در این کد، ابتدا شرط فعال بودن (صفر بودن) کلید بررسی می‌شود و درصورتی‌که کلید فعال باشد، LED برای مدت 250 میلی‌ثانیه، روشن و برای 250 میلی‌ثانیه خاموش می‌ماند. سپس در صورت فعال ماندن کلید این فرآیند تکرار خواهد شد. اکنون بعد از نوشتن کد، از پروژه Build می‌گیریم و سپس برنامه خروجی را روی میکرو Download می‌کنیم.

درصورتی‌که همه مراحل گفته‌شده را به‌درستی طی کرده باشیم، مشاهده می‌کنیم که درصورتی‌که کلید را بزنیم، LED موجود روی بورد شروع به چشمک زدن خواهد کرد.

در این قسمت از سری آموزش STM32 با توابع HAL، اولین برنامه را توسعه دادیم و با عملیات ابتدایی GPIO در stm32 آشنا شدیم. در قسمت بعدی می‌خواهیم در مورد جزییات بیشتر از GPIO که در این بخش بیان نشدند، صحبت کنیم. با ما همراه باشید.

انتشار مطالب با ذکر نام و آدرس وب سایت سیسوگ، بلامانع است.

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

6 دیدگاه در “پایه های ورودی و خروجی GPIO در STM32 | قسمت پنجم آموزش STM32 با توابع HAL

  1. Avatar for مجید مجید گفت:

    سلام و تشکر از مطلب مفیدتون

  2. Avatar for سلمان سلمان گفت:

    مرسی استاد بابت مطالب مفیدتان
    یه سوال داشتم
    اگر بخواهیم ‍‍یک port رو مقدار دهی کنیم یا کامل بخوانیم(نه بصورت pin )) چه باید کرد؟؟
    مثلا بخواهیم مقدار 0xA58B را بر ٰروی GPIOA بنویسیم ؟؟؟از چه توابعی باید استفاده کنیم؟؟

    1. Avatar for Zeus ‌ Zeus ‌ گفت:

      سلام دوست عزیز
      برای نوشتن مستقیم مقدار روی پورت، رجیستری داریم به نام ODR که با مقدار دهی اون می تونید مقدار مورد مورد نظر رو روی پورت بنوسید
      و برای خواندن دایرکت پورت هم از رجیستر IDR استفاده کنید

  3. Avatar for محمد حسین محمد حسین گفت:

    با عرض سلام و خسته نباشید…
    در قسمت “نوشتن کد پروژه برای خواندن پایه ورودی”، GPIO_ReadPin به اشتباه GPIO_WritePin نوشته شده است، لطفا اصلاح گردد. ممنون

    1. Avatar for Zeus ‌ Zeus ‌ گفت:

      ممنونم دوست عزیز اصلاح شد

  4. Avatar for yaser khodabandeloo yaser khodabandeloo گفت:

    با سلام
    خیلی مفید بود،تشکر از زحمات جنابعالی.

دیدگاهتان را بنویسید

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