مطالب علمی

چگونه همه پایه های میکروکنترلر AVR را PWM کنیم؟

PWM یا Pulse Width Modulation یا Pulse Duration Modulation ( مدولاسیون عرض پالس یا مدولاسیون مدت زمان پالس ) نام تکنیکی در الکترونیک است که استفاده گسترده ای در صنایع الکترونیک و مخابرات دارد. از این روش برای کارهایی مانند کدینگ پیام های مخابراتی ، کنترل توان دستگاه های الکتریکی ، شارژ باتری و … استفاده می شود.

در میکروکنترلر ها نیز PWM برای مصارف مختلفی مانند کنترل نور LED ها ( Fade کردن نور LED ) ، کنترل سرعت موتور های DC , انتقال پیام ، مبدل های ولتاژ و …. استفاده می شود.

pwm-wave

در اصل PWM موجی مربعی است که در برخی زمان ها ۰ و برخی زمان ها ۱ است و این ۰ و ۱ شدن ها با فرکانس مرتبی تکرار می شود. همانگونه که در شکل بالا مشاهده می کنید PWM مانند سایر امواج ، دارای دامنه یا Amplitude ، دور تناوب یا Period و فرکانس است. عبارت دیگری که در PWM مورد استفاده قرار می گیرد Duty Cycle است. دیوتی سایکل مدت زمان ۱ بودن به مدت زمان کل پریود در هر سیکل موج است که معمولا بر حسب درصد ( % ) نمایش داده می شود. به فرض مثال اگر Duty Cycle یک موج PWM برابر با ۴۰% باشد بدان معنی است که در هر سیکل ۴۰% ولتاژ برابر VCC و در ۶۰% اوقات ولتاژ برابر ۰ است. همانگونه که می دانید در چنین حالتی ولتاژ موثر یا Vrms برابر با ۴۰% VCC خواهد بود. به فرض مثال شما اگر با یک میکرو با تغذیه ۵V ، موج PWM با دیوتی سایکل ۵۰% ایجاد نمایید ولتاژ RMS شما برابر ۵۰% VCC یا به عبارتی ۲٫۵ ولت خواهد بود. در شکل زیر تعدادی موج PWM با فرکانس ثابت و دیوتی سایکل متفاوت نمایش داده شده است.

من امروز میخوام PWM نرم افزاری رو به شما معرفی کنم
چون خیلی از دوستان دیدم که گذرشون به این کارها میخوره همون طور که میدونید اکثر میکروهای AVR هشت بیتی PWM سخت افزاری محدودی دارند و اگر بخواهیم که آنها را به کار بگیریم باید از همون پایه ای که مشخص شده PWM بگیریم  بنابراین اگر کسی بخواهد که از پایه های دیگه PWM بگیرد باید به صورت نرم افزاری تعریف کند تا پایه دلخواه را PWM کند همانطور که می دانید برای هر PWM نیاز به یک تایمر دارد . البته در بعضی از تایمرها هستند که 3تا PWM تولید میکنند . اما در این برنامه ای که میخواهم برای شما معرفی کنم تنها یک تایمر بکار رفته است . تایمر صفر رو انتخاب کردم که امکاتاتش کمه و به کارمون میاد دلیل اینکه چرا از تایمر های دیگه استفاده نکردم اینه که گفتم شاید کسی از تایمر های دیگه که خیلی امکاناتش بالاتره بخواهد استفاده کند .
با استفاده از تایمر 0 میکروهای 128-64-ATmega16-32 این قابلیت را دارند که انجام مقایسه ای مقدار تایمر را انجام دهد(در بعضی از تایمر0 میکرو ها فقط این قابلیت رو دارند که Overflow کنند مثل ATmega8 ) . پس میتوانیم در این تایمر فرکانس را کم و زیاد کنیم . اگر فرکانس را بالا ببریم (مقدار OCR0 را کم کنیم) پردازش زیادتر میشود و دقت پایین می آید و اگر فرکانس رو کم کنیم دقت زیاد میشود . ما در این پروژه نیازی به این دو داریم ولی تا یک حدی این تایمر در مد CTC تنظیم شده است .
اگر برایتان فرکانس زیاد مهم نیست مقدار OCR0 را زیادتر کنید تا پردازش کمتر شود . دلیل اینکه روی بحث پردازش تاکید میکنم این هست که روی دستورات تاخیری تاثیر میگذارد و این باعث میشود که تاخیرات نرم افزاری دیرتر صورت بگیرد .

