آموزش STM32 با توابع LL قسمت بیست و هفتم: کالیبره کردن RTC

0
224
آموزش STM32 با توابع LL قسمت بیست و هفتم: کالیبره کردن RTC
کالیبره کردن RTC

در بخش قبلی RTC را راه‌اندازی کردیم و زمان و تاریخ را به‌وسیله آن نگهداری کردیم. نکته‌ای که به آن اشاره نشد دقت اندازه‌گیری زمان است. دقت اندازه‌گیری ما به فرکانس منبع کلاک RTC وابسته است. همان‌طور که دیدیم از منبع LSE به‌عنوان کلاک RTC استفاده شد. اما باید بدانیم که فرکانس نامی این نوسان کننده (یعنی 32.768) لزوماً همیشه دقیق و ثابت نیست. یک عامل مهم در تغییر این فرکانس تغییرات دما است و عامل دیگر تغییرات خود کریستال. در ادامه درباره‌ی این وابستگی و روش کالیبره کردن خودکار RTC برای جبران خطای تغییر فرکانس صحبت خواهیم کرد.

با ما همراه باشید.

منحنی نوعی مشخص کننده وابستگی دقت فرکانس نوسان‌کننده نسبت به تغییرات دما.

در شکل بالا می‌بینید که برای یک نوسان کننده خاص در بازه دمایی 20 تا 30 درجه سانتی‌گراد، بیشترین دقت وجود دارد و با کاهش یا افزایش دما نسبت به این محدوده دقت کاهش می‌یابد. واحد اندازه‌گیری این دقت همان‌طور که در نمودار دیده می‌شود، ppm است که خطای فرکانس را در هر 1 مگاهرتز نشان می‌دهد. یا به عبارتی نسبت تغییر فرکانس به فرکانس مرجع ضرب‌در یک‌میلیون می‌شود. رابطه ریاضی مربوط به منحنی بالا به‌صورت زیر نوشته می‌شود:

رابطه ریاضی مربوط به منحنی

که در آن To برابر است با:

رابطه ریاضی مربوط به منحنی و مقدار TO

همچنین ثابت k که وابسته به کریستال مورداستفاده است در این مورد خاص این‌گونه تعریف می‌شود:

 ثابت k که وابسته به کریستال

همان‌طور که در ابتدا اشاره شد، منبع کلاک مورداستفاده برای RTC، یعنی LSE فرکانس نامی 32.768 KHz دارد. البته دو منبع کلاک HSE/128 و LSI نیز قابل انتخاب هستند اما برای استفاده از حالت توان پایین و نیز داشتن دقت مناسب از منبع LSE استفاده می‌شود. به‌منظور جبران خطا در فرکانس کلاک، به‌جز استفاده از خازن (که اشکالات خودش را دارد، ازجمله جریان کشی بیشتر) راه دیجیتالی در نظر گرفته شده است. بدین منظور استفاده از مسیری است که از خروجی انتخاب کلاک RTC به پایه‌های میکرو وجود دارد:

کلاک RTC

در این شکل می‌بینیم که با تنظیم رجیستر BKP_RTCCR می‌توانیم کلاک RTC تقسیم‌بر 64 را روی پین TAMP داشته باشیم. با مقایسه این کلاک با مقدار مرجع آن (32768Hz/64=512Hz) می‌توان عمل کالیبره کردن را انجام داد. عمل کالیبره کردن RTC درواقع به این صورت است که در هر مرحله از آن‌یک سیکل اسیلاتور به ازای هر 1048576 (220) سیکل آن، حذف خواهد شد. به‌عبارت‌دیگر در هر قدم کالیبراسیون 0.954ppm(1000000/220) خطا تصحیح خواهد شد. درنتیجه امکان آهسته کردن کلاک اسیلاتور از 0 تا 121ppm وجود دارد.

برای اینکه بدانیم تأثیر هر ppm در زمان‌سنجی چقدر است، می‌توانیم به جدولی مراجعه کنیم که توسط سازنده میکرو یا اسلاتور منتشر می‌شود. نکته مهم این است که معمولاً این تغییرات بسیار کوچک هستند و فقط در بازه‌های بزرگ زمانی دیده می‌شوند، به‌طوری‌که واحد اندازه‌گیری آن تعداد ثانیه در ماه است.

 بخشی از جدول نوعی مربوط به مقادیر کالیبراسیون کلاک RTC.
بخشی از جدول نوعی مربوط به مقادیر کالیبراسیون کلاک RTC.

اشاره کردیم که عمل کالیبره کردن با حذف کردن کلاک‌های کریستال صورت می‌گیرد. با توجه به اینکه مقدار تقسیم‌کننده کلاک (برای شمارش یک ثانیه) روی 32768 تنظیم می‌شود، تنها خطای مربوط به فرکانس‌های سریع‌تر (بیشتر از 32768Hz) قابل جبران هستند و فرکانس‌های آهسته‌تر را نمی‌توان جبران کرد. پس یک راه‌حل برای این مشکل تنظیم مقدار تقسیم‌کننده روی 32766 است. به این طریق فرکانس‌های کلاک در بازه 32766 تا 32770 هرتز قابل جبران سازی هستند.

