Contiki 2.6

micro-common-internal.c

00001 /*
00002  * File: micro-common-internal.c
00003  * Description: STM32W108 internal, micro specific HAL functions.
00004  * This file is provided for completeness and it should not be modified
00005  * by customers as it comtains code very tightly linked to undocumented
00006  * device features
00007  *
00008  * <!--(C) COPYRIGHT 2010 STMicroelectronics. All rights reserved.        -->
00009  */
00010 
00011 #include PLATFORM_HEADER
00012 #include "error.h"
00013 #include "hal/micro/micro-common.h"
00014 #include "hal/micro/cortexm3/micro-common.h"
00015 #include "hal/micro/cortexm3/mfg-token.h"
00016 
00017 #define HAL_STANDALONE
00018 #ifdef HAL_STANDALONE
00019 
00020 #define AUXADC_REG (0xC0u)
00021 #define DUMMY                   0
00022 
00023 #define ADC_6MHZ_CLOCK          0
00024 #define ADC_1MHZ_CLOCK          1
00025 
00026 #define ADC_SAMPLE_CLOCKS_32    0
00027 #define ADC_SAMPLE_CLOCKS_64    1
00028 #define ADC_SAMPLE_CLOCKS_128   2
00029 #define ADC_SAMPLE_CLOCKS_256   3
00030 #define ADC_SAMPLE_CLOCKS_512   4
00031 #define ADC_SAMPLE_CLOCKS_1024  5
00032 #define ADC_SAMPLE_CLOCKS_2048  6
00033 #define ADC_SAMPLE_CLOCKS_4096  7
00034 
00035 #define CAL_ADC_CHANNEL_VDD_4   0x00  //VDD_PADS/4
00036 #define CAL_ADC_CHANNEL_VREG_2  0x01  //VREG_OUT/2
00037 #define CAL_ADC_CHANNEL_TEMP    0x02
00038 #define CAL_ADC_CHANNEL_GND     0x03
00039 #define CAL_ADC_CHANNEL_VREF    0x04
00040 #define CAL_ADC_CHANNEL_I       0x06
00041 #define CAL_ADC_CHANNEL_Q       0x07
00042 #define CAL_ADC_CHANNEL_ATEST_A 0x09
00043 
00044 void stCalibrateVref(void)
00045 {
00046   // Calibrate Vref by measuring a known voltage, Vdd/2.
00047   //
00048   // FIXME: add support for calibration if done in boost mode.
00049   tokTypeMfgAnalogueTrimBoth biasTrim;
00050   
00051   halCommonGetMfgToken(&biasTrim, TOKEN_MFG_ANALOG_TRIM_BOTH);
00052   
00053   if(biasTrim.auxadc == 0xFFFF) {
00054     assert(FALSE);
00055   } else {
00056     //The bias trim token is set, so use the trim directly
00057     int16u temp_value;
00058     int16u mask = 0xFFFF;
00059 
00060     // halClearLed(BOARDLED3);
00061 
00062     while (SCR_BUSY_REG) ;
00063 
00064     SCR_ADDR_REG = AUXADC_REG ;  // prepare the address to write to
00065 
00066     // initiate read (starts on falling edge of SCR_CTRL_SCR_READ)
00067     SCR_CTRL_REG = SCR_CTRL_SCR_READ_MASK;
00068     SCR_CTRL_REG = 0;
00069 
00070     // wait for read to complete
00071     while (SCR_BUSY_REG) ;
00072 
00073     temp_value = SCR_READ_REG & ~mask;
00074     temp_value |= biasTrim.auxadc & mask;
00075     
00076     SCR_WRITE_REG = temp_value;
00077 
00078     // initiate write (starts on falling edge of SCR_CTRL_SCR_WRITE_MASK)
00079     SCR_CTRL_REG = SCR_CTRL_SCR_WRITE_MASK;
00080     SCR_CTRL_REG = 0;
00081 
00082     while (SCR_BUSY_REG) ;
00083     
00084   }
00085 }
00086 
00087 
00088 void calDisableAdc(void) {
00089   // Disable the Calibration ADC to save current.
00090   CAL_ADC_CONFIG &= ~CAL_ADC_CONFIG_CAL_ADC_EN;
00091 }
00092 
00093 
00094 
00095 // These routines maintain the same signature as their hal- counterparts to
00096 // facilitate simple support between phys.
00097 // It is assumed (hoped?) that the compiler will optimize out unused arguments.
00098 StStatus calStartAdcConversion(int8u dummy1, // Not used.
00099                                   int8u dummy2, // Not used.
00100                                   int8u channel,
00101                                   int8u rate,
00102                                   int8u clock) {
00103   // Disable the Calibration ADC interrupt so that we can poll it.
00104   INT_MGMTCFG &= ~INT_MGMTCALADC;
00105 
00106   ATOMIC(
00107     // Enable the Calibration ADC, choose source, set rate, and choose clock.
00108     CAL_ADC_CONFIG =((CAL_ADC_CONFIG_CAL_ADC_EN)                  |
00109                      (channel << CAL_ADC_CONFIG_CAL_ADC_MUX_BIT)  |
00110                      (rate << CAL_ADC_CONFIG_CAL_ADC_RATE_BIT)    |
00111                      (clock << CAL_ADC_CONFIG_CAL_ADC_CLKSEL_BIT) );
00112     // Clear any pending Calibration ADC interrupt.  Since we're atomic, the
00113     // one we're interested in hasn't happened yet (will take ~10us at minimum).
00114     // We're only clearing stale info.
00115     INT_MGMTFLAG = INT_MGMTCALADC;
00116   )
00117   return ST_SUCCESS;
00118 }
00119 
00120 
00121 StStatus calReadAdcBlocking(int8u  dummy,
00122                                int16u *value) {
00123   // Wait for conversion to complete.
00124   while ( ! (INT_MGMTFLAG & INT_MGMTCALADC) );
00125   // Clear the interrupt for this conversion.
00126   INT_MGMTFLAG = INT_MGMTCALADC;
00127   // Get the result.
00128   *value = (int16u)CAL_ADC_DATA;
00129   return ST_SUCCESS;
00130 }
00131 
00132 
00133 
00134 
00135 //Using 6MHz clock reduces resolution but greatly increases conversion speed.
00136 //The sample clocks were chosen based upon empirical evidence and provided
00137 //the fastest conversions with the greatest reasonable accuracy.  Variation
00138 //across successive conversions appears to be +/-20mv of the average
00139 //conversion.  Overall function time is <150us.
00140 int16u stMeasureVddFast(void)
00141 {
00142   int16u value;
00143   int32u Ngnd;
00144   int32u Nreg;
00145   int32u Nvdd;
00146   tokTypeMfgRegVoltage1V8 vregOutTok;
00147   halCommonGetMfgToken(&vregOutTok, TOKEN_MFG_1V8_REG_VOLTAGE);
00148   
00149   //Measure GND
00150   calStartAdcConversion(DUMMY,
00151                         DUMMY,
00152                         CAL_ADC_CHANNEL_GND,
00153                         ADC_SAMPLE_CLOCKS_128,
00154                         ADC_6MHZ_CLOCK);
00155   calReadAdcBlocking(DUMMY, &value);
00156   Ngnd = (int32u)value;
00157   
00158   //Measure VREG_OUT/2
00159   calStartAdcConversion(DUMMY,
00160                         DUMMY,
00161                         CAL_ADC_CHANNEL_VREG_2,
00162                         ADC_SAMPLE_CLOCKS_128,
00163                         ADC_6MHZ_CLOCK);
00164   calReadAdcBlocking(DUMMY, &value);
00165   Nreg = (int32u)value;
00166   
00167   //Measure VDD_PADS/4
00168   calStartAdcConversion(DUMMY,
00169                         DUMMY,
00170                         CAL_ADC_CHANNEL_VDD_4,
00171                         ADC_SAMPLE_CLOCKS_128,
00172                         ADC_6MHZ_CLOCK);
00173   calReadAdcBlocking(DUMMY, &value);
00174   Nvdd = (int32u)value;
00175   
00176   calDisableAdc();
00177   
00178   //Convert the value into mV.  VREG_OUT is ideally 1.8V, but it wont be
00179   //exactly 1.8V.  The actual value is stored in the manufacturing token
00180   //TOKEN_MFG_1V8_REG_VOLTAGE.  The token stores the value in 10^-4, but we
00181   //need 10^-3 so divide by 10.  If this token is not set (0xFFFF), then
00182   //assume 1800mV.
00183   if(vregOutTok == 0xFFFF) {
00184     vregOutTok = 1800;
00185   } else {
00186     vregOutTok /= 10;
00187   }
00188   return ((((((Nvdd-Ngnd)<<16)/(Nreg-Ngnd))*vregOutTok)*2)>>16);
00189 }
00190 #endif
00191 
00192 void halCommonCalibratePads(void)
00193 {
00194   if(stMeasureVddFast() < 2700) {
00195     GPIO_DBGCFG |= GPIO_DBGCFGRSVD;
00196   } else {
00197     GPIO_DBGCFG &= ~GPIO_DBGCFGRSVD;
00198   }
00199 }
00200 
00201 
00202 void halInternalSetRegTrim(boolean boostMode)
00203 {
00204   tokTypeMfgRegTrim regTrim;
00205   int8u trim1V2;
00206   int8u trim1V8;
00207   
00208   halCommonGetMfgToken(&regTrim, TOKEN_MFG_REG_TRIM);
00209   // The compiler can optimize this function a bit more and keep the 
00210   // values in processor registers if we use separate local vars instead
00211   // of just accessing via the structure fields
00212   trim1V8 = regTrim.regTrim1V8;
00213   trim1V2 = regTrim.regTrim1V2;
00214   
00215   //If tokens are erased, default to reasonable values, otherwise use the
00216   //token values.
00217   if((trim1V2 == 0xFF) && (trim1V8 == 0xFF)) {
00218     trim1V8 = 4;
00219     trim1V2 = 0;
00220   }
00221   
00222   //When the radio is in boost mode, we have to increase the 1.8V trim.
00223   if(boostMode) {
00224     trim1V8 += 2;
00225   }
00226   
00227   //Clamp at 7 to ensure we don't exceed max values, accidentally set
00228   //other bits, or wrap values.
00229   if(trim1V8>7) {
00230     trim1V8 = 7;
00231   }
00232   if(trim1V2>7) {
00233     trim1V2 = 7;
00234   }
00235   
00236   VREG_REG = ( (trim1V8<<VREG_VREG_1V8_TRIM_BIT) |
00237                (trim1V2<<VREG_VREG_1V2_TRIM_BIT) );
00238 }
00239 
00240 
00241 // halCommonDelayMicroseconds
00242 // -enables MAC Timer and leaves it enabled.
00243 // -does not touch MAC Timer Compare registers.
00244 // -max delay is 65535 usec.
00245 // NOTE: This function primarily designed for when the chip is running off of
00246 //       the XTAL, which is the most common situation.  When running from
00247 //       OSCHF, though, the clock speed is cut in half, so the input parameter
00248 //       is divided by two.  With respect to accuracy, we're now limited by
00249 //       the accuracy of OSCHF (much lower than XTAL).
00250 void halCommonDelayMicroseconds(int16u us)
00251 {
00252   int32u beginTime = ReadRegister(MAC_TIMER);
00253   
00254   //If we're not using the XTAL, the MAC Timer is running off OSCHF,
00255   //that means the clock is half speed, 6MHz.  We need to halve our delay
00256   //time.
00257   if((OSC24M_CTRL&OSC24M_CTRL_OSC24M_SEL)!=OSC24M_CTRL_OSC24M_SEL) {
00258     us >>= 1;
00259   }
00260     
00261   //we have about 2us of overhead in the calculations
00262   if(us<=2) {
00263     return;
00264   }
00265   
00266   // MAC Timer is enabled in stmRadioInit, which may not have been called yet.
00267   // This algorithm needs the MAC Timer so we enable it here.
00268   MAC_TIMER_CTRL |= MAC_TIMER_CTRL_MAC_TIMER_EN;
00269 
00270   // since our max delay (65535<<1) is less than half the size of the 
00271   //  20 bit mac timer, we can easily just handle the potential for
00272   //  mac timer wrapping by subtracting the time delta and masking out
00273   //  the extra bits
00274   while( ((MAC_TIMER-beginTime)&MAC_TIMER_MAC_TIMER_MASK) < us ) {
00275     ; // spin
00276   }
00277 }
00278 
00279 
00280 //Burning cycles for milliseconds is generally a bad idea, but it is
00281 //necessary in some situations.  If you have to burn more than 65ms of time,
00282 //the halCommonDelayMicroseconds function becomes cumbersome, so this
00283 //function gives you millisecond granularity.
00284 void halCommonDelayMilliseconds(int16u ms)
00285 {
00286   if(ms==0) {
00287     return;
00288   }
00289   
00290   while(ms-->0) {
00291     halCommonDelayMicroseconds(1000);
00292   }
00293 }