یک توضیح مختصر در مورد برنامه :
برنامه طوری طراحی شده که با استفاده از آرایه میتوانیم مقدار تک تک PWM ها را تغییر بدهیم آرایه ای که []pwm نام دارد را میتوانید اندیس آن را برای PWM دلخواهتان تنظیم کنید بازه مقدار آرایه PWM از 0 تا 99 است که نشانگر دیوتی سایکل است این آرایه یه جورایی میشه گفت که مثل رجیستر عمل میکنه . یعنی اینکه تا مقدار به آن بدهیم PWM تنظیم میشود .

برنامه ATmega64-128 :
در این دو میکرو به دلیل اینکه دستور PORTx.y فقط برای پورتهای A,B,C,D کاربرد دارد گفتم چه بهتر که همه رو با استفاده از دستورات کتابخانه ای خود کدویژن به کار بگیرم .
از لحاظ پردازش هم با این فرکانس و این تقسیم کننده فرکانس در تایمر ، در میکروی اتمگا 16 و 32 تقریبا 15.75% و در میکرو های اتمگا 64 و 128 تقریبا 24.67% پردازش میکرو را میگیرد . (البته این محاسبات در بدترین شرایط در نظر گرفته شده است .)
اگر بخواهید از تمام PWM های نرم افزاری استفاده نکنید میتوانید یکسری تنظیمات را تغییر دهید …
البته که اگر PWM های کمتری را به کار بگیرید پردازش کمتری خواهد گرفت .

سورس کد برای میکروهای ATmega16-32 به زبان C در کدویژن

/*****************************************************
This program was produced by the
CodeWizardAVR V2.05.3 Standard
Automatic Program Generator
© Copyright 1998-2011 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project : 32-channel PWM (Rain LED)
Version : 2.1
Date  : 2/27/2014
Author : Saman.Asadi
www.ECA.ir
Chip type        : ATmega16
Program type      : Application
AVR Core Clock frequency: 8.000000 MHz
Memory model      : Small
External RAM size    : 0
Data Stack size     : 256
*****************************************************/
#include <mega16a.h>
#include <delay.h>

#define led1    PORTA.0
#define led2    PORTA.1
#define led3    PORTA.2
#define led4    PORTA.3
#define led5    PORTA.4
#define led6    PORTA.5
#define led7    PORTA.6
#define led8    PORTA.7
#define led9    PORTC.7
#define led10    PORTC.6
#define led11    PORTC.5
#define led12    PORTC.4
#define led13    PORTC.3
#define led14    PORTC.2
#define led15    PORTC.1
#define led16    PORTC.0
#define led17    PORTD.7
#define led18    PORTD.6
#define led19    PORTD.5
#define led20    PORTD.4
#define led21    PORTD.3
#define led22    PORTD.2
#define led23    PORTD.1
#define led24    PORTD.0
#define led25    PORTB.7
#define led26    PORTB.6
#define led27    PORTB.5
#define led28    PORTB.4
#define led29    PORTB.3
#define led30    PORTB.2
#define led31    PORTB.1
#define led32    PORTB.0
#define byte    unsigned char
#define max_var_PWM 99
#define factor
byte    a = max_var_PWM;
byte    pwm[35];
flash byte rain1[25]={99,70,40,20,10,5,4,3,2,1,0};
flash byte rain2[40]={0,1,2,3,4,5,10,20,40,70,99,70,40,20,10,5,4,3,2,1,0};
flash byte rain3[35]={0,1,2,3,4,5,10,20,40,70,99};
flash byte rain4[40]={1,2,3,4,5,10,20,40,70,99,70,40,20,10,5,4,3,2,1,0};

// Timer 0 output compare interrupt service routine
interrupt [TIM0_COMP] void timer0_comp_isr(void)
{  if(a)
  {  if(a == pwm[1] )led1=1;
    if(a == pwm[2] )led2=1;
    if(a == pwm[3] )led3=1;
    if(a == pwm[4] )led4=1;
    if(a == pwm[5] )led5=1;
    if(a == pwm[6] )led6=1;
    if(a == pwm[7] )led7=1;
    if(a == pwm[8] )led8=1;
    if(a == pwm[9] )led9=1;
    if(a == pwm[10])led10=1;
    if(a == pwm[11])led11=1;
    if(a == pwm[12])led12=1;
    if(a == pwm[13])led13=1;
    if(a == pwm[14])led14=1;
    if(a == pwm[15])led15=1;
    if(a == pwm[16])led16=1;
    if(a == pwm[17])led17=1;
    if(a == pwm[18])led18=1;
    if(a == pwm[19])led19=1;
    if(a == pwm[20])led20=1;
    if(a == pwm[21])led21=1;
    if(a == pwm[22])led22=1;
    if(a == pwm[23])led23=1;
    if(a == pwm[24])led24=1;
    if(a == pwm[25])led25=1;
    if(a == pwm[26])led26=1;
    if(a == pwm[27])led27=1;
    if(a == pwm[28])led28=1;
    if(a == pwm[29])led29=1;
    if(a == pwm[30])led30=1;
    if(a == pwm[31])led31=1;
    if(a == pwm[32])led32=1;
  }
  if(--a == 255)
  {  a = max_var_PWM;
    PORTA=0;
    PORTB=0;
    PORTC=0;
    PORTD=0;
  }
}

