فرض کنید میخواهید متنی را درون یک نرمافزار ترمینال در رایانه تایپ کنید و میکروکنترلر بدون نیاز به هیچ تراشهٔ مبدل جانبی نظیر چیپهای واسط USB به UART، همان بایتها را مستقیماً از طریق کابل USB دریافت کرده و عیناً به رایانه بازگرداند. در گذشته اتصال میکروکنترلر به رایانه اغلب از طریق پورت فیزیکی سریال RS-232 یا تراشههای واسط انجام میگرفت، اما در رایانههای مدرن درگاههای فیزیکی COM عملاً جای خود را به گذرگاه همهمنظوره و سریع USB دادهاند. میکروکنترلر STM32F303CBT6 دارای کنترلر اختصاصی USB Device در کلاس Full-Speed است که امکان اتصال مستقیم به خطوط تفاضلی داده را فراهم میسازد. در این درس، ابتدا مفاهیم پایهٔ گذرگاه USB، ساختار توصیفگرها و نقاط پایانی را بررسی میکنیم. سپس الزامات کلاک و مدار فیزیکی را شرح داده و با استفاده از کتابخانهٔ رسمی ST USB Device، کلاس پورت سریال مجازی (CDC) را بهصورت یک سامانهٔ پژواک (Echo) پیادهسازی مینماییم. این پروژه با ابزار STM32CubeMX نسخه 6.14.0 و پکیج STM32CubeF3 نسخه 1.11.6 پیکربندی شده و با زنجیرهٔ ابزار Arm GNU Toolchain 13.2.1 با گزینههای -O0 -g3 -Wall -Werror برای تمامی 30 واحد ترجمه بهصورت محلی و بدون هیچ هشداری کامپایل شده است؛ راستیآزمایی این درس در سطح کامپایل تمیز و آزمونهای ساختار باینری انجام گرفته و سنجش سیگنال با اسیلوسکوپ یا آزمون سختافزاری زنده در این مرحله انجام نشده است.
وضعیت اعتبارسنجی: کامپایلشده؛ پروژهٔ همراه با گزینههای -Wall -Werror بدون خطا و هشدار ساخته شده است. اجرای روی برد، آزمون رابط گرافیکی و اندازهگیری فیزیکی انجام نشدهاند. شکلهای مفهومی، مدلهای محاسباتی و مقادیر مورد انتظار، نتیجهٔ اندازهگیری نیستند.
پیشنیاز: درس 9 و درس 12. سختافزار مبنا: STM32F303CBT6 در بستهٔ LQFP48 روی GB100ST V2.0. قطعات جانبی، تغذیه و روش اتصال در متن همین درس توضیح داده شدهاند.
مفاهیم بنیادین گذرگاه USB Full-Speed و ساختار پروتکل
برای درک شیوهٔ کارکرد گذرگاه USB، مقایسهٔ آن با ارتباط سریال آسنکرون (UART) نقطهٔ شروع مناسبی است. در پروتکل UART دو خط مجزای فرستنده و گیرنده وجود دارد، اما طرفین ارتباط باید از پیش بر سر پارامترهایی چون نرخ تبادل (Baud Rate) و قالببندی بیتها به توافق رسیده باشند؛ زیرا هیچ سازوکار استانداردی در سطح سختافزار برای شناسایی خودکار دستگاه یا اطلاعرسانی قابلیتها به سیستمعامل رایانه تعبیه نشده است. از سوی دیگر، درگاههای فیزیکی COM در بیشتر رایانههای امروزی حذف شدهاند. گذرگاه USB (مخفف Universal Serial Bus) برای برطرف کردن این محدودیتها بهصورت یک شبکهٔ چندمنظوره، بستهمحور و کاملاً تحت کنترل میزبان (Host-Controlled) طراحی شده است؛ به این معنا که تمامی تراکنشها و زمانبندی ارسال و دریافت فریمها تنها با آغازگری میزبان هدایت میشوند.
سیگنالدهی تفاضلی و نرخ انتقال Full-Speed
در استاندارد USB 2.0 چند ردهٔ سرعتی تعریف شده است: سرعت پایین (Low-Speed با نرخ 1.5 Mbit/s)، سرعت کامل (Full-Speed با نرخ 12 Mbit/s) و سرعت بالا (High-Speed با نرخ 480 Mbit/s). واحد سختافزاری USB داخلی در میکروکنترلر STM32F303CBT6 دارای لایهٔ فیزیکی توکار (Embedded PHY) در کلاس Full-Speed است و با نرخ اسمی سیگنالدهی 12 Mbit/s روی دو خط تفاضلی با نامهای D+ و D- کار میکند. در روش ارسال تفاضلی، ولتاژ یک خط نسبت به خط دیگر سنجیده میشود؛ بنابراین نویزهای الکترومغناطیسی محیطی که همزمان به هر دو سیم نزدیک به هم القا میشوند (نویز حالت مشترک)، در صورت طراحی و سیمبندی مناسب تا حد مطلوبی توسط گیرنده حذف میگردند. توجه داشته باشید که نرخ 12 Mbit/s بیانگر سرعت خام بیتها روی سیم است؛ پهنای باند مؤثر تبادل داده به دلیل سربرگ بستهها، فاصلههای زمانی میان ارسال و دریافت و فرآیند پرسش متناوب میزبان (Polling) همواره مقداری کمتر از این نرخ اسمی خواهد بود.
هرم توصیفگرها و فرآیند شناسایی دستگاه (Enumeration)
هنگامی که یک قطعهٔ USB به رایانه متصل میشود، سیستمعامل باید از ماهیت دستگاه، نیازمندیهای توان مصرفی و کلاس راهانداز (Driver) مناسب آن آگاه شود. این زنجیره از تبادل اطلاعات، فرآیند شمارش یا شناسایی (Enumeration) نامیده میشود. در این مرحله، میکروکنترلر دادههای ساختاریافتهای به نام توصیفگر (Descriptor) را در پاسخ به درخواستهای کنترلی میزبان ارسال میکند:
- توصیفگر دستگاه (Device Descriptor): ساختار اصلی در بالاترین سطح است که نگارش استاندارد USB، اندازهٔ بافر نقطهٔ پایانی صفر، شناسهٔ سازنده (VID) و شناسهٔ محصول (PID) را اعلام میکند. در این پروژه از شناسههای پیشفرض نمایشی شرکت ST یعنی VID=0x0483 و PID=0x5740 استفاده شده است.
- توصیفگر پیکربندی (Configuration Descriptor): ویژگیهای تأمین توان را مشخص میسازد؛ برای نمونه در پروژهٔ حاضر پارامتر USBD_SELF_POWERED برابر 0 تنظیم شده که نشاندهندهٔ تغذیه از گذرگاه (Bus-Powered) است و حداکثر بودجهٔ توان درخواستی دستگاه 100 mA اعلام میشود.
- توصیفگر واسط (Interface Descriptor): وظایف و کلاس عملکردی دستگاه را تعریف میکند. در پورت سریال مجازی، دو واسط ارتباطی (Communication Interface 0) و داده (Data Interface 1) شکل میگیرند.
- توصیفگر نقطهٔ پایانی (Endpoint Descriptor): مسیرهای مستقل تبادل داده شامل شمارهٔ نقطهٔ پایانی، جهت انتقال (از دید رایانه میزبان)، نوع انتقال و حداکثر اندازهٔ هر بسته (MaxPacketSize) را معرفی مینماید.
کلاس ارتباطی CDC و اتصال پورت سریال مجازی
کلاس ادوات ارتباطی (CDC یا Communication Device Class) با زیرکلاس ACM (مدل کنترل انتزاعی) امکانی فراهم میسازد تا سیستمعامل میزبان، دستگاه USB را بهعنوان یک پورت سریال مجازی (Virtual COM Port) شناسایی کند. سیستمعاملهای رایج عموماً دارای درایور توکار برای این کلاس استاندارد هستند و نیازی به توسعهٔ درایور اختصاصی وجود ندارد؛ با این حال نام دقیق درگاه تخصیصیافته (مانند COM5 در ویندوز یا ttyACM0 در لینوکس) و فرآیند اتصال به پیکربندی سیستمعامل میزبان بستگی دارد. پس از شناسایی، نرمافزارهای ترمینال میتوانند دادهها را مانند یک درگاه سریال سنتی ارسال و دریافت کنند، در حالی که تبادل فیزیکی در قالب بستههای Full-Speed هدایت میشود.

