آموزش پروژه‌ای راه‌اندازی UART در STM32 با HAL – برنامه Hello World - قسمت 22 آموزش امبدد C

embedded C 22
MasoudHD
17 بازدید
۱۴۰۴-۰۴-۱۰
10 دقیقه
  • نویسنده: Alireza Abbasi
  • درباره نویسنده: ---

خب، بیایید یک پروژه جدید ایجاد کنیم. این پروژه نسخه‌ای نسبتاً طولانی از برنامه‌ی “Hello World” است. دلیل طولانی بودن آن این است که قرار است تمام کارهایی که سیستم عامل آن را برای ما پنهان می‌کند را انجام دهد. در ابتدا فایل‌های هدر مورد نیاز که اطلاعات مربوط به UART (و بقیه‌ی قسمت‌های میکرو) را تعریف می‌کنند، وارد می‌کنیم:

سپس با تابع اصلی (main) شروع می‌کنیم:

تابع اصلی کاملاً شبیه به تابعی است که در کدهای ۹-۱ و ۹-۲ دیدیم. البته یک مورد اضافه شده است و آن راه‌اندازی تمام سخت‌افزارهایی که قرار است از آن‌ها استفاده کنیم (۱)، از جمله کتابخانه سخت‌افزار (HAL_Init)، LED قرمز (led2_Init) و پورت سریال UART (uart2_Init). برنامه امبدد  نمی‌تواند متوقف شود، بنابراین یک حلقه بی‌نهایت (۲) داریم که رشته را ارسال می‌کند (۳) و سپس به مدت نیم ثانیه می‌خوابد (۴).

یکی از اولین کارهایی که باید در مرحله‌ی بعد انجام دهیم، ایجاد یک تابع ErrorHandler است که کتابخانه HAL در صورت بروز مشکل آن را فراخوانی می‌کند. در صورت وجود خطا ما نمی‌توانیم پیام خطا چاپ کنیم، زیرا کد چاپ ما به‌درستی کار نمی‌کند، بنابراین به چشمک‌زدن LED قرمز متوسل می‌شویم.  این که خطای رخ داده را با استفاده از یک LED نمایش دهیم خیلی محدود کننده به نظر می‌رسد اما کاربرد این LED درست مانند چراغ چک موتور در خودرو می‌باشد. ما در اینجا به تابع Error_Handler نمی‌پردازیم؛ این تابع همانند blink که در فصل 3 توضیح داده شد، با یک نام جدید است.

راه‌اندازی UART

یک دستگاه سریال باید به‌سادگی برنامه‌ریزی شود. بااین‌حال، مهندسان شرکت STMicroelectronics تصمیم گرفته‌اند با ارائه‌ی ویژگی‌های اضافی، UART ساده را بهبود بخشند. در نتیجه، دستگاه سریال ساده‌ی ما حالا برای توصیف نجوه‌ی راه‌اندازی یک دفترچه‌ی راهنمای ۴۵ صفحه‌ای دارد. ما فقط می‌خواهیم از این ابزار برای ارسال کاراکتر استفاده کنیم و حتی نیازی به دریافت اطلاعات روی پورت سریال نداریم.

