فرض کنید میخواهید نور یک LED را کم و زیاد کنید. در درس GPIO خروجی را High یا Low میکردیم. حالا بهجای تغییر سطح High، مدت روشنماندن را تغییر میدهیم: مثلاً در هر 1000 میکروثانیه، LED فقط 250 میکروثانیه روشن باشد. اگر این کار سریع تکرار شود، میتوان روشنایی دیدهشده را تنظیم کرد. برای این مثال، LED خارجی و مدار مناسب لازم است؛ LED خود برد به خروجی انتخابشدهٔ این درس وصل نیست.
این روش مدولاسیون پهنای پالس یا PWM نام دارد؛ مخفف Pulse Width Modulation است. در این درس، تایمر 3 موج را روی PA6 میسازد. ابتدا شکل موج و محاسباتش را میشناسیم، سپس تنظیمات و کد را دنبال میکنیم. پیشنیازها: درس 16، ساختمان تایمر و درس 18، وقفهٔ تایمر. برای خود PWM این پروژه به وقفهٔ تایمر نیاز نداریم.
چطور با قطع و وصل سریع، ولتاژ متوسط بسازیم؟
برای درک سازوکار PWM، ابتدا باید یک شکلموج پالسی متناوب را توصیف کنیم. هر موج پالسی در یک بازه زمانی تکرار میشود که به آن دوره تناوب (Period) میگوییم و با نماد T بر حسب ثانیه نمایش میدهیم. تعداد تکرار این چرخه در هر ثانیه، فرکانس (Frequency) سیگنال است که با نماد fPWM و با واحد هرتز (Hz) اندازه گرفته میشود. رابطه بین دوره و فرکانس همواره معکوس یکدیگر است:
در طول هر دوره تناوب، سیگنال برای مدتی در سطح منطقی بالا (High یا 3.3 ولت) قرار دارد که آن را با TON نشان میدهیم، و در باقیمانده دوره در سطح منطقی پایین (Low یا 0 ولت) میماند که TOFF نامیده میشود. حاصلجمع این دو زمان برابر با کل دوره تناوب است (T = TON + TOFF). نسبت مدتزمان روشن بودن به کل دوره تناوب، نسبت وظیفه (Duty Cycle) نام دارد و با نماد D مشخص میشود:
نسبت وظیفه یک کسر بدون بعد بین 0 و 1 است که معمولاً آن را به صورت درصد بیان میکنیم (D% = 100 × D). اگر در یک دوره زمانی 1000 میکروثانیهای، سیگنال به مدت 250 میکروثانیه روشن و 750 میکروثانیه خاموش باشد، نسبت وظیفه برابر 0.25 یا 25% خواهد بود.
تفاوت کلیدزنی سریع با ولتاژ آنالوگ واقعی
PWM روی پایهٔ دیجیتال، ولتاژ آنالوگ ثابت 1.65V تولید نمیکند؛ پایه میان دو سطح High و Low جابهجا میشود. برای محاسبهٔ ساده، آنها را 3.3V و 0V فرض میکنیم. در مدار واقعی، مقدار دقیق سطحها و زمان لبهها به تغذیه، بار و مشخصات پایه وابستهاند. میانگین یک دوره از رابطهٔ زیر به دست میآید؛ D در این رابطه کسر زمانی سطح High است.
با فرض ایدهآل VHigh = 3.3 V و VLow = 0 V، این رابطه به فرم ساده Vavg = D × 3.3 V خلاصه میشود. به عنوان مثال، برای نسبتهای وظیفه 25%، 50% و 75%، ولتاژ میانگین تئوری به ترتیب برابر 0.825 ولت، 1.650 ولت و 2.475 ولت خواهد بود.
اثر PWM به مدار بستگی دارد. فیلتر مقاومت و خازن میتواند نوسان را کاهش دهد، ولی خروجی آن ریپل و زمان نشست دارد و یک ولتاژ کاملاً صاف را تضمین نمیکند. LED همچنان قطع و وصل میشود؛ دیدهشدن چشمک به فرکانس و شرایط مشاهده وابسته است و تغییر روشناییِ دیدهشده لزوماً با Duty خطی نیست. موتور نیز به درایور و مدار حفاظت مناسب نیاز دارد و سرعتش صرفاً از ولتاژ میانگین به دست نمیآید. موتور را مستقیم به پایهٔ MCU وصل نکنید.

