آموزش میکروکنترلر Stm32f1 به صورت رجیستری قسمت دوم: GPIO-ورودی

0
1117
GPIO-ورودی
GPIO-ورودی

 مقدمه

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

آموزش STM32f103 به‌صورت کاملا رجیستری
آموزش STM32f103 به‌صورت کاملا رجیستری

 

خب اگر پست قبلی رو دنبال‌کرده‌باشید می‌دونید که CRL-H طبق تصویربالا، هریک از رجیسترهای آن دارای چهارحالت است که مطابق تصویر زیر بیت‌های رجیستر مثلا پین صفرم تنظیم می‌شود.

تنظیم بیت صفرم
تنظیم بیت صفرم

 

به تصویربالا با دقت بیشتری نگاه‌کنید حتما خواهید دید که در قسمت INPUT ما چهار حالت را در اختیار داریم که فعلا حالت ANALOG رو مورد‌بررسی قرار نمی‌دهیم و بر سه حالت قبلی تمرکز می‌کنیم.

حالت input-floating:

در این حالت پین ما با هر نویز و یا ولتاژ خواسته و یا ناخواسته تحریک می‌شود و تغییر حالت می‌دهد که برای جلوگیری از این اتفاق ناخوشآیند از مقاومت‌های پول خارجی استفاده می‌کنیم و رجیستر CRL-H ما هم در این حالت، عدد ۴ را به خود اتخاذ می‌کند:

GPIOA-> CRL &= ~ 0x0000000F;

این کد برای clearکردن پین صفرم استفاده می‌شود:

GPIOA-> CRL &= ~ 0x00000004;

این کد هم ورودی کردن پین موردنظر را انجام می‌دهد(floating).

حالت INPUT-PULL-DOWN:

در این حالت پین موردنظر با اعمال ولتاژ مثلا ۳.۳ولت تحریک می‌شود و این پین در حالت عادی صفر است. خب برای تنظیم این حالت می‌بینیم که دیتاشیت به ما می‌گه بیت CNF1 را برابر یک قرار می‌دهیم و همچنین رجیستر ODR را هم صفر می‌کنیم تا خروجی به حالت PULL Down قرار بگیرد.

GPIOA-> CRL &= ~ 0x0000000F;

این کد برای clearکردن پین صفرم استفاده می‌شود.

GPIOA-> CRL &= ~ 0x00000008;

این کد هم ورودی کردن پین موردنظر را انجام می‌دهد(PULL-DOWN).

GPIOA->ODR |= ~(1<<0);

برای پول داون کردن نیز از این کد استفاده می‌کنیم.

 GPIOA->ODR |= (0<<0);

 

حالت INPUT-PULL-UP:

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

GPIOA-> CRL &= ~ 0x0000000F;

این کد برای clearکردن پین صفرم استفاده می‌شود.

GPIOA-> CRL &= ~ 0x00000008;

این کد هم ورودی کردن پین موردنظر را انجام می‌دهد (PULL-DOWN)

GPIOA->ODR |= (1<<0);

برای پول‌آپ کردن نیز از این کد استفاده می‌کنیم. حال که تنظیمات پین موردنظرمون را درست انجام‌دادیم، می‌رویم سروقت استفاده‌از پین موردنظر. رجیستر مورد‌استفاده برای خواندن مقدار در حالت ورودی GPIOX-IDR است که شیوه‌ی استفاده این رجیستر به این حالت است:

شیوه‌ی استفاده از رجیستر
شیوه‌ی استفاده از رجیستر

 

در حالت PULL-DOWN
If(GPIOA->IDR & (1<<0))
{
کدی که میخواهیم بعد از فشردن کلید اجرا شود

}
در حالت PULL-UP
If(! (GPIOA->IDR & (1<<0)))
{
کدی که میخواهیم بعد از فشردن کلید اجرا شود

}

در حالت عادی رجیستر IDR به‌صورت 0x0000000000000000 است که با & کردن 0x0000000000000001 مشخص می‌کنیم که این بیت ۱ شده است (یعنی ANDبیتی) خب من یه کد ساده می‌نویسم که نشون‌بدم پین موردنظرم ۱ شده یا نه و اگر ۱ شد led را روشن کنه در غیر اینصورت خاموش کنه:

