diff --git a/src/CatenaBase.h b/src/CatenaBase.h index f7f8f7a..b77c1b3 100644 --- a/src/CatenaBase.h +++ b/src/CatenaBase.h @@ -55,7 +55,7 @@ Copyright notice: #define CATENA_ARDUINO_PLATFORM_VERSION_CALC(major, minor, patch, local) \ (((major) << 24u) | ((minor) << 16u) | ((patch) << 8u) | (local)) -#define CATENA_ARDUINO_PLATFORM_VERSION CATENA_ARDUINO_PLATFORM_VERSION_CALC(0, 16, 0, 1) /* v0.16.0.1 */ +#define CATENA_ARDUINO_PLATFORM_VERSION CATENA_ARDUINO_PLATFORM_VERSION_CALC(0, 16, 0, 50) /* v0.16.0.50 */ #define CATENA_ARDUINO_PLATFORM_VERSION_GET_MAJOR(v) \ (((v) >> 24u) & 0xFFu) diff --git a/src/CatenaStm32L0.h b/src/CatenaStm32L0.h index 2e1b66c..3e3963f 100644 --- a/src/CatenaStm32L0.h +++ b/src/CatenaStm32L0.h @@ -107,7 +107,7 @@ class CatenaStm32L0 : public CatenaStm32 return this->m_Rtc.GetTime(); } - uint32_t CalibrateSystemClock(bool fSetCalibVal, uint32_t CalibVal); + uint32_t CalibrateSystemClock(void); protected: // methods diff --git a/src/lib/stm32/stm32l0/CatenaStm32L0_CalibrateSystemClock.cpp b/src/lib/stm32/stm32l0/CatenaStm32L0_CalibrateSystemClock.cpp index 3cb16aa..d8affa0 100644 --- a/src/lib/stm32/stm32l0/CatenaStm32L0_CalibrateSystemClock.cpp +++ b/src/lib/stm32/stm32l0/CatenaStm32L0_CalibrateSystemClock.cpp @@ -26,7 +26,7 @@ using namespace McciCatena; | \****************************************************************************/ -static uint32_t GetMillisPerSecond(void); +static uint32_t MeasureMillisPerRtcSecond(void); /****************************************************************************\ @@ -58,17 +58,57 @@ Name: CatenaStm32L0::CalibrateSystemClock() ); Description: - This function calibrates system clock. + This function calibrates the system clock. + + On platforms using this code, the clock is running + either from the MSI or the HSI clock. These clocks + are based on an internal resonator, and so they're + not very accurate. Over temperature and Vdd + voltage, they can drift +/- 10%. This affects + SYSTICK, and therefore affects all the computed + timing in the system. So we have to calibrate + somehow. + + There are two ways to calibrate: either trim the + oscillator to match a known reference, or measure + the actual frequency of the system oscilator and + adjust values read from SYSTICK (and the SYSTICK.LOAD + register) accordingly. We chose to adjust the trim + of the oscillator. + + Calibration values are clock-speed specific. If MSI + clock is being used, then they range from 0 to 255; + if HSI is being used, tehn they range from 0 to 31. + The HSI clock cannot be as finely adjusted. + + The datasheet [section 7.2.15] mentions that you can + use TIM21, but there's no sample code. We're need to + do better than we've been doing, so the first version + of this function runs for several seconds and compares + millis (driven by system clock) to the output of the + RTC (driven by LSE). + + In general, this routine works by stepping the clock + rate up or down until the sign of the error changes. + Then we use the calibration value that got us closest. + + We recommend that you call this periodically, but + choose a time when you don't expect to do anything + for a while, becuase this routine can run for quite + a while if the calibration is badly off. Returns: New calibration value +Notes: + This can be quite time consuming in the current + implementation. But we don't use TIM21, which might + be considered an advantage, leaving it free for + other purposes. + */ -uint32_t CatenaStm32L0::CalibrateSystemClock( - bool fSetCalibVal, - uint32_t CalibVal - ) +uint32_t CatenaStm32L0::CalibrateSystemClock(void) { uint32_t Calib; uint32_t CalibNew; @@ -78,9 +118,10 @@ uint32_t CatenaStm32L0::CalibrateSystemClock( uint32_t mSecondNew; uint32_t mSecondLow; uint32_t mSecondHigh; - bool fCalibrateMSI; + bool fHaveSeenLow; + bool fHaveSeenHigh; + const bool fCalibrateMSI = GetSystemClockRate() < 16000000; - fCalibrateMSI = GetSystemClockRate() < 16000000; if (fCalibrateMSI) { Calib = (RCC->ICSCR & RCC_ICSCR_MSITRIM) >> 24; @@ -90,113 +131,149 @@ uint32_t CatenaStm32L0::CalibrateSystemClock( Calib = (RCC->ICSCR & RCC_ICSCR_HSITRIM) >> 8; } gLog.printf( - gLog.kAlways, + gLog.kTrace, "+CatenaStm32L0::CalibrateSystemClock: %cSICalib=%u\n", fCalibrateMSI ? 'M' : 'H', Calib ); - if (fSetCalibVal) - { - if (fCalibrateMSI) - { - __HAL_RCC_MSI_CALIBRATIONVALUE_ADJUST(CalibVal); - } - else + /* preapre to loop, setting suitable defaults */ + CalibNew = Calib; + CalibLow = 0; + CalibHigh = 0; + mSecondLow = 0; + mSecondHigh = 2000; + fHaveSeenLow = fHaveSeenHigh = false; + + /* loop until we have a new value */ + do { + /* meassure the # of millis per RTC second */ + mSecond = MeasureMillisPerRtcSecond(); + + /* invariant: */ + if (Calib == CalibNew) + mSecondNew = mSecond; + gLog.printf( + gLog.kTrace, + " Calib=%u %u msec\n", + Calib, + mSecond + ); + + /* if mSecond is low, this meaans we must increase the system clock */ + if (mSecond <= 1000) { - __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(CalibVal); - } - delay(500); - CalibNew = CalibVal; - mSecondNew = GetMillisPerSecond(); - } - else - { - CalibNew = Calib; - CalibLow = 0; - CalibHigh = 0; - mSecondLow = 0; - mSecondHigh = 2000; - - do { - mSecond = GetMillisPerSecond(); - if (Calib == CalibNew) - mSecondNew = mSecond; - gLog.printf( - gLog.kAlways, - " Calib=%u %u msec\n", - Calib, - mSecond - ); - if (mSecond <= 1000) + /* + || the following condition establishes that we're + || below the target frequency, but closer than we've been + || before (mSecondLow is the previous "low" limit). If + || so, we reduce the limit, and capture the "low" calibration + || value. + */ + if (mSecond > mSecondLow) + { + mSecondLow = mSecond; + CalibLow = Calib; /* save previous calibration value */ + fHaveSeenLow = true; + } + + /* + || if we are low, and we have never exceeded the high limit, + || we can increase the clock. + */ + if (! fHaveSeenHigh) { - if (mSecond > mSecondLow) + if (fCalibrateMSI) { - mSecondLow = mSecond; - CalibLow = Calib; + if (Calib < 0xFF) + { + ++Calib; + __HAL_RCC_MSI_CALIBRATIONVALUE_ADJUST(Calib); + } + else + break; } - if (CalibHigh == 0) + else { - if (fCalibrateMSI) - { - Calib = (Calib + 1) & 0xFFu; - __HAL_RCC_MSI_CALIBRATIONVALUE_ADJUST(Calib); - } + if (Calib < 0x1F) + { + ++Calib; + __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(Calib); + } else - { - Calib = (Calib + 1) & 0x1Fu; - __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(Calib); - } - delay(500); + { + break; + } } + + /* let the clock settle */ + delay(500); } - else + } + + /* if mSecond is high, we must reduce the system clock */ + else + { + /* + || the following condition establishes that we're + || above the target frequency, but closer than we've been + || before (mSecondHigh is the previous "high" limit). If + || so, we reduce the limit, and capture the calibration + || value. + */ + if (mSecond < mSecondHigh) + { + mSecondHigh = mSecond; + CalibHigh = Calib; + fHaveSeenHigh = true; + } + + /* + || if we are above the target frequency, and we have + || never raised the frequence, we can lower the + || frequency + */ + if (! fHaveSeenLow) { - if (mSecond < mSecondHigh) + if (Calib == 0) + break; + + --Calib; + if (fCalibrateMSI) { - mSecondHigh = mSecond; - CalibHigh = Calib; + __HAL_RCC_MSI_CALIBRATIONVALUE_ADJUST(Calib); } - if (CalibLow == 0) + else { - if (fCalibrateMSI) - { - Calib = (Calib - 1) & 0xFFu; - __HAL_RCC_MSI_CALIBRATIONVALUE_ADJUST(Calib); - } - else - { - Calib = (Calib - 1) & 0x1Fu; - __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(Calib); - } - delay(500); + __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(Calib); } + delay(500); } - } while ((Calib != CalibNew) && - (CalibLow == 0 || CalibHigh == 0)); - - // - // We are going to take higher calibration value first and - // it allows us not to call LMIC_setClockError(). - // - if (CalibHigh != 0) - { - mSecondNew = mSecondHigh; - CalibNew = CalibHigh; - } - else if (CalibLow != 0) - { - mSecondNew = mSecondLow; - CalibNew = CalibLow; - } - else - { - // Use original value - gLog.printf( - gLog.kError, - "?CatenaStm32L0::CalibrateSystemClock: can't calibrate\n" - ); } + } while ((Calib != CalibNew) && + (! fHaveSeenLow || !fHaveSeenHigh)); + + // + // We are going to take higher calibration value first and + // it allows us not to call LMIC_setClockError(). + // + if (fHaveSeenHigh) + { + mSecondNew = mSecondHigh; + CalibNew = CalibHigh; + } + else if (fHaveSeenLow) + { + mSecondNew = mSecondLow; + CalibNew = CalibLow; + } + else + { + // Use original value + gLog.printf( + gLog.kError, + "?CatenaStm32L0::CalibrateSystemClock: can't calibrate\n" + ); } if (CalibNew != Calib) @@ -213,7 +290,7 @@ uint32_t CatenaStm32L0::CalibrateSystemClock( } gLog.printf( - gLog.kAlways, + gLog.kTrace, "-CatenaStm32L0::CalibrateSystemClock: %cSICalib=%u %u msec\n", fCalibrateMSI ? 'M' : 'H', CalibNew, @@ -224,7 +301,7 @@ uint32_t CatenaStm32L0::CalibrateSystemClock( static uint32_t -GetMillisPerSecond( +MeasureMillisPerRtcSecond( void ) { @@ -233,21 +310,28 @@ GetMillisPerSecond( uint32_t start; uint32_t end; + /* get the starting time */ second = RTC->TR & (RTC_TR_ST | RTC_TR_SU); + /* wait for a new second to start, and capture millis() in start */ do { now = RTC->TR & (RTC_TR_ST | RTC_TR_SU); start = millis(); } while (second == now); + /* update our second of interest */ second = now; + + /* no point in watching the register until we get close */ delay(500); + /* wait for the next second to start, and capture millis() */ do { now = RTC->TR & (RTC_TR_ST | RTC_TR_SU); end = millis(); } while (second == now); + /* return the delta */ return end - start; } diff --git a/src/lib/stm32/stm32l0/CatenaStm32L0_begin.cpp b/src/lib/stm32/stm32l0/CatenaStm32L0_begin.cpp index 9c8c66a..f6987d6 100644 --- a/src/lib/stm32/stm32l0/CatenaStm32L0_begin.cpp +++ b/src/lib/stm32/stm32l0/CatenaStm32L0_begin.cpp @@ -160,8 +160,14 @@ bool CatenaStm32L0::begin() this->m_BootCount ); + /* don't calibarate unless the BSP is using LSE clock */ +#if defined(_mcci_arduino_version) && _mcci_arduino_version > _mcci_arduino_version_calc(2,4,0,0) + // Calibrate system clock - this->CalibrateSystemClock(false, 0); + this->CalibrateSystemClock(); + +#endif + return true; } diff --git a/src/lib/stm32/stm32l0/CatenaStm32L0_registerCommands.cpp b/src/lib/stm32/stm32l0/CatenaStm32L0_registerCommands.cpp index d71c2ef..9192334 100644 --- a/src/lib/stm32/stm32l0/CatenaStm32L0_registerCommands.cpp +++ b/src/lib/stm32/stm32l0/CatenaStm32L0_registerCommands.cpp @@ -95,21 +95,18 @@ doCalibration( if (argc <= 1) { - pCatena->CalibrateSystemClock(false, 0); + uint32_t Calib; + + Calib = pCatena->CalibrateSystemClock(); + pThis->printf("calibration result: %u\n", Calib); + + return cCommandStream::CommandStatus::kSuccess; } else { - char *pValue; - uint32_t CalibVal; - - pValue = argv[1]; - for (CalibVal = 0; '0' <= *pValue && *pValue <= '9'; ++pValue) - CalibVal = (CalibVal * 10) + (*pValue - '0'); - - pCatena->CalibrateSystemClock(true, CalibVal); + return cCommandStream::CommandStatus::kInvalidParameter; } - return cCommandStream::CommandStatus::kSuccess; } #endif // ARDUINO_ARCH_STM32