موج PWM یکی از مهم ترین و پرکاربردترین امواج در دنیال الکترونیک و دیجیتال به شمار می رود. این موج می تواند ولتاژی بین ۰ الی ۵ ولت در خروجی ایجاد کند. این در حالیست خروجی دیجیتال تنها قادر به ایجاد ولتاژ ۰ یا ۵ است. پلتفرم آردوینو تابع analogWrite را جهت ایجاد موج PWM در خروجی معرفی کرده است. این تابع در بردهای آردوینو با هسته AVR، تنها بر روی برخی از پایه ها قادر به ایجاد موج PWM است. در این آموزش به روشی ساده، اقدام به ایجاد موج PWM روی کلیه پایه های دلخواه می نماییم. در ادامه آموزش PWM آردوینو با مرجع تخصصی آردوینو به زبان فارسی ، دیجی اسپارک همراه باشید.
موج PWM تعریف و توضیح
همانطور که اطلاع دارید، بردهای آردوینو خروجی دیجیتال ایجاد می کنند. این خروجی دیجیتال یا ۰ یا ۱ است. از دید ولتاژی، این خروجی یا ۰ ولت یا ۵ ولت است. اما در بسیاری از پروژه ها، نیاز به ولتاژی بین ۰ الی ۵ داریم. در بعضی دیگر از پروژه ها می بایست ولتاژ را بین صفر و پنج تغییر دهیم. به عنوان مثال فرض کنید قصد داریم تا شدت نور یک LED را کنترل کنیم. در این حالت ولتاژ تغذیه LED باید تغییر کند. در بسیاری دیگر از پروژه ها، نیاز داریم تا از طریق یک ترانزیستور یا درایور موتور، دور یک موتور را کنترل نماییم. در این حالت هم با ارسال فرمان از طریق آردوینو، نیاز به تغییر ولتاژ پایه خروجی آردوینو امری مهم است.
به طور کلی، سیگنال PWM معادل آنالوگ یک سیگنال دیجیتال را تعریف می کند. به کمک سیگنال PWM، می توانیم ولتاژهایی بین ۰ الی ۵ را تولید کند. اما یک ولتاژ آنالوگ از روی دو ولتاژ دیجیتال چگونه تولید می شود؟ قسمت بعدی را مطالعه کنید.
سیکل وظیفه فرکانس و نحوه ایجاد ولتاژ آنالوگ از روی ولتاژ دیجیتال
در این قسمت، در رابطه با چگونگی ایجاد ولتاژ آنالوگ از روی ولتاژهای دیجیتال صحبت می کنیم. همانطور که می دانید، خروجی دیجیتال آردوینو تنها دو ولتاژ، ۵ ولت برای یک منطقی و ۰ ولت برای ۰ منطقی ایجاد می کند. تکنیک ساخت ولتاژ آنالوگ از روی ولتاژهای دیجیتال، با مفهومی به نام Duty Cycle تعریف می شود. برای ساده تر شدن موضوع، یک بازه ۱۰ ثانیه ای را در نظر بگیرید. در این بازه ۱۰ ثانیه ای، برای ۷ ثانیه پایه خروجی دیجیتال یک منطقی(۵ولت) و برای ۳ ثانیه این پایه صفر منطقی(۰ ولت) است.
مطابق تصویر فوق، در ۷۰ درصد مواقع خروجی پنج ولت(لامپ روشن) و در ۳۰ درصد باقی مانده، خروجی ولت ۰(لامپ خاموش) است. در این حالت، ۷۰ درصد پنج ولت، برابر با ۳٫۵ خواهد شد. این یعنی اینکه در یک بازه ۱۰ ثانیه ای، به اندازه ۷۰ درصد ۵ ولت یا به عبارت دیگر ۳٫۵ ولت، خروجی روشن بوده و در بقیه موارد، خروجی خاموش بوده است. بدین ترتیب، با این تعریف می توان ولتاژ آنالوگ تولید کرد. البته در این مثال ۱۰ ثانیه مقدار زیادی است، در عمل با فرکانس های بالا، مثلا ۵۰۰ هرتز این سیگنال ساخته می شود. همانطور که اطلاع دارید، یک تقسیم بر فرکانس، برابر با زمان خواهد شد. با تقسیم یک بر روی ۵۰۰، به عدد ۰٫۰۰۲ می رسیم. این عدد معادل ۲ میلی ثانیه خواهد بود. از آنجاییکه فرکانس بسیار بالاست، لذا عمل تغییر ولتاژ خیلی سریع انجام می شود. در این حالت ولتاژ خروجی را ۳٫۵ خواهیم دید. این مورد شبیه به برق شهری با فرکانس ۵۰ هرتز است. در این مورد، لامپ متصل به برق دائم در حال روشن و خاموش شدن است. اما چون فرکانس بسیار بالاست، این مورد احساس نمی شود. مطابق تصویر فوق، رابطه اساسی در Duty Cycle، به صورت زیر تعریف می شود.
Duty Cycle=TON/(TON+TOFF)
در رابطه فوق، TON مدت زمان روشن بودن لامپ(خروجی ۵ ولت یا یک منطقی) و TOFF مدت زمان خاموش بودن است. بدین ترتیب ولتاژ خروجی به کمک مدت زمان روشن و خاموش بودن با تعیین فرکانس، ساخته می شود. در رابطه با سیگنال PWM در آردوینو، آموزشی نیز منتشر شده که با کلیک بر روی این لینک، قابل دسترسی است. پس از اطلاع از نحوه کارکرد سیگنال PWM، در قسمت بعدی به نحوه ساخت این سیگنال در آردوینو و بدون تابع analogWrite، می پردازیم.
ایجاد سیگنال PWM بدون تابع analogWrite
در قسمت های پیشین، در رابطه با ایجاد سیگنال PWM و مبانی آن صحبت کردیم. اما در این جا قصد داریم تا به پیاده سازی آن با روشی غیر از تابع analogWrite بپردازیم. همانطور که در قسمت پیش گفته شد، سیگنال PWM، با دو پارامتر اصلی زیر همراه است.
- فرکانس
- Duty Cycle
در این روش قصد داریم تا به کمک تابع delayMicroseconds سیگنال PWM ایجاد کنیم. همانطور که اطلاع دارید، این تابع یک تاخیر به اندازه میکروثانیه ایجاد می کند. در این روش، با دریافت فرکانس و پس از آن محاسبه زمان(۱/F)، بازه زمانی محاسبه می شود. پس از آن، با دریافت سیکل وظیفه، مدت زمان خاموش و روشن بودن تعیین می گردد. پس از محاسبه زمان روشن و خاموش، پایه مورد نظر به مدت محاسبه شده، یک و صفر خواهد شد. به عبارت دیگر، ابتدا پایه مورد نظر یک منطقی می شود. سپس برای مدت زمانی که باید خروجی یک باشد(Duty Cycle)، تابع delayMicroseconds تاخیر لازم را ایجاد می کند. پس از طی زمان لازم برای یک بودن خروجی، پایه خروجی صفر شده و برای مدت زمانی که باید صفر باشد، صفر خواهد شد. برای این مورد، برنامه نوشته شده در قسمت بعد را بررسی کنید.
کد برنامه ایجاد سیگنال PWM بدون تابع analogWrite
در این قسمت به بررسی کد برنامه ایجاد PWM می پردازیم. این کدها را در پایین مشاهده می کنید. در این کدها یک تابع تحت عنوان run_pwm تعریف شده که در ورودی اول فرکانس، ورودی دوم Duty Cycle و در ورودی سوم شماره پایه ای که قصد ایجاد پالس PWM را داریم، تعیین می گردد.
void setup() { Serial.begin(115200); pinMode(3, OUTPUT); digitalWrite(3, LOW); } void loop() { run_pwm(500, 50, 4); //تابع ایجاد پالس PWM } void run_pwm(unsigned int f, unsigned long duty, int pin) { //ورودی اول تابع فرکانس، ورودی دوم Duty Cycle و ورودی سوم شماره پایه ایست کهسیگنال روی آن ایجاد می گردد. float t = (float)1 / f; //محاسبه زمان t = t * 1000000; //چون بازه را میکرو ثانیه در نظر گرفتیم، زمان بدست آمده را در یک میلیون ضرب م یکنیم تا معادل میکرو ثانیه آن ایجاد شود. unsigned long ton=0; //متغیر برای محاسبه زمان روشن بودن unsigned long toff=0; //متغیر جهت محاسبه مدت زمان خاموش بودن ton = (duty * t) /100; //زمان روشن بودن درصدی از زمان کل است. بنابراین در زمان کل ضرب و بر صد تقسیم می شود. toff=((100-duty) * t) /100; //زمان خاموش بودن برابر با ۱۰۰ منهای زمان روشن بودن است. به عتوان مثال با دیوتی سایکل ۷۰، زمان خاموش بودن ۱۰۰ منهای ۷۰، یعنی ۳۰ است. //چون در حال محاسبه درصد هستیم، مقدار را بر صد تقسیم می کنیم. pinMode(pin, OUTPUT); digitalWrite(pin, LOW); //تعریف پایه مورد نظر کاربر به عنوان خروجی //************************************ digitalWrite(pin, HIGH); delayMicroseconds(ton); //روشن بودن //**************************************** digitalWrite(pin, LOW); delayMicroseconds(toff); //خاموش بودن }
اجرای پروژه PWM آردوینو
در این قسمت به اجرای یک نمونه سیگنال PWM می پردازیم. مطابق کد فوق، سیکل وظیفه ۵۰ درصد و فرکانس ۵۰۰ هرتز، روی پایه ۴ تعریف شده است. با اجرای کد فوق و اندازه گیری ولتاژ پایه ۴، مطابق تصویر زیر ولتاژ ۲٫۴۹ را مشاهده خواهید کرد.
دیوتی سایکل ۵۰ درصد، ولتاژی نصف ولتاژ ۵ ولت، یعنی ۲٫۵ ولت ایجاد می کند. در تصویر فوق این ولتاژ با اختلاف ۰٫۱ ایجاد شده است. شما نیز می توانید با تغییر سیکل وظیفه، ولتاژهای متفاوت دیگری دریافت کنید.
اشکالات ایجاد PWM بدون تابع analogWrite
همانطور که در این آموزش مورد بررسی و اجرا قرار گرفت، یک سیگنال PWM به روشی غیر از تابع analogWrite ایجاد نمودیم. اما این روش یک اشکال اساسی دارد. لطفا در این رابطه، به سوالات زیر به صورت کامنت در انتهای پست پاسخ دهید.
۱-اشکال ایجاد PWM در این روش چیست؟
۲-جهت رفع این مشکل و ایجاد موج PWM همچنان بدون استفاده از تابع analogWrite، چه پیشنهادی دارید؟
توجه داشته باشید که به صورت سخت افزاری، برخی از پایه های برد آردوینو UNO قابلیت ایجاد سیگنال PWM دارند. سوال دوم را به گونه ای پاسخ دهید که سیگنال PWM در تمام پایه ها، قابل ایجاد باشد.
لوازم متناسب با آموزش
جمع بندی
در این آموزش به ایجاد پالس PWM بدون استفاده از تابع analogWrite پرداختیم. سیگنال PWM، سیگنالی است که ولتاژی آنالوگ از روی ولتاژهای دیجیتال ایجاد می کند. در پلتفرم آردوینو و بردهای مبتنی بر میکروکنترلرهای AVR، پایه های سخت افزاری قابلیت تولید پالس PWM را دارند. در این آموزش به روشی پرداختیم که به کمک آن می توان تمام پایه های GPIO بردهای آردوینو مبتنی بر AVR پرداختیم. اما این روش یک اشکال اساسی دارد. در صورتیکه این ایراد را یافته اید و یا راه حلی برای آن دارید، لطفا در انتهای پست به صورت کامنت، نظر خود را ثبت کنید.
چنانچه مطالب این آموزش را گنک یافتید، بدون هیچ نگرانی در انتهای همین پست، به صورت ثبت نظر سوالتان را مطرح کنید. من در سریعترین زمان ممکن پاسخ رفع مشکل شما را خواهم داد. همچنین اگر ایرادی در مطالب درج شده وجود دارد میتوانید از همین طریق اطلاع رسانی کنید.
با سلام و احترام خدمت جناب صابری عزیز
در روش ایجاد pwm در حالت عادی از تایمر های میکروکنترلر استفاده میشه ولی در این روش که شما توضیح دادید از تایمر استفاده نشده است.
دستورات تاخیر (مانند: delay و millis و micros) از وقفه تایمر ۰ میکرو استفاده میکنند. ولی دستور delayMicroseconds از تایمر ها استفاده نمیکنه و با شمردن پالس های نوسانساز داخلی کار میکنه.
بنابراین ایرادی که این کد داره اینه که اگر برنامه دیگری همزمان با این کد اجرا بشه تمامی زمانبندی ها به هم میریزه.
برای رفع این مساله چند روش وجود داره.
روش اول: میتونید از وقفه تایمر ها استفاده کنید. که انتخاب تایمر برای این کار دقت زیادی میخواهد و بستگی به برنامه کلی شما دارد.
مثلا اگر از کتابخانه سروو موتور استفاده کرده باشید، بسته به اینکه چند تا سروو موتور استفاده کرده اید، یک، دو یا سه تا تایمر توسط این کتابخانه مورد استفاده قرار میگیرند. یا کتابخانه ها و توابع دیگری هم هستند که از تایمر ها استفاده میکنند. بعنوان مثال تابع tone() از تایمر ۲ استفاده میکنه.
بنابراین روش استفاده از تایمر، روش زیاد جالبی نیست.
روش بهتر: به نظر من روش استفاده از کتابخانه RTOS است و اینکه شما این دستورات را در یک لوپ جداگانه راه اندازی کنید تا مستقلا کار کند و دستورات دیگر، خللی در اجرای این کدها بوجود نیاورد.
در حالت RTOS شما میتوانید در هر بار اجرای لوپ، زمان توقف لوپ را یکبار روی ton و یکبار روی toff تنظیم کنید.
با تشکر از آموزش های جالبی که در سایت قرار میدهید.
سلام
بسیار عالی
ممنونم از به اشتراک گذاری اطلاعات