سلام دوستان و همراهان عزیز مهدی حسن زاده آملی، با جلسه سوم آموزش کاملا رجیستری STM32F10x در خدمت شما هستم.
در قسمت دوم در رابطه با GPIO در حالت ورودی صحبت کردیم.
موضوع این جلسه رو بعد از کلی مشغله و دیر شدن به مبحث شیرین و بسیار کاربردی UART میپردازیم. خب همونطور که شاید میدونید ارتباط سریال به دو دسته سنکرون (همزمان) و آسنکرون (غیر همزمان) تقسیم میشود. اینکه سنکرون و یا آسنکرون چی هست رو به خودتون میسپارم بالاخره تحقیق کردن رو باید از چیزای کوچیک و دم دستی شروع کرد.
UART و USART
بدون اتلاف وقت برویم سر اصل مطلب. میکروکنترلر STM32F10x معمولا چند عدد USART و چند عدد UART در دسترس ما قرار داده است که در پریفرالهای USART این قابلیت وجود دارد که به مانند UART از این پریفرالها بهره بگیریم.
از نظر مداری یکی از سادهترین مدلهای ارتباط و خوشدستترین مدل ارتباط از نظر من همین ارتباط سریال است که انصافا با کمترین دانش مداری و برنامه نویسی (مثل من) میشه راهاندازیش کرد.
خب به عکس زیر توجه کنید:
در عکس بالا مشخصه که ارتباط سریال به سه سیم نیازمند است که یکی TXبرای ارسال و دیگری RX برای دریافت و سیم سوم که خیلی مهمه در این ارتباط GND است که عدم توجه به این اتصال باعث میشه کل کارمون رو هوا باشه.
خب از نظر مداری همین حد بسه دیگه وقتشه که بریم سروقت رجیسترها و تنظیمات UART.
اول مرتبه باید ببینیم پایه UART1 rx-tx بر روی میکرو موردنظرمان (stm32f103re) کجاست، که من برای پیدا کردنش از نرمافزار cubemx استفاده میکنم.
همین اول کار یه چیزی بگم تا یادم نرفته اگه خوب به عکس بالا توجه کنیم میبینیم پایه A9-A10 برای UART استفاده شده، اما ما نمیخواهیم از این پین استفاده کنیم (البته به دلایل متعدد) شرکت St به ما این دسترسی را از طریق رجیستر remap داده که بتونیم پایهها را جابهجا کنیم، البته نه هرچیزی که دلمون بخواد.
AFIO_MAPR رجیستر remap کردن پایهها
همینطور که در عکس بالا مشخصه خانه شماره 2 در این رجیستر برای USART1 هستش که طبق عکس زیر مقداردهی میشود.
فکر میکنم عکس بالا لازم به توضیح نداشته باشه، اما من اگر بخوام در برنامهنویسی مشخص کنم که ریمپ صورت نگیره چه کاری باید بکنم؟
C++
1
AFIO->MAPR&=~(1UL<<2);// clear remap
کد بالا بیشتر برای اطمینان نوشته میشه اگر هم بخواهیم ریمپ کنیم از کد زیر استفاده میکنم:
C++
1
AFIO->MAPR|=(1UL<<2);
تنظیمات UART دو بخش داره، اول پایههایی که میخواهیم ازشون برای UART استفاده کنیم و دوم تنظیمات خود پریفرال UART. در زیر به تشریح هر کدام خواهیم پرداخت:
بخش اول:
پایه هایی که میخواهیم از آنها اطلاعات بفرستیم و دریافت کنیم باید کانفیگ خاص خودشو داشته باشه مثلا زمانی که TX دیتایی رو میخواهد بفرستد، دیتای ارسالی مانند شکل زیر ترکیبی از صفر و یکهایی است که باید در خروجی نمایش داده شود:
پس نتیجه میگیریم پایه TX باید خروجی باشد اما خروجی معمولی نه، بلکه از نوع Alternate Function output به عکس زیر توجه کنید:
GPIOA->CRH|=(0x0BUL<<4);// Alternate Function output
اگر مقداردهی بالا گنگ است به عکس زیر و یا به مطلب قسمت اول مراجعه کنید.
اما پایه RX را چون دریافتکننده است ورودی از نوع Input floating میکنم.
C++
1
GPIOA->CRH|=0x00000400;// Input floating
بخش دوم:
در این قسمت تنظیمات خود UART رو پیکربندی میکنیم، اولین قدم برمیگرده به درک عملکرد UART. منظورم از درک عملکرد UART اینه که چون ما هیچ کلاکی جهت همزمان کردن دستگاه ارسال کننده و دریافت کننده پیام نداریم و دستگاه دریافتکننده نمیدونه دیتای شروع کدومه، پیام کدومه، سر و ته کدومه، به خاطر همین از چیزی به اسم بادریت استفاده میکنیم تا بتونیم همزمانی رو یک جوری به صورت قراردادی در هر دو سمت (فرستنده و گیرنده) داشته باشیم.
البته اینکه بادریت کلا چی هست، با خودتون (ساده است اما تحقیق کنید) اماخب برای تنظیم بادریت یک رجیستر داریم به اسم baud rate register.
UART_BRR:
به عکس بالا توجه کنید در این رجیستر دو قسمت داریم، [DIV_Fraction[3:0 و [DIV_Mantissa[11:0 مقداری که در این دو قسمت نوشته شود
بادریت رو مشخص میکند. اما چطور؟
فرمولش رو در عکس بالا میبینید، اما شیوه محاسبش را در ادامه خواهیم گفت.
اول فرکانسی که به پریفرال ما میاد رو باید بدونیم به دو عکس زیر توجه کنید:
TX/RX baud: بادریتی که میخواهیم.
Fclk: فرکانس UART ما (توجه کنید که فرکانس هر باس با هم فرق میکند طبق عکسهای بالا).
16: ثابت.
حالا میخواهیم بادریت 115200 را در رجیستر BRR قرار دهیم.
C++
1
(72000000)/(16*115200)=39.0625
خب عددی که حاصل شده از دو قسمت 39 و 0.0625 است که مقدار صحیح را در [DIV_Mantissa[11:0 قرار میدهیم، اما مقدار اعشاری 0.0625 را باید ضرب در عدد ثابت 16 کرده و در مقدار [DIV_Fraction[3:0 قرار دهیم.
C++
1
16*0.0625=1
که اگرمقدارها را تبدیل به عدد هگز کنیم 39 میشود 0x027 و عدد 1 که میشود همان 0x1 که برای نوشتن در رجیستر BRR از روش زیر استفاده میکنیم:
C++
1
USART1->BRR=0x0271// 0x027 --- 0x1
به طور مثال برای 9600 و فرکانس 12 مگ داریم:
C++
1
2
3
4
12000000/(16*9600)=78.125
78=0x4E
0.125*16=2
USART1->BRR=0x4E2//0x4E----2
اگر مقدار [DIV_Fraction[3:0 یک عددی باشد که کری داشته باشد به قسمت [DIV_Mantissa[11:0 اضافه میشود، حالا یعنی چی؟ یعنی [DIV_Fraction[3:0 که چهار بیت دارد مقدار 0 تا 15 رو میتواند در خود قرار دهد حالا اگر ضرب 16 با [DIV_Fraction[3:0 یک عدد بزرگتر از 15 بود یک کری به [DIV_Mantissa[11:0 اضافه میشود.
تا اینجای کار توانستیم بادریت رو فعال کنیم حالا میوریم سروقت رجیستر های UART.
رجیستر CR1 :Control register1
خب همونطور که از عکس بالا مشاهده میکنید 14 بیت از 32 بیت آن در دسترس است که هر کدام رو که لازم باشه در ارتباط UART توضیح میدهم.
RE: بیت شماره 2 از رجیستر CR1 برای فعال سازی حالت RX است.
Receiver enable This bit enables the receiver. It is set and cleared by software :Bit 2 RE
0: Receiver is disabled
1: Receiver is enabled and begins searching for a start bit
C++
1
USART1->CR1|=(1UL<<2);
TE: بیت شماره 3 از رجیستر CR1 برای فعال سازی حالت TXاست.
Transmitter enable This bit enables the transmitter. It is set and cleared by software :Bit 3 TE
0: Transmitter is disabled
1: Transmitter is enabled
C++
1
USART1->CR1|=(1UL<<3);
البته بعد از فعالسازی این رجیستر باید یک زمان کوتاهی رو delay در نظر بگیریم (طبق گفته دیتاشیت)
When TE is set there is a 1 bit-time delay before the transmission starts :Note
برای ایجاد تاخیر هم از روش زیر استفاده کنید:
C++
1
2
3
4
For(inti=0;I<0x1000;i++)
{
__nop();
}
تابع ()nop هم به کامپایلر میفهماند که این تکه کد جز حلقه اصلی است (مانند کد اسمبلی) که در هنگام کامپایل و بهینهسازی کامپایلر حذفش نکند.
M: بیت شماره 12 از رجیستر CR1 برای مشخص کردن تعداد data بیت است.
0: 1 استارت بیت و 8 دیتا بیت.
1: 1 استارت بیت و 9 دیتا بیت.
C++
1
USART1->CR1&=~(1<<12);
UE: بیت شماره 13 فعال کننده کل UART است (USART enable)
0: غیرفعالکننده UART.
1: فعالکننده UART.
C++
1
UART1->CR1|=(1<<13);
تا اینجای کار رو داشته باشید تا در قسمت چهارم ادامشو باهم یاد بگیریم.
سلام دوست عزیز.
شما وقتی به صورت رجیستری برنامه مینویسید، با سطوح پایین و لایههای مختلف سختافزار به خوبی آشنا میشوید. و خیلی بهتر و عمیقا مفاهیم را درک میکنید. اگر به صورت رجیستری برنامه بنویسید در اغلب اوقات سرعت بالاتری بدست خواهید آورد برای این موضوع میتوانید به عنوان مثال تابع ()HAL_GPIO_WritePin را مقایسه کنید با حالتی که خودتان به صورت رجیستری برنامه را مینویسید.
حجم برنامه میتواند کمتر باشد و بسیاری از موارد دیگر.
در کنار این همه مزایا یک عیب بزرگی که وجود دارد این است که سرعت توسعه با رجیستری خیلی کم است و این شاید اذیتکننده باشد.
به عنوان یک پیشنهاد میتوانید ابتدا رجیستری کار کنید تا به خوبی بفهمید در سطوح پایین چه اتفاقی میافتد و پس لز آن از توابع سطح بالا استفاده کنید تا سرعت توسعه بالا رود.
ریموت کنترل امروزه کاربرد زیادی پیدا کرده است؛ از ریموتهای درب بازکن تا ریموتهای دزدگیر و کنترل روشنایی همه از یک اصول اولیه پیروی میکنند و آنهم ارسال اطلاعات بهصورت بیسیم است....
امنیت همیشه و در همهی اعصار، مقولهی مهم و قابلتوجه ای بوده و همیشه نوع بشر به دنبال امنیت بیشتر، دست به ابداعات و اختراعات گوناگونی زده است. ریموت کنترل یکی از این اختراعات است. در این مقاله، به بررسی امنیت انواع ریموتهای کنترل خواهیم پرداخت....
سلام
خسته نباشید
اگه ممکنه راجب مزایای برنامه نویسی رجیستری هم توضیح بدین.
ممنون از بابت آموزش های ارزشمندتون … خیلی زحمت می کشید …
سلام دوست عزیز.
شما وقتی به صورت رجیستری برنامه مینویسید، با سطوح پایین و لایههای مختلف سختافزار به خوبی آشنا میشوید. و خیلی بهتر و عمیقا مفاهیم را درک میکنید. اگر به صورت رجیستری برنامه بنویسید در اغلب اوقات سرعت بالاتری بدست خواهید آورد برای این موضوع میتوانید به عنوان مثال تابع ()HAL_GPIO_WritePin را مقایسه کنید با حالتی که خودتان به صورت رجیستری برنامه را مینویسید.
حجم برنامه میتواند کمتر باشد و بسیاری از موارد دیگر.
در کنار این همه مزایا یک عیب بزرگی که وجود دارد این است که سرعت توسعه با رجیستری خیلی کم است و این شاید اذیتکننده باشد.
به عنوان یک پیشنهاد میتوانید ابتدا رجیستری کار کنید تا به خوبی بفهمید در سطوح پایین چه اتفاقی میافتد و پس لز آن از توابع سطح بالا استفاده کنید تا سرعت توسعه بالا رود.