با سلام، در این قسمت از آموزشها میریم سراغ مبحث کاربردی تایمرها، در این قسمت از آموزش فقط تایمر رو در مدهای مختلف راهاندازی میکنیم و سپس میریم سراغ قسمت NVIC یا همون وقفه خودمون و بعد از اون با استفاده از تایمر به CPU وقفه میدیم، در ادامه این آموزش بریم سراغ ساختار تایمرهای داخل میکرو های WCH،با من همراه باشید.
اگر با سازوکار تایمرها آشنایی ندارید از این دو آموزش استفاده کنید تا با تایمرها آشنا شوید.
این میکرو بهطورکلی سه نوع تایمر دارد،Advance یا پیشرفته،General purpose یا همهمنظوره و basic یا پایه، هر کدوم استفاده خاص خود شوند دارن ولی خب در برخی موارد میشه جای یکدیگر آنهارو استفاده کرد؛ چون بعضی اشتراکات با هم دارن، مثلاً هر سه تایمر میتونن یک عددی رو داخل کانتر بر اساس مقسم و فرکانس و اتوریلودرشون پر کنن، پس اینجا فرقی نمیکنه از کدوم استفاده بکنید؛ اما باید این مسئله در نظر داشته باشید که بعضاً محدودیت تعداد تایمر هست، پس سعی کنید برای کارهای ساده مثل شمردن و محاسبه زمان و کارهای ساده سراغ تایمرهای basic برید و برای کارهایی که سختافزار تایمر سنگینتری میخواد سراغ تایمرهای بالاتری مثل ادونس و همهمنظوره برید، برخی ویژگیهای آنها در دیتاشیت آی سی گفته شده:
خب تایمر بیسیک رو که گفتیم، تایمر ادونس که همهکاره اس تقریباً و تایمر همهکاره همه ویژگیهای تایمر بیسیک رو دارد با این فرق که بخش Compare/Capture رو دارد که جلوتر بهشون میرسیم؛ ولی خب تایمر ادونس همه بخش هارو دارد و در برنامهنویسی فرقی با بیسیک ندارد پس ما این آموزش ها رو با تایمر ادونس میریم جلو. در این آموزش انتظار میره که شما مفاهیم و کارکرد یک تایمر میکروکنترلر رو بشناسید، در این آموزشها بیشتر در مورد کدها و قابلیتهای خود میکرو صحبت میکنیم.
اولین کار برای شروع کار با تایمر دونستن کلاکی هست که به تایمر میرسد.
برای فهمیدن کلاک کاری میکرو در فایل system_ch32v30x.c بروید، در ابتدای فایل یک سری فرکانس از پیش تعیین شده مشخص شده است. اون فرکانسی که کامنت نیست، فرکانس کاری شماست.
الان فرکانس کاری میکروی من 96MHz هست که از کلاک خارجی تعیین میشود، بهصورت پیشفرض کریستال خارجی رو 8 مگاهرتز در نشر میگیرد،اگر مال شما مقدار دیگری داشت SYSCLK_FREQ_HSE رو از کامنت در بیاورید و مقدار کریستال خود را به هرتز وارد کنید.
اگر هم کریستال خارجی روی برد نداشتید گزینههایی که در انتهای آنها HSI قرار دارد تا انتخاب کنید تا از اسیلاتور داخلی استفاده کند، اسیلاتور داخلی دقیق نیست اگر کار شما وابسته با تایم هست و یا با پریفرالی کار میکنید که تایم وابسته هست سعی کنید از کریستال خارجی استفاده کنید، دقیق نبودن این کلاک روی دقیق نبودن تایمرها نیز اثر مستقیم دارد.
و با استفاده از توابع RCC کلاک تایمر خود را تأمین کنید.
خب پس از اطمینان از کلاک ورودی تایمر الان میتونیم فرکانس موردنظر خود را تعیین کنیم.
محاسبه فرکانس تایمر
محاسبه دوره تناوب تایمر
محاسبه فرکانس PWM
محاسبه DutyCycle تایمر
از طریق فرمولهای بالا میتوان تایمر رو کانفیگ کرد، برای شروع کانفیگ تایمر برای شمردن با فرکانسی که ما براش تعیین کردیم از توابع زیر میتوانیم استفاده کنیم.
1 2 3 4 5 6 7 | TIM_TimeBaseInitTypeDef TIMBCFG; برای استراکت آن یک اسم انتخاب میکنیم// TIMBCFG.TIM_ClockDivision = TIM_CKD_DIV4; فرکانسی که وارد تایمر میشود را تقسیم بر 4 میکنیم// TIMBCFG.TIM_CounterMode = TIM_CounterMode_Up;مد کاری تایمر رو به صورت بالا شمار تعیین میکنیم// TIMBCFG.TIM_Period = 100; مقدار اتوریلد خود در این رجیستر قرار میدیم// TIMBCFG.TIM_Prescaler = 24;مقدار مقسم // TIM_TimeBaseInit(TIM2,&TIMBCFG); //مقادیری که در استراکت تنظیم کردیم رو اعمال میکنیم در نهایت تایمر رو فعال میکنیم TIM_Cmd(TIM2,ENABLE); |
مقادیر ARR و Prescaler این تایمر 16 بیتی هست،پس نهایت عددی ک میتواند داخل آن بزارید 2 به توان 16 یا به عبارتی 65535 هست.این ریجستر ها از مقدار منفی پشتیبانی نمیکنند.
با همین چند خط کد بالا تایمر ما شروع به شمردن بر اساس تنظیماتی که برای انجام دادیم میکند.
از این طریق هم میتوانید مقدار شمارش شده را بخوانید TIM2->CNT
این رجیستر توانایی نوشتن در آن نیز دارد میتوانید برای دادن مقدار پیشفرض یا مقدار جدید از این طریق اقدام کنید
TIM2->CNT = 10
قابلیت بعدی این تایمرها PWM هست که برای راهاندازیاش بهصورت زیر عمل میکنیم.
اولازهمه از داخل دیتاشیت پایههای متناظر با کانالهای خروجی تایمرمون رو پیدا میکنیم.
من از پایه B6 و B7 استفاده کردهام که کانال 1 و کانال 2 تایمر 4 هست.
پایههای GPIO رو خروجی میکنیم.
میخواهیم یکی از چنلها بهصورت معکوس باشد.
کد کانفیگش به این صورت هست:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ///////////////////////////////////////////////////////////PWM TIMER TIM4 NON INV PB6 TIM_OCInitTypeDef TIMOCCFG; TIMOCCFG.TIM_OCMode = TIM_OCMode_PWM1; از بین مدهای کاری مقایسهگر دو مد PWm1 و PWM2 مربوط به PWM هستند. که فرقی بینشون فقط پلاریته اشون هست. TIMOCCFG.TIM_OCPolarity = TIM_OCPolarity_High; برای تعیین معکوس بودن یا نبودن سیگنال TIMOCCFG.TIM_OutputState = TIM_OutputState_Enable; TIMOCCFG.TIM_Pulse = 10; //DUTY 10% TIM4->PSC = 5; TIM4->ATRLR =100; TIM_OC1Init(TIM4,&TIMOCCFG); فعالکردن output compare1 در تایمر 4 و اعمال تنظیمات انجام شده. ///////////////////////////////////////////////////////////PWM TIMER TIM4 INV PB7 TIM_OCInitTypeDef TIMOC2CFG; TIMOC2CFG.TIM_OCMode = TIM_OCMode_PWM1; TIMOC2CFG.TIM_OCIdleState = TIM_OCIdleState_Set; TIMOC2CFG.TIM_OCPolarity = TIM_OCPolarity_Low; TIMOC2CFG.TIM_OutputState = TIM_OutputState_Enable; TIMOC2CFG.TIM_Pulse = 25; //DUTY 75% این تنظیمات همانند بالایی هست اما پلاریته ان برعکس شده. TIM_OC2Init(TIM4,&TIMOC2CFG); TIM_Cmd(TIM4,ENABLE); |
شما از این طریق میتونید بازه دیوتیسایکل تایمرتون رو تغییر بدید
TIMX->CH1CVR = XX
باید توجه داشته باشید که این بازه باید کوچکتر مساوی با ATRLR باشد و از صفر بزرگتر، مثلاً اگر ATRLR ما 250 بود بازه 0 تا 100 درصد PWM ما میشود 0 تا 250 شما مجاز نیستید عدد بزرگ تر از مقدار ATRLR به مقایسه گر نسبت بدهید،زیرا عملکرد PWM مختل میشود و باید به بازه مجاز خود برگردد.
حالت کاری بعدی تایمر بهصورت Input Capture میباشد که برای کانفیگ کردنش بهصورت زیر عمل میکنیم:
همانطور که از اسمش پیداست این دفعه کانال تایمرمون باید بهصورت ورودی تعریف شود، پس تنظیمات GPIO آن را انجام میدهیم.
1 2 3 | TIM_ICInitTypeDef TIMICCFG; TIMICCFG.TIM_Channel = TIM_Channel_1; |
انتخاب کانال ورودی.
1 | TIMICCFG.TIM_ICFilter = 0x00; |
مقدار فیلترهای ورودی تایمر که باز ان از 0 تا 0x0f هست.
1 | TIMICCFG.TIM_ICPolarity = TIM_ICPolarity_Rising; |
لبه کلاک رو روی بالارونده تنظیم میکنیم.
1 | TIMICCFG.TIM_ICPrescaler = TIM_ICPSC_DIV4; |
مقسم Input capture هست که مقادیر 1 و و4 و8 رو دارد.
1 2 3 | TIMICCFG.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInit(TIM4,&TIMICCFG); |
اعمال تنظیمات انجام شده
1 | TIM_Cmd(TIM4,ENABLE); |
اگر همه مراحل رو درست رفته باشید برای هر یک از بخشهای تایمر باید خروجی دلخواه رو گرفته باشید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | #include "debug.h" /* Global typedef */ /* Global define */ /* Global Variable */ /********************************************************************* * @fn main * * @brief Main program. * * @return none */ int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init(115200); printf("SystemClk:%d\r\n",SystemCoreClock); printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() ); printf("This is printf example\r\n"); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); ///////////////////////////////////////////////////////////BASE TIME TIMER TIM2 TIM_TimeBaseInitTypeDef TIMBCFG; TIMBCFG.TIM_ClockDivision = TIM_CKD_DIV4; TIMBCFG.TIM_CounterMode = TIM_CounterMode_Up; TIMBCFG.TIM_Period = 300; TIMBCFG.TIM_Prescaler = 10; TIM_TimeBaseInit(TIM2,&TIMBCFG); TIM_Cmd(TIM2,ENABLE); ///////////////////////////////////////////////////////////GPIO PWM GPIO_InitTypeDef GPCFG; GPCFG.GPIO_Mode = GPIO_Mode_AF_PP; GPCFG.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; GPCFG.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPCFG); ///////////////////////////////////////////////////////////PWM TIMER TIM4 NON INV PB6 TIM_OCInitTypeDef TIMOCCFG; TIMOCCFG.TIM_OCMode = TIM_OCMode_PWM1; TIMOCCFG.TIM_OCPolarity = TIM_OCPolarity_High; TIMOCCFG.TIM_OutputState = TIM_OutputState_Enable; TIMOCCFG.TIM_Pulse = 10; //DUTY 10% TIM4->PSC = 5; TIM4->ATRLR =100; TIM_OC1Init(TIM4,&TIMOCCFG); ///////////////////////////////////////////////////////////PWM TIMER TIM4 INV PB7 TIM_OCInitTypeDef TIMOC2CFG; TIMOC2CFG.TIM_OCMode = TIM_OCMode_PWM1; TIMOC2CFG.TIM_OCPolarity = TIM_OCPolarity_Low; TIMOC2CFG.TIM_OutputState = TIM_OutputState_Enable; TIMOC2CFG.TIM_Pulse = 25; //DUTY 75% TIM_OC2Init(TIM4,&TIMOC2CFG); TIM_Cmd(TIM4,ENABLE); while(1) { printf( "Counter:%08d\r\n",TIM2->CNT ); printf( "PWM_NONINV DC :%02d%%\r\n",TIM4->CH1CVR ); printf( "PWM_INV DC :%02d%%\r\n",TIM4->CH2CVR ); Delay_Ms(100); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #include "debug.h" /* Global typedef */ /* Global define */ /* Global Variable */ /********************************************************************* * @fn main * * @brief Main program. * * @return none */ int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init(115200); printf("SystemClk:%d\r\n",SystemCoreClock); printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() ); printf("This is printf example\r\n"); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitTypeDef GPCFG; GPCFG.GPIO_Mode = GPIO_Mode_IPU; GPCFG.GPIO_Pin = GPIO_Pin_6; GPIO_Init(GPIOB,&GPCFG); TIM_ICInitTypeDef TIMICCFG; TIMICCFG.TIM_Channel = TIM_Channel_1; TIMICCFG.TIM_ICFilter = 0x00; TIMICCFG.TIM_ICPolarity = TIM_ICPolarity_Rising; TIMICCFG.TIM_ICPrescaler = TIM_ICPSC_DIV4; TIMICCFG.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInit(TIM4,&TIMICCFG); TIM_Cmd(TIM4,ENABLE); ///////////////////////////////////////////////////////////////////////// while(1) { printf( "TIM CNT:%05d\r\n", TIM_GetCapture1(TIM4)); Delay_Ms(300); } } |
در قسمت بعدی میریم سراغ وقفهها و در قسمت بعد با تایمر هم وقفه ایجاد میکنیم، تا قسمت بعد خدانگهدارتان.
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.