خوشبختانه، کتابخانه HAL تابعی به نام HAL_UART_Init فراهم کرده که بسیاری از جزئیات پیچیده را از دید ما پنهان می‌کند. اما متأسفانه، خود فراخوانی HAL_UART_Init همچنان نیاز به تنظیم برخی جزئیات دارد — نمی‌توان همه چیز را ساده‌سازی کرد. در تابع uart2_Init، باید یک ساختار مقداردهی اولیه تنظیم کنیم و سپس HAL_UART_Init را صدا بزنیم.

  1. ابتدا به سیستم می‌گوییم که از کدام UART استفاده کند .
  2. تراشه‌ی ما بیش از یک UART دارد و دومی به رابط سریال به USB متصل است (همان سریالی که ما می‌خواهیم استفاده کنیم). سپس سرعت را روی ۹۶۰۰ باود (بیت بر ثانیه) (۲) یا ۹۶0۰ کاراکتر در ثانیه تنظیم می‌کنیم.
  3. چرا نسبت ۱۰ به ۱ است؟ ما ۱ بیت شروع، ۸ بیت داده و ۱ بیت توقف داریم. تعداد بیت‌های هر کاراکتر ۸ است، زیرا C کاراکترها را در واحدهای ۸ بیتی ذخیره می‌کند. داشتن سیستم‌هایی با ۵، ۶، ۷ یا ۹ بیت در هر کاراکتر ممکن است، اما تقریباً همه از ۸ بیت استفاده می‌کنند، به جز دستگاه ارتباطی ناشنوای TDD که از ۵ بیت استفاده می‌کند. ما باید به سیستم بگوییم که از ۸ بیت استفاده می‌کنیم (۳).
  4. خط بعدی تعداد بیت‌های توقف را پیکربندی می‌کند.
  5.  که مدت زمانی توقف بین کاراکتر‌ها است. اکثر افراد از ۱ بیت توقف استفاده می‌کنند. (اگر فرستنده از ۲ و گیرنده از ۱ استفاده کند، همچنان کار می‌کند. بیت اضافی به‌عنوان زمان بیکاری بین کاراکترها تفسیر می‌شود.) دستگاه‌های سریال اولیه از یک کاراکتر ۷ بیتی و ۱ بیت پریتی (parity) استفاده می‌کردند. بیت پریتی یک روش ساده و ابتدایی برای بررسی خطا ارائه می‌دهد. ما از این ویژگی استفاده نمی‌کنیم، بنابراین پریتی را خاموش می‌کنیم.
  6. سپس فرستنده و گیرنده را فعال می‌کنیم.
  7. رابط اصلی سریال (استاندارد RS-232) دارای تعدادی خط کنترل جریان سخت‌افزاری است. روی برد ما، این خطوط سیم‌کشی نشده‌اند و ما از آن‌ها استفاده نمی‌کنیم .
یکی از ویژگی‌های پیشرفته‌ی این UART بیش نمونه‌برداری است که به گیرنده اجازه می‌دهد قبل از تصمیم‌گیری برای یک یا صفر بودن بیت، چندین بار وضعیت بیت ورودی را بررسی کند. این ویژگی گاهی اوقات در محیط‌های الکتریکی پر نویز و کابل‌های سریال با مسافت طولانی مفید است. کابل سریال ما از دو رشته تشکیل شده است که از پایین برد به بالای برد با طول حدوی 8 سانتی‌متر کشیده شده‌اند. ما به نمونه‌برداری بیش از حد نیاز نداریم، پس باید آن را خاموش کنیم (۸). در نهایت، از هیچ ویژگی پیشرفته‌ای استفاده نمی‌کنیم.

در مرحله‌ی بعد برای راه‌اندازی UART تابع HAL_UART_Init را فراخوانی می‌کنیم (۹) که برای اینکه این تابع بتواند وظیفه‌ی خود را انجام دهد نیاز به کمک دارد. پین‌های ورودی/خروجی چندمنظوره (GPIO) روی پردازنده‌ی ما می‌توانند کارهای مختلفی انجام دهند، از جمله اینکه به‌عنوان پین‌های GPIO عمل کنند. اکثر آن‌ها دارای «توابع جایگزین» هستند، به این معنی که می‌توانید آن‌ها را برای انجام کاربردهای مختلف برنامه‌ریزی کنید (پین GPIO، دستگاه USART، باس SPI، باس I2C، پین PWM و غیره). توجه داشته باشید که همه‌ی پین‌های میکروکنتلر از همه‌ی امکانات و دستگاه‌ها پشتیبانی نمی‌کنند و هر کدام ممکن است امکانات مختلفی داشته باشند. در نهایت، HAL_UART_Init تابع HAL_UART_MspInit را فراخوانی می‌کند که پین‌ها را برای UART راه‌اندازی می‌کند (تابع  HAL_UART_MspInit همان کمکی است که تابع HAL_UART_Init برای انجام کار خود نیاز دارد):

برای اینکه تابع HAL_UART_MspInit کار خود را به درستی انجام دهد ما باید آن را به نسبت نیاز خود تنظیم کنیم .به طور پیش‌فرض، دو پینی که دستگاه سریال ما را راه‌اندازی می‌کنند، به نام PA2 و PA3 بوده که در‌واقع همان پین‌های GPIO هستند. ما باید به سیستم بگوییم که از تابع جایگزین پین‌ها استفاده کند و آن‌ها را به پین‌های دستگاه سریال تبدیل کند.

تابع HAL_UART_MspInit ما شبیه به کد راه‌اندازی پین GPIO است که برای «blink» استفاده کردیم، اما با برخی تفاوت‌های جزئی:

این تابع با بررسی اینکه از کدام USART استفاده می‌کنیم شروع می‌شود. ما در این کد فقط USART2 را راه‌اندازی می‌کنیم (۱). سپس کلاک قسمت مربوط به USART2 را فعال می‌کنیم (۲). در مرحله بعد، پین‌های GPIO را پیکربندی می‌کنیم (۳) (ما قبلاً این کار را در برنامه‌ی blink انجام داده‌ایم) که به تراشه می‌گوید PA2/PA3 پین‌های GPIO نیستند؛ بلکه باید به USART2 متصل شوند.

ارسال یک کاراکتر

برای ارسال کاراکترها با پورت سریال، از تابع myPutchar استفاده می‌کنیم. USARTUSART یک وسیله‌ی I/O با نگاشت در حافظه (Memory-Mapped I/O) است. برای فرستادن یک کاراکتر، باید آن را به یک مکان حافظه خاص (یک رجیستر) اختصاص دهیم (بنویسیم) و سپس آن کاراکتر از طریق سیم خارج می‌شود.

در کد زیر، رجیستر TDR (Transmitter Data Register) را برای ارسال کاراکتر ch به USART تنظیم می‌کنیم:

بااین‌حال، برای ارسال صحیح کاراکتر، باید زمان‌بندی دقیقی را رعایت کنیم. برای این کار، از کد زیر استفاده می‌کنیم:

رجیستری که کاراکتر مورد نظر برای ارسال را در آن می‌نویسیم، رجیستر انتقال‌دهنده داده (TDR: transmit data register) نامیده می‌شود. اگر درحالی‌که یک کاراکتر منتقل می‌شود، رجیستر TDR را بنویسیم، کاراکتر جدید کل کاراکتر قبلی را پاک می‌کند و باعث ایجاد خطا و سردرگمی در دریافت اطلاعات می‌شود. برای ارسال حروف a، b، c، کد زیر استفاده می‌شود:

در این کد، مشکل اینجاست که زمان نوشتن کاراکتر b  رجیستر TDR، کاراکتر a هنوز به طور کامل ارسال نشده است. در نتیجه، بخشی از اطلاعات a پاک شده و با b جایگزین می‌شود. به همین ترتیب، در زمان نوشتن c  رجیستر TDR، بخشی از اطلاعات b پاک شده و با c جایگزین می‌شود.

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

این نوع زمان‌بندی به‌خصوص زمانی که می‌خواهیم بین کاراکترها کد اجرا کنیم، کار سختی است. تراشه STM32 برای هر چیزی بیتی دارد، از جمله «TDR خالی است، اکنون می‌توانید کاراکتر دیگری بنویسید». این بیت در رجیستری به نام رجیستر وقفه و وضعیت (ISR) قرار دارد که دارای تعدادی بیت برای نشان‌دادن وضعیت دستگاه است.

شکل ۹-۲ نموداری این رجیستر را از راهنمای مرجع STM32F030R8

محتویات رجیستر وقفه و وضعیت

محتویات رجیستر وقفه و وضعیت

در تصویر بالا  بیت TXE (بیت 0) برای ما جالب است . HAL بیت TXE را با استفاده از نام UART_FLAG_TXE تعریف می‌کند. قبل از اینکه بتوانیم بدون تداخل با کاراکتر در حال ارسال، داده‌ای را به TDR ارسال کنیم، باید منتظر بمانیم تا بیت TXE پاک شود (صفر شود). توجه کنید که ISR توسط خود سخت‌افزار کنترل می‌شود و ما نمیتوانیم مقداری در آن بنویسیم و فقط امکان خواندن از آن را داریم که در اینجا هم TXE را میخوانیم.

بااین‌حال، uartHandle.Instance->ISR یک آدرس حافظه‌ی خاص است که به‌ یک سخت‌افزار الکتریکی (USART) متصل است. وضعیت دستگاه سریال زمانی تغییر می‌کند که چیزی مانند تکمیل انتقال یک کاراکتر اتفاق بیفتد، و زمانی که این اتفاق می‌افتد، محتویات uartHandle.Instance->ISR نیز توسط خود سخت‌افزار تغییر می‌کند.

توجه: فیلد ISR به‌صورت volatile اعلام می‌شود تا به کامپایلر C بگوید که این مقدار می‌تواند به طور جادویی در هر زمانی به روشی که خارج از کنترل کامپایلر است، تغییر کند.

