New Contiki port to STM32W108.
This commit is contained in:
parent
324796cd1a
commit
ec5e3ce0d7
130 changed files with 43157 additions and 0 deletions
473
cpu/stm32w108/hal/micro/cortexm3/clocks.c
Normal file
473
cpu/stm32w108/hal/micro/cortexm3/clocks.c
Normal file
|
@ -0,0 +1,473 @@
|
|||
/*
|
||||
* File: clocks.c
|
||||
* Description: STM32W108 internal, clock specific HAL functions
|
||||
* This file is provided for completeness and it should not be modified
|
||||
* by customers as it comtains code very tightly linked to undocumented
|
||||
* device features
|
||||
*
|
||||
* <!--(C) COPYRIGHT 2010 STMicroelectronics. All rights reserved. -->
|
||||
*/
|
||||
|
||||
#include PLATFORM_HEADER
|
||||
#include "error.h"
|
||||
|
||||
#include "hal/hal.h"
|
||||
#include "hal/micro/cortexm3/mpu.h"
|
||||
#include "hal/micro/cortexm3/mfg-token.h"
|
||||
|
||||
|
||||
//Provide a simple means for enabling calibration debug output
|
||||
#define CALDBG(x)
|
||||
//#define CALDBG(x) x
|
||||
|
||||
//The slowest frequency for the 10kHz RC source is 8kHz (125us). The PERIOD
|
||||
//register updates every 16 cycles, so to be safe 17 cycles = 2125us. But,
|
||||
//we need twice this maximum time because the period measurement runs
|
||||
//asynchronously, and the value of CLKRC_TUNE is changed immediately before
|
||||
//the delay.
|
||||
#define SLOWRC_PERIOD_SETTLE_TIME 4250
|
||||
//The CLK_PERIOD register measures the number of 12MHz clock cycles that
|
||||
//occur in 16 cycles of the SlowRC clock. This is meant to smooth out the the
|
||||
//noise inherently present in the analog RC source. While these 16 cycles
|
||||
//smooths out most noise, there is still some jitter in the bottom bits of
|
||||
//CLK_PERIOD. To further smooth out the noise, we take several readings of
|
||||
//CLK_PERIOD and average them out. Testing has shown that the bottom 3 and 4
|
||||
//bits of CLK_PERIOD contain most of the jitter. Averaging 8 samples will
|
||||
//smooth out 3 bits of jitter and provide a realiable and stable reading useful
|
||||
//in the calculations, while taking much less time than 16 or 32 samples.
|
||||
#define SLOWRC_PERIOD_SAMPLES 8
|
||||
//The register CLK1K_CAL is a fractional divider that divides the 10kHz analog
|
||||
//source with the goal of generating a 1024Hz, clk1k output.
|
||||
// 10000Hz / CLK1K_CAL = 1024Hz.
|
||||
//Since the CLK_PERIOD register measures the number of 12MHz cycles in 16
|
||||
//cycles of the RC:
|
||||
// 16 * 12000000
|
||||
// ------------- = ~10kHz
|
||||
// CLK_PERIOD
|
||||
//and
|
||||
// ~10kHz / 1024 = X
|
||||
//where X is the fractional number that belongs in CLK1K_CAL. Since the
|
||||
//integer portion of CLK1K_CAL is bits 15:11 and the fractional is 10:0,
|
||||
//multiplying X by 2048 (bit shift left by 11) generates the proper CLK1K_CAL
|
||||
//register value.
|
||||
//
|
||||
//Putting this all together:
|
||||
// 16 * 12000000 * 2048 384000000
|
||||
// -------------------- = ------------ = CLK1K_CAL
|
||||
// CLK_PERIOD * 1024 CLK_PERIOD
|
||||
//
|
||||
#define CLK1K_NUMERATOR 384000000
|
||||
void halInternalCalibrateSlowRc( void )
|
||||
{
|
||||
int8u i;
|
||||
int32u average=0;
|
||||
int16s delta;
|
||||
int32u period;
|
||||
|
||||
CALDBG(
|
||||
stSerialPrintf(ST_ASSERT_SERIAL_PORT, "halInternalCalibrateSlowRc:\r\n");
|
||||
)
|
||||
|
||||
////---- STEP 1: coarsely tune SlowRC in analog section to ~10kHz ----////
|
||||
//To operate properly across the full temperature and voltage range,
|
||||
//the RC source in the analog section needs to be first coarsely tuned
|
||||
//to 10kHz. The CLKRC_TUNE register, which is 2's compliment, provides 16
|
||||
//steps at ~400Hz per step yielding approximate frequences of 8kHz at 7
|
||||
//and 15kHz at -8.
|
||||
//Start with our reset values for TUNE and CAL
|
||||
CLK_PERIODMODE = 0; //measure SlowRC
|
||||
CLKRC_TUNE = CLKRC_TUNE_RESET;
|
||||
CLK1K_CAL = CLK1K_CAL_RESET;
|
||||
//wait for the PERIOD register to properly update
|
||||
halCommonDelayMicroseconds(SLOWRC_PERIOD_SETTLE_TIME);
|
||||
//Measure the current CLK_PERIOD to obtain a baseline
|
||||
CALDBG(
|
||||
stSerialPrintf(ST_ASSERT_SERIAL_PORT,
|
||||
"period: %u, ", CLK_PERIOD);
|
||||
stSerialPrintf(ST_ASSERT_SERIAL_PORT, "%u Hz\r\n",
|
||||
((int16u)(((int32u)192000000)/((int32u)CLK_PERIOD))));
|
||||
)
|
||||
//For 10kHz, the ideal CLK_PERIOD is 19200. Calculate the PERIOD delta.
|
||||
//It's possible for a chip's 10kHz source RC to be too far out of range
|
||||
//for the CLKRC_TUNE to bring it back to 10kHz. Therefore, we have to
|
||||
//ensure that our delta correction does not exceed the tune range so
|
||||
//tune has to be capped to the end of the vailable range so it does not
|
||||
//wrap. Even if we cannot achieve 10kHz, the 1kHz calibration can still
|
||||
//properly correct to 1kHz.
|
||||
//Each CLKRC_TUNE step yields a CLK_PERIOD delta of *approximately* 800.
|
||||
//Calculate how many steps we are off. While dividing by 800 may seem
|
||||
//like an ugly calculation, the precision of the result is worth the small
|
||||
//bit of code and time needed to do a divide.
|
||||
period = CLK_PERIOD;
|
||||
//Round to the nearest integer
|
||||
delta = (19200+400) - period;
|
||||
delta /= 800;
|
||||
//CLKRC_TUNE is a 4 bit signed number. cap the delta to 7/-8
|
||||
if(delta > 7) {
|
||||
delta = 7;
|
||||
}
|
||||
if(delta < -8) {
|
||||
delta = -8;
|
||||
}
|
||||
CALDBG(
|
||||
stSerialPrintf(ST_ASSERT_SERIAL_PORT, "TUNE steps delta: %d\r\n",
|
||||
delta);
|
||||
)
|
||||
CLKRC_TUNE = delta;
|
||||
//wait for PERIOD to update before taking another sample
|
||||
halCommonDelayMicroseconds(SLOWRC_PERIOD_SETTLE_TIME);
|
||||
CALDBG(
|
||||
stSerialPrintf(ST_ASSERT_SERIAL_PORT,
|
||||
"period: %u, ", CLK_PERIOD);
|
||||
stSerialPrintf(ST_ASSERT_SERIAL_PORT, "%u Hz\r\n",
|
||||
((int16u)(((int32u)192000000)/((int32u)CLK_PERIOD))));
|
||||
)
|
||||
//The analog section should now be producing an output of ~10kHz
|
||||
|
||||
////---- STEP 2: fine tune the SlowRC to 1024Hz ----////
|
||||
//Our goal is to generate a 1024Hz source. The register CLK1K_CAL is a
|
||||
//fractional divider that divides the 10kHz analog source and generates
|
||||
//the clk1k output. At reset, the default value is 0x5000 which yields a
|
||||
//division of 10.000. By averaging several samples of CLK_PERIOD, we
|
||||
//can then calculate the proper divisor need for CLK1K_CAL to make 1024Hz.
|
||||
for(i=0;i<SLOWRC_PERIOD_SAMPLES;i++) {
|
||||
halCommonDelayMicroseconds(SLOWRC_PERIOD_SETTLE_TIME);
|
||||
average += CLK_PERIOD;
|
||||
}
|
||||
//calculate the average, with proper rounding
|
||||
average = (average+(SLOWRC_PERIOD_SAMPLES/2))/SLOWRC_PERIOD_SAMPLES;
|
||||
CALDBG(
|
||||
stSerialPrintf(ST_ASSERT_SERIAL_PORT, "average: %u, %u Hz\r\n",
|
||||
((int16u)average), ((int16u)(((int32u)192000000)/((int32u)average))));
|
||||
)
|
||||
|
||||
//using an average period sample, calculate the clk1k divisor
|
||||
CLK1K_CAL = (int16u)(CLK1K_NUMERATOR/average);
|
||||
CALDBG(
|
||||
stSerialPrintf(ST_ASSERT_SERIAL_PORT,"CLK1K_CAL=%2X\r\n",CLK1K_CAL);
|
||||
)
|
||||
//The SlowRC timer is now producing a 1024Hz tick (+/-2Hz).
|
||||
|
||||
CALDBG(
|
||||
stSerialPrintf(ST_ASSERT_SERIAL_PORT, "DONE\r\n");
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
//The slowest frequency for the FastRC source is 4MHz (250ns). The PERIOD
|
||||
//register updates every 256 cycles, so to be safe 257 cycles = 64us. But,
|
||||
//we need twice this maximum time because the period measurement runs
|
||||
//asynchronously, and the value of OSCHF_TUNE is changed immediately before
|
||||
//the delay.
|
||||
#define FASTRC_PERIOD_SETTLE_TIME 128
|
||||
//The CLK_PERIOD register measures the number of 12MHz cycles in 256
|
||||
//cycles of OSCHF:
|
||||
// 256 * 12000000
|
||||
// ------------- = ~12MHz
|
||||
// CLK_PERIOD
|
||||
void halInternalCalibrateFastRc(void)
|
||||
{
|
||||
int32s newTune = -16;
|
||||
|
||||
CALDBG(
|
||||
stSerialPrintf(ST_ASSERT_SERIAL_PORT, "halInternalCalibrateFastRc:\r\n");
|
||||
)
|
||||
|
||||
////---- coarsely tune FastRC in analog section to ~12MHz ----////
|
||||
//The RC source in the analog section needs to be coarsely tuned
|
||||
//to 12MHz. The OSCHF_TUNE register, which is 2's compliment, provides 32
|
||||
//steps at ~0.5MHz per step yielding approximate frequences of 4MHz at 15
|
||||
//and 20MHz at -16.
|
||||
CLK_PERIODMODE = 1; //measure FastRC
|
||||
CALDBG(
|
||||
//start at the fastest possible frequency
|
||||
OSCHF_TUNE = newTune;
|
||||
//wait for the PERIOD register to properly update
|
||||
halCommonDelayMicroseconds(FASTRC_PERIOD_SETTLE_TIME);
|
||||
//Measure the current CLK_PERIOD to obtain a baseline
|
||||
stSerialPrintf(ST_ASSERT_SERIAL_PORT,
|
||||
"period: %u, ", CLK_PERIOD);
|
||||
stSerialPrintf(ST_ASSERT_SERIAL_PORT, "%u kHz\r\n",
|
||||
((int16u)((((int32u)3072000000)/((int32u)CLK_PERIOD))/1000)));
|
||||
)
|
||||
//For 12MHz, the ideal CLK_PERIOD is 256. Tune the frequency down until
|
||||
//the period is <= 256, which says the frequency is as close to 12MHz as
|
||||
//possible (without going over 12MHz)
|
||||
//Start at the fastest possible frequency (-16) and increase to the slowest
|
||||
//possible (15). When CLK_PERIOD is <=256 or we run out of tune values,
|
||||
//we're done.
|
||||
for(;newTune<16;newTune++) {
|
||||
//decrease frequency by one step (by increasing tune value)
|
||||
OSCHF_TUNE = newTune;
|
||||
//wait for the PERIOD register to properly update
|
||||
halCommonDelayMicroseconds(FASTRC_PERIOD_SETTLE_TIME);
|
||||
//kickout if we're tuned
|
||||
if(CLK_PERIOD>=256) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
CALDBG(
|
||||
//Measure the current CLK_PERIOD to show the final result
|
||||
stSerialPrintf(ST_ASSERT_SERIAL_PORT,
|
||||
"period: %u, ", CLK_PERIOD);
|
||||
stSerialPrintf(ST_ASSERT_SERIAL_PORT, "%u kHz\r\n",
|
||||
((int16u)((((int32u)3072000000)/((int32u)CLK_PERIOD))/1000)));
|
||||
)
|
||||
|
||||
//The analog section should now be producing an output of 11.5MHz - 12.0MHz
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define OSC24M_BIASTRIM_OFFSET (0x2)
|
||||
#define OSC24M_BIASTRIM_MIN (0+OSC24M_BIASTRIM_OFFSET)
|
||||
#define OSC24M_BIASTRIM_MAX OSC24M_BIASTRIM_OSC24M_BIAS_TRIM_MASK
|
||||
#define OSC24M_BIASTRIM_MSB (1 << (OSC24M_BIASTRIM_OSC24M_BIAS_TRIM_BITS-1))
|
||||
#define OSC24M_BIASTRIM_UNINIT (0xFFFF)
|
||||
tokTypeMfgOsc24mBiasTrim biasTrim=OSC24M_BIASTRIM_UNINIT;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//This function is intended to be called periodically, from the stack and
|
||||
//application, to check the XTAL bias trim is within appropriate levels
|
||||
//and adjust if not. This function is *not* designed to be used before
|
||||
//halInternalSwitchToXtal has been called.
|
||||
void halCommonCheckXtalBiasTrim(void)
|
||||
{
|
||||
//HI is set indicating the trim value is too high. Decrement the trim.
|
||||
if((OSC24M_COMP & OSC24M_HI) == OSC24M_HI) {
|
||||
biasTrim--;
|
||||
}
|
||||
|
||||
//LO is cleared indicating the trim value is too low. Inrement the trim.
|
||||
if((OSC24M_COMP & OSC24M_LO) != OSC24M_LO) {
|
||||
biasTrim++;
|
||||
//Add an offset to the bias trim as a factor of safety.
|
||||
if(biasTrim < (OSC24M_BIASTRIM_MAX - OSC24M_BIASTRIM_OFFSET)) {
|
||||
biasTrim += OSC24M_BIASTRIM_OFFSET;
|
||||
} else {
|
||||
biasTrim = OSC24M_BIASTRIM_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
//Don't allow bias trim to dip below the offset regardless of LO.
|
||||
if(biasTrim<OSC24M_BIASTRIM_OFFSET) {
|
||||
biasTrim = OSC24M_BIASTRIM_OFFSET;
|
||||
}
|
||||
|
||||
OSC24M_BIASTRIM = biasTrim;
|
||||
}
|
||||
|
||||
static boolean setBiasCheckLow(void)
|
||||
{
|
||||
OSC24M_BIASTRIM = biasTrim;
|
||||
halCommonDelayMicroseconds(1500);
|
||||
return ((OSC24M_COMP & OSC24M_LO) == OSC24M_LO);
|
||||
}
|
||||
|
||||
void halInternalSearchForBiasTrim(void)
|
||||
{
|
||||
int8u bit;
|
||||
|
||||
//Enable the XTAL so we can search for the proper bias trim (NOTE: This
|
||||
//will also forcefully ensure we're on the OSCHF so that we don't
|
||||
//accidentally trip the NMI while searching.)
|
||||
OSC24M_CTRL = OSC24M_CTRL_OSC24M_EN;
|
||||
|
||||
//Do a binary search of the 4-bit bias trim values to find
|
||||
//smallest bias trim value for which LO = 1.
|
||||
biasTrim = 0;
|
||||
bit = (OSC24M_BIASTRIM_MSB << 1);
|
||||
do {
|
||||
bit >>= 1;
|
||||
biasTrim += bit;
|
||||
//Set trim and wait for 1.5ms to allow the oscillator to stabilize.
|
||||
if(setBiasCheckLow()) {
|
||||
biasTrim -= bit;
|
||||
}
|
||||
} while(bit);
|
||||
|
||||
//If the last bias value went too low, increment it.
|
||||
if((OSC24M_COMP & OSC24M_LO) != OSC24M_LO) {
|
||||
biasTrim++;
|
||||
}
|
||||
|
||||
//Add an offset to the bias trim as a factor of safety.
|
||||
if(biasTrim < (OSC24M_BIASTRIM_MAX - OSC24M_BIASTRIM_OFFSET)) {
|
||||
biasTrim += OSC24M_BIASTRIM_OFFSET;
|
||||
} else {
|
||||
biasTrim = OSC24M_BIASTRIM_MAX;
|
||||
}
|
||||
|
||||
//Using the shadow variable, the clock switch logic will take over from here,
|
||||
//enabling, verifying, and tweaking as needed.
|
||||
}
|
||||
|
||||
|
||||
//This function configures the flash access controller for optimal
|
||||
//current consumption when FCLK is operating at 24MHz. By providing
|
||||
//this function the calling code does not have to be aware of the
|
||||
//details of setting FLASH_ACCESS.
|
||||
static void halInternalConfigXtal24MhzFlashAccess(void)
|
||||
{
|
||||
ATOMIC(
|
||||
BYPASS_MPU(
|
||||
#if defined(CORTEXM3_STM32W108)
|
||||
FLASH_ACCESS = (FLASH_ACCESS_PREFETCH_EN |
|
||||
(1<<FLASH_ACCESS_CODE_LATENCY_BIT));
|
||||
#endif
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
//NOTE: The global "shadow" variable biasTrim will be set by either:
|
||||
// A) TOKEN_MFG_OSC24M_BIAS_TRIM when booting fresh
|
||||
// B) searchForBiasTrim() when booting fresh and the token is not valid
|
||||
// C) halInternalSwitchToXtal() if halInternalSwitchToXtal() already ran
|
||||
void halInternalSwitchToXtal(void)
|
||||
{
|
||||
boolean loSet;
|
||||
boolean hiSet;
|
||||
boolean setTrimOneLastTime = FALSE;
|
||||
|
||||
//If it hasn't yet been initialized,
|
||||
//preload our biasTrim shadow variable from the token. If the token is
|
||||
//not set, then run a search to find an initial value. The bias trim
|
||||
//algorithm/clock switch logic will always use the biasTrim shadow
|
||||
//variable as the starting point for finding the bias, and then
|
||||
//save that new bias to the shadow variable.
|
||||
if(biasTrim == OSC24M_BIASTRIM_UNINIT) {
|
||||
halCommonGetMfgToken(&biasTrim, TOKEN_MFG_OSC24M_BIAS_TRIM);
|
||||
if(biasTrim == 0xFFFF) {
|
||||
halInternalSearchForBiasTrim();
|
||||
}
|
||||
}
|
||||
|
||||
//Ensure the XTAL is enabled (with the side effect of ensuring we're
|
||||
//still on OSCHF).
|
||||
OSC24M_CTRL = OSC24M_CTRL_OSC24M_EN;
|
||||
|
||||
do {
|
||||
//Set trim to our shadow variable and wait for 1.5ms to allow the
|
||||
//oscillator to stabilize.
|
||||
loSet = setBiasCheckLow();
|
||||
hiSet = (OSC24M_COMP & OSC24M_HI) == OSC24M_HI;
|
||||
|
||||
//The bias is too low, so we need to increment the bias trim.
|
||||
if(!loSet) {
|
||||
biasTrim++;
|
||||
}
|
||||
|
||||
//The bias is too high, so we need to decrement the bias trim.
|
||||
if(hiSet) {
|
||||
//but don't trim below our min value
|
||||
if(biasTrim>OSC24M_BIASTRIM_MIN) {
|
||||
biasTrim--;
|
||||
setTrimOneLastTime = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
//Kickout when HI=0 and LO=1 or we've hit the MAX or the MIN
|
||||
} while( (hiSet || !loSet) &&
|
||||
(biasTrim<OSC24M_BIASTRIM_MAX) &&
|
||||
(biasTrim>OSC24M_BIASTRIM_MIN) );
|
||||
|
||||
//The LO bit being cleared means we've corrected up from the bottom and
|
||||
//therefore need to apply the offset. Additionally, if our trim value
|
||||
//is below the offset, we still need to apply the offset. And, when
|
||||
//applying the offset respect the max possible value of the trim.
|
||||
if(!loSet || (biasTrim<OSC24M_BIASTRIM_OFFSET)){
|
||||
if(biasTrim < (OSC24M_BIASTRIM_MAX - OSC24M_BIASTRIM_OFFSET)) {
|
||||
biasTrim += OSC24M_BIASTRIM_OFFSET;
|
||||
} else {
|
||||
biasTrim = OSC24M_BIASTRIM_MAX;
|
||||
}
|
||||
setTrimOneLastTime = TRUE;
|
||||
}
|
||||
|
||||
if(setTrimOneLastTime) {
|
||||
setBiasCheckLow();
|
||||
}
|
||||
|
||||
//We've found a valid trim value and we've waited for the oscillator
|
||||
//to stabalize, it's now safe to select the XTAL
|
||||
OSC24M_CTRL |= OSC24M_CTRL_OSC24M_SEL;
|
||||
|
||||
//If the XTAL switch failed, the NMI ISR will trigger, creeping the bias
|
||||
//trim up higher, and if max bias is reached the ISR will trigger a reset.
|
||||
|
||||
//Our standard mode of operation is 24MHz (CPU/FCLK is sourced from SYSCLK)
|
||||
CPU_CLKSEL = CPU_CLKSEL_FIELD;
|
||||
//Configure flash access for optimal current consumption at 24MHz
|
||||
halInternalConfigXtal24MhzFlashAccess();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue