در بخش قبلی RTC را راهاندازی کردیم و زمان و تاریخ را بهوسیله آن نگهداری کردیم. نکتهای که به آن اشاره نشد دقت اندازهگیری زمان است. دقت اندازهگیری ما به فرکانس منبع کلاک RTC وابسته است. همانطور که دیدیم از منبع LSE بهعنوان کلاک RTC استفاده شد. اما باید بدانیم که فرکانس نامی این نوسان کننده (یعنی 32.768) لزوماً همیشه دقیق و ثابت نیست. یک عامل مهم در تغییر این فرکانس تغییرات دما است و عامل دیگر تغییرات خود کریستال. در ادامه دربارهی این وابستگی و روش کالیبره کردن خودکار RTC برای جبران خطای تغییر فرکانس صحبت خواهیم کرد.
با سیسوگ همراه باشید.
در شکل بالا میبینید که برای یک نوسان کننده خاص در بازه دمایی 20 تا 30 درجه سانتیگراد، بیشترین دقت وجود دارد و با کاهش یا افزایش دما نسبت به این محدوده دقت کاهش مییابد. واحد اندازهگیری این دقت همانطور که در نمودار دیده میشود، ppm است که خطای فرکانس را در هر 1 مگاهرتز نشان میدهد. یا به عبارتی نسبت تغییر فرکانس به فرکانس مرجع ضربدر یکمیلیون میشود. رابطه ریاضی مربوط به منحنی بالا بهصورت زیر نوشته میشود:
که در آن To برابر است با:
همچنین ثابت k که وابسته به کریستال مورداستفاده است در این مورد خاص اینگونه تعریف میشود:
همانطور که در ابتدا اشاره شد، منبع کلاک مورداستفاده برای RTC، یعنی LSE فرکانس نامی 32.768 KHz دارد. البته دو منبع کلاک HSE/128 و LSI نیز قابل انتخاب هستند اما برای استفاده از حالت توان پایین و نیز داشتن دقت مناسب از منبع LSE استفاده میشود. بهمنظور جبران خطا در فرکانس کلاک، بهجز استفاده از خازن (که اشکالات خودش را دارد، ازجمله جریان کشی بیشتر) راه دیجیتالی در نظر گرفته شده است. بدین منظور استفاده از مسیری است که از خروجی انتخاب کلاک RTC به پایههای میکرو وجود دارد:
در این شکل میبینیم که با تنظیم رجیستر BKP_RTCCR میتوانیم کلاک RTC تقسیمبر 64 را روی پین TAMP داشته باشیم. با مقایسه این کلاک با مقدار مرجع آن (32768Hz/64=512Hz) میتوان عمل کالیبره کردن را انجام داد. عمل کالیبره کردن RTC درواقع به این صورت است که در هر مرحله از آنیک سیکل اسیلاتور به ازای هر 1048576 (220) سیکل آن، حذف خواهد شد. بهعبارتدیگر در هر قدم کالیبراسیون 0.954ppm(1000000/220) خطا تصحیح خواهد شد. درنتیجه امکان آهسته کردن کلاک اسیلاتور از 0 تا 121ppm وجود دارد.
برای اینکه بدانیم تأثیر هر ppm در زمانسنجی چقدر است، میتوانیم به جدولی مراجعه کنیم که توسط سازنده میکرو یا اسلاتور منتشر میشود. نکته مهم این است که معمولاً این تغییرات بسیار کوچک هستند و فقط در بازههای بزرگ زمانی دیده میشوند، بهطوریکه واحد اندازهگیری آن تعداد ثانیه در ماه است.
اشاره کردیم که عمل کالیبره کردن با حذف کردن کلاکهای کریستال صورت میگیرد. با توجه به اینکه مقدار تقسیمکننده کلاک (برای شمارش یک ثانیه) روی 32768 تنظیم میشود، تنها خطای مربوط به فرکانسهای سریعتر (بیشتر از 32768Hz) قابل جبران هستند و فرکانسهای آهستهتر را نمیتوان جبران کرد. پس یک راهحل برای این مشکل تنظیم مقدار تقسیمکننده روی 32766 است. به این طریق فرکانسهای کلاک در بازه 32766 تا 32770 هرتز قابل جبران سازی هستند.
در ادامه روش ما برای کالیبره کردن RTC نیز بر همین اساس خواهد بود.
ایجاد پروژه
این پروژه را مانند پروژه قبل تنظیم میکنیم، با این تفاوت که دیگر نیازی به پین خروجی تعریفشده در آن پروژه نداریم و همچنین در بخش مربوط به تنظیم RTC، کلاک را به خروجی میفرستیم:
تایمر1 را نیز برای اندازهگیری فرکانس، در حالت input capture تنظیم میکنیم و وقفه capture compare آن را فعال میکنیم؛
اکنون پروژه را ایجاد میکنیم و وارد بخش کد نویسی میشویم.
نوشتن کد پروژه
کد این پروژه را نیز مثل پروژه قبل مینویسیم، با این تفاوت که دیگر نیازی به تنظیم هشدار و نوشتن وقفه مربوط به آن نداریم. در فایل stm32f1xx_it.c توابع زیر را اضافه و ثابت فرکانس کلاک را تعریف میکنیم؛
1 2 3 | #include "stdio.h" //to use printf/scanf functions #include "stdlib.h" #define TIM1Clock 72000000/72 //Defining a constant for timer1's clock |
متغیرهای مورد نیاز برای اندازهگیری فرکانس و خطا را تعریف میکنیم؛
1 2 3 4 | volatile uint8_t CaptureIndex = 0; volatile uint32_t ARR_count; volatile float Frequency; volatile uint16_t RTC_Cal_ppm; |
در بدنه تابع وقفهی تایمر کد زیر را مینویسیم :
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 | /* USER CODE BEGIN TIM1_CC_IRQn 0 */ if(LL_TIM_IsActiveFlag_CC1(TIM1) == 1) { /* Clear the update interrupt flag*/ LL_TIM_ClearFlag_CC1(TIM1); // static uint8_t CaptureIndex = 0; static uint32_t ICValue1 = 0; static uint32_t ICValue2 = 0; static uint32_t DiffCapture = 0; if(CaptureIndex == 0) { ICValue1 = LL_TIM_IC_GetCaptureCH1(TIM1); CaptureIndex = 1; } else if(CaptureIndex == 1) { ICValue2 = LL_TIM_IC_GetCaptureCH1(TIM1); if (ICValue2 > ICValue1) { DiffCapture = (ICValue2 - ICValue1); } else if (ICValue2 < ICValue1) { DiffCapture = ((TIM1->ARR - ICValue1) + ICValue2) + 1; } Frequency = (float) TIM1Clock / DiffCapture; // printf("Frequency is %3.3f Hz\r\n", Frequency); RTC_Cal_ppm = abs( (int)(((Frequency - 511.968) / 511.968) * 1000000)); // printf("RTC_Cal_ppm is %d \r\n", RTC_Cal_ppm); if(RTC_Cal_ppm > 121) RTC_Cal_ppm = 121; printf("A day has passed and RTC is calibrated. error in ppm was: %d\r\n", RTC_Cal_ppm); CaptureIndex = 0; LL_TIM_DisableIT_CC1(TIM1); } } /* USER CODE END TIM1_CC_IRQn 0 */ |
حالا به سراغ تابع به روزرسانی تاریخ میرویم و به صورت زیر آنرا برای کالیبره کردن به روز میکنیم؛
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 | 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 + 1; } /* calibration (every 24 hours) */ LL_TIM_EnableIT_CC1(TIM1); // to enabel tim1's capture interrupt LL_RTC_CAL_SetCoarseDigital(BKP, (uint32_t) RTC_Cal_ppm); /* end of calibration */ if (Date.month == 13) { Date.month = 1; Date.year += 1; } } } |
همانطور که اشاره کردیم مقدار تقسیمکننده کلاک برای کالیبراسیون باید تغییر کند:
1 2 3 | RTC_InitStruct.AsynchPrescaler = 0x00007FFDU; LL_RTC_Init(RTC, &RTC_InitStruct); LL_RTC_SetAsynchPrescaler(RTC, 0x00007FFDU); |
حالا قبل از حلقه while(1) کد مربوط به فعالسازی تایمر1 و وقفه آن را بنویسیم، همچنین کد مربوط به فرستادن کلاک RTC به پایه خروجی را مینویسیم؛
1 2 3 | LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1); LL_TIM_EnableCounter(TIM1); LL_RTC_SetOutputSource(BKP, LL_RTC_CALIB_OUTPUT_RTCCLOCK); |
حالا باید پایه خروجی کلاک RTC را به ورودی capture تایمر متصل کنیم.
همانطور که میبینیم، کالیبراسیون و جبران خطا در هرروز یک بار اتفاق میافتد. در همین راستا، باید به چند نکته توجه کنیم.
- نکته1: به دلیل اینکه محدودیت جبران سازی در این روش، تا 121ppm است، خطاهای بالاتر به همین مقدار تقریب زدهشدهاند.
- نکته2: ازآنجاییکه کالیبراسیون RTC بر اساس حذف سیکل کلاکهای کریستال عمل میکند، برای بالا بردن دقت، در شمارشهای کم، کارایی ندارد و تنها برای مدتزمانهای طولانی مؤثر است. بنابراین برای مدتزمانهای کوتاه، دقت شمارش بدون کالیبراسیون از شمارش با کالیبراسیون بیشتر است. پس باید حتماً با کاربرد توجه شود.
- نکته3: اگرچه روش کالیبره کردن خودکار گفتهشده، نسبت به روش آنالوگ (اضافه کردن خازن) مزیتهایی دارد و میتواند دقت اندازهگیری زمان را بالا ببرد، اما خالی از اشکال هم نیست. زیرا برای اندازهگیری فرکانس از تایمر استفاده کردیم که خود وابسته به فرکانس کریستال سرعتبالا است و میتواند دچار خطا شود. بهعنوانمثال در پروژهای که انجام دادیم فرکانس RTC (تقسیمبر 64) بهوسیله فرکانس متر دقیق، مقدار 512.172 اندازهگیری شد که با مقدار اندازهگیری شده بهوسیله تایمر اختلاف دارد. افزایش دقت در این عمل مستلزم استفاده از رزوناتور با دقت بالا (تا 3 رقم اعشار یا بیشتر) است.
سلام مجدد
گزارش تست:
با میکروی STM32F100RBT روی برد Discovery VL بعد از کمی ور رفتن با پایه بوت و اذیت جواب گرفتم و برنامه رو اجرا کردم، مشکلی نبود.
اما با STM32F103C8T رو برد Blue Pill که اورجینال هم تهیه کردم با هزینه چند برابری خیر !
LED مربوط به PC13 روشن میشه اما هیچ شمارشی در ثانیه شمار در کار نیست، اما قسمت های دیگه برنامه مثلا تغییر مقدار ساعت کار میکنه.
پایه های خروجی TIM1 که PA8 هست هم به PC13 متصله.
بنظر شما بخاطر سخت افزاره و میکرو قلابی انداختند بهم به اسم اورجینال؟
ظاهرش که از نظر چاپ روی میکرو و غیره اصل بنظر میاد…. چون قلابیش رو قبلا داشتم از قیافش داد میزنه … !
ترس زمانی ایجاد میشه که رو برد اصلی که میخوام چند وقت دیگه بزنم این بازی ها رو دربیاره….
سلام مجدد
بنظر شما برای کالیبره کردن هر 30 روز یکبار مثلا اول هر ماه مناسبه یا هر 24 ساعت مثل کدی که در این مقاله اومده؟
دارم ساعت رومیزی برا خودم میسازم.
سلام ببنید کالیره کردن دوره زمانی نمیخواد، یک بار که انجام بدید مشکل خاصی نباید باشه
علیک سلام و سپاس از این همه نوآوری در خدمات به ملت
ایها الناس شما معادل این کد رو که در آموزش استفاده شده در کتابخانه HAL میدونید؟
LL_RTC_CAL_SetCoarseDigital(BKP, (uint32_t) RTC_Cal_ppm);
دارم به HAL باز نویسی میکنم.
سپاس
سلام
استفاده کردیم، خوب بود
میشه با توایع HAL هم بنویسید؟
برای من که خیلی به این مشکل بوده بالاخص stm32h743 های در بازار ایران که برای بقیه هم پرسیدم مشکل داره اینهم که جلو میره یا عقب میمونه یک چیز ثابت نیست یعنی یکبار جلو میفته یکبار عقب میفته برنامه را هم من ننوشسم خود کیوب ام ایکس کدش را مینویسه و این مشکل هم اغلب روی میکروهای جنس چین پیش میاد ولی مالزی هم بعضا میشد
برای بدست اوردن دقت خیلی بالا نیاز هست هر عدد برد را با کریستال قرار داده شده کالیبره کنید
درسته دوست عزیز
به نظر من دقیقا مشکل همینه که برنامه رو خودتون ویرایش نمیکنید، کیوب ام اکس بر اساس یه سری پیشفرض این کار رو انجام میده و حالا اگر پیش فرض ها رعایت نشده باشه چی ؟
اون موقع است که مشکل پیش میآد دیگه، برای همینه که نیازه برنامه رو خودتون هم تغییر بدید بسته به شرایطی که دارید.
سلام خسته نباشید البته این میکرو که استفاده کردید اگر در تنظیماتش توجه کنید خودش کالیبره خودکار داره ولی میکروهای جدیدتر متاسفانه ندارن ولی مشکل دیگه ای هم که هست اکثر میکروهای در بازار rtc آنها مشکل دارد و اگر کریستال خارجی بگذارید در خیلی از موارد میکرو موقع بالا اومدن قفل میکنه و مشکل دیگه ای هم که هست با تغییر مقدار دو تقسیم کننده اغلب مشکل حل نمیشه چون چیزی که من در عمل دیدم مثلا در میکرو stm32h743 بعضی وقتها یواش میشماره بعضی اوقات هم تند میشماره بهترین راه برای بیشترین دقت که برای این میکروها دیدم اینه که فرکانس rtc را از تقسیم کننده کریستال اصلی بگیریم و بعد با تقسیم کننده ها یک هرتز را تولید کنیم ولی برای این میکرو که شما گذاشتید هم کالیبره اتومات خودش جواب میده ولی همونم نباید از کریستال ساعت خارجی استفاده کرد چون دقت داره ولی خیلی اوقات میکرو هنگ میکنه
سلام و درود دوست عزیز
این مشکلی که ازش حرف میزنید من تا حالا ندیدم، مدتی زیاد با RTC کار میکردم و کریستال خارجی وصل میکردم، احتمالا مشکل از برنامه ای نوشتید باشد، مثلا اگر برنامه شما موفق به راه اندازی LSE نشه اون موقع ممکنه بره توی هاردفالت یا یه جا گیر کنه و اینطور به نظر برسه که میکرو بالانمیآد
البته تجربه این که کریستال کیفیت مناسبی نداشته باشه رو داشتم و همین باعث میشده که این اتفاق بیفته ، برای تریم کردن فرکانس هم ما یکی از خازن های کریستال رو به شکل تریمر میذاشتم و با فرکانس متر اسیلاتور رو تنظیم می کردیم و عملا مشکل عقب افتادن یا جلو افتادن حل میشد