حالا اگر سعی کنید uartHandle.Instance->ISR را با استفاده از دیباگر بررسی کنید، به نظر می‌رسد که فلگ UART_FLAG_TXE همیشه مقدار یک را دارد. دلیل اینکه ما این بیت را همیشه یک میبینیم این است که در‌واقع این بیت با سرعت 1/960 ثانیه توسط سخت‌افزار پاک می‌شود که این زمان از دید کامپیوتر زمان زیادی است اما از دید انسان آن‌قدر زمان کمی است که حتی دیده هم نمی‌شود.

برای اینکه بفهمیم در پشت صحنه چه اتفاقی می‌افتد، یک دستور بی‌استفاده به کد اضافه کرده‌ایم:

این دستورالعمل مقدار بیت UART_FLAG_TXE را از ISR می‌خواند و آن را در result ذخیره می‌کند. حالا ممکن است مقدار (uartHandle.Instance->ISR & UART_FLAG_TXE) در طول اجرای برنامه به صورت سخت‌افزاری تغییر کند، اما مقدار result که در همین لحظه محاسبه شده است (در زمان ارسال کاراکتر) تا پایان ثابت می‌ماند.

شما می‌توانید در دیباگر به result نگاه کنید و ببینید که مقدار بیت در ابتدای حلقه چه بوده است. عبارت عجیبی در کد به چشم شما خواهد خورد:

در GCC (کامپایلر GNU)، وقتی متغیری تعریف می‌کنید ولی در ادامه استفاده‌اش نمی‌کنید (معمولاً جهت استفاده در حالت دیباگ این را تعریف می‌کنیم)، کامپایلر اخطار (warning) می‌دهد.

رشته‌ای که برای “Hello World” ارسال می‌کنیم با \r\n (بازگشت به ابتدای خط، پرش به خط بعدی) تمام می‌شود. در برنامه‌ی اصلی “Hello World” ما از فصل ۱، سیستم‌عامل خروجی (stream) را ویرایش کرد و \n را برای ما به \r\n تغییر داد. ما سیستم‌عاملی نداریم، بنابراین باید همه کار را خودمان انجام دهیم.

کد 9-3 نسخه‌ی سریال کامل برنامه‌ی “Hello World” ما را شامل می‌شود.

کد 9-3: برنامه 08.serial

اطلاعات
17
0
0
لینک و اشتراک
profile

Alireza Abbasi

متخصص الکترونیک

مقالات بیشتر
slide

پالت | بازار خرید و فروش قطعات الکترونیک

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

آیسی | موتور جستجوی قطعات الکترونیک

سامانه آی سی سیسوگ (Isee) قابلیتی جدید و کاربردی از سیسوگ است. در این سامانه سعی شده است که جستجو، انتخاب و خرید مناسب تر قطعات برای کاربران تسهیل شود. جستجو در آیسی
family

سیسوگ‌شاپ | فروشگاه محصولات Quectel

فروشگاه سیسوگ مجموعه ای متمرکز بر تکنولوژی های مبتنی بر IOT و ماژول های M2M نظیر GSM، GPS، LTE، NB-IOT، WiFi، BT و ... جایی که با تعامل فنی و سازنده، بهترین راهکارها انتخاب می شوند. برو به فروشگاه سیسوگ
family

سیسوگ فروم | محلی برای پاسخ پرسش‌های شما

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

سیکار | اولین مرجع متن باز ECU در ایران

بررسی و ارائه اطلاعات مربوط به ECU (واحد کنترل الکترونیکی) و نرم‌افزارهای متن باز مرتبط با آن برو به سیکار
become a writer

نویسنده شو !

سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.

ارسال مقاله
become a writer

نویسنده شو !

سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.

ارسال مقاله
خانواده سیسوگ
سیسوگ‌شاپ

فروشگاه محصولات Quectel

پالت
سیسوگ فروم

محلی برای پاسخ پرسش‌های شما

سیسوگ جابز
سیسوگ
سیسوگ فروم
سی‌کار

اولین مرجع متن باز ECU در ایران

سیسوگ مگ
آی‌سی

موتور جستجوی قطعات الکترونیکی

سیسوگ آکادمی
پالت

بازار خرید و فروش قطعات الکترونیک

دیدگاه ها

become a writer

نویسنده شو !

سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.

ارسال مقاله
become a writer

نویسنده شو !

سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.

ارسال مقاله