#include <stm32f10x.h>
int main (void)
{
RCC -> APB2ENR |= (1<<2);
GPIOA -> CRL &= ~(0x000000FF) ;
GPIOA -> CRL |= (0x00000038);
GPIOA -> ODR |=(1<<0);
while(1)
{
if( !(GPIOA -> IDR & (1<<0)))
{
GPIOA -> BSRR |= (1<<1);
while(!(GPIOA -> IDR & (1<<0)));
}
else{
GPIOA -> BRR |= (1<<1);
}
}

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

interrupt:

در فعال سازی اینتراپت چند پارامتر و پیکربندی‌ای رو باید انجام بدیم.

فعال‌ساز اینتراپت مورد (پین و یا پریفرال) نظر به صورت سراسری:

NVIC_EnableIRQ(نام اینتراپت مورد استفاده );

نام اینتراپت مورداستفاده رو در قسمت هدر میکرو خواهیم یافت. خب دوستان اینو تا یادم نرفته بگم که اینتراپت چیکار می‌کنه؟ مطمئنم می‌دونید اما گفتنش بد نیست! زمانیکه اینتراپت خارجی و یا اینتراپت داخلی (پریفرال‌ها) اتفاق بیافته برنامه‌ای که CPU درحال اجراش بود رو رها می‌کنه و برنامه ای که در تابع مخصوص اینتراپت نوشته‌شده رو اجرا می‌کنه و بعد‌از اتمام کدهای تابع اینتراپت CPU برمی‌گرده و برنامه قبلی خودش رو ادامه می‌ده. تابع اینتراپت برای اینتراپت خارجی به زیر تعریف می‌شود: (نام هر تابع اینتراپت در داخل فایل startup قرار دارد)

Void EXTI0_IRQHandler (void)
{

}

ما در stm32f10x به تعداد ۱۵ اینتراپت خارجی دراختیار داریم که در اکثر پایه‌های میکرو قابل‌دسترسی هستند.

نکته مهم: در این سری میکرو برای اینتراپت‌های خارجی ۰ تا ۴ هرکدام تابع‌های وقفه جداگانه در اختیار ما است اما از اینتراپت خارجی ۵ تا ۹ فقط یک تابع وقفه و از اینتراپت‌های ۱۰تا ۱۵ هم یک تابع وقفه در اختیار داریم. به‌طورمثال اگر بخواهیم همزمان از اینتراپت خارجی ۵ و ۸ و ۹ استفاده‌کنیم به‌صورت‌زیر عمل‌خواهیم‌کرد:

Void EXTI9-5_Handler (void)
{
If(EXTI -> PR & (1UL << 5))
{
//your code 
EXTI -> PR |= (1UL << 5); 
}
If(EXTI -> PR & (1UL << 8))
{
//your code 
EXTI -> PR |= (1UL << 8); 
}
If(EXTI -> PR & (1UL << 9))
{
//your code 
EXTI -> PR |= (1UL << 9);//clear interrupt flag 
}
}

 

توضیحات رجیسترهای بالا رو جلوتر خواهم گفت، اما شیوه‌ی نوشتن تابع وقفه رو باهم یاد بگیریم:

تنظیمات AFIO:

خب با این رجیستر می‌تونیم مشخص کنیم که وقفه ۰ در چه پورتی (A یا B یا…) قرار بگیرد. در قسمت تنظیمات AFIO ما چهار رجیستر داریم که این رجیسترها عبارتند از:

External interrupt configuration register 1 (AFIO_EXTICR1)

AFIO_EXTICR1
AFIO_EXTICR1

 

External interrupt configuration register 2 (AFIO_EXTICR2)

AFIO_EXTICR2
AFIO_EXTICR2

 

External interrupt configuration register 3 (AFIO_EXTICR3)

AFIO_EXTICR3
AFIO_EXTICR3

 

External interrupt configuration register 4 (AFIO_EXTICR4)

AFIO_EXTICR4
AFIO_EXTICR4

 

خب این چیزی که از هر عکس بالا مشخصه AFIO_EXTICRX هرکدام چهار پین را برای هر پورت مشخص می‌کند. طریقه کدنویسی هم به‌صورت‌زیر است:

AFIO -> EXTICR[x-1] |= AFIO_EXTICR1_EXTI0_PA;

X شماره رجیستر AFIO_EXTICRx است. خب کد بالا به این معناست که از داخل رجیستر EXTICR1 پین شماره ۰ از پورت A، که این (AFIO_EXTICR1_EXTI0_PA) جمله به‌صورت define در داخل هدر میکرو وجود دارد. اما از نظر بیتی جمله AFIO_EXTICR1_EXTI0_PA به‌صورت‌زیر نوشته می‌شود:

AFIO -> EXTICR[1-1] |= (0x0 << 0);

اگر بخواهیم پورت C را برای اینتراپت ۷ تنظیم کنیم به‌صورت‌زیر عمل می‌کنیم(طبق تصویر AFIO_EXTICR2):

AFIO -> EXTICR[2-1] |= (0x2 << 12);

حال می‌رویم سر وقت رجیسترهای مختص اینتراپت خارجی:

Interrupt mask register (

EXTI_IMR
EXTI_IMR

 

این رجیستر برای به این منظور استفاده می‌شود که در هر پایه‌ای اینتراپت بخواهیم داشته‌باشیم اون پایه رو فعال‌کنیم.

EXTI -> IMR |= (1UL << 0);// for pin 0 interrupt
EXTI -> IMR |= EXTI_IMR_M0; // for pin 0 interrupt
فعالسازی پایه‌ی اینتراپت
فعالسازی پایه‌ی اینتراپت

 

Rising trigger selection register (EXTI_RTSR):

EXTI_RTSR
EXTI_RTSR

 

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

EXTI -> FTSR &=~ EXTI_FTSR_TR0;

این خط برای clearکردن لبه پایین رونده استفاده می‌شود.

EXTI -> RTSR |=  EXTI_RTSR_TR0;

این خط برای فعال کردن لبه بالا رونده استفاده می‌شود.

Falling trigger selection register (EXTI_FTSR):

EXTI_FTSR
EXTI_FTSR

 

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

EXTI -> RTSR &=~ EXTI_RTSR_TR0;

این خط برای clear‌کردن لبه بالا رونده استفاده می‌شود.

EXTI -> FTSR |=  EXTI_FTSR_TR0;

این خط برای فعال کردن لبه پایین رونده استفاده می‌شود.

Pending register (EXTI_PR):

EXTI_PR
EXTI_PR

 

این رجیستر تشخیص می‌دهد که وقفه در کدام پایه اتفاق افتاده است که در تابع وقفه استفاده می‌شود.

If(EXTI -> PR & (1UL << 5))
{
//your code 
EXTI -> PR |= (1UL << 5); 
}

بعد‌از تشخیص باید با ۱ کردن در این رجیستر عمل clear به‌صورت نرم‌افزاری صورت بگیرد تا اینتراپت‌های بعدی را هم تشخیص‌دهیم برای پایه موردنظر.

EXTI -> PR |= (1UL << 5); //clear

نکته همیشگی: برای استفاده‌از هر پریفرالی حتما باید کلاک اون پریفرال رو فعال کنید وگرنه برنامه شما اصلا عمل نمی‌کنه.

RCC -> APB2ENR |= RCC_APB2ENR_AFIOEN;

نکته: برای استفاده‌از اینتراپت خارجی باید حتما پین موردنظرمون را اول ورودی کنیم.

کد پایانی

final code
final code

 

امیدوارم موفق باشید.

 

منبع: سیسوگ

مطلب قبلیOTA چیست؟ و راه‌اندازی آن با NodeMcu
مطلب بعدیپروژه راه‌اندازی سنسور تشخیص باران با آردوینو

پاسخ دهید

لطفا نظر خود را وارد کنید!
لطفا نام خود را در اینجا وارد کنید