ادامه
در قسمت دوم در رابطه با 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 کردن پایهها
همینطور که در عکسبالا مشخصه خانه شماره۲ در این رجیستر برای USART1 هستش که طبق عکسزیر مقداردهی میشود.
فکر میکنم عکسبالا لازم به توضیح نداشتهباشه، اما من اگر بخوام در برنامهنویسی مشخصکنم که ریمپ صورت نگیره چه کاری باید بکنم؟
AFIO -> MAPR &=~ (1UL << 2); // clear remap
کد بالا بیشتر برای اطمینان نوشته میشه اگر هم بخواهیم ریمپ کنیم از کد زیر استفاده میکنم:کد بالا بیشتر برای اطمینان نوشتهمیشه اگر هم بخواهیم ریمپ کنیم از کد زیر استفاده میکنم:
AFIO -> MAPR |= (1UL << 2);
تنظیمات UART دو بخش داره، اول پایههایی که میخواهیم ازشون برای UART استفاده کنیم و دوم تنظیمات خود پریفرال UART. در زیر به تشریح هرکدام خواهیمپرداخت:
بخش اول:
پایههایی که میخواهیم از آنها اطلاعات بفرستیم و دریافتکنیم باید کانفیگ خاص خودشو داشتهباشه مثلا زمانیکه TX دیتایی رو میخواهد بفرستد، دیتای ارسالی مانند شکلزیر ترکیبی از صفر و یکهایی است که باید در خروجی نمایشدادهشود:
پس نتیجه میگیریم پایه TX باید خروجی باشد اما خروجی معمولی نه، بلکه از نوع Alternate Function output به عکسزیر توجهکنید:
خب به این صورت برنامهنویسی میکنیم:
GPIOA -> CRH &= ~ (1 << 1 );// GPIOA -> CRH &= ~ (0xFF << 4 ); GPIOA -> CRH |= (0x0BUL << 4 );// Alternate Function output
اگر مقداردهی بالا گنگ است به عکسزیر و یا به مطلب قسمت اول مراجعهکنید.
اما پایه RX را چون دریافتکننده است ورودی از نوع Input floating میکنم.
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 قرار دهیم.
(72 000 000)/(16*115200) = 39.0625
خب عددی که حاصلشده از دو قسمت ۳۹ و ۰.۰۶۲۵ است که مقدارصحیح را در [DIV_Mantissa[11:0 قرار میدهیم، اما مقدار اعشاری۰.۰۶۲۵ را باید ضرب در عدد ثابت ۱۶ کرده و در مقدار [DIV_Fraction[3:0 قراردهیم:
16*0.0625 = 1
که اگرمقدارها را تبدیل به عدد هگز کنیم ۳۹ میشود 0x027 و عدد ۱ که میشود همان 0x1 که برای نوشتن در رجیستر BRR از روشزیر استفاده میکنیم:
USART1 -> BRR = 0x0271 // 0x027 --- 0x1
بهطورمثال برای ۹۶۰۰ و فرکانس ۱۲مگ داریم:
12 000 000 /(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 که چهاربیت دارد مقدار ۰ تا ۱۵ رو میتواند در خود قرار دهد حالا اگر ضرب ۱۶ با [DIV_Fraction[3:0 یک عدد بزرگتر از ۱۵ بود یک کری به [DIV_Mantissa[11:0 اضافه میشود.
تا اینجای کار توانستیم بادریت رو فعالکنیم حالا میوریم سروقت رجیسترهای UART.
رجیستر CR1 :Control register 1UART
خب همونطورکه از عکسبالا مشاهده میکنید ۱۴بیت از ۳۲بیت آن دردسترس است که هرکدام رو که لازمباشه در ارتباط UART توضیح میدهم.
RE: بیت شماره۲ از رجیستر 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
USART1 -> CR1 |= (1UL<<2);
TE: بیت شماره۳ از رجیستر 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
USART1 -> CR1 |= (1UL<<3);
البته بعداز فعالسازی این رجیستر باید یک زمان کوتاهی رو delay درنظر بگیریم(طبق گفته دیتاشیت) When TE is set there is a 1 bit-time delay before the transmission starts :Note برای ایجاد تاخیر هم از روشزیر استفادهکنید:
For(int i = 0 ; I < 0x1000 ; i++) { _ _nop(); }
تابع ()nop هم به کامپایلر میفهماند که این تکه کد جز حلقه اصلی است(مانند کد اسمبلی) که درهنگام کامپایل و بهینهسازی کامپایلر حذفش نکند.
M: بیت شماره۱۲ از رجیستر CR1 برای مشخصکردن تعداد data بیت است. 0: 1 استارت بیت و ۸دیتا بیت. 1: 1 استارت بیت و ۹دیتا بیت.
USART1 -> CR1 &=~ (1<<12);
UE: بیت شماره ۱۳ فعالکننده کل UART است (USART enable) 0: غیرفعالکننده UART. 1: فعالکننده UART.
UART1 -> CR1 |= (1<<13);
تا اینجای کار رو داشته باشید تا در قسمت چهارم ادامشو باهم یاد بگیریم.
منبع: سیسوگ