الزامات سختافزاری F303: کلاک 48 MHz و مقاومت خارجی D+
پیش از اجرای نرمافزار، لایهٔ فیزیکی واحد USB در میکروکنترلر STM32F303CBT6 به دو الزام سختافزاری قطعی وابسته است: تأمین کلاک اسمی 48 MHz برای کنترلر جانبی و اتصال مدار مقاومت بالاکش (Pull-up) خارجی بر روی پایهٔ D+.
تولید کلاک اسمی 48 MHz در درخت کلاک
کنترلر USB Full-Speed برای زمانبندی صحیح پردازش بیتها و همگامسازی منطق داخلی، نیازمند کلاک اسمی 48 MHz با تلرانس فرکانسی استاندارد است. نوسانساز داخلی RC (HSI) به دلیل وابستگی به دما و تغییرات ولتاژ، پایداری لازم برای تضمین عملکرد مطمئن USB در تمامی شرایط کاری را ارائه نمیدهد. در این پروژه، کلاک اسمی مورد نیاز از یک کریستال خارجی 8 MHz (HSE) تولید میشود. در درخت کلاک، فرکانس 8 MHz کریستال وارد مدار ضربکنندهٔ PLL شده و با ضریب 9 فرکانس سیستم (SYSCLK) را به 72 MHz میرساند. سپس در واحد RCC، مقسم اختصاصی USB فعال شده و با تقسیم فرکانس 72 MHz بر 1.5، فرکانس اسمی 48 MHz حاصل میگردد. قطعه کد زیر از تابع SystemClock_Config در فایل Core/Src/main.c این پیکربندی را نشان میدهد:
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB;
PeriphClkInit.USBClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
توجه داشته باشید که رابطهٔ ریاضی تقسیم 72 بر 1.5 صرفاً یک محاسبهٔ پیکربندی رجیستری است و به معنای اندازهگیری فیزیکی دقت کریستال برد تلقی نمیشود. همچنین باید سه مقیاس فرکانسی را به روشنی از یکدیگر تفکیک کرد: فرکانس کلاک واحد جانبی (48 MHz اسمی)، نرخ سیگنالدهی فیزیکی خطوط تفاضلی (12 Mbit/s) و نرخ نمادین مجازی در نرمافزارهای ترمینال (مانند 115200 bps).
سیمبندی مدار خارجی و اتصال مقاومت بالاکش 1.5 kΩ
برد آموزشی GebraBit GB100ST V2.0 فاقد کانکتور فیزیکی تعبیهشدهٔ USB است. برای انجام آزمایش، استفاده از یک برد مبدل ساده (Breakout) دارای کانکتور مادگی USB 2.0 از نوع Type-B یا Micro-B پیشنهاد میشود؛ استفاده از سوکتهای خام Type-C بدون مدار مقاومتی CC ممکن است فرآیند اتصال را با پیچیدگی مواجه سازد. پایههای تفاضلی USB در میکروکنترلر STM32F303CBT6 (بستهٔ LQFP48) بر روی پایه 32 (PA11 بهعنوان USB_DM) و پایه 33 (PA12 بهعنوان USB_DP) قرار دارند.
طبق مشخصات استاندارد USB، رایانه برای تشخیص اتصال یک دستگاه با سرعت Full-Speed، منتظر بالا کشیده شدن ولتاژ خط D+ میماند. از آنجا که لایهٔ فیزیکی خانوادهٔ STM32F303xB/C فاقد مقاومت بالاکش داخلی قابل برنامهریزی روی خط D+ است، استفاده از یک مقاومت خارجی 1.5 kΩ الزامی است.
نکتهٔ ایمنی سیمبندی: مقاومت بالاکش 1.5 kΩ باید دقیقاً میان خط D+ (پایه PA12) و ولتاژ تغذیهٔ تثبیتشدهٔ 3.3V برد (VDD) قرار گیرد. اتصال نادرست این مقاومت به خط 5V گذرگاه (VBUS) رفتاری خارج از مشخصات کاری لایهٔ فیزیکی بوده و میتواند به درایورهای داخلی فرستنده/گیرنده روی پایههای تفاضلی آسیب جدی برساند.
در این پروژه، دستگاه طبق توصیفگر بهصورت تغذیهشونده از گذرگاه (Bus-Powered) تعریف شده است. برای این مدار آزمایشی، خط 5V کابل USB (VBUS) تنها باید به ورودی رگولاتور 5V برد متصل شود تا ولتاژ کاری 3.3V مدار را تأمین کند. نباید هیچ منبع تغذیهٔ دوم یا آداپتور جداگانهای بهصورت همزمان متصل باشد؛ اتصال پروگرم ST-LINK نیز تنها برای ارسال برنامه و اشتراک زمین استفاده میشود و منبع تغذیهٔ دوم به شمار نمیآید. مدار باید به گونهای باشد که در صورت قطع VBUS، تغذیهٔ برد و مقاومت بالاکش نیز خاموش شود. توجه نمایید که سیمبندی موقت روی بردبُرد جایگزین طراحی مسیرهای امپدانس تفاضلی و مدارهای حفاظتی ESD در بردهای تجاری نیست.