void main(void)
{
  byte x1,x2,x3;
  //Config all
  //Config timers
  //Config PORTS
  {
    DDRA=255;
    DDRB=255;
    DDRC=255;
    DDRD=255;
    // Timer/Counter 0 initialization
    // Clock source: System Clock
    // Clock value: 1000.000 kHz
    // Mode: CTC top=OCR0
    // OC0 output: Disconnected
    TCCR0=0x0A;
    OCR0=150;
    // Timer(s)/Counter(s) Interrupt(s) initialization
    TIMSK=0x02;
    // Global enable interrupts
    #asm("sei")
  }
  //End Config ......
  while(1)
  {
    /////////////////////////////////////////
    for(x1=0;x1<25;x1++)
    {  pwm[1]=rain1[x1];
      for(x2=17;x2>0;x2--)pwm[x2+1]=pwm[x2];
      delay_ms(40);
    }
    for(x1=0;x1<35;x1++)
    {  pwm[1]=rain2[x1];
      for(x2=17;x2>0;x2--)pwm[x2+1]=pwm[x2];
      delay_ms(40);
    }
    for(x1=0;x1<25;x1++)
    {  pwm[1]=rain3[x1];
      for(x2=17;x2>0;x2--)pwm[x2+1]=pwm[x2];
      delay_ms(40);
    }
    /////////////////////////////////////////
    for(x1=0;x1<25;x1++)
    {  pwm[17]=rain1[x1];
      for(x2=0;x2<17;x2++)pwm[x2]=pwm[x2+1];
      delay_ms(40);
    }
    for(x1=0;x1<35;x1++)
    {  pwm[17]=rain2[x1];
      for(x2=0;x2<17;x2++)pwm[x2]=pwm[x2+1];
      delay_ms(40);
    }
    for(x1=0;x1<25;x1++)
    {  pwm[17]=rain3[x1];
      for(x2=0;x2<17;x2++)pwm[x2]=pwm[x2+1];
      delay_ms(40);
    }
    //////////////////////////////////////////
    for(x3=0;x3<3;x3++)
    for(x1=0;x1<14;x1++)
    {  for(x2=0;x2<8;x2++)pwm[x2]=pwm[x2+1];
      for(x2=17;x2>9;x2--)pwm[x2]=pwm[x2-1];
      pwm[8]=rain1[x1];
      pwm[9]=rain1[x1];
      delay_ms(40);
    }
    for(x3=0;x3<3;x3++)
    for(x1=0;x1<14;x1++)
    {  for(x2=8;x2>00;x2--)pwm[x2]=pwm[x2-1];
      for(x2=9;x2<17;x2++)pwm[x2]=pwm[x2+1];
      pwm[0]=rain1[x1];
      pwm[17]=rain1[x1];
      delay_ms(40);
    }
    //////////////////////////////////////////
    for(x3=0;x3<4;x3++)
    for(x1=0;x1<19;x1++)
    {  for(x2=0;x2<8;x2++)pwm[x2]=pwm[x2+1];
      for(x2=17;x2>9;x2--)pwm[x2]=pwm[x2-1];
      pwm[8]=rain4[x1];
      pwm[9]=rain4[x1];
      delay_ms(40);
    }
    for(x3=0;x3<4;x3++)
    for(x1=0;x1<19;x1++)
    {  for(x2=8;x2>00;x2--)pwm[x2]=pwm[x2-1];
      for(x2=9;x2<17;x2++)pwm[x2]=pwm[x2+1];
      pwm[0]=rain4[x1];
      pwm[17]=rain4[x1];
      delay_ms(40);
    }
    //////////////////////////////////////////
  }
}

نویسنده:SAMAN.ASADI

مطلب اصلی در انجمن: https://www.eca.ir/forums/thread48596.html

نوشته های مشابه

1 دیدگاه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا