در قسمت قبلی نحوه راهاندازی PWM را آموختیم و در این قسمت، میخواهیم نحوه ساخت تایمرهای 32 بیتی و 64 بیتی را بررسی کنیم. همانطور که احتمالا میدانید، در میکروکنترلرهای مورد استفاده در این آموزش، همه تایمرها (به جز SysTick) 16 بیتی هستند. که البته ممکن است این مسئله مشکل ساز نباشد. اما گاهی پیش میآید که برای اندازهگیری طول یک پالس با دقت بالا، نیاز پیدا کنیم که تایمری با تعداد بیتهای بیشتری در اختیار داشته باشیم. یا اینکه اگر قصد شمارش تعداد پالسها یا لبههای یک سیگنال خارجی را داشته باشیم که تعداد آنها از ۶۵۵۳۶ بیشتر باشد، تایمرهای 16 بیتی کارساز نیستند. پس در این موقعیتها، راه حل چه خواهد بود؟ آیا لازم است که از خانوادهای از میکروکنترلر استفاده کرد که تایمرهایی با دقت بالا دارند؟ یا میشود به شکلی این مسئله را حل کرد؟ البته باید گفت که راههای نرمافزاری برای حل این این مشکلها وجود دارد. اما در این قسمت یک راه حل ساده سختافزاری برای عبور کردن از این محدودیت، معرفی خواهیم کرد. پس با سیسوگ همراه باشید.
چطور میشود تایمر ۶۴ بیتی ساخت؟
همانطور که میدانید، در میکروکنترلر STM32F103C8T6 که در بورد Blue Pill بهکاررفته است، تنها تایمرهای 16 بیتی وجود دارد. اگر برای کاربرد خاصی نیاز به تایمر بزرگتری داشته باشیم (مثلاً برای اندازهگیری فرکانسهای بالا یا شمارشهای طولانی یک واقعه یا گذر زمان)، باید با استفاده از این تایمرهای 16 بیتی، و اتصال آنها بهصورت سختافزاری، عمل موردنظرمان را انجام دهیم. به عبارت دقیقتر اگر هر بار سرریز تایمر 1 بهعنوان کلاک تایمر 2 عمل کند، بدین گونه یک تایمر 32 بیتی ساختهایم. به همین ترتیب اگر 3 یا 4 تایمر را به هم متصل کنیم، تایمر 48 و 64 بیتی در اختیار خواهیم داشت.
برای اینکه مسئله موردنظر را بهتر درک کنیم، میتوانیم به یک حالت سادهتر این موضوع نگاه کنیم؛ زمانی که میخواهیم بهوسیله اتصال چند شمارنده، یک شمارنده بزرگتر بسازیم، میتوانیم با استفاده از پایه سرریز یا پایه پرارزش یک شمارنده، کلاک یک شمارنده پرارزشتر را کنترل کنیم. بدینوسیله هرگاه شمارش به حداکثر مقدار خود در شمارنده اول میرسد، شمارنده پرارزشتر یک واحد شمارش میکند. به همین ترتیب شمارنده دوم نیز میتواند کلاک شمارنده سوم را کنترل کند و این روند ادامه پیدا کند. همچنین میدانیم که تایمرهای میکروکنترلرها نیز درواقع شمارنده هستند. پس احتمالاً به روش مشابه میتوان تایمرها را نیز به یکدیگر متصل کرد.
قبلاً گفته شد که برای هر تایمر، منبع کلاک میتواند بهصورت داخلی یا خارجی تنظیم میشود. همچنین، برای هر تایمر یک منبع تریگر میتوان تعریف کرد. این تریگر میتواند از منبعهای مختلفی فرستاده شود. در حالتی که تایمر روی حالت Slave Mode و External Clock Mode1 تنظیمشده باشد، منبع تریگر بهعنوان منبع کلاک در نظر گرفته میشود. بنابراین با این روش میتوان کلاک یک تایمر را توسط یک تایمر دیگر تأمین نمود. در ادامه برای ساخت تایمر 32 و 64 بیتی با ما همراه باشید.
ایجاد پروژه
در محیط Cube MX یک پروژه جدید ایجاد میکنیم و مراحل تنظیم کلاک و دیباگ را مانند گذشته انجام میدهیم. تایمرها را به ترتیب و بهصورت زیر تنظیم میکنیم.
تنظیم تایمر 2
تنظیم تایمر 4
بدین ترتیب هر تایمر با update event یا سرریز کردن یک تایمر دیگر، شمارش میکند و عملاً هر 4 تایمر را به هم متصل کردهایم. کلاک تایمر اول را هم از پایه خارجی تأمین میکنیم تا بتوانیم تعداد پالس یک عمل خاص را بشماریم.
اندازهگیری فرکانس با تایمر 32 بیتی
بهعنوان یک کاربرد نمونه برای تایمر 32 بیتی، میخواهیم فرکانس یک سیگنال ورودی (که دوره تناوب کوچک و فرکانس بالایی دارد) را بدون استفاده از حالت Input Capture، اندازهگیری کنیم. بدین منظور تایمرهای 1 و 2 را طبق شکلهای بالا تنظیم میکنیم و تنظیم تایمر 3 را برای اندازهگیری زمان 1 ثانیه، طبق شکل زیر انجام میدهیم:
و همچنین وقفه تایمر 3 را فعال میکنیم.
در ادامه، برای ایجاد یک موج با فرکانس 12 مگاهرتز (برای معیار درستی اندازهگیری) تایمر 4 را در حالت PWM تنظیم میکنیم؛
همچنین واحد USART1 را برای نمایش دادن فرکانس محاسبهشده فعال میکنیم. بقیهی مراحل ایجاد پروژه را مانند گذشته طی میکنیم و کد پروژه را ایجاد میکنیم تا وارد محیط نرمافزار Keil شویم.
نوشتن کد پروژه
در نوشتن کد مربوط به این پروژه یکی از کارهایی که باید انجام دهیم فعالسازی هر 4 تایمر و همچنین فعالسازی وقفه مربوط به تایمر 3 است؛
1 2 3 4 5 6 7 8 | LL_TIM_EnableCounter(TIM1); //Enable Timer1's counter LL_TIM_EnableCounter(TIM2); //Enable Timer2's counter LL_TIM_EnableCounter(TIM3); //Enable Timer3's counter LL_TIM_EnableIT_UPDATE(TIM3); //Enable Update Event Interrupt for Timer3 LL_TIM_EnableCounter(TIM4); //Enable Timer4's counter LL_TIM_CC_EnableChannel(TIM4, LL_TIM_CHANNEL_CH1); //Enable channel1 of Timer4(PWM) LL_TIM_EnableAllOutputs(TIM4); //Enable Timer4's outputs LL_TIM_OC_SetCompareCH1(TIM4, ( (LL_TIM_GetAutoReload(TIM4) + 1 ) / 2)); //Set Duty cycle to 50% |
در ادامه، در فایل stm32f1xx_it.c متغیرهای زیر را تعریف میکنیم:
1 2 3 | float frequency; uint32_t counterLS; uint32_t counterMS; |
میتوانیم برای نمایش اطلاعات، از ریدایرکت تابع printf استفاده کنیم، بدین منظور هدرفایل stdio.h را اضافه کرده و توابع زیر را تعریف میکنیم:
1 2 3 4 5 6 7 8 9 10 11 12 | /* Function for transmitting 8bit data via USART */ void write_uart(char data) { while(!LL_USART_IsActiveFlag_TXE(USART1)); LL_USART_TransmitData8(USART1, (uint8_t)data); } /* Retargeting stdout_putchar as to use USART_TX for data output */ int stdout_putchar (int ch) { write_uart(ch); return (ch); } |
در تابع وقفه تایمر 3 کد زیر را برای محاسبه و نمایش فرکانس مینویسیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* USER CODE BEGIN TIM3_IRQn 0 */ if(LL_TIM_IsActiveFlag_UPDATE(TIM3) == 1) { /* Clear the update interrupt flag*/ LL_TIM_ClearFlag_UPDATE(TIM3); //Clear Timer3's Update flag counterLS = LL_TIM_GetCounter(TIM1); //Get the counter value of Timer1 counterMS = LL_TIM_GetCounter(TIM2); //Get the counter value of Timer2 frequency = (float)((counterMS * LL_TIM_GetAutoReload(TIM1)) + counterLS ) / 1000000; //Calculate the frequency of the input signal LL_TIM_SetCounter(TIM1, 0); //Reset the counter value of Timer1 LL_TIM_SetCounter(TIM2, 0); //Reset the counter value of Timer2 printf("frequency is %2.6f MHz\r\n", frequency); //Print the frequency printf("n. of pulses is %d\r\n\r\n", (counterMS * LL_TIM_GetAutoReload(TIM1)) + counterLS ); //print the number of pulses counted } /* USER CODE END TIM3_IRQn 0 */ |
اکنون تنها کاری که باید انجام دهیم اتصال خروجی PWM به پایه مربوط به کلاک خارجی تایمر 1 است (پایههای B6 و A12)، تا فرکانس سیگنال PWM اندازهگیری شود. درصورتیکه تمامی مراحل بهدرستی انجامشده باشه در ترمینال سریال مقدار فرکانس و تعداد پالسهای دریافت شده را میبینیم و درنهایت توانستیم تایمر 64 بیتی با توابع LL را پیادهسازی کنیم.