/**
* @author Cory Sharp <cssharp@eecs.berkeley.edu>
+ * @author Vlado Handziski <handzisk@tkn.tu-berlind.de>
*/
#include "Msp430Timer.h"
-module Msp430ClockP
+generic module Msp430ClockP(uint16_t TARGET_DCO_KHZ, uint16_t ACLK_KHZ) @safe()
{
provides interface Init;
provides interface Msp430ClockInit;
+ provides interface McuPowerOverride;
}
implementation
{
MSP430REG_NORACE(TBCTL);
MSP430REG_NORACE(TBIV);
+ #if defined(__MSP430_HAS_BC2__) /* basic clock module+ */
+ #define FIRST_STEP 0x1000
+ #else /* orig basic clock module */
+ #define RSEL3 0
+ #define FIRST_STEP 0x800
+ #endif
+
enum
{
+ DCOX = DCO2 + DCO1 + DCO0,
+ MODX = MOD4 + MOD3 + MOD2 + MOD1 + MOD0,
+ RSELX = RSEL3 + RSEL2 + RSEL1 + RSEL0,
ACLK_CALIB_PERIOD = 8,
- ACLK_KHZ = 32,
- TARGET_DCO_KHZ = 4096, // prescribe the cpu clock rate in kHz
TARGET_DCO_DELTA = (TARGET_DCO_KHZ / ACLK_KHZ) * ACLK_CALIB_PERIOD,
};
+ async command mcu_power_t McuPowerOverride.lowestState() {
+ return MSP430_POWER_LPM3;
+ }
+
+ command void Msp430ClockInit.defaultSetupDcoCalibrate()
+ {
+
+ // --- setup ---
+
+ TACTL = TASSEL1 | MC1; // source SMCLK, continuous mode, everything else 0
+ TBCTL = TBSSEL0 | MC1;
+ BCSCTL1 = XT2OFF | RSEL2;
+ BCSCTL2 = 0;
+ TBCCTL0 = CM0;
+ }
+
command void Msp430ClockInit.defaultInitClocks()
{
+ const unsigned int divider = TARGET_DCO_KHZ / 1000;
+
// BCSCTL1
// .XT2OFF = 1; disable the external oscillator for SCLK and MCLK
// .XTS = 0; set low frequency mode for LXFT1
// .DIVA = 0; set the divisor on ACLK to 1
// .RSEL, do not modify
- BCSCTL1 = XT2OFF | (BCSCTL1 & (RSEL2|RSEL1|RSEL0));
+ BCSCTL1 = XT2OFF | (BCSCTL1 & RSELX);
// BCSCTL2
// .SELM = 0; select DCOCLK as source for MCLK
// .DIVM = 0; set the divisor of MCLK to 1
// .SELS = 0; select DCOCLK as source for SCLK
- // .DIVS = 2; set the divisor of SCLK to 4
+ // .DIVS = see below
// .DCOR = 0; select internal resistor for DCO
- BCSCTL2 = DIVS1;
+ //
+ // TinyOS upper layers assume SMCLK runs at 1 binary MHz, or 1,048,576HZ.
+ // If DCOCLK has been set to 1, 2, 4 or 8 binary MHz, we can correctly set
+ // SMCLK to the expected value. Platforms using different clocks should
+ // set the divider by overriding Msp430ClockInit.initClocks(), calling
+ // Msp430ClockInit.defaultInitClocks(), then massaging the DIVS bits as
+ // required.
+ if (divider >= 8)
+ BCSCTL2 = DIVS_3;
+ else if (divider >= 4)
+ BCSCTL2 = DIVS_2;
+ else if (divider >= 2)
+ BCSCTL2 = DIVS_1;
+ else
+ BCSCTL2 = DIVS_0;
+
// IE1.OFIE = 0; no interrupt for oscillator fault
CLR_FLAG( IE1, OFIE );
TBCTL = TBSSEL0 | TBIE;
}
+ default event void Msp430ClockInit.setupDcoCalibrate()
+ {
+ call Msp430ClockInit.defaultSetupDcoCalibrate();
+ }
+
default event void Msp430ClockInit.initClocks()
{
call Msp430ClockInit.defaultInitClocks();
void set_dco_calib( int calib )
{
- BCSCTL1 = (BCSCTL1 & ~0x07) | ((calib >> 8) & 0x07);
+ BCSCTL1 = (BCSCTL1 & ~RSELX) | ((calib >> 8) & RSELX);
DCOCTL = calib & 0xff;
}
int calib;
int step;
- // --- setup ---
-
- TACTL = TASSEL1 | MC1; // source SMCLK, continuous mode, everything else 0
- TBCTL = TBSSEL0 | MC1;
- BCSCTL1 = XT2OFF | RSEL2;
- BCSCTL2 = 0;
- TBCCTL0 = CM0;
-
// --- calibrate ---
// Binary search for RSEL,DCO,DCOMOD.
// It's okay that RSEL isn't monotonic.
- for( calib=0,step=0x800; step!=0; step>>=1 )
+ for( calib=0,step=FIRST_STEP; step!=0; step>>=1 )
{
// if the step is not past the target, commit it
if( test_calib_busywait_delta(calib|step) <= TARGET_DCO_DELTA )
calib |= step;
}
- // if DCOx is 7 (0x0e0 in calib), then the 5-bit MODx is not useable, set it to 0
- if( (calib & 0x0e0) == 0x0e0 )
- calib &= ~0x01f;
+ // if DCOx is all 1s in calib, then MODx is not useable, set it to 0
+ if( (calib & DCOX) == DCOX )
+ calib &= ~MODX;
set_dco_calib( calib );
}
atomic
{
+ signal Msp430ClockInit.setupDcoCalibrate();
busyCalibrateDco();
signal Msp430ClockInit.initClocks();
signal Msp430ClockInit.initTimerA();