294 lines
8.7 KiB
C
294 lines
8.7 KiB
C
/*
|
|
* File: micro-common-internal.c
|
|
* Description: STM32W108 internal, micro 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/micro/micro-common.h"
|
|
#include "hal/micro/cortexm3/micro-common.h"
|
|
#include "hal/micro/cortexm3/mfg-token.h"
|
|
|
|
#define HAL_STANDALONE
|
|
#ifdef HAL_STANDALONE
|
|
|
|
#define AUXADC_REG (0xC0u)
|
|
#define DUMMY 0
|
|
|
|
#define ADC_6MHZ_CLOCK 0
|
|
#define ADC_1MHZ_CLOCK 1
|
|
|
|
#define ADC_SAMPLE_CLOCKS_32 0
|
|
#define ADC_SAMPLE_CLOCKS_64 1
|
|
#define ADC_SAMPLE_CLOCKS_128 2
|
|
#define ADC_SAMPLE_CLOCKS_256 3
|
|
#define ADC_SAMPLE_CLOCKS_512 4
|
|
#define ADC_SAMPLE_CLOCKS_1024 5
|
|
#define ADC_SAMPLE_CLOCKS_2048 6
|
|
#define ADC_SAMPLE_CLOCKS_4096 7
|
|
|
|
#define CAL_ADC_CHANNEL_VDD_4 0x00 //VDD_PADS/4
|
|
#define CAL_ADC_CHANNEL_VREG_2 0x01 //VREG_OUT/2
|
|
#define CAL_ADC_CHANNEL_TEMP 0x02
|
|
#define CAL_ADC_CHANNEL_GND 0x03
|
|
#define CAL_ADC_CHANNEL_VREF 0x04
|
|
#define CAL_ADC_CHANNEL_I 0x06
|
|
#define CAL_ADC_CHANNEL_Q 0x07
|
|
#define CAL_ADC_CHANNEL_ATEST_A 0x09
|
|
|
|
void stCalibrateVref(void)
|
|
{
|
|
// Calibrate Vref by measuring a known voltage, Vdd/2.
|
|
//
|
|
// FIXME: add support for calibration if done in boost mode.
|
|
tokTypeMfgAnalogueTrimBoth biasTrim;
|
|
|
|
halCommonGetMfgToken(&biasTrim, TOKEN_MFG_ANALOG_TRIM_BOTH);
|
|
|
|
if(biasTrim.auxadc == 0xFFFF) {
|
|
assert(FALSE);
|
|
} else {
|
|
//The bias trim token is set, so use the trim directly
|
|
int16u temp_value;
|
|
int16u mask = 0xFFFF;
|
|
|
|
// halClearLed(BOARDLED3);
|
|
|
|
while (SCR_BUSY_REG) ;
|
|
|
|
SCR_ADDR_REG = AUXADC_REG ; // prepare the address to write to
|
|
|
|
// initiate read (starts on falling edge of SCR_CTRL_SCR_READ)
|
|
SCR_CTRL_REG = SCR_CTRL_SCR_READ_MASK;
|
|
SCR_CTRL_REG = 0;
|
|
|
|
// wait for read to complete
|
|
while (SCR_BUSY_REG) ;
|
|
|
|
temp_value = SCR_READ_REG & ~mask;
|
|
temp_value |= biasTrim.auxadc & mask;
|
|
|
|
SCR_WRITE_REG = temp_value;
|
|
|
|
// initiate write (starts on falling edge of SCR_CTRL_SCR_WRITE_MASK)
|
|
SCR_CTRL_REG = SCR_CTRL_SCR_WRITE_MASK;
|
|
SCR_CTRL_REG = 0;
|
|
|
|
while (SCR_BUSY_REG) ;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void calDisableAdc(void) {
|
|
// Disable the Calibration ADC to save current.
|
|
CAL_ADC_CONFIG &= ~CAL_ADC_CONFIG_CAL_ADC_EN;
|
|
}
|
|
|
|
|
|
|
|
// These routines maintain the same signature as their hal- counterparts to
|
|
// facilitate simple support between phys.
|
|
// It is assumed (hoped?) that the compiler will optimize out unused arguments.
|
|
StStatus calStartAdcConversion(int8u dummy1, // Not used.
|
|
int8u dummy2, // Not used.
|
|
int8u channel,
|
|
int8u rate,
|
|
int8u clock) {
|
|
// Disable the Calibration ADC interrupt so that we can poll it.
|
|
INT_MGMTCFG &= ~INT_MGMTCALADC;
|
|
|
|
ATOMIC(
|
|
// Enable the Calibration ADC, choose source, set rate, and choose clock.
|
|
CAL_ADC_CONFIG =((CAL_ADC_CONFIG_CAL_ADC_EN) |
|
|
(channel << CAL_ADC_CONFIG_CAL_ADC_MUX_BIT) |
|
|
(rate << CAL_ADC_CONFIG_CAL_ADC_RATE_BIT) |
|
|
(clock << CAL_ADC_CONFIG_CAL_ADC_CLKSEL_BIT) );
|
|
// Clear any pending Calibration ADC interrupt. Since we're atomic, the
|
|
// one we're interested in hasn't happened yet (will take ~10us at minimum).
|
|
// We're only clearing stale info.
|
|
INT_MGMTFLAG = INT_MGMTCALADC;
|
|
)
|
|
return ST_SUCCESS;
|
|
}
|
|
|
|
|
|
StStatus calReadAdcBlocking(int8u dummy,
|
|
int16u *value) {
|
|
// Wait for conversion to complete.
|
|
while ( ! (INT_MGMTFLAG & INT_MGMTCALADC) );
|
|
// Clear the interrupt for this conversion.
|
|
INT_MGMTFLAG = INT_MGMTCALADC;
|
|
// Get the result.
|
|
*value = (int16u)CAL_ADC_DATA;
|
|
return ST_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
//Using 6MHz clock reduces resolution but greatly increases conversion speed.
|
|
//The sample clocks were chosen based upon empirical evidence and provided
|
|
//the fastest conversions with the greatest reasonable accuracy. Variation
|
|
//across successive conversions appears to be +/-20mv of the average
|
|
//conversion. Overall function time is <150us.
|
|
int16u stMeasureVddFast(void)
|
|
{
|
|
int16u value;
|
|
int32u Ngnd;
|
|
int32u Nreg;
|
|
int32u Nvdd;
|
|
tokTypeMfgRegVoltage1V8 vregOutTok;
|
|
halCommonGetMfgToken(&vregOutTok, TOKEN_MFG_1V8_REG_VOLTAGE);
|
|
|
|
//Measure GND
|
|
calStartAdcConversion(DUMMY,
|
|
DUMMY,
|
|
CAL_ADC_CHANNEL_GND,
|
|
ADC_SAMPLE_CLOCKS_128,
|
|
ADC_6MHZ_CLOCK);
|
|
calReadAdcBlocking(DUMMY, &value);
|
|
Ngnd = (int32u)value;
|
|
|
|
//Measure VREG_OUT/2
|
|
calStartAdcConversion(DUMMY,
|
|
DUMMY,
|
|
CAL_ADC_CHANNEL_VREG_2,
|
|
ADC_SAMPLE_CLOCKS_128,
|
|
ADC_6MHZ_CLOCK);
|
|
calReadAdcBlocking(DUMMY, &value);
|
|
Nreg = (int32u)value;
|
|
|
|
//Measure VDD_PADS/4
|
|
calStartAdcConversion(DUMMY,
|
|
DUMMY,
|
|
CAL_ADC_CHANNEL_VDD_4,
|
|
ADC_SAMPLE_CLOCKS_128,
|
|
ADC_6MHZ_CLOCK);
|
|
calReadAdcBlocking(DUMMY, &value);
|
|
Nvdd = (int32u)value;
|
|
|
|
calDisableAdc();
|
|
|
|
//Convert the value into mV. VREG_OUT is ideally 1.8V, but it wont be
|
|
//exactly 1.8V. The actual value is stored in the manufacturing token
|
|
//TOKEN_MFG_1V8_REG_VOLTAGE. The token stores the value in 10^-4, but we
|
|
//need 10^-3 so divide by 10. If this token is not set (0xFFFF), then
|
|
//assume 1800mV.
|
|
if(vregOutTok == 0xFFFF) {
|
|
vregOutTok = 1800;
|
|
} else {
|
|
vregOutTok /= 10;
|
|
}
|
|
return ((((((Nvdd-Ngnd)<<16)/(Nreg-Ngnd))*vregOutTok)*2)>>16);
|
|
}
|
|
#endif
|
|
|
|
void halCommonCalibratePads(void)
|
|
{
|
|
if(stMeasureVddFast() < 2700) {
|
|
GPIO_DBGCFG |= GPIO_DBGCFGRSVD;
|
|
} else {
|
|
GPIO_DBGCFG &= ~GPIO_DBGCFGRSVD;
|
|
}
|
|
}
|
|
|
|
|
|
void halInternalSetRegTrim(boolean boostMode)
|
|
{
|
|
tokTypeMfgRegTrim regTrim;
|
|
int8u trim1V2;
|
|
int8u trim1V8;
|
|
|
|
halCommonGetMfgToken(®Trim, TOKEN_MFG_REG_TRIM);
|
|
// The compiler can optimize this function a bit more and keep the
|
|
// values in processor registers if we use separate local vars instead
|
|
// of just accessing via the structure fields
|
|
trim1V8 = regTrim.regTrim1V8;
|
|
trim1V2 = regTrim.regTrim1V2;
|
|
|
|
//If tokens are erased, default to reasonable values, otherwise use the
|
|
//token values.
|
|
if((trim1V2 == 0xFF) && (trim1V8 == 0xFF)) {
|
|
trim1V8 = 4;
|
|
trim1V2 = 0;
|
|
}
|
|
|
|
//When the radio is in boost mode, we have to increase the 1.8V trim.
|
|
if(boostMode) {
|
|
trim1V8 += 2;
|
|
}
|
|
|
|
//Clamp at 7 to ensure we don't exceed max values, accidentally set
|
|
//other bits, or wrap values.
|
|
if(trim1V8>7) {
|
|
trim1V8 = 7;
|
|
}
|
|
if(trim1V2>7) {
|
|
trim1V2 = 7;
|
|
}
|
|
|
|
VREG_REG = ( (trim1V8<<VREG_VREG_1V8_TRIM_BIT) |
|
|
(trim1V2<<VREG_VREG_1V2_TRIM_BIT) );
|
|
}
|
|
|
|
|
|
// halCommonDelayMicroseconds
|
|
// -enables MAC Timer and leaves it enabled.
|
|
// -does not touch MAC Timer Compare registers.
|
|
// -max delay is 65535 usec.
|
|
// NOTE: This function primarily designed for when the chip is running off of
|
|
// the XTAL, which is the most common situation. When running from
|
|
// OSCHF, though, the clock speed is cut in half, so the input parameter
|
|
// is divided by two. With respect to accuracy, we're now limited by
|
|
// the accuracy of OSCHF (much lower than XTAL).
|
|
void halCommonDelayMicroseconds(int16u us)
|
|
{
|
|
int32u beginTime = ReadRegister(MAC_TIMER);
|
|
|
|
//If we're not using the XTAL, the MAC Timer is running off OSCHF,
|
|
//that means the clock is half speed, 6MHz. We need to halve our delay
|
|
//time.
|
|
if((OSC24M_CTRL&OSC24M_CTRL_OSC24M_SEL)!=OSC24M_CTRL_OSC24M_SEL) {
|
|
us >>= 1;
|
|
}
|
|
|
|
//we have about 2us of overhead in the calculations
|
|
if(us<=2) {
|
|
return;
|
|
}
|
|
|
|
// MAC Timer is enabled in stmRadioInit, which may not have been called yet.
|
|
// This algorithm needs the MAC Timer so we enable it here.
|
|
MAC_TIMER_CTRL |= MAC_TIMER_CTRL_MAC_TIMER_EN;
|
|
|
|
// since our max delay (65535<<1) is less than half the size of the
|
|
// 20 bit mac timer, we can easily just handle the potential for
|
|
// mac timer wrapping by subtracting the time delta and masking out
|
|
// the extra bits
|
|
while( ((MAC_TIMER-beginTime)&MAC_TIMER_MAC_TIMER_MASK) < us ) {
|
|
; // spin
|
|
}
|
|
}
|
|
|
|
|
|
//Burning cycles for milliseconds is generally a bad idea, but it is
|
|
//necessary in some situations. If you have to burn more than 65ms of time,
|
|
//the halCommonDelayMicroseconds function becomes cumbersome, so this
|
|
//function gives you millisecond granularity.
|
|
void halCommonDelayMilliseconds(int16u ms)
|
|
{
|
|
if(ms==0) {
|
|
return;
|
|
}
|
|
|
|
while(ms-->0) {
|
|
halCommonDelayMicroseconds(1000);
|
|
}
|
|
}
|