هر زمان صحبت از کنترل موتور الکتریکی با میکروکنترلر به میان می آید، حتما پای واحد تایمر / کانتر نیز در میان است. در میکروکنترلرهای STM32 نیز قابلیت هایی به عنوان ارزش افزوده برای واحد های تایمر کانتر در کاربردهای کنترل دور موتور درنظر گرفته شده است. باتوجه به امکانات تایمر در میکروکنترلرهای STM32، کنترل موتور DC با میکروکنترلرهای STM32 می تواند بسیار حرفه ای تر انجام شود. در صورت عدم اطلاعات کافی در رابطه با تایمر در STM32 و بخصوص قابلیت Capture در تایمر STM32 پیشنهاد می شود حتما دو پیش نیاز زیر را مطالعه بفرمایید:
انواع تایمر کانتر در میکروکنترلرهای STM32F1
کپچر در تایمر میکروکنترلرهای STM32 و دور موتور الکتریکی
فیلم آموزش تایمر در STM32 (فیلم آموزش ARM STM32 مقدماتی)
فیلم آموزش تایمر پیشرفته در STM32
فیلم آموزش تایمر در AVR (فیلم آموزش میکروکنترلرهای AVR مقدماتی)
فیلم آموزش تایمر پیشرفته در AVR
فیلم آموزش تایمر در آردوینو (فیلم آموزش آردوینو مقدماتی)
فیلم آموزش تایمر پیشرفته در آردوینو
ارتباط انکودر (Encoder) با آموزش تایمر در میکروکنترلرهای STM32
قبل از شروع لازم است تا کمی با Encoder آشنا شوید. انکودرها دستگاه هایی هستند که وظیفۀ تولید پالس خروجی متناسب با چرخش موتور را بر عهده دارند. قصد داریم سرعت چرخش یک موتور الکتریکی را بررسی کنیم و یا حتی قبل از آن، قصد داریم بدانیم سرعت چرخش موتور الکتریکی ما چقدر است؟ چطور می توان این سرعت را اندازه گیری کرد؟
پاسخ به این سوالات به صورت پایه ای و مفصل در نوشتۀ «کنترل دور موتور DC با AVR» ارائه شده است. اما اجازه دهید در این آموزش تایمر در میکروکنترلرهای STM32 هم مقداری انکودر را بررسی کنیم. برای کنترل موتور DC و بطور کلی کنترل دور موتور در وهله اول نیاز به قرائت دور موتور داریم. برای قرائت دور موتور هم نیاز به قطعه ای به نام انکودر موتور (Motor Encoder) داریم. انکودر موتور بر روی شفت موتورهای الکتریکی وصل می شود. انکودرها از یک صفحه فلزی دایره ای شکل و یک فرستندۀ مادون قرمز و یک گیرندۀ مادون قرمز تشکیل شده اند. فرستندۀ مادون قرمز وظیفۀ ارسال نور مادون قرمز را دارد که از محدودۀ بینایی انسان خارج است و امکان دیدن آن وجود ندارد و گیرندۀ انکودر وظیفۀ دریافت نور مادون قرمز تولید شده توسط فرستنده را دارد. اگر نور تولید شده توسط فرستنده، توسط گیرنده دریافت شود، یک خروجی (می تواند 1 یا 0 باشد) و اگر این نور به گیرنده نرسد، یک خروجی دیگر (می تواند 1 یا 0 باشد) تولید می کند. با مجموعۀ سطح های صفر و یک، یک پالس در خروجی انکودر ساخته می شود. این پالس خروجی به عنوان ورودی به یک میکروکنترلر داده شده و توسط میکروکنترلر آنالیز می شود. اگر به تصویر 1 توجه کنید، پروانۀ موجود در انکودر، بین فرستنده و گیرنده قرار می گیرد. با چرخش موتور این پروانه یا حلقه نیز می چرخد. اگر بین ورودی و خروجی، حفره قرار گیرد، نور از فرستنده به گیرنده با موفقیت منتقل می شود و سطح یک تولید می شود و اگر بخش فلزی این حلقه بین فرستنده و گیرنده قرار گیرد، نور از فرستنده به گیرنده نمی رسد و سطح صفر تولید می شود. البته انکودرها معمولاً دارای دو سیم خروجی هستند که سیگنال خروجی سیم دوم، در فاز مخالف سیگنال روی سیم اول است. ( آموزش تایمر در میکروکنترلرهای STM32 هنوز شروع نشده)
تصویر 1 – نمایش انکودر و ساختار داخلی آن
کنترل موتور شامل خواندن وضعیت موتور و فرمانی مناسب برای حرکت آن به صورت مطلوب است. این پروسه در تصویر زیر تشریح شده است.
تصویر 2 – دیاگرام پروسۀ کنترل موتور
تعریف پروژه کنترل موتور DC در آموزش تایمر در میکروکنترلرهای STM32
در این آموزش تایمر در میکروکنترلرهای STM32 روال کار به این صورت است که می خواهیم از قابلیت Capture تایمر General Purpose استفاده کنیم. خروجی انکودر را که پالسی است متناظر با حرکت موتور، به عنوان ورودی به تایمر مشخصی وارد می کنیم. سپس با استفاده از نرم افزار STM32CubeMX و روش های محاسبۀ سرعت حرکت موتور، به قرائت سرعت موتور می پردازیم. همچنین فرمان به موتور را توسط کلیدهایی، انجام می دهیم. به این صورت که با استفاده از چهار کلید، فرکانس و Duty Cycle پالس اعمال شده به موتور را افزایش یا کاهش می دهیم. همین طور می خواهیم اطلاعاتی نظیر فرکانس، Duty Cycle و سرعت چرخش موتور را در هر لحظه بر روی یک نمایشگر LCD نمایش دهیم. توجه کنید که نکتۀ مهمی که باید در این پروژه رعایت شود، عدم تأثیر تغییر Duty Cycle بر روی فرکانس و برعکس است.
آموزش تایمر در میکروکنترلرهای STM32، تنظیمات STM32CubeMX
در ابتدای آموزش تایمر در میکروکنترلرهای STM32 و کنترل دور موتور DC، لازم است تا وارد نرم افزار STM32CubeMX شوید و یک پروژۀ جدید بسازید. در این پروژه، در ابتدا میکروکنترلر مورد استفادۀ خود را انتخاب کنید. برای مثال ما در پروژه اصلی که در ویدئوی مربوط به این نوشته وجود دارد از میکروکنترلر STM32F103VET6 استفاده کرده ایم. اما توجه کنید که ادامۀ این متن را با استفاده از میکروکنترلر STM32F103C8T6 بررسی می کنیم. پس از آن صفحۀ پروژۀ شما باز شده و میکروکنترلر و بخش های تنظیمات مربوط به آن نمایش داده می شود.
در این قسمت از آموزش تایمر در میکروکنترلرهای STM32 لازم است تا چهار پایه را برای چهار کلید مورد نظر تعیین کنیم. به عنوان مثال ما پایۀ PA2 را به عنوان کلید سمت چپ، پایۀ PA3 را به عنوان کلید سمت راست، پایۀ PA4 را به عنوان کلید پایین و پایۀ PA6 را به عنوان کلید بالا انتخاب کرده ایم. روی هر پایه کلیک کرده، با انتخاب گزینۀ GPIO OUTPUT وضعیت پایه را مشخص کنید و با لیبل گذاری، برای هر پایه یک نام مناسب انتخاب کنید. اطلاعات بیشتر در رابطه با واحد GPIO در نوشتۀ «GPIO در STM32» ارائه شده است.
تصویر 3 – تعیین پایه های مربوط به کلید ها
بعد از آن به سراغ LCD کاراکتری خود می رویم. اتصال LCD به دو صورت می تواند انجام شود. می توان پایه های D1 تا D7 را به طور کامل به میکروکنترلر متصل کرد. در این حالت، اطلاعات به صورت هشت بیتی انتقال می یابد. یا اینکه می توان با اتصال پایه های D4 تا D7 اطلاعات را به صورت چهار بیتی از میکروکنترلر دریافت کرد. از روش دوم به منظور کاهش پایه های مورد نیاز استفاده می شود و روش اول نیز سرعت انتقال اطلاعات بالایی دارد. در آموزش تایمر در میکروکنترلرهای STM32 به منظور کاهش پایه های مورد نیاز، از روش دوم استفاده می کنیم. برای پایه های D4 تا D7، پایه هایی از میکروکنترلر را اختصاص می دهیم. به این صورت که پایه ها را در وضعیت GPIO OUTPUT قرار داده و برای هر کدام لیبل مشخصی را وارد می کنیم. همچنین برای استفاده از LCD لازم است تا از پایه های دیگری به منظور RS و RW و EN استفاده کنیم که این پایه ها را نیز بر روی میکروکنترلر مانند قبل تنظیم می کنیم.
مرحلۀ بعد در آموزش تایمر در میکروکنترلرهای STM32، فعال کردن پایه های Debug است. از سمت چپ در نوار Pinout به سراغ گزینۀ SYS رفته و نوع دیباگ را Serial Wire انتخاب می کنیم. در این مرحله، پایه های PA13 و PA14 به این بخش اختصاص داده می شود.
تصویر 4 – انتخاب روش Serial Wire به عنوان دیباگر در پروژۀ آموزش تایمر در میکروکنترلرهای STM32
تصویر 5 – نمایش پایه های LCD و Serial Wire در میکروکنترلر STM32F103C6T8
به سراغ مهم ترین بخش پروژه، یعنی تنظیم Timer در میکروکنترلرهای STM32F1 می رویم. در این پروژه ما می خواهیم تایمر 2 در STM32 را در مد ورودی قرار داده و آن را روی پایۀ PA0 فعال کنیم. همچنین از تایمر 3 به عنوان خروجی استفاده کرده و یک پالس PWM روی آن ایجاد کنیم و بدین منظور می خواهیم از پایۀ PB5 استفاده کنیم.
ابتدا از نوار ابزار سمت راست به سراغ TIM2 به معنی تایمر دوم بروید.Clock Source آن را بر روی کلاک داخلی یعنی Internal Clock تنظیم کنید. از کانال یک نیز در مد Input Capture استفاده کرده و آن را فعال کنید. همان طور که در گذشته نیز عنوان کردیم، مد Capture یکی از بهترین روش ها برای کنترل دور موتور DC با استفاده از میکروکنترلرهای STM32F1، نسبت به سایر روش هاست.
تصویر 6 – مرحلۀ اول تنظیمات تایمر دوم در پروژۀ آموزش تایمر در میکروکنترلرهای STM32
محاسبات در آموزش تایمر در میکروکنترلرهای STM32 از این قسمت آغاز می شود. به بخش Configuration مراجعه کنید تا تنظیمات مربوط به آن را انجام دهید. اولین مرحله انتخاب و تنظیم زمان است. تنظیم کنید که هر بار که شمارندۀ تایمر یک واحد می شمارد، چه زمانی می گذرد. به عنوان مثال ما در این پروژه مقدار Prescaler را برابر 7999 قرار داده ایم. به این معنی که کلاک CPU که 8 مگاهرتز تعیین شده است بر مقدار Prescaler به علاوه 1 تقسیم می شود و کلاک تایمر برابر خواهد شد با 1 کیلوهرتز. یعنی هر شمارش تایمر 1 میلی ثانیه زمان می برد. سپس به سراغ مقدار Auto Reload رفته و مقدار آن را نیز تعیین کنید. بهتر است برای جلوگیری از بعضی خطاها و مشکلات و بهینه سازی کد، این مقدار را در حداکثر مقدار ممکن یعنی 60000 تنظیم کنید. Polarity Selection را هم بر روی Rising Edge تنظیم کنید.
تصویر 7 – مرحلۀ دوم تنظیمات تایمر دوم در پروژۀ آموزش تایمر در میکروکنترلرهای STM32
به سراغ تایمر سوم می رویم. تایمری که قرار است در مد خروجی PWM Generation قرار گیرد. دو فرمول مهم و اساسی برای به دست آوردن مقادیر ARR و CCR داریم. در مرحلۀ اول مقدار PSC را برابر 1 قرار می دهیم. با توجه به اینکه این مقدار با عدد یک جمع می شود، کلاک CPU در نهایت بر دو تقسیم می شود و کلاک تایمر برابر با 4MHz خواهد شد. فرکانس کاری تایمر بر اساس رابطۀ زیر به دست می آید:
F=(4MHz)/Auto Reload
در نتیجه مقدار ARR برابر می شود با:
ARR=4MHz/F
در پروژه کنترل موتور DC با STM32،فرکانس کاری تایمر برابر 500Hz تعیین شده است. در نتیجه مقدار ARR برابر با 80 خواهد شد.
همچنین برای محاسبۀ CRR نیز باید از رابطۀ زیر استفاده کرد:
CCR=(Duty Cylce)*ARR/100
مقدار Duty Cycle را 50 درصد در نظر می گیریم، مقدار ARR نیز در مرحلۀ قبلی به دست آمد. در نهایت برای CRR عدد 40 به دست می آید.
در این بخش از آموزش تایمر در میکروکنترلرهای STM32 به سراغ تنظیمات تایمر سوم می رویم. اگر در نوار ابزار سمت چپ ابتدا کانال دوم تایمر سوم را فعال کنیم، پایۀ PA7 به این منظور فعال می شود. اما چون ما می خواهیم از پایۀ PB5 به این منظور استفاده کنیم، لازم است تا ابتدا روی پایۀ PB5 کلیک کرده، گزینۀ TIM3 CH2 را انتخاب کنیم و سپس به سراغ نوار ابزار سمت چپ صفحه یعنی Pinout برویم. در این بخش به سراغ TIM3 بروید. گزینۀ Internal Clock را تیک بزنید، در بخش Channel2 نیز، گزینۀ PWM Generation را انتخاب کنید.
تصویر 8 – مرحلۀ اول تنطیمات تایمر سوم در پروژۀ آموزش تایمر در میکروکنترلرهای STM32
اکنون به منوی Configuration در STM32CubeMX بروید و بر روی TIM3 کلیک کنید. مقدار PSC را برابر 1، مقدار Counter Period را برابر 80، مقدار Pulse را برابر 40 و CH Polarity را بر روی LOW قرار دهید.
تصویر 9 – مرحلۀ دوم تنظیمات تایمر سوم در پروژۀ آموزش تایمر در میکروکنترلرهای STM32
تا بدین جا تنظیمات مربوط به نرم افزار STM32CubeMX در آموزش تایمر در میکروکنترلرهای STM32 و کنترل دور موتور DC به اتمام رسید. گزینۀ Generate Code را بزنید و با تنظیم KEIL ورژن پنج، کد مورد نظر را تولید کرده و سپس آن را باز کنید. نوشتۀ « نحوۀ ساخت پروژه در نرم افزار KEIL » دربارۀ این موضوع مفید است.
آموزش تایمر در میکروکنترلرهای STM32، کدنویسی در نرم افزار KEIL
در ابتدای راه اندازی تایمر در STM32 لازم است تا کتابخانه های مورد نظر را به پروژه کنترل موتور DC با STM32 در نرم افزار KEIL اضافه و آن ها را فراخوانی کنیم. پس کتابخانۀ LCD کاراکتری که با توابع کتابخانۀ HAL نوشته شده است را درون فایل src قرار می دهیم و سپس از بخش File Extensions، در قسمت Groups، روی گروه Application Users کلیک کرده و بر روی Add Files کلیک کنید و کتابخانه را از درون فایل src وارد کنید. پس از آن خواهید دید که فایل کتابخانه به کتابخانه های موجود دیگر در لیست اضافه شده است. حال باید آدرس فایل های با پسوند .h کتابخانه را به نرم افزار KEIL بدهیم. برای این کار به سراغ گزینۀ Options for Target رفته و وارد منوی ++C شده، روی بخش Include Paths کلیک کرده، یک آدرس جدید ایجاد کنید و آدرس فایل کتابخانه را به این لیست اضافه کنید.
تصویر 10 – اضافه کردن کتابخانۀ my lib به فایل اصلی پروژه
تصویر 11 – اضافه کردن فایل کتابخانه به فایل پروژه در نرم افزار KEIL
تصویر 12 – اضافه کردن آدرس کتابخانه به پروژه
توضیحات تکمیلی دربارۀ کتابخانه زبان C، در لینک «ساخت کتابخانه در زبان C» ارائه شده است. اکنون می توانید این کتابخانه را در ابتدای پروژه فراخوانی کنید:
#include "my_clcd_lib.h"
در این آموزش تایمر در میکروکنترلرهای STM32، از LCD نیز استفاده شده است. در نتیجه باید کتابخانۀ آن را نیز فراخوانی کرد:
#include <stdio.h>
تا اینجا کتابخانه های لازم فراخوانی شدند. اکنون زمان آن است که متغیرهای مورد استفاده که به صورت global و محلی هستند به پروژه اضافه شوند. درون کد، بخش Variables وجود دارد. برای نوشتن متغیر ها، که متغیرها را باید در قسمت Begin و End آن که مشخص شده، نوشت. توضیحات تکمیلی در رابطه با متغیرها، در لینک «انواع متغیر در زبان برنامه نویسی C» و همچنین «کلاس حافظه در C و C++» ارائه شده است. در این قسمت متغیرهای مورد نیاز Global را تعریف کنید:
volatile unsigned char TcntStartPoint=0, DutyCycle=50, LcdRefCounter=0;
volatile float RpmIn=0;
volatile char CaptureCounter=0 ;
پس از آن، نوبت می رسد به متغیرهای محلی که باید درون تابع اصلی main تعریف شوند. پس درون تابع main در همان بخش ابتدایی درون Begin و End متغیرهای محلی را تعریف کنید:
char LcdStr[20] , counter=0;
static int Freq = 200;
اکنون زمان آن است که کد مورد نظر برای کنترل موتور را درون حلقۀ while بنویسید:
if ( HAL_GPIO_ReadPin(KeyLeft_GPIO_Port,KeyLeft_Pin) == 0 )
{
if (DutyCycle>10)
{
while ( HAL_GPIO_ReadPin(KeyLeft_GPIO_Port,KeyLeft_Pin) == 0 );
DutyCycle -=10;
TIM3->CCR2 = (int)(((DutyCycle * 0.01) * (TIM3->ARR)) );
}
}
else if ( HAL_GPIO_ReadPin(KeyRight_GPIO_Port,KeyRight_Pin) == 0 )
{
if ( DutyCycle<90 )
{
while ( HAL_GPIO_ReadPin(KeyRight_GPIO_Port,KeyRight_Pin)== 0 );
DutyCycle +=10;
TIM3->CCR2 = (int)(((DutyCycle * 0.01) * (TIM3->ARR)) );
}
}
وقتی در نرم افزار STM32CubeMX پایه ها را نام گذاری و پروژه را می سازیم، به صورت خودکار در فایل main.h یک سری Define برای اختصاص نام وارد شده توسط ما به پایۀ مورد نظر انجام می شود. در قسمتی از کدهای فوق که به عنوان ورودی های توابع نوشته شده اند(مثل KeyRight_GPIO_Port)، از این دیفاین های فایل main.h استفاده شده است. در این قسمت از نمونه کد نوشته شده برای آموزش تایمر در میکروکنترلرهای STM32 برای کنترل دور موتور از دو شرط if استفاده کرده ایم که در هر کدام یک شرط if دیگر نیز وجود دارد. اگر کلید سمت چپ فشار داده شود، برنامه وارد حلقۀ اول می شود و اگر کلید سمت راست فشار داده شود، برنامه وارد حلقۀ دوم می شود. اگر کلید چپ فشار داده شود و برنامه وارد حلقۀ اول شود، شرط دومی چک می شود. به این معنی که چک می شود که آیا مقدار Duty Cylce از ده بیشتر است یا خیر. اگر این شرط برقرار باشد، تا زمانی که کلید سمت چپ فشار داده شود، از مقدار Duty Cycle، ده تا کم می کند و عدد حاصل را درون متغیر Duty Cycle می ریزد و در نهایت با استفاده از فرمول از پیش ذکر شده، مقدار CCR2 را محاسبه می کند. در واقع این بخش کد می خواهد به کلید سمت چپ اجازۀ کاهش مقدار Duty Cycle را دهد. اگر کلید سمت راست فشرده شود، چک می شود، در صورتی که مقدار Duty Cycle از نود کمتر باشد، هر بار که کلید فشرده شود، ده تا به مقدار Duty Cycle اضافه می کند و مقدار CCR2 را محاسبه کرده و درون آن می ریزد. عبارت int نوشته شده پیش از فرمول به این جهت بوده که اگر حاصل عبارت عددی اعشاری شد، به عدد صحیح تبدیل شود و سپس حاصل که عددی صحیح خواهد بود را ذخیره کند.
if ( HAL_GPIO_ReadPin(KeyDown_GPIO_Port,KeyDown_Pin) == 0 )
{
if ( Freq>500 )
{
while ( HAL_GPIO_ReadPin(KeyDown_GPIO_Port,KeyDown_Pin) == 0 );
Freq -=500;
TIM3->ARR= (int) (400000/Freq);
TIM3->CCR2 = (int)(((DutyCycle * 0.01) * (TIM3->ARR)) );
}
}
else if ( HAL_GPIO_ReadPin(KeyUp_GPIO_Port,KeyUp_Pin) == 0 )
{
if ( Freq < 4000 )
{
while ( HAL_GPIO_ReadPin(KeyUp_GPIO_Port,KeyUp_Pin) == 0 );
Freq +=500;
TIM3->ARR= (int) (400000/Freq);
TIM3->CCR2 = (int)(((DutyCycle * 0.01) * (TIM3->ARR)) );
}
}
دو شرط دیگر، دقیقاً می خواهد همان اعمال دو شرط قبل را تکرار کند، با این تفاوت که کلید بالا و پایین قرار است تغییر دهندۀ فرکانس باشند. هر بار فشردن کلید 500Hz به فرکانس اضافه می کند یا از آن کم می کند. در نهایت در هر حلقه مقادیر ARR و CCR2 محاسبه می شوند.
در ادامه با بررسی وقفه ها خواهیم دید که متغیری به نام LcdRefCounter تعریف شده است، که به مقدار آن اضافه می شود. هر زمان که این مقدار به ده برسد، نمایشگر مقادیر و عبارات لازم را نمایش می دهد.
if (LcdRefCounter >= 10)
{
lcd_clear();
sprintf(LcdStr,"Fr=%d", Freq );
lcd_gotoxy(0,0);
lcd_puts(LcdStr);
sprintf(LcdStr,"Du=%d",(char)DutyCycle );
lcd_gotoxy(9,0);
lcd_puts(LcdStr);lcd_putchar(37);
sprintf(LcdStr,"RPM=%d",(int)RpmIn );
lcd_gotoxy(0,1);
lcd_puts(LcdStr);
LcdRefCounter=0;
}
هر بار رسیدن به انتهای حلقه While با سرعت بالایی انجام می شود. در صورتی که شرط فوق نوشته نشده باشد، نمایشگر به سرعت Refresh شده و دیتای جدید بر روی آن نوشته می شود. تا حدی که خواندن تغییرات سریع آن برای کاربر ممکن نیست. برای رفع این مشکل از شرط فوق استفاده شده است نمونه کد را به این نحو نوشته ایم. متغیری که درون شرط if فوق قرار دارد، در روتین وقفه تایمر یک واحد به آن اضافه می شود. وقتی 10 مرتبه این کار انجام شد، یعنی تاخیر مناسبی سپری شده و زمان مناسبی برای نمایش بر روی نمایشگر است. اما توجه شود که در پروژه های بسیار بهینه و حرفه ای مدیریت نمایش بر روی نمایشگر این چنین نیست.
تا بدین جای کار در آموزش تایمر در میکروکنترلرهای STM32 و کنترل موتور بهتر است که یک بار برنامه را کامپایل کنید تا اگر خطا و مشکلی دارد، نشان داده شود و سپس رفع شود. اگر در پروژه ای تمامی کدها را بنویسید و سپس کامپایل کنید، رفع کردن خطاهای موجود در کد بسیار سخت خواهد شد.
اکنون زمان آن رسیده که به سراغ کتابخانۀ وقفه برویم، یعنی stm32f1xx-it.c که در نوار ابزار سمت چپ در بخش Application User وجود دارد. آن را باز کنید. یک سری از متغیرها را که در فایل main.c از آنها استفاده کردیم را نیاز داریم که در فایل stm32f1xx-it.c نیز از آنها استفاده کنیم. در زمانی که با استفاده از STM32CubeMX کدی را می نویسیم و قصد داریم که یک متغیر را در دو فایل مجزا استفاده کنیم، باید که در زمان تعریف متغیر در فایل دوم، از عبارت extern استفاده کنیم تا بتوانیم به آن متغیر دسترسی داشته باشیم. پس در ابتدا متغیرهایی زیر که در فایل main.c نیز تعریف شده بودند و در فایل stm32f1xx-it.c نیز به آنها نیاز داریم را به صورت extern volatile تعریف می کنیم:
extern volatile unsigned char LcdRefCounter;
extern volatile float RpmIn;
extern volatile char CaptureCounter;
سپس متغیرهای محلی مورد نیاز را نیز در ابتدای روتین وقفۀ تایمر دوم قرار دهید:
static unsigned int Pulse1=0 , Pulse2=0 ,Pulses=0 ;
static float one_encoder_period_pulse_time=0 ,one_route_time=0;
پس از معرفی متغیر ها به سراغ کدی که در روتین وقفه نوشته می شود، می رویم:
LcdRefCounter++;
if (CaptureCounter == 0)
{
Pulse1 = TIM2->CCR1;
CaptureCounter = 1 ;
}
else if (CaptureCounter == 1)
{
Pulse2 = TIM2->CCR1;
Pulses = Pulse2 - Pulse1 ;
one_encoder_period_pulse_time = Pulses * 0.001 ;
one_route_time = one_encoder_period_pulse_time ;
RpmIn = 60 / one_route_time ;
CaptureCounter = 0 ;
}
تقریباً می توان گفت که در آموزش تایمر در میکروکنترلرهای STM32 با هدف کنترل دور موتور نمی توان از Capture در میکروکنترلرهای STM32 استفاده نکرد و نمونه کد خوبی نوشت. یعنی برای نوشتن یک کد مناسب برای کنترل موتور، لازم است از کپچر استفاده شود. در این وقفه عملیات Capture انجام می شود. خط اول همان متغیر LcdRefCounter است که با هر بار ایجاد وقفه، یک واحد به آن اضافه می شود، که از آن در فایل اصلی main برای نمایش بر روی LCD استفاده می شود. پس از آن متغیری به نام CaptureCounter داریم. از این متغیر استفاده می شود برای اینکه بفهمیم بار اولی است که وقفه رخ می دهد یا بار دوم. اگر بار اول باشد، مقدار آن صفر است و وارد حلقۀ اول می شود. در این هنگام کاری که انجام می دهد، این است که مقدار رجیستر CCR1 را درون متغیری به نام Pulse1 می ریزید و آن را ذخیره می کند. سپس مقدار متغیر CaptureCounter را برابر یک قرار می دهیم تا زمانی که بار دیگر وقفه رخ داد، این بار وارد حلقۀ دوم شویم. وقتی برای بار دوم وقفه رخ می دهد، یعنی بار دومی است که نور مادون قرمز از فرستنده به گیرنده رسیده است و سطح یک روی پالس ورودی ایجاد شده است. در این صورت برنامه وارد حلقۀ دوم می شود، مقدار جدید رجیستر CCR1 را درون متغیر Pulse2 می ریزید. در نهایت همان طور که در نوشتۀ “کپچر در STM32 ” توضیح داده شد، لازم است تا این دو مقدار از هم کم شوند. پس مقدار Pulse1 و Pulse2 از یکدیگر کم شده و درون متغیر جدیدی به نام Pulses ریخته می شود. گفتیم با توجه به مقادیری که از قبل تعیین کردیم، هر شمارش شمارندۀ تایمر، 0.001 ثانیه زمان می برد. پس اگر بین دو لبۀ پایین روندۀ پالس، Pulses بار شمارش وجود داشته باشد، برای محاسبۀ این زمان باید این مقدار را در 0.001 ثانیه ضرب کرد. مقدار متغیر one-encoder-period-pulse-time، زمانی است که طول می کشد تا تا یک لبۀ پایین رونده برای بار دوم تکرار شود. حال اگر حلقۀ Encoder ما دارای دو حفره باشد، این زمان دقیقاً برابر است با زمانی که شفت موتور یک بار می چرخد. اما اگر حلقۀ Encoder دارای چند حفره باشد، برای محاسبۀ زمان یک دور گردش موتور، باید عدد متغیر one-encoder-period-pulse-time را در تعداد حفره ها ضرب کرد و آن را درون متغیر one-route-time ریخت. در نهایت نیز با تقسیم این زمان بر 60، سرعت گردش موتور بر حسب RPM به دست می آید. متغیر CaptureCounter را دوباره برابر با مقدار صفر قرار دهید تا اگر بار دیگر وقفه ای رخ داد، برنامه به حلقۀ اول رفته و تکرار شود.
در این مرحله نیز بهتر است یک بار دیگر برنامه را کامپایل کنید تا مطمئن شوید، خطایی وجود نداشته باشد.
در مرحلۀ پایانی آموزش تایمر در میکروکنترلرهای STM32 و کنترل دور موتور، در فایل اصلی یعنی main.c، باید LCD و شمارش تایمرها را فعال کرد. پس به فایل main.c برگشته و درون Initialize all configured peripherals، بین Begin و Start، کدهای زیر را بنویسید:
lcd_init();
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
پس در ابتدا LCD را فعال کرده و سپس در خط دوم و سوم، اقدام به فعال سازی شمارندۀ کانال اول تایمر 1 و کانال دوم تایمر 3، کنید.
در این مرحله کد نویسی پروژه نیز به اتمام می رسد. نتیجۀ نهایی آموزش تایمر در میکروکنترلرهای STM32 و کنترل دور موتور را می توانید در تصویر زیر مشاهده فرمایید.
تصویر 13 – نمایش پروژۀ نهایی شدۀ بهترین روش برای انجام پروژۀ آموزش تایمر در میکروکنترلرهای STM32
نتایج آموزش تایمر در میکروکنترلرهای STM32 و کنترل دور موتور:
- یکی از مهمترین ابزارها برای کنترل دور موتور، ابزار انکودر موتور (Motor Encoder) است.
- بهترین روش کنترل دور موتور DCدر STM32 استفاده از Capture در میکروکنترلرهای STM32است.
- در ساخت یک شکل موج PWM به این نکته توجه شود که تغییر Duty Cycle باعث تغییر فرکانس نشود و برعکس.
- در آموزش Timer در STM32دیدیم که کلاک CPU بر مقدار Prescaler+1و نه Prescalerتقسیم می شود.
- در زمان ساخت پروژه با استفاده از STM32CubeMXوقتی یک متغیر را در دو فایل مجزا استفاده می کنیم، باید در زمان تعریف متغیر در فایل دوم، از عبارت extern استفاده کنیم تا بتوانیم به آن متغیر در فایل دوم دسترسی داشته باشیم.
سلام. خیلی کد خوبی بود. چنین کدهایی رو در سایت های دیگه به عنوان پروژه به فروش می رسونن. واقعا سطح نمونه کدهایی که قرار میدید از نمونه کدهای توی اینترنت حداقل سایت های فارسی زبان بالاتره. میشه لطفا آموزش کدنویسی اصولی رو هم قرار بدید که یک نفر متوجه بشه که بهترین روش کدنویسی چیه؟
سلام. خیلی ممنون. لطف دارید. به آموزش زبان C میکروکنترلرها مراجعه کنید.
ضمن عرض سلام و تشکر. این نمونه کد با استفاده از کتابخونه های HAL نوشته شده. نمونه کد رجیستری موجود نیست؟
سلام. خواهش میکنم. این نمونه کد بصورت رجیستری در آموزش مقدماتی و متوسط STM32 بررسی می شود.
سلام. چرا فایل پیوست موجودنیست؟ در صورت امکان بارگذاری شود.
سلام. فایل پیوست قرار گرفت