به نام خدا
سلام!
واقعا راست میگن که نابرده رنج گنج میسر نمیشود مزد آن گرفت جان برادر که کار کرد!
چند وقتی بود خیلی نارحت بودم که نمیتونم برای خودم برنامه بریزم که چه کار کنم! مثلا صبح از خواب بیدار میشدم میگفتم امروز فلان ماژول رو وصل میکردم! تا ظهر پا کامپیوتر میشستم موقع نماز میدیدم چیزی گیرم نیومد! (یعنی هیچ کاری نکردم) بعد از ظهر هم پا کامپیوتر میشستم تا شب دوباره وقت نماز شب میدیدم هیچ کاری نکردم! خیلی اعصابم خورد میشد! (دلیلش این بود که هر روز میخواستم یه کاری کنم ولی نمیشد!) امروز گفتم میرم سراغ PWM تا ظهر نشستم دوباره هیچ کاری نکردم! بعد از ظهر اومدم چند تا سرچ زدم تا به این فیلم در یوتیوب رسیدم! بعد نشستم هرچی نوشت رو منم نوشتم! نمیدونم چرا سورس رو نذاشته بود مثل آدم دانلود کنیم!
بالاخره خدا رو شکر درست شد!
حالا میخوام از روی کد و مطالعه یوزرمانول که میتونید از اینجا دانلود کنید PWM رو توضیح بدم! (البته خودم بلد نیستم و قطعا نمیتونم به شما یاد بدم فقط چیزایی که تو دیتاشیت نوشته رو میارم اینجا تا کارتون راحت تر بشه)!
خوب بریم سراغ اصل مطلب:
قبل از هر چیز توصیه میکنم این مطلب رو از سایت الکترولت بخونید!
کلمات کلیدی مربوطه:
– Update: Counter overflow/underflow, counter initialization (by software or internal/external trigger)
– Trigger event (counter start, stop, initialization or count by internal/external trigger)
– Input capture
– Output compare
– Break input
کانال تایمر چیست؟!
اولین سوال که برام پیش اومد که اگه شما هم مبتدی باشید براتون پیش اومده اینه که کانال تایمر چیه؟! مثلا زمان رو نشون میده یا میگه چند تا سیکل و پالس گذسته یا در هر پالس اونم 0 یا 1 میشه؟! خیر هیچکدام از اینها نیست!
جوابی که من از طریق کد و دیتاشیت بدست آوردم اینه که طبق یه چیزی که به میکرو اعلام میکنیم، هر وقت تایمر به مقداری (که اونم ما تعیین میکنیم) رسید پایه مربوط به کانال مربوطه (مثلا کانال 1) در حالت 5 ولت یا 0 ولت قرار میگیره!
یعنی مثلا فرض کنید ما تایمر 8 بیتی داریم و یک کانال (در این میکرو تایمر 4 یک تایمر 8 بیتی است اما کانال ندارد، ولی ما فرض میکنیم) خوب نهایت یه تایمر 8 بیتی 255 هست ما میایم رجیستر CCR رو روی 100 تنظیم میکنیم! حالا تایمر از 0 تا 100 که میشمره کانال مقدار 0 ولت داره و وقتی از 100 تا 255 میشمره کانال مقدار 5 ولت داره!
خوب امیدوارم فهمیده باشید!
حالت PWM
TIM2_CCMR2_OC2M = 0X07; //PWM Mode
توی دیتاشیت (بالا معرفی کردم) داریم: صفحه 236
Bits 6:4 OC1M[2:0]: Output compare 1 mode
These bits defines the behavior of the output reference signal OC1REF from which OC1 is derived.
OC1REF is active high whereas OC1 active level depends on the CC1P bit.
000: Frozen - The comparison between the output compare register TIMx_CCR1 and the counter
TIMx_CNT has no effect on the outputs
001: Set channel 1 to active level on match. OC1REF signal is forced high when the counter
TIMx_CNT matches the capture/compare register 1 (TIMx_CCR1).
010: Set channel 1 to inactive level on match. OC1REF signal is forced low when the counter
TIMx_CNT matches the capture/compare register 1 (TIMx_CCR1).
011: Toggle - OC1REF toggles when TIMx_CNT=TIMx_CCR1
100: Force inactive level - OC1REF is forced low
101: Force active level - OC1REF is forced high
110: PWM mode 1 - In up-counting, channel 1 is active as long as TIMx_CNT< TIMx_CCR1.
Otherwise, channel 1 is inactive. In down-counting, channel 1 is inactive (OC1REF = 0) as long as
TIMx_CNT> TIMx_CCR1. Otherwise, channel 1 is active (OC1REF = 1).
111: PWM mode 2 - In up-counting, channel 1 is inactive as long as TIMx_CNT< TIMx_CCR1 Otherwise, channel 1 is active.
(نکته: چند خط انگلیسی بالا در مورد TIM1_CCMR1 هست که در واقع کانال 1 از تایمر 1 هست. در صورتی که ما با کانال 2 از تایمر 2 کار میکنیم! دلیل اینکه این متن رو آوردم این بود که چون همه ی رجیستر های مربوط با کانال ها در همه تایمر ها یکسان هست! خود User Manual فقط از این تایمر رو توضیح داده و برای بقیه توضیحی ننوشته و گفته برای توضیحات به تایمر 1 برید)
الان ما اون مدی که پر رنگ کردم رو انتخاب کردیم! نوشته مادامی که TIMx_CCR1 بزگتر از TIMx_CNT باشه! کانال ما inactive هست! حالا چرا گفته inactive خوب میگفت 0 هست! خیر. دلیل این که گفته inactive اینه که ما در رجیستر TIM2_CCER1_CC2P میتونیم تعیین کنیم که active Low باشه یا active High یعنی در این مثال که کدش رو در زیر قرار میدم، ما گفتیم که active Low باشه! یعنی در حالت active یا فعال مقدار 0 ولت داشته باشه و در حالت inactive یا غیر فعال مقدار 5 ولت (یا 1 منطقی داشته باشه) داشته باشه (یعنی برعکس اونچیزی که رایج هست)
خوب حالا در اینجا طبق توضیحات بالا هر وقت که TIMx_CCR1 مقدار 0 داشته باشه! شرط غلط میشه و کانال active میشه و طبق چیزایی که گفتیم active برای ما همون 0 هست! پس اگه TIMx_CCR1 صفر باشه خروجی هم صفر هست! و برعکس اگه 0xFFFF باشه خروجی هم 1 هست!
امیدوارم فهمیده باشید! اگر هم نفهمیدید باید حداقل دو بار مطلب رو بخونید تا متوجه بشید! چون شاید بعضی از این چیزایی که در بالا گفتم رو در ادامه تکمیل کنم....!
انتخاب کانال به عنوان خروجی!
TIM2_CCMR2_CC2S = 00; // CC2 Chanels as output
طبق دیتاشیت : (صفحه : 239)
CC2S[1:0]: Capture/compare 2 selection
This bitfield defines the direction of the channel (input/output) as well as the used input.
00: CC2 channel is configured as output
01: CC2 channel is configured as input, IC2 is mapped on TI2FP2
10: CC2 channel is configured as input, IC2 is mapped on TI1FP2
11:CC2 channel is configured as input, IC2 is mapped on TRC. This mode works only if an internal trigger input is selected through the TS bit (TIM5_SMCR register).
که ما حالت اول یعنی خروجی رو انتخاب کردیم!
active Low یا active High مسئله این است؟
خوب این موضوع رو کمی در قسمت حالت PWM توضیح دادم.
TIM2_CCER1_CC2P = 1 ; // OC2 Active Low
در دیتاشیت صفحه 242
CC1P: Capture/compare 1 output polarity
CC1 channel configured as output:
0: OC1 active high
1: OC1 active low
CC1 channel configured as input for capture function (see Figure 64):
0: Capture is done on a rising edge of TI1F or TI2F
1: Capture is done on a falling edge of TI1F or TI2F
خوب دوباره من از قسمت CC1P کپی کردم در حالی که ما از CC2P استفاده میکنیم! چون همش مثل هم هست توی دیتا شیت یه بار توضیح داده! دیگه از CC2P رو توضیح نداده!
فعال کردن کانال
خوب داریم:
TIM2_CCER1_CC2E = 1 ; // Timer OutPut to Pin PD.3
ببینید خوب اگه ما اینکار رو نکنیم پایه PD.3 به عنوان یک GPIO شناخته میشه و کاربرد کانال تایمر 2 رو نداره! با اینکار میگیم این پین رو به تایمر وصل کنه!
فعال کردن تایمر 2 و وقفه مربوطه!
خوب داریم :
TIM2_IER_UIE = 1; // ENABLE UPDATE INTERRUPT
TIM2_CR1_CEN = 1; // ENABLE Timer 2
asm ("rim");// ENABLE iNTERRUPT
فقط اینو بگم که اونجور که من فهمیدم UPDATE INTERRUPT وقتی رخ میده که تایمر به آخرش برسه! یعنی در تایمر 8 بیتی وقتی تایمر به 255 برسه روال وقفه یا همون IRQHandler اجرا میشه! (شایدم اشتباه گفته باشم! مطمئن نیستم)
رجیستر TIM2_CCR2
خوب این چیزی که همون اول درباره اش صحبت کردیم! تایمر تا مقداری که در این رجیستر قرار داره میشمره و بعد از اینکه از این مقدار رد شد پایه (کانال) مربوطه رو تغییر میده! مثلا اگه رجیستر TIM2_CCR2 رو روی 90 تنظیم کنیم تا وقتی که کوچکتراز 90 هست (مثلا) پایه مربوطه 0 هست وقتی از 90 رد میشه پایه مربوطه 1 میشه! این رجیستر 16 بیتی چون تایمر 2 شونزده بیتی هست!
بنابراین به صورت TIM2_CCR2H و TIM2_CCR2L باید بهش دسترسی داشته باشید!
کد کامل:
خوب بعد از این همه حرف رسیدیم به آخر خط، اینم از کد زیر (اینم بگم که این کد رو من از روی فیلمی که توی یوتیوب بود نوشتم)
البته یه چیزایی هم داره که من توضیح ندادم! چیز سختی نبود دیگه ربطی به تایمر نداره. بخونید میفهمید!
#include "iostm8s003f3.h"
#include "stdint.h"
#include "stdbool.h"
volatile int16_t pwmval =0;
volatile bool increment = true;
uint16_t values[] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11,
12, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 31, 32, 33, 35, 36, 38, 40, 41,
43, 45, 47, 49, 52, 54, 56, 59, 61, 64, 67, 70, 73, 76, 79,
83, 87, 91, 95, 99, 103, 108, 112, 117, 123, 128, 134, 140,
146, 152, 159, 166, 173, 181, 189, 197, 206, 215, 225, 235,
245, 256, 267, 279, 292, 304, 318, 332, 347, 362, 378, 395,
412, 431, 450, 470, 490, 512, 535, 558, 583, 609, 636, 664,
693, 724, 756, 790, 825, 861, 899, 939, 981, 1024, 1069,
1117, 1166, 1218, 1272, 1328, 1387, 1448, 1512, 1579,
1649, 1722, 1798, 1878, 1961, 2048, 2139, 2233, 2332,
2435, 2543, 2656, 2773, 2896, 3025, 3158, 3298, 3444,
3597, 3756, 3922, 4096, 4277, 4467, 4664, 4871, 5087,
5312, 5547, 5793, 6049, 6317, 6596, 6889, 7194, 7512,
7845, 8192, 8555, 8933, 9329, 9742, 10173, 10624, 11094,
11585, 12098, 12634, 13193, 13777, 14387, 15024, 15689,
16384, 17109, 17867, 18658, 19484, 20346, 21247, 22188,
23170 , 24196, 25267, 26386, 27554, 28774, 30048, 31378,32768,
34218, 35733, 37315, 38967, 40693, 42494, 44376, 46340,
48392, 50534, 52772, 55108, 57548, 60096, 62757, 65535
};
#pragma vector = TIM2_OVR_UIF_vector
__interrupt void TIM2_OVR_IRQHandler (void){
uint16_t currentVal = values [pwmval];
if (pwmval == 0)
increment = true;
else if (pwmval >= 254)
increment = false;
if (increment)
pwmval += 3;
else
pwmval -= 3;
TIM2_CCR2H = (currentVal >> 8);
TIM2_CCR2L = (currentVal & 0XFF);
TIM2_SR1 &= ~ (1<<0); // Clear Interrupt Flag
}
void intiTimer (void)
{
CLK_PCKENR1 = 0xFF; // Enable Clock for Timer
TIM2_PSCR_PSC = 00;
TIM2_CCMR2_OC2M = 0X07; //PWM Mode
TIM2_CCMR2_CC2S = 00; // CC2 Chanels as output PWM
TIM2_CCER1_CC2P = 1 ; // OC2 Active Low
TIM2_CCER1_CC2E = 1 ; // Timer OutPut to Pin PD.3
TIM2_IER_UIE = 1; // ENABLE UPDATE INTERRUPT
TIM2_CR1_CEN = 1; // ENABLE Timer 2
asm ("rim");// ENABLE iNTERRUPT
}
int main( void )
{
CLK_CKDIVR = 0X0F; // Div 128 to 125KHz
intiTimer();
while (1);
return 0;
}
البته در کد بالا فرکانس کاری میکرو رو روی 125 کیلوهرتز گذاشته که ما میتونیم اونو بزاریم روی 16 و کلاک تایمر رو بزاریم روی 125 کیلوهرتز چون اینجوری اگه کد دیگه ای هم بخوایم اجرا کنیم! سرعت بسیار پایین میاد!
خوب امیدوارم مفید بوده باشه!
نزدیک 2 ساعت زمان برد!
ببینید چقدر دوستتون دارم که نشستم براتون این مطلب رو نوشتم هرچند خیلی نقص داره (به بزرگی خودتون ببخشید) قصد داشتم یه پستی باشه که حداقل یه چیزی هرچند کوچک یاد داده باشم!
موفق باشید!
یا علی مدد....!
قسمت جدید اضافه شده:
#include "iostm8s003f3.h"
#include "stdint.h"
#include "stdbool.h"
volatile int16_t pwmval =0;
volatile bool increment = true;
uint16_t values[] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11,
12, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 31, 32, 33, 35, 36, 38, 40, 41,
43, 45, 47, 49, 52, 54, 56, 59, 61, 64, 67, 70, 73, 76, 79,
83, 87, 91, 95, 99, 103, 108, 112, 117, 123, 128, 134, 140,
146, 152, 159, 166, 173, 181, 189, 197, 206, 215, 225, 235,
245, 256, 267, 279, 292, 304, 318, 332, 347, 362, 378, 395,
412, 431, 450, 470, 490, 512, 535, 558, 583, 609, 636, 664,
693, 724, 756, 790, 825, 861, 899, 939, 981, 1024, 1069,
1117, 1166, 1218, 1272, 1328, 1387, 1448, 1512, 1579,
1649, 1722, 1798, 1878, 1961, 2048, 2139, 2233, 2332,
2435, 2543, 2656, 2773, 2896, 3025, 3158, 3298, 3444,
3597, 3756, 3922, 4096, 4277, 4467, 4664, 4871, 5087,
5312, 5547, 5793, 6049, 6317, 6596, 6889, 7194, 7512,
7845, 8192, 8555, 8933, 9329, 9742, 10173, 10624, 11094,
11585, 12098, 12634, 13193, 13777, 14387, 15024, 15689,
16384, 17109, 17867, 18658, 19484, 20346, 21247, 22188,
23170 , 24196, 25267, 26386, 27554, 28774, 30048, 31378,32768,
34218, 35733, 37315, 38967, 40693, 42494, 44376, 46340,
48392, 50534, 52772, 55108, 57548, 60096, 62757, 65535
};
#define LED_BLICK() PA_ODR_ODR3=!PA_ODR_ODR3
#pragma vector = TIM2_OVR_UIF_vector
__interrupt void TIM2_OVR_IRQHandler (void){
uint16_t currentVal = values [pwmval];
if (pwmval == 0)
increment = true;
else if (pwmval >= 254)
increment = false;
if (increment)
pwmval += 3;
else
pwmval -= 3;
TIM2_CCR2H = (currentVal >> 8);
TIM2_CCR2L = (currentVal & 0XFF);
LED_BLICK ();
TIM2_SR1 &= ~ (1<<0); // Clear Interrupt Flag
}
void intiTimer (void)
{
CLK_PCKENR1 = 0xFF; // Enable Clock for Timer
TIM2_PSCR_PSC = 0X01; // fCK_CNT = fCK_PSC/2^(PSCR[3:0]) -- note : fCK_PSC = fmaster
TIM2_CCMR2_OC2M = 0X07; //PWM Mode
TIM2_CCMR2_CC2S = 00; // CC2 Chanels as output PWM
TIM2_CCER1_CC2P = 1 ; // OC2 Active Low
TIM2_CCER1_CC2E = 1 ; // Timer OutPut to Pin PD.3
TIM2_IER_UIE = 1; // ENABLE UPDATE INTERRUPT
TIM2_CR1_CEN = 1; // ENABLE Timer 2
asm ("rim");// ENABLE iNTERRUPT
}
int main( void )
{
PA_DDR_DDR3 =1;
PA_ODR_ODR3 =0;
PA_CR1_C13 = 1;
PA_CR2_C23 = 1;
CLK_CKDIVR = 0X00;
intiTimer();
while (1);
return 0;
}
TIM2_PSCR_PSC = 0X01; // fCK_CNT = fCK_PSC/2^(PSCR[3:0]) -- note : fCK_PSC = fmaster
TIM2_PSCR_PSC = 0X08; // fCK_CNT = fCK_PSC/2^(PSCR[3:0]) -- note : fCK_PSC = fmaster
جدید:
#include "iostm8s003f3.h"
#include "stdint.h"
#include "stdbool.h"
void intiTimer (void)
{
CLK_PCKENR1 = 0xFF; // Enable Clock for Timer
TIM2_PSCR_PSC = 0X01; // fCK_CNT = fCK_PSC/2^(PSCR[3:0]) -- note : fCK_PSC = fmaster
TIM2_CCMR2_OC2M = 0X07; //PWM Mode
TIM2_CCMR2_CC2S = 00; // CC2 Chanels as output PWM
TIM2_CCER1_CC2P = 1 ; // OC2 Active Low
TIM2_CCER1_CC2E = 1 ; // Timer OutPut to Pin PD.3
// TIM2_IER_UIE = 1; // ENABLE UPDATE INTERRUPT
TIM2_CR1_CEN = 1; // ENABLE Timer 2
// asm ("rim");// ENABLE iNTERRUPT
}
void SetDutyPWM (int persent)
{
int val = 65535 * persent/100;
TIM2_CCR2H = (val >> 8);
TIM2_CCR2L = (val & 0XFF);
}
void delay (void)
{
long int i = 10000;
while (i--);
}
int main( void )
{
int Counter = 0;
CLK_CKDIVR = 0X00;
intiTimer();
while (1)
{
for (Counter = 0; Counter <=100 ; Counter += 1){
SetDutyPWM (Counter);
delay ();
}
}
return 0;
}
چطور میشه فرکانس pwm رو زیاد کرد
با اینکه کریستال داخلی فعال هست و pscr =0 قرار داده شده ماکزیمم فرکانسم برابر 245hz میشه؟؟
clk-div هم برابر 0 هست