بررسی وضعیت حافظه اختصاصی میان USB و CAN در خانواده STM32F303
یکی از نکات حائز اهمیت در بررسی کنترلرهای تعبیهشده، مسئلهٔ منابع حافظه و قابلیت استفادهٔ همزمان از کنترلرهای ارتباطی است. در خانوادهٔ قدیمیتر STM32F103، واحدهای USB و CAN از یک بلوک حافظهٔ اشتراکی 512 بایتی (PMA یا Packet Buffer Memory) استفاده میکردند که به دلیل تداخل کامل دسترسی به حافظه، امکان فعالسازی همزمان هر دو رابط سختافزاری وجود نداشت. این سابقهٔ تاریخی در برخی مراجع موجب ایجاد یک ادعای نادرست شده است که گویی در خانوادهٔ STM32F303xB/C نیز حافظهٔ این دو ماژول مشترک است.
تفکیک کامل حافظه در اسناد رسمی ST
مستندات رسمی سازنده، بهویژه جدول 6 در یادداشت کاربردی AN4228 (صفحهٔ 12 چاپی) و جدول 3 در یادداشت کاربردی AN4879، حقیقت فنی را به وضوح اثبات میکنند: در میکروکنترلرهای STM32F303xB/C و STM32F302xB/C، حافظهٔ 512 بایتی بافر بستهٔ واحد USB کاملاً مستقل و مجزا از حافظهٔ اختصاصی 512 بایتی کنترلر CAN طراحی شده است. بنابراین فرضیهٔ محدودیت اشتراک فیزیکی بلوک حافظه میان این دو ماژول در سری F303xB/C نادرست است.
تداخل پایهها و اشتراک بردار وقفه NVIC
با وجود تفکیک بلوکهای حافظهٔ PMA، طراح برای بهکارگیری هر دو ماژول با دو مسئلهٔ دیگر مواجه است:
- همپوشانی پایههای پیشفرض: پایههای پیشفرض سختافزاری USB (PA11 و PA12) با پایههای پیشفرض CAN (یعنی CAN_RX و CAN_TX) مشترک هستند. در صورتی که نیاز به استفادهٔ همزمان باشد، پایههای CAN باید از طریق رجیسترهای Alternate Function به پایههای جایگزین نظیر PB8 و PB9 با نگاشت AF9 منتقل شوند (خانوادهٔ F3 از سامانهٔ نوین AF استفاده میکند و دستور نگاشت سراسری AFIO سری F1 در آن کاربرد ندارد).
- اشتراک خط بردار وقفه: در جدول بردار وقفهٔ میکروکنترلر، وقفهٔ اولویت پایین USB با وقفهٔ دریافت کانال صفر CAN در قالب یک خط وقفهٔ مشترک با شناسهٔ
USB_LP_CAN_RX0_IRQn(شماره وقفه 20) به NVIC متصل است. اشتراک نام بردار وقفه دلیلی بر اشتراک حافظه نیست، بلکه تنها نیازمند تفکیک پرچمها در روتین وقفه در صورت فعالسازی هر دو خواهد بود.
در پروژهٔ این درس، برای سادهسازی پیادهسازی و جلوگیری از پیچیدگیهای آزمایشی، کنترلر CAN غیرفعال نگه داشته شده است و کارکرد همزمان دو واحد مورد آزمایش قرار نگرفته است. کل فضای 512 بایتی حافظهٔ بسته (PMA) که روی گذرگاه APB1 قرار داشته و کاملاً مستقل از حافظهٔ اصلی SRAM به ظرفیت 32 KiB و حافظهٔ CCM به ظرفیت 8 KiB است، به بافرهای نقاط پایانی اختصاص یافته است. این نگاشت بر مبنای بازههای گسسته (بسته در آغاز و باز در انتها) به شرح زیر پیکربندی شده است:
- جدول توصیفگر بافر (BTABLE): در بازهٔ [0x00, 0x18) با طول 24 بایت برای سه نقطهٔ پایانی 0 تا 2
- نقطه پایانی EP0 OUT: در بازهٔ [0x18, 0x58) با طول 64 بایت
- نقطه پایانی EP0 IN: در بازهٔ [0x58, 0x98) با طول 64 بایت
- نقطه پایانی EP1 IN: در بازهٔ [0xC0, 0x100) با طول 64 بایت
- نقطه پایانی EP2 IN: در بازهٔ [0x100, 0x108) با طول 8 بایت
- نقطه پایانی EP1 OUT: در بازهٔ [0x110, 0x150) با طول 64 بایت

