/* * 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 * * */ #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(®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<>= 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); } }