در آموزشهای قبلی، در چند بخش از تایمرها استفاده کردیم. Real Time Clock یا همان RTC عنصری است که از آن برای اندازهگیری دقیق زمان واقعی استفاده میشود. ممکن است این سؤال پیش بیاید که این عمل بدون استفاده از RTC نیز امکانپذیر است. چه نیازی به استفاده از این واحد داریم؟ که در جواب باید گفت استفاده از RTC مزایایی دارد که ازجمله آنها میتوان به موارد زیر اشاره کرد:
- مصرف توان پایین (در کاربردهایی که از باتری یا منابع محدود برای توان استفاده میشود اهمیت دارد).
- باعث خالی بودن سیستم جهت انجام عملیات مهم با وابستگی زمانی دقیق میشود.
- بعضیاوقات از روشهای دیگر دقیقتر است.
در این بخش میخواهیم واحد RTC داخلی میکرو را راهاندازی کنیم و علاوه بر اندازهگیری تاریخ و ساعت، یک هشدار یا Alarm تنظیم کنیم. با ما همراه باشید.
ایجاد پروژه
مثل قبل بخشهای دیباگ و USART را تنظیم میکنیم. در این پروژه برای بخش کلاک هر دو بخش HSE و LSE را فعال میکنیم. زیرا میخواهیم برای کلاک RTC از کریستال فرکانس پایین استفاده کنیم. توجه کنید که اگر روی بورد BluePill شما این کریستال تعبیه نشده است از هدر بورد چیپ STM32F103RET6 استفاده کنید.
مرحله بعدی تنظیم کلاک میکرو و انتخاب منبع کلاک برای واحد RTC است، طبق شکل زیر LSE را بهعنوان منبع کلاک انتخاب میکنیم.
حالا باید به سراغ واحد RTC برویم. مانند شکلهای زیر تنظیم این بخش را انجام میدهیم و وقفه را برای آن فعال میکنیم.
بقیهی مراحل را مانند قبل انجام میدهیم و وارد بخش برنامهنویسی میشویم.
نوشتن کد برنامه
مثل پروژه قبل، در ابتدا کتابخانه stdio را به برنامه اضافه کرده و توابع ریدایرکت به USART را مینویسیم و تنظیمات مربوط به آن را انجام میدهیم. سپس ساختارها و متغیرهای مورد نیاز برای تاریخ و ساعت را تعریف میکنیم؛
struct time_t { uint8_t sec; uint8_t min; uint8_t hour; }; struct time_t Time; struct time_t Alarm; struct date_t { uint8_t month; uint8_t day; uint16_t year; }; struct date_t Date; uint8_t EndOfMonth[12]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; uint32_t TimeCounter = 0;
اکنون باید توابع موردنیازمان برای تنظیم و همچنین بهروزرسانی تاریخ و ساعت، و نیز تنظیم هشدار را تعریف کنیم. این توابع را بهصورت زیر مینویسیم:
void DATE_Config( uint8_t fMonth, uint8_t fDay, uint16_t fYear) { Date.month = fMonth; Date.day = fDay; Date.year = fYear; }
void DATE_Update(void) { if ((Time.hour == 00) & (Time.min == 00) & (Time.sec == 00)) { if(Date.day == EndOfMonth[Date.month -1]) { Date.day = 1U; Date.month += 1U; } else { Date.day = Date.day + 0x1U; } if (Date.month == 13) { Date.month = 1U; Date.year += 1U; } } }
void TIME_Update(void) { TimeCounter = LL_RTC_TIME_Get(RTC); Time.hour = (TimeCounter/3600) % 24; Time.min = (TimeCounter % 3600) / 60; Time.sec = (TimeCounter % 3600) % 60; }
void TIME_Config(uint8_t fHour, uint8_t fMin, uint8_t fSec) { Time.hour = fHour; Time.min = fMin; Time.sec = fSec; LL_RTC_TIME_Set(RTC,((Time.hour * 3600) + (Time.min * 60) + Time.sec)); }
void ALARM_Config(uint8_t fHour, uint8_t fMin, uint8_t fSec) { Alarm.hour = fHour; Alarm.min = fMin; Alarm.sec = fSec; LL_RTC_ALARM_Set(RTC,((Alarm.hour * 3600) + (Alarm.min * 60) + Alarm.sec)); }
حالا باید درون int main و قبل از حلقه while(1) تنظیم واحد RTC را انجام دهیم؛
LL_RTC_DisableWriteProtection(RTC); LL_RTC_EnterInitMode(RTC); DATE_Config(12, 13, 2021); TIME_Config(11, 59, 59); ALARM_Config(12, 00, 05); LL_RTC_EnableIT_ALR(RTC); LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_17); LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_17); LL_RTC_ExitInitMode(RTC); LL_RTC_EnableWriteProtection(RTC);
تنها کاری که برای آمادهسازی واحد RTC باقیمانده است، تنظیم Prescaler برای شمارش ثانیه است. همانطور که گفتیم برای این واحد از LSE بهعنوان منبع کلاک استفاده کردهایم. بنابراین فرکانس کلاک RTC برابر با 32.768KHz است. برای اینکه شمارش ثانیه بهدرستی انجام شود، باید مقدار Prescaler را برابر با معادل هگز 32768 یعنی 00008000 قرار دهیم؛
RTC_InitStruct.AsynchPrescaler = 0x00008000U; LL_RTC_Init(RTC, &RTC_InitStruct); LL_RTC_SetAsynchPrescaler(RTC, 0x00008000U);
اکنون میتوانیم زمان را اندازهگیری کنیم. کافی است در حلقه while(1) از توابع بهروزرسانی زمان که نوشتیم استفاده کنیم. برای اطمینان از درستی عملکرد، زمان و تاریخ را به پورت سریال میفرستیم؛
for(int i=0; i<10; i++) { TIME_Update(); DATE_Update(); printf("Time: %.2d:%.2d:%.2d\r\n", Time.hour, Time.min, Time.sec); LL_mDelay(998); } printf("Date: %.2d/%.2d/%.4d\r\n", Date.month, Date.day, Date.year);
در کدهای قبلی زمان هشدار را تنظیم کردهایم. برای روشن کردن یک LED در زمان وقوع وقفه، در فایل stm32f1xx_it.c و در بدنه تابع مربوط به وقفه RTC کد زیر را مینویسیم:
/* USER CODE BEGIN RTC_Alarm_IRQn 0 */ if (LL_RTC_IsEnabledIT_ALR(RTC) == 1) { LL_GPIO_SetOutputPin(GPIOC, LL_GPIO_PIN_13); /* Clear the RTC Second interrupt */ LL_RTC_ClearFlag_ALR(RTC); /* Wait until last write operation on RTC registers has finished */ LL_RTC_WaitForSynchro(RTC); } /* Clear the EXTI's Flag for RTC Alarm */ LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_17); /* USER CODE END RTC_Alarm_IRQn 0 */
پس از رسیدن به زمان تعیینشده برای هشدار، LED روشن خواهد شد. در قسمت بعدی در مورد کالیبره کردن RTC صحبت خواهیم کرد.
منبع:سیسوگ