osd-contiki/cpu/stm32w108/hal/micro/cortexm3/micro-common-internal.c
Adam Dunkels a5046e83c7 Cleanup and refactoring of the STM32w port
This is a general cleanup of things like code style issues and code structure of the STM32w port to make it more like the rest of Contiki is structured.
2013-03-18 13:31:26 +01:00

294 lines
8.8 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
uint16_t temp_value;
uint16_t 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(uint8_t dummy1, // Not used.
uint8_t dummy2, // Not used.
uint8_t channel,
uint8_t rate,
uint8_t 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(uint8_t dummy,
uint16_t *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 = (uint16_t)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.
uint16_t stMeasureVddFast(void)
{
uint16_t value;
uint32_t Ngnd;
uint32_t Nreg;
uint32_t 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 = (uint32_t)value;
//Measure VREG_OUT/2
calStartAdcConversion(DUMMY,
DUMMY,
CAL_ADC_CHANNEL_VREG_2,
ADC_SAMPLE_CLOCKS_128,
ADC_6MHZ_CLOCK);
calReadAdcBlocking(DUMMY, &value);
Nreg = (uint32_t)value;
//Measure VDD_PADS/4
calStartAdcConversion(DUMMY,
DUMMY,
CAL_ADC_CHANNEL_VDD_4,
ADC_SAMPLE_CLOCKS_128,
ADC_6MHZ_CLOCK);
calReadAdcBlocking(DUMMY, &value);
Nvdd = (uint32_t)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;
uint8_t trim1V2;
uint8_t trim1V8;
halCommonGetMfgToken(&regTrim, 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(uint16_t us)
{
uint32_t 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(uint16_t ms)
{
if(ms==0) {
return;
}
while(ms-->0) {
halCommonDelayMicroseconds(1000);
}
}