ZHCAEI9 September 2024 MSPM0L1227 , MSPM0L1227-Q1 , MSPM0L1228 , MSPM0L1228-Q1 , MSPM0L2227 , MSPM0L2227-Q1 , MSPM0L2228 , MSPM0L2228-Q1
使用 RT0PS 的 Q0 输出对 RTC 进行校准需要超过 60 秒的时间。通过每四分之一秒增加或减去 1 个时钟脉冲,可以近似得出 ±1ppm 的校正值。一个校准周期内最大可调节范围为 ±240ppm。每个周期都包含对偏移误差和温度误差的调整。超过 ±240ppm 界限的调整会达到饱和状态,但可以在多个周期内完成总补偿。如果未启用 RTC,校准将被禁用。
有 CAL 和 TCMP 两个寄存器用于校准。偏移调整值写入 CAL 寄存器,温度写入 TCMP。但是,TCMP 寄存器会存储两种误差类型之间的净补偿值。当设置 CAL 寄存器时,温度调整值复位为 0,并且 TCMP 仅存储偏移调整值。
进行校准以调整偏移误差时,RTC 提供一系列频率输出以用于外部测量。测试频率为 LFCLK (32kHz)、512Hz、256Hz 和 1Hz。选择结果存储在 CAL 寄存器的 RTCOCALFX 位字段中,如果设置为 0,则会禁用偏移校准。可以使用方程式 6 来计算偏移误差。
其中
计算出误差后,可以写入符号和幅度位字段。符号指示是向上还是向下调整。如果误差为正(向上校准),则设置 RTCOCALS,如果符号为负(向下校准),则清除 RTCOCALS。RTCOCALX 是幅度,限制为最大 240ppm。如果将有符号结果写入 RTCOCALX,负值用二进制补码表示,并饱和至 240ppm。
温度漂移校准方法是获取 ADC 的温度近似值,计算由相对于晶体周转温度的偏差引起的频率误差,然后将结果写入 TCMP 寄存器。获取温度近似值的过程如节 3.5.1所述。使用方程式 7 可计算频率误差。
其中
前面的信息可以在晶体振荡器数据表中找到。
该公式会根据时钟信号的来源而变化。具体而言,周转温度和抛物线系数变量取决于所使用的振荡器类型。此示例中使用的是音叉外部晶体,周转温度和抛物线系数见数据表。音叉晶体的一个独特属性就是温度偏差会导致负频率偏差。这意味着补偿方向是向上。计算出误差后,与偏移误差类似,符号(向上)将写入 RTCTCMPS 位字段,而幅度将写入 RTCTCMPX 位字段。写入成功后,当读取 TCMP 寄存器时,该寄存器已存储偏移误差和温度偏差之间的净补偿值。如果净补偿值太大,RTCTCMPX 将饱和至 240ppm。由温度偏差确定的补偿将在下一个校准周期引入,而不是在当前校准周期引入。由于每个周期的长度为 60 秒,此示例每分钟只测量一次温度,并由 RTC_A 间隔中断进行触发。可以更改该值来多次测量温度并取平均值。
#include "ti_msp_dl_config.h"
/*
* The following trim parameter is provided in the device datasheet in chapter
* "Temperature Sensor"
*/
#define TEMP_TS_TRIM_C ((uint32_t)30)
/*
* Constant below is (1/TSc). Where TSc is Temperature Sensor coefficient
* available in the device datasheet
*/
#define TEMP_TS_COEF_mV_C (-555.55f)
#define ADC_VREF_VOLTAGE (1.4f)
#define ADC_BIT_RESOLUTION ((uint32_t)(1)<<12)
/*
* The following turnover and Parabolic Coefficient parameter is provided
* in the crystal oscillator datasheet
*/
#define TEMP_CRYSTAL_Ti_C ((uint32_t)25)
#define TEMP_CRYSTAL_B_PPM_C2 (-0.04f)
/* Offset measurements */
typedef struct measFreq {
double freq;
DL_RTC_COMMON_OFFSET_CALIBRATION_SIGN sign;
} measFreq;
volatile bool gStatus = false;
volatile bool gCheckADC;
void offset_error_correction(void) {
measFreq slowCrystal;
measFreq fastCrystal;
/* Simulated frequency measurements (will need to be manually measured)*/
slowCrystal.freq = 511.9658;
fastCrystal.freq = 512.0241;
/* Correction sign is adjusted up for slow measured frequencies and down for fast measured frequencies */
slowCrystal.sign = DL_RTC_COMMON_OFFSET_CALIBRATION_SIGN_UP;
fastCrystal.sign = DL_RTC_COMMON_OFFSET_CALIBRATION_SIGN_DOWN;
measFreq examples[2] = {slowCrystal, fastCrystal};
/* Process for updating calibration register (for both slow and fast measurements)*/
for(uint8_t i = 0; i < 2; i++) {
/* Division Factor for 512Hz output frequency */
uint8_t divFact = 64;
/* Ideal Crystal Oscillation frequency*/
uint16_t optOsc = 32768;
/* Error Correction Formula:
* Frtcclk = Frtcclk,meas * divider factor
* Offset Value = Round(60 * 16384 * (1 - Frtcclk/32768))
*/
double Frtcclk = examples[i].freq * divFact;
uint8_t offVal = fabs(round(60 * 16384 * (1 - (Frtcclk / optOsc))));
/* Wait until RTC is ready for compensation */
while (!DL_RTC_A_isReadyToCalibrate(RTC_A)) {
;
}
/* Sets the offset error calibration adjustment value */
DL_RTC_A_setOffsetCalibrationAdjValue(RTC_A, examples[i].sign, offVal);
/* Check if write was successful */
gStatus = DL_RTC_A_isCalibrationWriteResultOK(RTC_A);
/* Stop the debugger to examine the output. At this point,
* gStatus should be equal to "true" and the CAL register
* should be updated.
* slow crystal adjustment should be +66ppm
* fast crystal adjustment should be -46ppm
*/
__BKPT(0);
}
}
void temp_drift_correction(float vTrim) {
uint32_t adcResult, tComp;
float vSample, tSample;
DL_RTC_COMMON_TEMP_CALIBRATION tSign;
gCheckADC = false;
/* Read stored ADC value */
adcResult = DL_ADC12_getMemResult(ADC12_0_INST, ADC12_0_ADCMEM_0);
/*
* Convert ADC result to equivalent voltage:
* Vsample = (VREF_VOLTAGE_MV*(adcResult -0.5))/(2^ADC_BIT_RESOLUTION)
*/
vSample = ADC_VREF_VOLTAGE / ADC_BIT_RESOLUTION * (adcResult -0.5);
/*
* Apply temperature sensor calibration data
* TSAMPLE = (TEMP_TS_COEF_mV_C) * (vSample - vTrim) + TEMP_TS_TRIM_C
*/
tSample = TEMP_TS_COEF_mV_C * (vSample - vTrim) + TEMP_TS_TRIM_C;
/*
* Compensation sign and value
* TCOMP = (tSample - TEMP_CRYSTAL_Ti_C)^2 * TEMP_CRYSTAL_B_PPM_C2
*/
tSign = RTC_TCMP_RTCTCMPS_UP;
tComp = fabs(pow((int16_t)tSample - (int16_t)TEMP_CRYSTAL_Ti_C, 2) * TEMP_CRYSTAL_B_PPM_C2);
/* Wait until RTC is ready for compensation */
while (!DL_RTC_A_isReadyToCalibrate(RTC_A)) {
;
}
/* Sets the temperature error calibration value */
DL_RTC_Common_setTemperatureCompensation(RTC_A, tSign, tComp);
/* Check if write was successful */
gStatus = DL_RTC_A_isCalibrationWriteResultOK(RTC_A);
/* Stop the debugger to examine the output. At this point,
* gStatus should be equal to "true" and the TCMP register
* should be updated.
* The TCMP register will show the net error
* of the offset and temperature errors.
*/
__BKPT(0);
}
int main(void)
{
/* Output frequency for offset calculation initialized to 512Hz */
SYSCFG_DL_init();
/* Enable RTC interrupts on device */
NVIC_EnableIRQ(RTC_A_INST_INT_IRQN);
/* Start RTC clock */
DL_RTC_A_enableClockControl(RTC_A);
/* Disclaimers:
* Writing to the RTCOCAL register resets the temperature
* to zero.
*
* The maximum correction in one calibration cycle is +-240ppm.
* Any error outside this range will be ignored.
*/
/* Offset Error Correction Mechanism */
offset_error_correction();
/* Temperature Drift Correction Mechanism */
/*
* Convert TEMP_SENSE0 result to equivalent voltage:
* Vtrim = (ADC_VREF_VOLTAGE*(TEMP_SENSE0 -0.5))/(2^12)
*/
float vTrim;
vTrim = ADC_VREF_VOLTAGE / ADC_BIT_RESOLUTION * (DL_SYSCTL_getTempCalibrationConstant() -0.5);
gCheckADC = false;
while (1) {
if(gCheckADC) {
temp_drift_correction(vTrim);
}
DL_ADC12_startConversion(ADC12_0_INST);
__WFI();
DL_ADC12_stopConversion(ADC12_0_INST);
}
}
void RTC_A_INST_IRQHandler(void)
{
switch (DL_RTC_A_getPendingInterrupt(RTC_A)) {
case DL_RTC_A_IIDX_INTERVAL_TIMER:
gCheckADC = true;
break;
default:
break;
}
}