چرا خود تایمر پالس را بسازد؟
در درس 18 پایه با اجرای callback معکوس میشد. اگر لبههای موج را نرمافزار بسازد، CPU باید بهموقع کد اجرا کند؛ وقفهها یا تأخیر کارهای دیگر میتوانند زمان لبهها را جابهجا کنند. در PWM سختافزاری، پس از تنظیم و شروع، تایمر شمارش و مقایسه را انجام میدهد؛ لازم نیست CPU برای هر لبه وارد ISR شود. تنظیم و تغییر پارامترها هنوز هزینه دارد و دقت موج به کلاک و سختافزار وابسته است؛ درصد بار CPU این پروژه اندازهگیری نشده است.
تایمر چگونه شکلموج خروجی را در سختافزار میسازد؟
برای پیادهسازی PWM در سختافزار، تایمر از ترکیب دو بخش اصلی بهره میبرد: یک «شمارنده صعودی» و یک «واحد مقایسهگر دیجیتال». همانطور که در درس 16: تایمرهای عمومی STM32 شرح داده شد، رجیستر شمارنده (CNT سرنام Counter) با هر پالس دریافتی از مقسم کلاک (PSC سرنام Prescaler)، یک واحد افزایش مییابد. سقف این شمارش توسط رجیستر بازنشانی خودکار (ARR سرنام Auto-Reload Register) تعیین میشود.
در کنار شمارنده، رجیستر مقایسه یا CCR قرار دارد؛ Capture/Compare Register. رجیستر محل نگهداری یک مقدار در سختافزار است. برای کانال 1 از CCR1 استفاده میکنیم. در شمارش صعودی با PWM Mode 1 و قطبیت High و کانال فعال، وقتی CNT کوچکتر از CCR1 باشد، خروجی High است؛ وقتی CNT به CCR1 برسد یا از آن بیشتر شود، خروجی Low است. مثلاً با CCR1=250، نخستین 250 تیک هر دوره High میشوند.
با ARR=999، شمارنده از 0 تا 999 میرود و در تیک بعد به 0 برمیگردد؛ یعنی هر دوره 1000 تیک دارد. در تنظیم عادی این پروژه، پایان دور رخداد بهروزرسانی یا UEV میسازد؛ Update Event. در مثال CCR1=250، خروجی در ابتدای دورهٔ بعد دوباره High میشود. پس ARR طول دوره و CCR1 طول بخش High را تعیین میکنند. حالت CCR1=0 را جدا در بخش مرزها بررسی میکنیم.

مسیر فرکانس کلاک ورودی تایمر 3 در STM32F303
در پروژهٔ همراه، کریستال HSE اسمی 8MHz با PREDIV برابر 1 و PLL برابر 9، کلاک SYSCLK/HCLK اسمی 72MHz میسازد. APB1 با تقسیم 2 برابر 36MHz و APB2 با تقسیم 1 برابر 72MHz است؛ Flash Latency روی 2WS قرار دارد. TIM3 به APB1 وصل است، اما برای محاسبهٔ PWM نباید مستقیماً 36MHz را بهجای کلاک تایمر بگذاریم.
در مسیر کلاک TIM3 این تراشه، اگر مقسم APB1 برابر 1 باشد کلاک تایمر همان PCLK1 است؛ اگر مقسم غیر از 1 باشد دو برابر PCLK1 میشود. مقسم این پروژه 2 است؛ بنابراین:
آگاهی از این کلاک مبنای 72 مگاهرتزی، پایه تمامی محاسبات دقیق فرکانس و نسبت وظیفه در ادامه این درس است.
چگونه فرکانس و تفکیکپذیری PWM را محاسبه کنیم؟
تمامی روابط این بخش به طور صریح مشروط به برقراری سه وضعیت کاری مشخص هستند: شمارش صعودی (Upcounting)، مد خروجی PWM Mode 1 و قطبیت فعال بالا (Active-High) روی کانال فعال خروجی. در این شرایط، شمارنده از 0 تا ARR را میشمارد؛ بنابراین تعداد کل کوانتاهای زمانی یا پلههای گسسته در هر دوره تناوب برابر با N = ARR + 1 است.
فرکانس سیگنال خروجی با در نظر گرفتن مقسم کلاک و رجیستر پریود از رابطه زیر محاسبه میشود:
نسبت وظیفه کسری (D) و پهنای پالس خروجی (TON) برای هر مقدار عددی قابلبیان در محدوده 0 ≤ CCR ≤ ARR + 1 از رابطه زیر پیروی میکنند:
نمونه محاسباتی: تولید سیگنال 1 کیلوهرتز
هدف، فرکانس اسمی 1kHz یا 1000Hz است. با کلاک تایمر 72MHz، PSC=71 را انتخاب میکنیم؛ تقسیم بر PSC+1 یعنی 72، نرخ شمارش را به 1MHz و زمان هر تیک را به 1us میرساند. اگر ARR=999 باشد، 1000 تیک در هر دوره خواهیم داشت. حالا مقدارها را در فرمول قرار میدهیم:
کل دوره تناوب برابر T = 1 / 1000 = 1 ms = 1000 µs است. اکنون مقادیر CCR را برای سه نسبت وظیفه پرکاربرد بررسی میکنیم:
- نسبت وظیفه 25%: با انتخاب CCR = 250، پهنای پالس برابر TON = (250 / 1000) × 1000 µs = 250 µs و ولتاژ میانگین ایدهآل برابر Vavg = 0.25 × 3.3 V = 0.825 V خواهد بود.
- نسبت وظیفه 50%: با انتخاب CCR = 500، پهنای پالس برابر TON = 500 µs و ولتاژ میانگین ایدهآل برابر Vavg = 0.50 × 3.3 V = 1.650 V است.
- نسبت وظیفه 75%: با انتخاب CCR = 750، پهنای پالس برابر TON = 750 µs و ولتاژ میانگین ایدهآل برابر Vavg = 0.75 × 3.3 V = 2.475 V میشود.
مفهوم تفکیکپذیری و مصالحه مهندسی فرکانس و دقت
تفکیکپذیری (Resolution) تعیین میکند که کوچکترین گام تغییر نسبت وظیفه چقدر است. در مثال فوق با N = 1000 پله زمانی، حداقل تغییر ممکن در نسبت وظیفه با افزایش یک واحدی در رجیستر مقایسه برابر است با ΔD = 1 / 1000 = 0.1% (0.1 واحد درصد). از دیدگاه معادل بیتی، تفکیکپذیری زمانی سیستم برابر log2(1000) ≈ 9.9658 بیت است. باید توجه داشت که این مقدار صرفاً یک شاخص محاسباتی گسستهسازی زمانی است و نباید آن را با «تعداد بیتهای موثر» (ENOB) در مبدلهای داده اشتباه گرفت.
حال این پرسش مطرح میشود: اگر بخواهیم همان فرکانس 1 کیلوهرتز را با بیشترین تفکیکپذیری ممکن بسازیم، آیا میتوانیم PSC = 0 را انتخاب کنیم؟ پاسخ منفی است. بیایید فرمول را معکوس کنیم: اگر PSC = 0 باشد، مخرج کسر باید 72000 باشد (ARR + 1 = 72,000,000 / 1000 = 72000)، یعنی باید مقدار ARR = 71999 درون رجیستر قرار گیرد. اما تایمر 3 یک تایمر 16 بیتی است! حداکثر عددی که یک رجیستر 16 بیتی بدون علامت میتواند در خود ذخیره کند 216 – 1 = 65535 است. عدد 71999 در این رجیستر جا نمیشود؛ چنین تنظیمی معتبر نیست و نباید به نتیجهٔ نوشتن آن تکیه کرد.
بهترین انتخاب مهندسی برای رسیدن به فرکانس 1 کیلوهرتز با بالاترین تفکیکپذیری ممکن در تایمر 16 بیتی، انتخاب PSC = 1 است. در این حالت ARR + 1 = 72,000,000 / (2 × 1000) = 36000 میشود که نتیجه آن ARR = 35999 است. این مقدار کاملاً درون بازه 16 بیتی قرار میگیرد و به ما 36000 پله زمانی با گام 0.002778% معادل 15.1357 بیت تفکیکپذیری میدهد.
اگر هدف را به 20kHz تغییر دهیم، PSC=0 و ARR=3599 تعداد 3600 تیک در هر دوره میدهند؛ گام Duty حدود 0.02778 واحد درصد و معادل زمانی حدود 11.8138 بیت است. این یک محاسبه بر اساس کلاک اسمی است. انتخاب 20kHz بهتنهایی تضمین حذف صدای هر مدار یا موتور نیست.

حالتهای کاری، جهت شمارش و رفتارهای مرزی چه تفاوتی دارند؟
تا اینجا PWM Mode 1 و قطبیت High را به کار بردیم. برای مقایسه، Mode 2 منطق فعالبودن کانال را معکوس میکند؛ بیت CC1P نیز قطبیت خروجی را تعیین میکند. جدول زیر برای شمارش صعودی، کانال فعال و مقدار ثابت CCR است. در جدول، D همان کسر CCR/(ARR+1) در بازهٔ 0 تا 1 است؛ ستون آخر مشخص میکند پایه چه کسری از دوره واقعاً High میماند. «فعال بودن بار» با High بودن پایه همیشه یک معنی ندارد.
| مد کاری تایمر | قطبیت (CC1P) | وضعیت در زمان CNT < CCR | وضعیت در زمان CNT ≥ CCR | کسر زمانی High فیزیکی |
|---|---|---|---|---|
| PWM Mode 1 | Active-High (0) | High (3.3V) | Low (0V) | D |
| PWM Mode 1 | Active-Low (1) | Low (0V) | High (3.3V) | 1 – D |
| PWM Mode 2 | Active-High (0) | Low (0V) | High (3.3V) | 1 – D |
| PWM Mode 2 | Active-Low (1) | High (3.3V) | Low (0V) | D |
همانطور که مشاهده میشود، تغییر مد از 1 به 2 یا معکوس کردن بیت قطبیت، کسر زمانی سطح High فیزیکی را از D به 1 – D تبدیل میکند.

شمارش لبهای در برابر شمارش متقارن مرکزی
حالت فعلی، لبهتراز و صعودی است: شمارنده از 0 تا ARR میرود و دور بعد را از 0 شروع میکند. TIM3 حالت مرکزتراز هم دارد؛ در آن شمارنده تا ARR بالا و سپس تا 0 پایین میرود. برای ARR مثبت، یک رفتوبرگشت کامل 2×ARR تیک دارد. این بخش فقط معرفی است؛ پروژهٔ همراه مرکزتراز نیست و رفتار رخدادهای Update آن را نیز نباید با حالت صعودی یکی دانست.
دوره تناوب در شمارش متقارن مرکزی برای مقادیر مثبت ARR از رابطه زیر به دست میآید:
یک خطای متداول مهندسی، قرار دادن کورکورانه عبارت ARR + 1 در فرمول شمارش مرکزی است. توجه کنید که در شمارش مرکزی، ضریب دقیقاً 2 × ARR است و نباید با حالت لبهای اشتباه شود.
تحلیل دقیق رفتارهای مرزی در مقادیر انتهایی
مرزهای زیر فقط برای شمارش صعودی، PWM Mode 1، قطبیت High و خروجی فعالاند. اگر مد یا قطبیت عوض شود، نتیجهٔ High/Low نیز عوض میشود:
- حالت CCR = 0: شرط CNT < 0 برای یک شمارنده بدون علامت هرگز برقرار نمیشود؛ بنابراین خروجی در تمام طول دوره تناوب در سطح Low میماند (D = 0%).
- حالت CCR = ARR + 1: از آنجا که شمارنده تنها در محدوده 0 تا ARR حرکت میکند، شرط CNT < CCR همواره در تمام چرخه برقرار است و خروجی به طور دائمی High میماند (D = 100%). با احتساب این نقطه انتهایی، تعداد سطوح گسسته خروجی برابر با N + 1 سطح خواهد بود.
- حالت CCR > ARR + 1: خروجی کماکان 100% High باقی میماند و نسبت وظیفه به بیش از 100% جهش نمیکند.
- حالت خاص ARR = 65535: در این حالت دوره تناوب شامل N = 65536 پله است. از آنجا که رجیستر 16 بیتی CCR نمیتواند عدد 65536 را در خود نگه دارد و حداکثر مقدار آن 65535 است، حداکثر نسبت وظیفه تنظیمی برابر با 65535 / 65536 ≈ 99.9985% خواهد بود و همواره یک تکپالس بسیار کوتاه Low در انتهای چرخه باقی میماند. برای دستیابی به 100% کامل در این فرکانس، باید از روشهای مهندسی دیگر مانند تغییر موقت مد خروجی یا معکوسسازی قطبیت استفاده کرد. همچنین مقدار ARR = 0 شمارنده را در سطح سختافزار مسدود میکند.
چرا رجیسترهای پیشبارگذاری از پرش ناخواسته پالس جلوگیری میکنند؟
فرض کنید ARR=999 و CCR1=750 است. اگر Preload خاموش باشد و در CNT نزدیک 500 مقدار CCR1 را به 250 تغییر دهیم، آستانهٔ مقایسه در میانهٔ همان دوره عوض میشود؛ پالس گذار میتواند با پهنای قدیم یا پهنای جدید ناسازگار شود. مدل آموزشی شکل، تغییر را دقیقاً در تیک 500 فرض میکند؛ کد Polling چنین لحظهٔ دقیقی را تضمین نمیکند. این فقط تصویرسازیِ منطق مقایسهگر است. مرجع RM0316 برای این تایمر، PWM بدون Preload را فقط در حالت تکپالس OPM معتبر میداند؛ در غیر این صورت رفتار را تضمین نمیکند. بنابراین در آزمایش PWM پیوسته با OC1PE خاموش، حتی پهنای 500 تیکِ این تصویرسازی هم نتیجهٔ تضمینشدهٔ سختافزار نیست.
برای تغییر منظم Duty، از پیشبارگذاری یا Preload استفاده میکنیم. آن را مثل دو محل نگهداری عدد ببینید: یکی عدد بعدی را میگیرد و دیگری عدد فعلیِ مورد استفادهٔ مقایسهگر را نگه میدارد. با Preload فعال، نوشتن CCR1 مقدار فعلی را همان لحظه تغییر نمیدهد. در رخداد بهروزرسانی مجاز، عدد بعدی به بخش فعال یا Shadow منتقل میشود. در تنظیم صعودی این پروژه، Update طبیعی در مرز دوره رخ میدهد؛ بنابراین تغییر CCR1 میتواند بدون دستکاری پالس جاری به دورهٔ بعد منتقل شود.

کنترل بیتهای ARPE، OC1PE و بافر خودکار PSC
سازوکار بافر شدن در بخشهای مختلف تایمر تفاوتهایی دارد که باید مد نظر قرار گیرد:
- رجیستر PSC: همواره در سختافزار بافر شده است و نیازی به فعالسازی بیتی ندارد. تغییر مقسم کلاک فقط در رخداد UEV اعمال میشود.
- رجیستر ARR: با تنظیم بیت ARPE (سرنام Auto-Reload Preload Enable) در رجیستر TIMx_CR1 بافر میشود.
- رجیستر CCR1: با تنظیم بیت OC1PE (سرنام Output Compare 1 Preload Enable) در رجیستر TIMx_CCMR1 بافر میگردد.
انتقال دادهها از رجیستر پیشبارگذاری به سایه، وابسته به رخداد طبیعی UEV در پایان شمارش است؛ مشروط بر آنکه بیت UDIS (غیرفعالساز بهروزرسانی) در رجیستر TIMx_CR1 روی صفر باشد.
خطرات تحریک دستی بیت UG در رجیستر رویدادها
میکروکنترلر دارای رجیستری به نام TIMx_EGR است که بیتی به نام UG (Update Generation) در آن وجود دارد. نوشتن عدد 1 در این بیت توسط نرمافزار، بلافاصله یک رخداد بهروزرسانی مصنوعی تولید میکند. با این کار، رجیسترهای سایه فوراً بهروز میشوند، اما همزمان شمارنده CNT نیز به صفر ریست شده و فاز مقسم کلاک بازنشانی میگردد!
صدور UG هنگام اجرا میتواند شمارنده و فاز مقسم را دوباره آغاز کند؛ در نتیجه طول دوره یا یک بخش پالس تغییر میکند. اثر آن بسته به لحظهٔ اجرا و مقدارها ممکن است کوتاه یا بلندشدن یک بخش موج باشد؛ هر UG الزاماً یک پالس کوتاه یکسان نمیسازد. برای تغییر معمول CCR1 در این پروژه، OC1PE را فعال نگه دارید و منتظر Update طبیعی بمانید. در تابع بازگشت از Stop، UG زمانی صادر میشود که تایمر متوقف است تا مقدارهای بافر پیش از شروع بارگذاری شوند. نوشتن PSC، ARR و CCR سه دسترسی مستقل است؛ Preload بهتنهایی آنها را به یک تغییر اتمیک همزمان تبدیل نمیکند.

چگونه تایمر 3 را روی پایه PA6 برد GebraBit پیکربندی کنیم؟
در این پروژه از برد آموزشی GebraBit GB100ST V2.0 مجهز به میکروکنترلر STM32F303CBT6 در بستهبندی LQFP48 استفاده میکنیم. طبق برگه اطلاعات رسمی (Datasheet DS9118 Rev 14)، کانال 1 تایمر 3 (TIM3_CH1) از طریق نگاشت عملکرد متناوب شماره 2 (AF2) به پایه فیزیکی 16 یعنی PA6 هدایت میشود.
LED کاربر برد به PB6، پایهٔ فیزیکی 42 تراشه، وصل است؛ با PWM روی PA6 نور آن تغییر نمیکند. برای آزمایش اصلی، PA6 نامگذاریشدهٔ برد را به ورودی پرامپدانس اسیلوسکوپ وصل میکنیم. عدد 16 شمارهٔ پایهٔ بدنهٔ LQFP48 است، نه پین شمارهٔ 16 هدر؛ روی برد، برچسب PA6 را پیدا کنید. اگر برای تمرین LED خارجی اضافه میکنید، مقاومت سری و جریان مجاز را بر اساس LED و مشخصات خروجی انتخاب کنید؛ در بستهٔ این درس قطعهٔ LED خارجی مشخصی آزموده نشده است.
جدول نگاشت تنظیمات CubeMX به رجیسترهای میکروکنترلر
هنگام پیکربندی در محیط STM32CubeMX، گزینههای گرافیکی مستقیماً به رجیسترهای سختافزاری زیر متصل میشوند:
| پارامتر در محیط CubeMX | مقدار تنظیمی برای پروژه | رجیستر یا بیت سختافزاری متناظر | توضیح عملکرد مهندسی |
|---|---|---|---|
| Prescaler (PSC) | 71 | TIM3_PSC | تقسیم کلاک مبنای 72MHz بر 72 برای رسیدن به فرکانس شمارش 1MHz |
| Counter Period (ARR) | 999 | TIM3_ARR | تعیین سقف شمارش و ایجاد دوره تناوب 1000 میکروثانیهای (1kHz) |
| auto-reload preload | Enable | بیت ARPE در TIM3_CR1 | بافر کردن رجیستر پریود جهت جلوگیری از تغییر ناگهانی سقف شمارش |
| Mode | PWM mode 1 | بیتهای OC1M[2:0] در TIM3_CCMR1 | فعالسازی سطح High تا زمان کوچکتر بودن CNT از CCR1 |
| Pulse (CCR1) | 250 | TIM3_CCR1 | تعیین نسبت وظیفه اولیه سیگنال خروجی |
| Preload کانال؛ فعالسازی OC1PE در HAL | Enable | بیت OC1PE در TIM3_CCMR1 | بافر کردن رجیستر مقایسه برای اعمال مقدار در مرز Update |
| CH Polarity | High | بیت CC1P در TIM3_CCER | انتخاب High به عنوان سطح فعال خروجی |
این تنظیمات در کد تولیدشده چه شکلی دارند؟
چند خط منتخب از MX_TIM3_Init در Core/Src/tim.c واقعی؛ این تابع را دوباره در main تعریف نکنید. تنظیم را از IOC تغییر دهید و کد را بازتولید کنید. OC1PE درون HAL_TIM_PWM_ConfigChannel همین نسخه فعال میشود.
htim3.Instance = TIM3;
htim3.Init.Prescaler = 71;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 999;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 250;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
در HAL دقیق همین پروژه، TIM_Base_SetConfig هنگام UG راهاندازی موقتاً URS را فعال میکند تا UIF ناشی از آن ایجاد نشود؛ سپس CR1 را برمیگرداند. این رفتار را با UG صریح کاربر پس از راهاندازی یکی نگیرید. ثبت نسخهٔ درایور برای چنین تفاوتهایی مهم است.
چگونه PWM را شروع، تغییر و متوقف کنیم؟
پس از تولید کدهای اولیه توسط CubeMX، کنترل خروجی در دست نرمافزار قرار میگیرد. درایور HAL توابع استانداردی را برای شروع، تغییر و توقف سیگنال فراهم کرده است.
آغاز سیگنال با HAL_TIM_PWM_Start
برای آنکه موج مربعی روی پایه فیزیکی ظاهر شود، باید خروجی کانال و شمارنده تایمر را با دستور زیر روشن کنیم:
در Core/Src/main.c، پس از MX_TIM3_Init و داخل USER CODE 2:
/* USER CODE BEGIN 2 */
if (HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1) != HAL_OK)
{
lesson_hal_error = 1U;
Error_Handler();
}
/* USER CODE END 2 */
این تابع در لایه رجیستری، بیت CC1E را در رجیستر TIM3_CCER یک میکند تا بافر خروجی به پایه فیزیکی متصل شود و سپس بیت CEN در TIM3_CR1 را برای به حرکت درآوردن شمارنده فعال میسازد.
تغییر Duty با نوشتن مقدار مقایسه
در حین اجرای برنامه، برای تغییر دادن نسبت وظیفه نیازی به توقف تایمر نیست. مناسبترین روش، نوشتن مستقیم مقدار جدید درون رجیستر مقایسه با ماکروی کارآمد زیر است:
دستورهای واقعی case 0 در USER CODE 3 فایل main.c؛ خود switch و حلقه در پروژهٔ کامل هستند:
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 250U);
HAL_Delay(1000U);
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 500U);
HAL_Delay(1000U);
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 750U);
HAL_Delay(1000U);
این ماکرو مقدار مقایسهٔ کانال انتخابشده را مینویسد. چون OC1PE فعال است، مقدار در بخش پیشبارگذاری میماند تا Update مجاز آن را اعمال کند. اگر چند بار پیش از یک Update بنویسید، آخرین مقدار موجود منتقل میشود؛ Preload صفِ همهٔ درخواستها نیست. این قطعه از case 0 حلقهٔ اصلی پروژه آمده است؛ برای اجرا آن را همراه پروژهٔ کامل استفاده کنید.
توقف سیگنال و مدیریت الزامات سطح ایمن پایه
هنگامی که کار سیستم به پایان میرسد، با فراخوانی دستور توقف میتوان خروجی تایمر را قطع کرد:
تابع واقعی در USER CODE 4 فایل main.c؛ در حالت 3 حلقه فراخوانی میشود:
static void PWM_StopLow(void)
{
GPIO_InitTypeDef pin = {0};
if (HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1) != HAL_OK)
{
lesson_hal_error = 2U;
Error_Handler();
}
HAL_GPIO_WritePin(PWM_OUT_GPIO_Port, PWM_OUT_Pin, GPIO_PIN_RESET);
pin.Pin = PWM_OUT_Pin;
pin.Mode = GPIO_MODE_OUTPUT_PP;
pin.Pull = GPIO_NOPULL;
pin.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(PWM_OUT_GPIO_Port, &pin);
}
HAL_TIM_PWM_Stop کانال را غیرفعال میکند و در شرایط مجاز شمارنده را نیز متوقف میکند؛ آن را فرمان عمومیِ «تبدیل پایه به GPIO خروجی Low» ندانید. برای داشتن سطح Low مشخص در این نمونه، تابع PWM_StopLow ابتدا Stop را بررسی میکند، مقدار خروجی GPIO را Low مینویسد و PA6 را به Output Push-Pull تبدیل میکند؛ یعنی پایه بهطور فعال صفر منطقی را میراند.
این روش مربوط به حالت عادیِ MCU تغذیهشده و اجرای صحیح برنامه است؛ مدار ایمنیِ مستقل برای Reset یا قطع تغذیه نیست. برای بار قدرت، طراحی درایور و وضعیت پیشفرض سختافزاری باید جدا بررسی شود. در این درس فقط خروجی منطقی کمتوان را آزمایش میکنیم.
در عمل چه نتایجی میبینیم و چطور مدار را آزمایش کنیم؟
برای آزمایش پیشنهادی، برد، ST-LINK با SWD، اسیلوسکوپ و پروب مناسب سیگنال 3.3V لازم است. نوک پروب را به PA6 نامگذاریشدهٔ برد و گیرهٔ زمین را به GND همان مدار وصل کنید. ورودی اسیلوسکوپ پرامپدانس باشد، نه بار 50Ω؛ اگر پروب روی 10x است، ضریب کانال دستگاه نیز 10x تنظیم شود. تغذیه را از ورودی مجاز برد بدهید؛ 5V را به PA6 یا پایهٔ 3V3 وارد نکنید. شکل، اتصال منطقی است و شمارهٔ هدر را از شمارهٔ پایهٔ تراشه نتیجه نگیرید.

آزمایش 1: ارزیابی شکلموج 1 کیلوهرتز در سه نسبت وظیفه
همهٔ نتیجههای زیر پیشبینیاند؛ پروگرام، اجرای برد و ثبت اسیلوسکوپ در تهیهٔ این مقاله انجام نشدهاند. ابتدا جدول زمان و میانگین ولتاژ را خودتان حساب کنید. پروژه را بسازید و پروگرام کنید و در اجرای آزاد اندازه بگیرید؛ توقف دیباگر میتواند مشاهدهٔ زمان را تغییر دهد. با lesson_mode=0، برنامه 25%، 50% و 75% را با HAL_Delay(1000) بهترتیب انتخاب میکند. این تأخیر نرمافزاری حدود 1s است، نه تضمین تغییر دقیق در هر ثانیه؛ خود دورهٔ PWM را سختافزار تعیین میکند.
این قطعه از switch در USER CODE 3 است؛ تابع کامل PWM_PreloadTrial و نگهبان انتظار در پروژه آمدهاند:
case 1U:
PWM_PreloadTrial(1U);
break;
case 2U:
PWM_PreloadTrial(0U);
break;
برای شمارش صعودی، PWM1 و قطبیت High با PSC=71 و ARR=999، انتظار اسمی چنین است. میانگین و مقدار مؤثر RMS یک کمیت نیستند؛ جدول فقط میانگین را نشان میدهد.
| CCR1 | Duty | T | T_ON | میانگین ایدهآل؛ High=3.3V و Low=0V |
|---|---|---|---|---|
| 250 | 25% | 1000us | 250us | 0.825V |
| 500 | 50% | 1000us | 500us | 1.65V |
| 750 | 75% | 1000us | 750us | 2.475V |
دوره و زمان High را برای هر گام در کاربرگ همراه ثبت کنید؛ تلرانس کلاک، ولتاژ، بار، تنظیم پروب و دقت دستگاه را نیز بنویسید. معیار پذیرش، سازگاری با این محاسبه در حدود خطای واقعی ابزار و کلاک است؛ عدد محاسبهشده را بهجای نتیجهٔ اندازهگیری وارد نکنید.
آزمایش 2: بازتولید پالس ناقص با خاموش کردن رجیستر پیشبارگذاری
برای مقایسهٔ کنترلشده، در اجرای آزاد و با کمک متغیر lesson_mode، حالت 1 را برای Preload روشن و حالت 2 را برای خاموش انتخاب کنید. کد ابتدا CCR1=750 را در یک مرز دوره آماده میکند، سپس پس از رسیدن شمارنده به حوالی 500، CCR1=250 مینویسد. برای شکار دورهٔ گذار از Trigger و ثبت تکمرحلهای مناسب دستگاه استفاده کنید؛ هر چرخهٔ صفحه لزوماً پالس گذار نیست. مسیر خاموشکردن Preload، آزمایش عمدی اثر تغییر ناهمزمان است و الگوی پیشنهادی PWM پیوستهٔ برنامهٔ نهایی نیست. تأخیر Polling و وقفهها مانع ادعای نوشتن دقیقاً در 500us میشوند.
انتظارهای فاز در کد با تابع PWM_WaitPhase محدود شدهاند؛ اگر انتظار بیش از 20 تیک HAL طول بکشد، خطای 5 ثبت، خروجی Low و Error_Handler اجرا میشود. این نگهبان به پیشرفت SysTick وابسته است و Watchdog مستقل نیست.
برای آزمایش Stop، lesson_mode=3 را انتخاب کنید. برای آزمودن مقدار ثابت، حالت 4 و lesson_duty_permille در بازهٔ 0 تا 1000 را به کار ببرید؛ مثلاً 250 یعنی 25% و 1000 یعنی 100% در ARR ثابت 999. مقدار بزرگتر خطای 4 میدهد. تغییر mode در حالت پلهای ممکن است تا پایان دور فعلی حلقه، حدود 3s، منتظر بماند؛ این کنترل دیباگر فرمان توقف اضطراری نیست. برگشت از حالت 3، تنظیم AF و PWM را از تابع بازگشت پروژه برقرار میکند. محوشدن تدریجی نور هنوز تمرین است و در حالت پیشفرض اجرا نمیشود.
اگر خروجی مطابق انتظار نبود چه کنیم؟
از مسیر واقعی سیگنال شروع کنید: کلاک TIM3، تنظیم شمارش و کانال، شروع موفق HAL و در پایان نگاشت PA6. این بررسیها به پیدا کردن علت کمک میکنند:
- فراموش کردن تابع استارت: پیکربندی CubeMX عالی است، اما شکلموجی تولید نمیشود. دلیل آن فراموش کردن فراخوانی HAL_TIM_PWM_Start در بدنه اصلی برنامه است.
- اشتباه گرفتن پایه خروجی با LED برد: تلاش برای دیدن تغییر نور روی LED متصل به PB6، در حالی که تایمر روی PA6 تنظیم شده است.
- خطای محاسباتی مخرج فرمول: فراموش کردن عدد 1 در عبارت ARR + 1 یا PSC + 1 که محاسبهٔ فرکانس یا Duty را تغییر میدهد.
- نادیده گرفتن دوبرابرکننده کلاک APB1: استفاده از عدد 36 مگاهرتز به جای 72 مگاهرتز در محاسبات دستی که سبب میشود فرکانس عملی دقیقاً دو برابر مقدار پیشبینیشده باشد.
- اتصال مستقیم موتور به پایه میکروکنترلر: پایههای میکروکنترلر حداکثر توانایی تامین جریانی در حدود چند میلیآمپر را دارند. اتصال مستقیم بار القایی به پایه میکروکنترلر میتواند جریان برگشتی شدیدی ایجاد کرده و تراشه را بسوزاند. همواره از ترانزیستور، ماسفت یا درایور مناسب مجهز به دیود هرزگرد (Flyback Diode) استفاده کنید.
پرسشهای خودآزمایی
دانشجویان عزیز میتوانند با پاسخ دادن به پرسشهای مفهومی زیر، میزان درک خود را از مباحث ارائهشده ارزیابی کنند:
پرسش 1: با وجود آنکه کلاک گذرگاه APB1 برابر 36 مگاهرتز است، چرا کلاک تایمر 3 برابر 72 مگاهرتز محاسبه شد؟
پاسخ: زیرا در مسیر کلاک APB1 به TIM3 این تراشه، وقتی مقسم APB1 غیر از 1 باشد، سختافزار فرکانس ساعت ورودی تایمرهای آن گذرگاه را به طور خودکار در 2 ضرب میکند (36 × 2 = 72 MHz).
پرسش 2: در شمارش صعودی با ARR = 999، دقیقاً چند پله زمانی در یک دوره تناوب وجود دارد و چند سطح گسسته برای تنظیم نسبت وظیفه در دسترس است؟
پاسخ: تعداد پلهها یا کوانتاهای زمانی برابر با N = ARR + 1 = 1000 پله است. از آنجا که مقدار رجیستر مقایسه میتواند از 0 تا 1000 تغییر کند، در مجموع N + 1 = 1001 سطح گسسته (شامل صفر درصد تا صد درصد کامل) وجود دارد.
پرسش 3: اگر ARR = 999 باشد و کاربر به اشتباه مقدار CCR = 1500 را در رجیستر بنویسد، خروجی چه رفتاری نشان خواهد داد؟
پاسخ: شمارنده حداکثر تا 999 میشمارد؛ بنابراین شرط CNT < CCR همواره در طول تمام چرخه برقرار است. در نتیجه خروجی به طور دائمی در سطح High باقی میماند و نسبت وظیفه روی 100% اشباع میشود (نه فراتر از 100%).
پرسش 4: نقش کلیدی بیت OC1PE چیست و عدم فعالسازی آن چه اثری دارد؟
پاسخ: این بیت بافر پیشبارگذاری رجیستر مقایسه کانال 1 را فعال میکند. اگر فعال نباشد، هرگونه تغییر در مقدار CCR بلافاصله در وسط دوره به مقایسهگر اعمال میشود که میتواند منجر به تولید پالس ناقص یا گلیچ گردد.
پرسش 5: چرا برای داشتن خروجی Low مشخص، Stop بهتنهایی جای تنظیم GPIO را نمیگیرد؟
پاسخ: زیرا Stop کانال را غیرفعال میکند، اما جای تنظیم صریح GPIO خروجی Low را نمیگیرد. برای نیاز این نمونه، PWM_StopLow هم Stop و هم تغییر تنظیم پایه را انجام میدهد؛ وضعیت Reset یا قطع برق باید در طراحی مدار جدا بررسی شود.
پرسش 6: با ARR=65535 و CCR شانزدهبیتی، چرا در PWM1 صعودی و قطبیت High، مقایسه بهتنهایی 100% کامل نمیدهد؟
پاسخ: در شمارش صعودی، PWM1 و قطبیت High، برای 100% با روش مقایسه باید CCR=65536 باشد، ولی CCR شانزدهبیتی حداکثر 65535 است. بنابراین با همین مد و قطبیت، بالاترین کسر High برابر 65535/65536 است. راه دیگر، انتخاب آگاهانهٔ حالت خروجی ثابت مناسب است؛ نوشتن عدد خارج ظرفیت راهحل نیست.
تمرینهای کاربردی و توسعه مهارت
- تمرین 1 (تنظیم بالاترین تفکیکپذیری در 20 کیلوهرتز): مقادیر PSC و ARR را به گونهای محاسبه کنید که در کلاک 72 مگاهرتز، سیگنال PWM با فرکانس دقیق 20 کیلوهرتز و بالاترین تفکیکپذیری ممکن تولید شود. (راهنمایی: مقدار PSC = 0 و ARR = 3599 منجر به 3600 پله زمانی با تفکیکپذیری 0.0278% میشود).
- تمرین 2، تنفس LED خارجی: ابتدا مدار LED و مقاومت سری مناسب را مشخص کنید. برای نمونهٔ آموزشی با ARR=999 و x در بازهٔ 0 تا 1، از رابطهٔ CCR = round(1000 × x^γ) استفاده کنید؛ x را آهسته از 0 تا 1 و برعکس تغییر دهید. γ=2.2 فقط یک انتخاب تجربی برای مقایسه با تغییر خطی است، نه قانون همگانی چشم یا کالیبراسیون LED. خروجی را به 0 تا 1000 محدود کنید و اگر ARR را تغییر دادید، N=ARR+1 و ظرفیت CCR را دوباره بررسی کنید. این تمرین در پروژهٔ پیشفرض پیاده نشده است.
- تمرین 3 (دو کانال همفرکانس با نسبت وظیفه مستقل): کانال 2 تایمر 3 (TIM3_CH2) را روی پایه PA7 (پایه فیزیکی شماره 17 با نگاشت AF2) فعال کنید. با استفاده از شمارنده مشترک TIM3، برنامهای بنویسید که هر دو کانال با فرکانس یکسان 1 کیلوهرتز کار کنند، اما کانال 1 دارای نسبت وظیفه 30% و کانال 2 دارای نسبت وظیفه 70% باشد.
وضعیت ساخت، اسناد مرجع و فایلهای پروژه
پروژهٔ قابل دریافت بر پایهٔ کد واقعی CubeMX ساخته شده است. خلاصهٔ بررسی انجامشده در ادامه است؛ گزارش کامل نسخهها، فایلها و محدودیتها داخل ZIP قرار دارد.
پروژهٔ واقعی gebrabit_lesson19_pwm_refresh با CubeMX 6.14.0، DB 6.0.140 و CubeF3 1.11.6 تولید و با GCC 13.2.1، ساخت پاک Debug و گزینههای -O0 -g3 -Wall -Werror بدون خطا و هشدار کامپایل شده است. Flash برابر 12668B و RAM ناحیهٔ لینک برابر 1672B است؛ مصرف زنده اندازهگیری نشده است. مدل محاسباتی و بررسی فایلها 36 کنترل را گذراندهاند. این نتایج کامپایل و مدلاند؛ اجرای برد، محیط گرافیکی CubeIDE و اندازهگیری اسیلوسکوپ انجام نشدهاند. خروجی پروژه CMake است؛ راه واردکردن یا تولید خروجی CubeIDE در راهنما آمده و اجرای آن ادعا نشده است.
برای مطالعه عمیقتر مباحث سختافزاری و بررسی ساختار درایورها، منابع رسمی و معتبر زیر پیشنهاد میشوند:
- برگه اطلاعات ریزکنترلر: STM32F303xB/C Datasheet DS9118 Rev 14 جهت بررسی مشخصات پایهها و نگاشت AF.
- کتابچه راهنمای مرجع: STM32F303 Reference Manual RM0316 برای تحلیل کامل ثباتهای تایمر عمومی.
- یادداشت کاربردی تایمرهای STM32: AN4776 Application Note (Rev 5) حاوی نکات کاربردی تولید موج و تنظیمات پیشرفته.
- شماتیک سختافزاری برد: GebraBit STM32F303 Board Schematic V2.0 جهت بررسی آرایش پایهها و پینهدرها.
- مخزن درایور رسمی HAL: STM32F3xx HAL Driver Source (Commit 953955a) جهت مشاهده پیادهسازی داخلی توابع تایمر.
فایلهای کامل پروژه شامل پیکربندی CubeMX، کدهای مبدا و مستندات تکمیلی از پیوند زیر قابل دریافت هستند:
دانلود پروژه و فایلهای همراه درس 19
پروژهٔ کامل با فایل IOC و سورس، ELF/MAP واقعی و گزارش کامپایل، مدل محاسباتی، کاربرگ آزمایش و 7 تصویر با فایل قابل ویرایش داخل ZIP است. ابتدا فایل را استخراج و README را بخوانید.
پیوند دانلود به نسخهٔ مشخص همراه مقاله متصل است. گزارشها به معنی انجام آزمایش روی برد نیستند.