در ادامه روش ما برای کالیبره کردن RTC نیز بر همین اساس خواهد بود.

 

ایجاد پروژه

این پروژه را مانند پروژه قبل تنظیم می‌کنیم، با این تفاوت که دیگر نیازی به پین خروجی تعریف‌شده در آن پروژه نداریم و همچنین در بخش مربوط به تنظیم RTC، کلاک را به خروجی می‌فرستیم:

کالیبره کردن RTC
تنظیم RTC.

تایمر1 را نیز برای اندازه‌گیری فرکانس، در حالت input capture تنظیم می‌کنیم و وقفه capture compare آن را فعال می‌کنیم؛

کالیبره کردن RTC
تنظیم تایمر1.

اکنون پروژه را ایجاد می‌کنیم و وارد بخش کد نویسی می‌شویم.

 

نوشتن کد پروژه

کد این پروژه را نیز مثل پروژه قبل می‌نویسیم، با این تفاوت که دیگر نیازی به تنظیم هشدار و نوشتن وقفه مربوط به آن نداریم. در فایل stm32f1xx_it.c توابع زیر را اضافه و ثابت فرکانس کلاک را تعریف می‌کنیم؛

#include "stdio.h" //to use printf/scanf functions
#include "stdlib.h"
#define TIM1Clock 72000000/72 //Defining a constant for timer1's clock

متغیرهای مورد نیاز برای اندازه‌گیری فرکانس و خطا را تعریف می‌کنیم؛

volatile uint8_t CaptureIndex = 0;
volatile uint32_t ARR_count;
volatile float Frequency;
volatile uint16_t RTC_Cal_ppm;

در بدنه تابع وقفه‌ی تایمر کد زیر را می‌نویسیم :

/* 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 */

حالا به سراغ تابع به روزرسانی تاریخ می‌رویم و به صورت زیر آن‌را برای کالیبره کردن به روز می‌کنیم؛

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;
}
}

}

 

همان‌طور که اشاره کردیم مقدار تقسیم‌کننده کلاک برای کالیبراسیون باید تغییر کند:

 RTC_InitStruct.AsynchPrescaler = 0x00007FFDU;
LL_RTC_Init(RTC, &RTC_InitStruct);
LL_RTC_SetAsynchPrescaler(RTC, 0x00007FFDU);

حالا قبل از حلقه while(1) کد مربوط به فعال‌سازی تایمر1 و وقفه آن را بنویسیم، همچنین کد مربوط به فرستادن کلاک RTC به پایه خروجی را می‌نویسیم؛

 LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1);
LL_TIM_EnableCounter(TIM1);
LL_RTC_SetOutputSource(BKP, LL_RTC_CALIB_OUTPUT_RTCCLOCK);

حالا باید پایه خروجی کلاک RTC را به ورودی capture تایمر متصل کنیم.

کالیبره کردن RTC

همان‌طور که می‌بینیم، کالیبراسیون و جبران خطا در هرروز یک بار اتفاق می‌افتد. در همین راستا، باید به چند نکته توجه کنیم.

  • نکته1: به دلیل اینکه محدودیت جبران سازی در این روش، تا 121ppm است، خطاهای بالاتر به همین مقدار تقریب زده‌شده‌اند.
  • نکته2: ازآنجایی‌که کالیبراسیون RTC بر اساس حذف سیکل کلاک‌های کریستال عمل می‌کند، برای بالا بردن دقت، در شمارش‌های کم، کارایی ندارد و تنها برای مدت‌زمان‌های طولانی مؤثر است. بنابراین برای مدت‌زمان‌های کوتاه، دقت شمارش بدون کالیبراسیون از شمارش با کالیبراسیون بیشتر است. پس باید حتماً با کاربرد توجه شود.
  • نکته3: اگرچه روش کالیبره کردن خودکار گفته‌شده، نسبت به روش آنالوگ (اضافه کردن خازن) مزیت‌هایی دارد و می‌تواند دقت اندازه‌گیری زمان را بالا ببرد، اما خالی از اشکال هم نیست. زیرا برای اندازه‌گیری فرکانس از تایمر استفاده کردیم که خود وابسته به فرکانس کریستال سرعت‌بالا است و می‌تواند دچار خطا شود. به‌عنوان‌مثال در پروژه‌ای که انجام دادیم فرکانس RTC (تقسیم‌بر 64) به‌وسیله فرکانس متر دقیق، مقدار 512.172 اندازه‌گیری شد که با مقدار اندازه‌گیری شده به‌وسیله تایمر اختلاف دارد. افزایش دقت در این عمل مستلزم استفاده از رزوناتور با دقت بالا (تا 3 رقم اعشار یا بیشتر) است.

 

   لینک این پروژه در گیت‌هاب

 

منبع:سیسوگ

مطلب قبلیمولتی ترن ( Multi Turn) چیست
مطلب بعدیمکان‌یابی در مکانهای بسته با WiFi (بدون GPS)

پاسخ دهید

لطفا نظر خود را وارد کنید!
لطفا نام خود را در اینجا وارد کنید