راهاندازی پورت سریال مجازی (CDC) با کتابخانه USB Device
برای پیادهسازی استک پروتکل پورت سریال مجازی، از کتابخانهٔ استاندارد ST USB Device Library نسخه 2.5.3 متصل به درایورهای HAL پکیج CubeF3 استفاده شده است. تعامل با برنامهٔ کاربر از طریق توابع واسط فایل USB_DEVICE/App/usbd_cdc_if.c انجام میپذیرد.
نگاشت نقاط پایانی (Endpoints) در ساختار CDC ACM
کلاس CDC-ACM در این پروژه چهار مسیر مجزای تبادل بسته بر روی نقاط پایانی تعریف میکند:
- نقطه پایانی کنترلی (EP0 دوطرفه): برای شناسایی دستگاه، ارسال توصیفگرها و دریافت درخواستهای کنترلی سیستمعامل نظیر SET_LINE_CODING و SET_CONTROL_LINE_STATE استفاده میشود. سیگنالهای کنترلی مانند DTR و RTS درخواستهای کنترلی دریافتی از سوی میزبان هستند که روی همین نقطهٔ پایانی 0 تبادل میشوند.
- نقطه پایانی وقفه (Interrupt IN روی آدرس 0x82): با حداکثر طول بسته 8 بایت، در توصیفگر CDC برای ارسال اعلانها (Notifications) نظیر وضعیت خطوط سریال به میزبان پیشبینی شده است. در پیادهسازی این برنامهٔ پژواک، هیچ اعلانی روی نقطهٔ پایانی 0x82 ارسال نمیشود.
- نقطه پایانی داده خروجی (Bulk OUT روی آدرس 0x01): با حداکثر طول بسته 64 بایت، دادههایی را که کاربر در رایانه ارسال کرده است از میزبان دریافت میکند.
- نقطه پایانی داده ورودی (Bulk IN روی آدرس 0x81): با حداکثر طول بسته 64 بایت، دادههای ارسالی از میکروکنترلر را به رایانه منتقل میسازد.
مفهوم پارامترهای مجازی Line Coding
هنگامی که در نرمافزار ترمینال رایانه تنظیماتی نظیر Baud Rate برابر 115200، بیت توقف 1 و بدون توازن (8-N-1) انتخاب میشود، میزبان درخواستی کنترلی به نام SET_LINE_CODING برای میکروکنترلر ارسال میکند. از آنجا که در این پروژه هیچ سختافزار فیزیکی UART به کار گرفته نشده است، این تنظیمات تأثیری بر سرعت یا نحوهٔ سیگنالدهی 12 Mbit/s کابل USB ندارند. ساختار 7 بایتی Line Coding صرفاً در متغیرهای حافظه ذخیره شده و در پاسخ به درخواست GET_LINE_CODING بازگردانده میشود تا سازگاری نرمافزاری با ترمینال حفظ گردد.
مدیریت طول عمر بافر و خطای USBD_BUSY
تابع CDC_Transmit_FS دادهها را بهصورت غیرهمزمان به کتابخانهٔ USB تحویل میدهد؛ به این معنا که تابع بیدرنگ و پس از مقداردهی آدرس بافر برمیگردد و ارسال فیزیکی بر عهدهٔ کنترلر سختافزاری بر روی فریمهای بعدی گذرگاه است. در این کتابخانه هیچ بافر صف حلقوی (FIFO) داخلی یا کپیبرداری داده در کار نیست، بلکه تنها اشارهگر بافر به تابع ارسال معرفی میشود:
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
{
uint8_t result = USBD_OK;
/* USER CODE BEGIN 7 */
USBD_CDC_HandleTypeDef *hcdc=(USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;
if (hUsbDeviceFS.dev_state!=USBD_STATE_CONFIGURED || hcdc==NULL) return USBD_FAIL;
if (hcdc->TxState!=0U) return USBD_BUSY;
USBD_CDC_SetTxBuffer(&hUsbDeviceFS,Buf,Len);
result=USBD_CDC_TransmitPacket(&hUsbDeviceFS);
/* USER CODE END 7 */
return result;
}
در این تابع، اگر ارسال بستهٔ قبلی هنوز به پایان نرسیده باشد، شرط hcdc->TxState != 0U برقرار بوده و تابع مقدار USBD_BUSY را بازمیگرداند. این خطا به خودی خود بافر قبلی را مخدوش نمیکند، بلکه اعلام میدارد که درخواست جدید پذیرفته نشده است. از سوی دیگر، چون سختافزار حافظهٔ RAM را قفل یا محافظت نمیکند، اگر برنامه محتوای بافر ارسال را پیش از صفر شدن TxState تغییر دهد، بایتهای در حال ارسال مخدوش خواهند شد. علاوه بر این، در نسخه 2.5.3 کتابخانهٔ USB Device برای کلاس CDC هیچ تابع فراخوان اتمام ارسال (Transmit Complete Callback) در لایهٔ برنامه تعبیه نشده است؛ بنابراین پایش اتمام ارسال باید با بررسی TxState در حلقهٔ اصلی انجام شود.

آزمایش عملی: پیادهسازی پورت سریال مجازی و پژواک داده (Echo)
برای آزمایش عملکرد ارتباط دوطرفه، یک سامانهٔ پژواک داده (Echo) پیادهسازی شده است تا هر کاراکتر یا متنی که از سمت رایانه فرستاده میشود، بدون تغییر دریافت شده و مجدداً به رایانه بازگردانده شود.
صندوق پستی تکخانهای 64 بایتی و کنترل پسفشار (Backpressure)
تابع CDC_Receive_FS در متن وقفهٔ دریافت بسته فراخوانی میشود. برای جلوگیری از پیچیدگیهای سیستمعامل بلادرنگ و پرهیز از تداخل، از الگوی صندوق پستی تکخانهای با کنترل پسفشار سختافزاری بهره گرفته شده است:
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
/* USER CODE BEGIN 6 */
if (*Len>sizeof(echo_buffer) || echo_phase!=0U) {
++g_rx_errors; return USBD_FAIL;
}
if (*Len==0U) {
USBD_CDC_SetRxBuffer(&hUsbDeviceFS,UserRxBufferFS);
return (int8_t)USBD_CDC_ReceivePacket(&hUsbDeviceFS);
}
memcpy(echo_buffer,Buf,*Len);
echo_length=(uint16_t)*Len;
++g_rx_packets; g_rx_bytes+=*Len;
echo_phase=1U;
/* Do not rearm OUT here: keep the mailbox stable until IN completes. */
return USBD_OK;
/* USER CODE END 6 */
}
نکتهٔ اساسی در این تابع آن است که در انتهای دریافت بستهٔ معتبر، نقطهٔ پایانی OUT دوباره مسلح نمیشود (دستور USBD_CDC_ReceivePacket فراخوانی نمیگردد). تا زمانی که بستهٔ دریافتی در حلقهٔ اصلی ارسال نشده و پاسخ آن پایان نیافته باشد، سختافزار USB میکروکنترلر به هر بستهٔ خروجی بعدی رایانه پاسخ NAK میدهد. این سازوکار استاندارد سختافزاری (Backpressure) بدون نیاز به حافظهٔ اضافه مانع از سرریز بافر میشود. البته اگر یک بسته با طول صفر (*Len == 0U) دریافت گردد، تابع بیدرنگ دریافت را مسلح میکند تا تبادل متوقف نشود.
حلقهٔ اصلی و نظارت بر ارسال با تابع USB_EchoPoll
تابع USB_EchoPoll در حلقهٔ اصلی برنامه قرار گرفته و فرآیند ارسال و آزادسازی بافر را مدیریت میکند:
void USB_EchoPoll(void)
{
uint32_t saved_primask=__get_PRIMASK();
__disable_irq();
USBD_CDC_HandleTypeDef *hcdc=(USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;
if (hUsbDeviceFS.dev_state==USBD_STATE_CONFIGURED && hcdc!=NULL) {
if (echo_phase==1U) {
uint8_t status=CDC_Transmit_FS(echo_buffer,echo_length);
if (status==USBD_OK) echo_phase=2U;
else if (status==USBD_BUSY) ++g_tx_busy;
else ++g_tx_failures;
} else if (echo_phase==2U && hcdc->TxState==0U) {
++g_echo_completed;
echo_phase=0U; echo_length=0U;
USBD_CDC_SetRxBuffer(&hUsbDeviceFS,UserRxBufferFS);
if (USBD_CDC_ReceivePacket(&hUsbDeviceFS)==USBD_OK) ++g_rx_rearms;
else ++g_rx_errors;
}
}
__set_PRIMASK(saved_primask);
}
در این تابع، ابتدا با خواندن رجیستر PRIMASK و غیرفعالسازی موقت وقفهها، از ایجاد شرایط مسابقه (Race Condition) میان وقفهٔ USB و حلقهٔ اصلی جلوگیری به عمل آمده و در پایان وضعیت قبلی PRIMASK بازیابی میشود. در فاز 1، تابع تلاش میکند داده را با CDC_Transmit_FS ارسال کند؛ در صورت پذیرش، فاز به 2 تغییر مییابد و در صورت شلوغ بودن خط، شمارندهٔ g_tx_busy افزایش یافته و ارسال در دور بعدی حلقهٔ اصلی تکرار میشود. در فاز 2، برنامه در دورهای بعدی حلقه منتظر میماند تا TxState برابر صفر شود (در درایور ST CDC نسخهٔ 2.5.3 پروژه، این وضعیت پس از پایان انتقال IN و در صورت طول غیرصفرِ مضرب 64، پس از ارسال ZLP حاصل میشود). پس از تکمیل ارسال، دریافت مجدداً مسلح میشود. صفر شدن TxState تنها نشاندهندهٔ ارسال بسته روی کابل به میزبان است، نه الزاماً خوانده شدن داده توسط نرمافزار کاربر روی رایانه. اگر هنگام روشن ماندن میکروکنترلر، ریست گذرگاه USB یا پایان پیکربندی کلاس به اجرای CDC_DeInit منجر شود، دادهٔ معلق باطل میشود و شمارندهٔ RAM با نام g_usb_session_discards افزایش مییابد. این شمارنده ماندگار نیست. در مدار تغذیهشونده از USB این درس، جدا کردن کابل برق برد را نیز قطع میکند؛ اجرای CDC_DeInit هنگام قطع برق تضمین نمیشود و پس از اتصال دوباره، دادهها و شمارندهها از نو مقداردهی میشوند.
مراحل تجربی و مشاهدات مورد انتظار
- در وضعیت خاموش، مدار را با دقت متصل کنید: پایه PA11 به D-، پایه PA12 به D+، مقاومت 1.5 kΩ میان PA12 و 3.3V (VDD برد)، و خط زمین به GND متصل گردد. کابل VBUS 5V تنها ورودی رگولاتور 5V برد را تغذیه کند و از اتصال منبع تغذیهٔ دوم خودداری نمایید.
- پایهٔ BOOT0 را به زمین متصل کنید. کابل USB را برای تأمین توان برد وصل کرده و پیش از پروگرم، وجود ولتاژ 3.3V را بررسی کنید. سپس فایل باینری کامپایلشده را از طریق SWD با ST-LINK روی فلش بنویسید. زمین مشترک و ورودی مرجع ولتاژ پروب (VTref) مطابق راهنمای آن متصل باشند؛ VTref منبع تغذیه نیست و خروجی تغذیهٔ اضافی پروب نباید به برد وصل شود.
- پس از پروگرم، در صورت نیاز کابل USB را جدا و دوباره وصل کنید تا شناسایی دستگاه از نو انجام شود. در پنجرهٔ Device Manager ویندوز انتظار میرود دستگاه بهعنوان یک پورت سریال مجازی در شاخهٔ Ports (COM & LPT) با شناسههای پیشفرض ظاهر شده و شمارهٔ درگاه اختصاصیافته (مثلاً COM5) نمایش داده شود.
- نرمافزار PuTTY را اجرا کرده، نوع اتصال را روی Serial قرار دهید، نام پورت مربوطه را وارد نموده و مطمئن شوید گزینهٔ Local Echo غیرفعال باشد.
- شروع به تایپ کاراکترها در ترمینال کنید. هر کاراکتر ارسالشده باید پس از طی فرآیند پژواک، توسط رایانه دریافت شده و روی پنجرهٔ ترمینال چاپ گردد.
- برای آزمون طولهای مرزی داده، اسکریپت میزبان ارائهشده (echo_test.py) را با تعیین صریح نام پورت میتوان به کار برد. طول محمولهٔ نرمافزار میزبان، از جمله 1، 63، 64، 65، 128 و 257 بایت، با طول یک بستهٔ USB یکی نیست؛ داده در بستههای حداکثر 64 بایتی جابهجا میشود و یک فراخوانی ارسال در میزبان لزوماً برابر یک فراخوانی CDC_Receive_FS نیست. اسکریپت، برابری کل جریان بایتهای ارسالی و دریافتی را بررسی میکند و ردگیری بستههای گذرگاه نیست.

عیبیابی، رفتارهای گذرگاه و چکلیست اعتبارسنجی
راهاندازی پورت USB به دلیل حساسیتهای لایهٔ فیزیکی و زمانبندیهای پروتکل، همواره نیازمند دقت در سیمبندی و درک صحیح کدهای بازگشتی کتابخانه است. جدول زیر متداولترین نشانهها، علل و اقدامات لازم را بر مبنای منطق کد و شمارندههای این پروژه جمعبندی کرده است:
| نشانه | علت محتمل | بررسی و اقدام |
|---|---|---|
| دستگاه توسط رایانه شناسایی نمیشود یا خطای درخواست توصیفگر رخ میدهد. | عدم تأمین کلاک اسمی 48 MHz، قطعی یا نبود مقاومت 1.5 kΩ میان D+ و 3.3V، یا جابهجا وصل شدن خطوط D+ و D-. | بررسی مسیر کلاک RCC در ایجاد فرکانس 48 MHz از PLL 72 MHz با تقسیم 1.5؛ سنجش ولتاژ 3.3V مقاومت بالاکش با مولتیمتر و اطمینان از سلامت کابل دیتا. |
| تابع ارسال CDC_Transmit_FS مقدار USBD_FAIL برمیگرداند و دادهای ارسال نمیشود. | فراخوانی ارسال پیش از رسیدن دستگاه به وضعیت USBD_STATE_CONFIGURED یا عدم تخصیص اشارهگر pClassData. | حفظ شروط محافظتی ابتدای تابع CDC_Transmit_FS برای جلوگیری از دسترسی به اشارهگر تهی؛ فراخوانی ارسال تنها پس از بررسی وضعیت Configured در حلقه اصلی. |
| افزایش شمارنده g_tx_busy و بازگشت مکرر وضعیت USBD_BUSY. | ارسال بستهٔ قبلی هنوز روی خط پایان نیافته (TxState مخالف صفر) و تابع ارسال فراخوانی شده است. | این وضعیت به معنای گم شدن بسته نیست؛ شمارنده g_tx_busy دفعات تکرار در حلقه را میشمارد. داده در echo_buffer حفظ شده و در فاز 1 تا صفر شدن TxState منتظر میماند. |
| تنها یک بسته (حداکثر 64 بایت) دریافت میشود و پس از آن دریافت دادههای بعدی متوقف میگردد. | مسلح نشدن مجدد نقطه پایانی OUT به دلیل عدم اتمام چرخه ارسال قبلی یا خوانده نشدن داده توسط نرمافزار میزبان. | این رفتار ناشی از کنترل پسفشار (NAK) عمدی برای حفظ بافر است. بررسی کنید نرمافزار رایانه پورت را باز نگه داشته و بایتها را بخواند تا TxState صفر شده و ReceivePacket اجرا شود. |
| پس از پروگرم کردن مجدد تراشه یا ریست سختافزاری، ارتباط شناسایی نمیشود. | وجود مقاومت بالاکش فیزیکی ثابت روی خط D+ که به کنترلر امکان قطع نرمافزاری لایه فیزیکی (PCD Soft Disconnect) را نمیدهد. | کابل USB را یک بار از رایانه جدا کرده و مجدداً متصل کنید تا فرآیند شمارش از سر گرفته شود (یا در بردهای تجاری از ترانزیستور برای کنترل مقاومت بالاکش استفاده شود). |
| تغییر نرخ Baud در نرمافزار ترمینال رایانه اثری بر سرعت واقعی انتقال ندارد. | عدم وجود ماژول سختافزاری فیزیکی UART در مسیر تبادل داده. | این رفتار در کلاس CDC کاملاً طبیعی است؛ تبادل داده با نرخ فیزیکی 12 Mbit/s انجام میشود و ساختار 7 بایتی Line Coding صرفاً برای هماهنگی با نرمافزارهای ترمینال ذخیره میگردد. |
گزارش اعتبارسنجی فنی و کامپایل پروژه
کدهای منبع این پروژه با ابزار STM32CubeMX نسخه 6.14.0 بر مبنای پکیج STM32CubeF3 نسخه 1.11.6 تولید شده و تغییرات کاربر بهصورت متمرکز درون بلوکهای محافظتشدهٔ USER CODE در فایلهای Core/Src/main.c و USB_DEVICE/App/usbd_cdc_if.c درج گردیدهاند. این سورسکدها در محیط محلی CMake با زنجیرهٔ ابزار رسمی Arm GNU Toolchain نسخه 13.2.1 و با گزینههای سختگیرانهٔ -O0 -g3 -Wall -Werror بهطور تمیز کامپایل شدند و تمام 30 فایل منبع C بدون هیچ هشدار یا خطایی تأیید گشتند. تصویر خروجی برنامه 31932 بایت از حافظهٔ فلش و 6608 بایت از حافظهٔ رم را به خود اختصاص داده است. صحت بردارهای وقفهٔ بازنشانی، پشتهٔ اصلی (MSP)، وقفهٔ USB و تطابق مقادیر باینری در 14 آزمون مستقل نرمافزاری بررسی و تأیید شده است. با توجه به دامنهٔ اعتبارسنجی ارائهشده، تأییدیهٔ حاضر صرفاً گواهی بر کامپایل موفق و صحت باینری پروژه است و اندازهگیری شکلموجهای فیزیکی یا ردگیری ترافیک داده با تحلیلگر پروتکل روی سختافزار در این گزارش ارائه نشده است.
پرسشهای خودآزمایی
1. چرا نوسانساز داخلی RC (HSI) در این پروژه برای تأمین کلاک واحد USB Full-Speed استفاده نمیشود؟
واحد USB Full-Speed برای بازسازی صحیح زمانبندی دادهها و استخراج کلاک از لبههای سیگنال NRZI به یک منبع کلاک پایدار با فرکانس اسمی 48 MHz نیاز دارد. نوسانساز داخلی HSI به دلیل وابستگی به دما و نوسانات ولتاژ کاری، پایداری فرکانسی لازم برای گذرگاه USB را فراهم نمیکند. در پیکربندی رسمی این پروژه، کلاک اسمی 48 MHz از یک کریستال خارجی 8 MHz (HSE) و از طریق مدار PLL (ضرب در 9 برای رسیدن به 72 MHz) و مقسم اختصاصی USB در واحد RCC (تقسیم بر 1.5) تأمین شده است.
2. چرا مقاومت بالاکش 1.5 kΩ خط D+ نباید به ولتاژ 5V کانکتور USB (خط VBUS) متصل شود؟
پایههای تفاضلی USB در میکروکنترلر STM32F303CBT6 (پایههای PA11 و PA12) با سطح منطقی 3.3V کار میکنند. اتصال مقاومت 1.5 kΩ به خط 5V گذرگاه (VBUS)، ولتاژی فراتر از محدودهٔ مجاز کاری به طبقهٔ فرستنده/گیرندهٔ لایهٔ فیزیکی (PHY) داخلی تراشه تحمیل میکند و میتواند به درایورهای فیزیکی ورودی/خروجی آسیب جدی برساند. این مقاومت باید منحصراً به خط تغذیهٔ تثبیتشدهٔ 3.3V برد (VDD) متصل گردد.
3. مفهوم کد وضعیت USBD_BUSY در تابع CDC_Transmit_FS چیست و چگونه باید آن را مدیریت کرد؟
کد USBD_BUSY زمانی بازگردانده میشود که تابع ارسال پیش از اتمام انتقال بستهٔ قبلی (یعنی در شرایطی که متغیر hcdc->TxState مخالف صفر است) فراخوانی شود. این بازگشت به خودی خود دادههای در حال ارسال را مخدوش نمیکند، بلکه اعلام میدارد که درخواست ارسال جدید پذیرفته نشده است. با توجه به اینکه کتابخانهٔ نسخه 2.5.3 روال فراخوان (Callback) برای تکمیل ارسال در لایهٔ برنامه ندارد و اشارهگر بافر ارسالی را بدون کپیبرداری نگه میدارد، برنامه باید وضعیت TxState را در نوبتهای بعدی حلقهٔ اصلی (تابع USB_EchoPoll) پایش کند و تا صفر شدن آن، از بازنویسی محتوای بافر یا ارسال مجدد خودداری نماید.
4. آیا در میکروکنترلر STM32F303CBT6 امکان استفاده همزمان از رابط USB و شبکه CAN از نظر معماری حافظه وجود دارد؟
بله، برخلاف میکروکنترلر قدیمیتر STM32F103، در خانوادهٔ STM32F303xB/C حافظهٔ 512 بایتی بافر بستهٔ (PMA) واحد USB کاملاً مستقل از حافظهٔ اختصاصی پیامهای CAN است (همانطور که در جدول 6 یادداشت کاربردی AN4228 تصریح شده است). با این حال، پایههای پیشفرض دو واحد (PA11 و PA12) و بردار وقفهٔ اولویت پایین آنها (USB_LP_CAN_RX0_IRQn) مشترک هستند. بنابراین برای فعالسازی هر دو، باید پایههای CAN از طریق نگاشت Alternate Function به پایههای دیگر (نظیر PB8 و PB9 با AF9) هدایت شوند. در پروژهٔ این درس برای حفظ سادگی و اجتناب از تداخل، واحد CAN غیرفعال شده است.
5. سازوکار پسفشار (Backpressure) در تابع CDC_Receive_FS چگونه کار میکند و چه محدودیتهایی دارد؟
در این پیادهسازی، پس از دریافت یک بستهٔ داده در CDC_Receive_FS، تابع USBD_CDC_ReceivePacket برای مسلحسازی مجدد نقطهٔ پایانی OUT فراخوانی نمیشود تا زمانی که داده در حلقهٔ اصلی ارسال شده و ارسال آن پایان یابد. در طول این مدت، سختافزار USB میکروکنترلر به بستههای خروجی جدید میزبان بهطور خودکار پاسخ NAK میدهد که موجب توقف موقت جریان ارسال از سوی میزبان و جلوگیری از سرریز بافر میگردد. با این حال، این حفاظت تنها در طول نشست فعال برقرار است؛ اگر میکروکنترلر روشن بماند و ریست گذرگاه یا پایان پیکربندی کلاس باعث اجرای CDC_DeInit شود، دادهٔ معلق پاک میشود و g_usb_session_discards افزایش مییابد. این شمارنده در RAM است و پس از ریست میکروکنترلر یا قطع برق حفظ نمیشود. در مدار این درس، قطع کابل USB برق برد را هم قطع میکند؛ بنابراین نمیتوان پس از اتصال مجدد از این شمارنده برای اثبات ثبت دادهٔ ازدسترفته استفاده کرد.
6. چرا تغییر پارامترهای ارتباطی (مانند Baud Rate روی 115200) در نرمافزار ترمینال رایانه بر سرعت فیزیکی انتقال داده اثری ندارد؟
در کلاس پورت سریال مجازی (CDC ACM)، دادهها مستقیماً در قالب بستههای استاندارد USB Full-Speed با نرخ سیگنالدهی فیزیکی 12 Mbit/s روی خطوط تفاضلی تبادل میشوند و هیچ واحد سختافزاری USART واقعی در مسیر وجود ندارد. درخواستهای کنترلی SET_LINE_CODING ارسالی از سوی سیستمعامل صرفاً یک ساختار اطلاعاتی 7 بایتی حاوی تنظیمات مجازی درگاه سریال هستند که در حافظه ذخیره میشوند تا سازگاری با نرمافزارهای سنتی پورت سریال حفظ شود.
تمرینها
تمرینهای تغییر کد، پیشنهاد آموزشیاند؛ نسخهٔ تغییریافته باید دوباره کامپایل و جداگانه روی برد بررسی شود.
1. تمرین 1 (سطح پایه): پایش شمارندههای بسته و افزودن پیشوند با بررسی ظرفیت بافر
کدهای پروژه دارای شمارندههای سراسری g_rx_packets و g_rx_bytes هستند. تابع CDC_Receive_FS را طوری تغییر دهید که برای دادهٔ غیرصفر، فقط اگر مقدار واقعی *Len در همان فراخوانی کمتر از 50 بایت بود، یک پیشوند 8 بایتی اضافه شود. برای *Len برابر 50 یا بیشتر، داده بدون پیشوند بماند. داده را بدون همپوشانی نامعتبر کپی کنید و echo_length را با طول جدید هماهنگ سازید؛ بیشترین طول شاخهٔ پیشونددار 49+8=57 بایت است و در echo_buffer با ظرفیت 64 بایت جا میگیرد. شمارندهٔ g_rx_bytes همچنان تعداد بایتهای دریافتشده از میزبان را بشمارد، نه بایتهای پیشوند را. رفتار دریافت با طول صفر و چرخهٔ مالکیت بافر را حفظ کنید. یک ارسال میزبان یا خط تایپشده لزوماً برابر یک فراخوانی دریافت نیست. پس از تغییر، پروژه را دوباره کامپایل کنید.
معیار بررسی: پس از کامپایل، طول واقعی هر فراخوانی دریافت را در دیباگر مشاهده کنید: دادهٔ غیرصفر با طول کمتر از 50 باید پیشوند داشته باشد و داده با طول 50 یا بیشتر نباید پیشوند بگیرد. طول پاسخ باید با echo_length هماهنگ و حداکثر 64 بایت باشد. نتیجه را فقط از طول متن تایپشده یا اندازهٔ یک ارسال میزبان استنتاج نکنید؛ درایور میزبان ممکن است آن را به چند بخش تقسیم کند.
2. تمرین 2 (سطح متوسط): پیادهسازی فیلتر تبدیل حروف کوچک به بزرگ با حفظ چرخهٔ عمر بافر
میخواهیم کاراکترهای دریافتی را پیش از ارسال به حروف بزرگ تبدیل کنیم. با توجه به اینکه تغییر محتوای بافر در طول فاز 2 (زمانی که اشارهگر در اختیار درایور ارسال است و TxState مخالف صفر است) باعث مخدوش شدن بستهٔ در حال ارسال میشود، پردازش تبدیل حروف را دقیقاً در فاز 1 و پیش از فراخوانی CDC_Transmit_FS انجام دهید. حلقهٔ پردازشی باید حروف کوچک انگلیسی ('a' تا 'z') را بررسی کرده و با تفریق عدد 32 به حروف بزرگ متناظر تبدیل کند. پس از ویرایش، پروژه را بهصورت تمیز بیلد نمایید.
معیار بررسی: متن 'stm32 echo test' را به پورت ارسال کنید و مشاهده نمایید که پاسخ دریافتی بهصورت 'STM32 ECHO TEST' بازگردانده میشود و شمارندهٔ g_echo_completed بدون بروز خطای USBD_FAIL افزایش مییابد.
3. تمرین 3 (سطح پیشرفته): آزمون طولهای مرزی داده، بسته با طول صفر (ZLP) و شمارندههای نشست
در نقاط پایانی Bulk پروژه، بیشترین طول هر بسته 64 بایت است. درایور ST CDC نسخهٔ 2.5.3 همراه پروژه، پس از انتقال IN با طول غیرصفر و مضرب 64، یک بسته با طول صفر (ZLP) میفرستد و سپس TxState را آزاد میکند؛ این رفتار همین درایور برای پایان انتقال است و قاعدهای عمومی برای مرزبندی پیامهای برنامه نیست. با echo_test.py محمولههای میزبان با طول 63، 64، 65 و 128 بایت ارسال کنید. محمولههای بزرگتر به بستههای حداکثر 64 بایتی تقسیم میشوند و تعداد فراخوانیهای CDC_Receive_FS را نباید از تعداد ارسالهای میزبان حدس زد. در یک اجرای روشن و فعال، شمارندههای g_rx_packets و g_echo_completed را مشاهده کنید. اگر میزبان امکان ریست گذرگاه بدون قطع توان برد را فراهم میکند، میتوان رفتار g_usb_session_discards را هنگام اجرای CDC_DeInit و وجود دادهٔ معلق بررسی کرد؛ افزایش آن برای هر ریست تضمینشده نیست. برای حفظ شمارنده، منبع تغذیهٔ دوم اضافه نکنید.
معیار بررسی: برای هر طول محموله، برابری کامل جریان بایتهای ارسالشده و پژواکشده را با اسکریپت بررسی کنید. پس از تکمیل انتقالها، TxState باید آزاد شود؛ دیباگر میتواند این وضعیت نرمافزاری را نشان دهد، اما اثبات مشاهدهٔ ZLP روی کابل به ردگیری واقعی گذرگاه نیاز دارد و در این درس انجام نشده است. پس از قطع و وصل کابل در مدار Bus-Powered، آزمون را یک اجرای تازه بدانید: RAM و شمارندهها از نو مقداردهی میشوند و g_usb_session_discards شاهد ماندگار قطع ارتباط نیست.
منابع و مبنای بررسی
مبنای فنی این درس، اسناد رسمی و نسخهٔ مشخص کتابخانهها و پروژهٔ قابل دریافت آن است. نسخهها و محدودیتها در راهنمای فایلهای همراه ثبت شدهاند.
- AN4228 Rev6 Table6, printed page12/90
- AN4879 Rev12 June2026, Table3 USB implementations
- AN2606 STM32F302xB(C)/303xB(C) systemmemoryboot table
- USB device middleware V2.5.3 CDC source
- USB device middleware V2.5.3 CDC header
- Generated PCD/HAL binding and F3 PCD sources
- RM0316 reference manual
دانلود پروژه و فایلهای همراه
دانلود بستهٔ کامل پروژه، خروجی ساخت، اسکریپتها و 6 تصویر آموزشی · راهنمای اجرا · پوشهٔ همین نسخه در گیتهاب
حجم بسته: 1,788,699 بایت. شناسهٔ SHA256: 77796c860de2349e6462351fba327f4d18d805ca4d23439625fd06b57b49438f. گزارش ساخت و برگهٔ خالی ثبت آزمایش در بسته قرار دارد.