analog.c

Go to the documentation of this file.
00001 /* This file has been prepared for Doxygen automatic documentation generation.*/
00024 /* Validation Test for Current Measurement:
00025 Using the I-SIM circuit, apply 20.0mV to the ATmega406 CCADC input.
00026 Read back the variable LatestCCI.  Multiply this value by 53.7uV/count.
00027 You should get approximately 20,000uV, or 20mV.  The variable LatestCCI
00028 is intended to be used for cell impedance measurements and is not filtered
00029 or scaled in any way, but is a copy of the raw result of the instantaneous
00030 CC reading.
00031 
00032 The variable CCarray contains averaged 1-second Current readings based on the
00033 results of the Instantaneous reading of the CCADC.  These calculations
00034 assume a 5mOhm sense resistor.  To verify these readings, pick one
00035 sample out of the CCarray and convert to decimal.  The value should be read
00036 directly in mA.  If using 20mV as noted above, this would correspond to
00037 a value of 20mV/5mOhm = 4000mA, therefore all non-zero elements in the array
00038 should read approx. 4000 (decimal).
00039 
00040 The Accumulator should also be tested, since its reading must also be
00041 scaled in order to be read as mA.  Assuming 20mV applied to the input,
00042 and each bit corresponding to 1.678uV, then each 1-second reading of the
00043 CCADC accumulated result should be approx. 20mV/1.678uV = 0x2EF8.
00044 This number must be multiplied by 3600 to convert from seconds to hours.
00045 Dividing this result by 10727 will yield the current in mAHr, in this
00046 case approximately 4000mAHr.
00047 
00048 
00049 */
00050 
00051 
00052 
00053 
00054 
00055 //#include "iom406_320.h"
00056 #include <iom406.h>     // IAR headerfile for Mega406 (EW 410)
00057 #include <inavr.h>
00058 
00059 #include "main.h"
00060 #include "pack.h"
00061 
00062 #define MODULE_ANALOG
00063 #include "analog.h"
00064 
00065 #define MODULE_CALIBRATION
00066 #include "calibration.h"
00067 
00068 
00069 #include "smbus.h"
00070 #include "timer.h"      // (for access to the Timer32KHz variable)
00071 #include "pwrmgmt.h"
00072 #include "ee.h"
00073 
00074 
00075 //Local prototypes (not exposed in analog.h)
00076 void CCarray_Init(void);
00077 
00078 
00079 
00080 
00081 //Local variables (not exposed in analog.h)
00082 
00083 //signed long RunningAcc = 0;           //this must be externally visible for Hibernate mode estimated power loss
00084 signed long MaxTopAcc = 0;
00085 signed long MaxBottomAcc = 0;
00086 
00087 unsigned char CC_delay_acc = 0;
00088 unsigned char CC_delay_inst = 0;
00089 
00090 //Calculated, calibrated results
00091 unsigned int  CellV[4] = {0};
00092 unsigned int  OnChipTemp = 0;
00093 
00094 //Intermediate results
00095 unsigned int  Thermistor[4] = {0};      // this hold an Ohmic value, scaled per "FixedThermistorPullup"
00096 unsigned int  VPA4 = 0;                 // this is V(ADC4) * 5
00097 
00098 
00099 // The following variables are used for the 1-minute (approx) average of current flow.
00100 // We treat CCarray as a circular buffer, containing the 64 most recent samples available.
00101 // Each sample is properly scaled (mA) but does not include temperature or offset effects.
00102 // These functions are kept local to this file and are not made public.
00103 signed int CCarray[64];         //positive (charging) or negative (discharging).
00104 char CCindex = 0;
00105 char CCvalidsamples = 0;
00106 
00107 
00108 
00109 
00110 //Support variables
00111 unsigned int  ADCbuffer[10];            //raw converted results
00112 unsigned int ADCgain[4];                //gain values for Cell1-4
00113 unsigned int  VTgain;                   //gain for on-chip temperature sensor
00114 unsigned char ADCchannel;               //visible so we can force the start channel
00115 
00116 
00117 #define FixedThermistorPullup 1000      /* ohmic value */
00118 
00119 /* =====================================================================================
00120    =====================================================================================
00121    ===================================================================================== */
00122 
00123 
00124 //Public functions (exposed in analog.h)
00125 
00126 void SetMaxTopAcc(long value)
00127 {
00128   MaxTopAcc = value;
00129 }
00130 
00131 void FullChargeReached(void)
00132 {
00133   signed long temp;
00134 
00135   temp = RunningAcc - MaxBottomAcc;
00136   RunningAcc = MaxTopAcc = temp;
00137   MaxBottomAcc = 0;
00138   SMBvariables[SMBV_BattStatus][lobyte] &= ~FULLY_DISCHARGED;
00139   SMBvariables[SMBV_BattStatus][lobyte] |= FULLY_CHARGED;
00140 }
00141 
00142 
00143 
00144 void FullDischargeReached(void)
00145 {
00146   MaxTopAcc -= RunningAcc;
00147   RunningAcc = MaxBottomAcc = 0;
00148   SMBvariables[SMBV_BattStatus][lobyte] |= FULLY_DISCHARGED;
00149   SMBvariables[SMBV_BattStatus][lobyte] &= ~FULLY_CHARGED;
00150 }
00151 
00152 
00153 /* =====================================================================================
00154    =====================================================================================
00155    ===================================================================================== */
00156 
00157 
00158 //Assorted local-support math functions
00159 
00160 unsigned int GetVoltage(void)   //also serves as "cmd = 9"
00161 {
00162   unsigned int volt;
00163 
00164   volt = ReadCell(1);
00165   volt += ReadCell(2);
00166   if(PACKSTACK > 2)
00167     volt += ReadCell(3);
00168   if(PACKSTACK > 3)
00169     volt += ReadCell(4);
00170   return volt;
00171 }       
00172 
00173 
00174 long GetCharge(void)                    //in mAHrs
00175 {
00176   unsigned long calc = (RunningAcc / 10727);    // 10,727 LSB/mAh, see app-note for more details
00177   return calc;
00178 }
00179 
00180 
00181 long GetCharge_mAmins(void)        //in mAmins
00182 {
00183   unsigned long calc = (RunningAcc / 179);      // 10727 / 60 ~= 179
00184   return calc;
00185 }
00186 
00187 
00188 long GetChgUntilFull_mAmins(void)       //in mAmins
00189 {
00190   unsigned long calc = (MaxTopAcc - RunningAcc);
00191   return (calc / 179);
00192 }
00193 
00194 
00195 unsigned int GetMaxChg(void)    //calculate the maximum charge that the pack is presently
00196                                 //capable of holding (not same as DESIGN capacity)
00197 {
00198   unsigned long calc = MaxTopAcc / 10727;
00199   return (unsigned int) calc;
00200 }
00201 
00202 
00203 
00204 /*
00205 unsigned int GetCapacity(void)          //designed capacity @1C, in mAHr
00206 {
00207   return PACK_DESIGNCAPTYP;
00208 }
00209 
00210 
00211 unsigned long GetCapacity_mAmins(void)  //designed capacity @1C, in mAmins
00212 {
00213   return (PACK_DESIGNCAPTYP*60);
00214 }
00215 */
00216 
00217 
00218 
00219 
00220 /* =====================================================================================
00221    =====================================================================================
00222    ===================================================================================== */
00223 
00224 //Functions to support specific SMBus Slave READ commands
00225 
00226 unsigned int AtRateTTF(void)    // cmd = 5
00227 {
00228   signed long calc;
00229   unsigned int temp = (unsigned int)SMBvar_int[SMBV_AtRate];
00230 
00231   if((signed int) temp > 0)     //For TTF, AtRate must be POSITIVE & NON-ZERO.
00232   {
00233     if(SMBvariables[SMBV_BattMode][hibyte] & CAPACITY_MODE)  //use mW
00234     {
00235       temp = 65535;                     //optional mode, not implemented.
00236     }
00237     else //use mA in calculations
00238     {
00239       calc = PACK_DESIGNCAPTYP;
00240       calc = calc * 60;
00241       calc -= GetCharge_mAmins();
00242       temp = calc / temp;               // (mAmins / mA) = mins
00243     }
00244   }
00245   else  //error
00246     temp = 65535;
00247 
00248   return temp;
00249 }
00250 
00251 
00252 unsigned int AtRateTTE(void)    // cmd = 6
00253 {
00254   signed long calc;
00255   unsigned int temp = (unsigned int)SMBvar_int[SMBV_AtRate];
00256 
00257   if((signed int) temp < 0)     //For TTE, AtRate must be NEGATIVE & NON-ZERO.
00258   {
00259     temp = -((signed int) temp);
00260 
00261     if(SMBvariables[SMBV_BattMode][hibyte] & CAPACITY_MODE)     //use mW
00262     {
00263       calc = GetCharge_mAmins() * PACK_MINV;    //this is now in uWmins
00264       calc = calc / 10000;                      //this is now in 10mWmins
00265       temp = calc / temp;                       // (10mWmins / 10mW) = mins
00266     }
00267     else //use mA in calculations
00268     {
00269       calc = GetCharge_mAmins();
00270       temp = calc / temp;                       // (mAmins / mA) = mins
00271     }
00272   }
00273   else  //error
00274     temp = 65535;
00275 
00276   return temp;
00277 }
00278 
00279 
00280 unsigned int AtRateOK(void)     // cmd = 7
00281 {
00282   unsigned long calc;                            //used as available capacity
00283   unsigned int temp = (unsigned int)SMBvar_int[SMBV_AtRate]; //used as total rate of consumption
00284 
00285   if((signed int) temp < 0)     //For AtRateOK, AtRate must be NEGATIVE & NON-ZERO.
00286   {
00287     temp = -((signed int) temp);
00288 
00289     if(SMBvariables[SMBV_BattMode][hibyte] & CAPACITY_MODE)     //use mW in calculations
00290     {
00291       calc = Current1Sec() * GetVoltage();      //in uW (alternate: can use PACK_MINV)
00292       calc = calc / 10000;                      //in 10mW
00293       temp += calc;     //combine AtRate and present load; shouldn't overflow!
00294 
00295       calc = GetCharge_mAmins();
00296       calc = calc * PACK_MINV;
00297       calc = calc * 6;                          //this is now in mW-10Secs
00298       calc = calc / 10;                         //this is now in 10mW-10Secs
00299     }
00300     else //use mA in calculations
00301     {
00302       temp += Current1Sec();                    //add in the present discharge rate too!
00303       calc = GetCharge_mAmins() * 6;            //convert to 10-second rate
00304     }
00305 
00306     if(calc > temp)
00307       temp = 65535;     //return TRUE, as there is enough energy.
00308     else
00309       temp = 0;         //return FALSE. We're almost dead!
00310   }
00311   else  //error
00312     temp = 65535;
00313 
00314   return temp;
00315 }
00316 
00317 
00318 unsigned int GetTemperature(void)       // cmd = 8
00319 { // Returns temperature of MEGA406, in 0.1 degrees Kelvin.
00320   return ReadTemperature(0);    //channel 0 is the on-chip sensor.
00321 }
00322 
00323 
00324 
00325 unsigned char RelativeSOC(void)         // cmd = 13
00326 {
00327   unsigned long charge = (RunningAcc * 100) / MaxTopAcc;
00328 //  return (unsigned int) charge;
00329   return (unsigned char) charge;
00330 }
00331 
00332 
00333 unsigned int AbsoluteSOC(void)          // cmd = 14
00334 {
00335   unsigned long charge = (GetCharge() * 100);
00336   charge = charge / PACK_DESIGNCAPTYP;
00337   return (unsigned int) charge;
00338 }
00339 
00340 
00341 unsigned int RemainingCap(void)         // cmd = 15
00342 { 
00343   unsigned long calc;
00344   unsigned int charge = GetCharge();    //in mAH
00345 
00346   if(SMBvariables[SMBV_BattMode][hibyte] & CAPACITY_MODE)       //use mW in calculations
00347   {
00348     calc = charge * GetVoltage();       //in uWH
00349     charge = calc  / 10000;             //in 10mWH
00350   }
00351 
00352   return charge;
00353 }
00354 
00355 
00356 
00357 unsigned int FullChgCap(void)           //cmd = 16
00358 { 
00359   unsigned long calc = GetMaxChg();
00360 
00361   if(SMBvariables[SMBV_BattMode][hibyte] & CAPACITY_MODE)       //use mW in calculations
00362   {
00363     calc = calc * PACK_NOMINALV;
00364     calc = calc / 10000;
00365   }
00366 
00367   return (unsigned int) calc;
00368 }
00369 
00370 
00371 // Pass in 0 for 1-sec basis, 1 for 1-minute avg'd basis.
00372 unsigned int TimeToEmpty(unsigned char avgd)    //cmd = 17,18; how many mins until battery is discharged at present rate
00373 {
00374   signed int rate;
00375   unsigned long presentrate;
00376   unsigned long cap;
00377 
00378   if(0 == avgd)
00379     rate = Current1Sec();
00380   else
00381     rate = CCarray_Average();
00382 
00383   if(rate >= 0)
00384     return 65535;
00385 
00386   rate = -rate;
00387 
00388   if(SMBvariables[SMBV_BattMode][hibyte] & CAPACITY_MODE)       //use mW in calculations
00389   {
00390     //First, determine actual rate of Wattage being used.
00391     presentrate = rate * GetVoltage();          //this is in uW scale
00392 
00393     //Next, determine capacity at pack's minimum V (full discharge)
00394     cap = GetCharge_mAmins() * PACK_MINV;       //this is in uWmins scale
00395 
00396     //Divide uWmins by uW and you get minutes.
00397     cap = cap / presentrate;
00398   }
00399   else
00400   {
00401     cap = GetCharge_mAmins() / rate;    // (mAmins / mA) = minutes
00402   }
00403 
00404   return (unsigned int) cap;
00405 }
00406 
00407 
00408 
00409 
00410 unsigned int AvgTimeToFull(void)        //cmd = 19
00411 {
00412   signed int rate;
00413   unsigned long presentrate;
00414   unsigned long cap;
00415 
00416   rate = Current1Sec();
00417   if(rate <= 0)
00418     return 65535;
00419 
00420 
00421   if(SMBvariables[SMBV_BattMode][hibyte] & CAPACITY_MODE)       //use mW in calculations
00422   {
00423     //First, determine actual rate of Wattage being used.
00424     presentrate = rate * GetVoltage();          //this is in uW scale
00425 
00426     //Next, determine capacity at pack's minimum V (full discharge)
00427     cap = GetChgUntilFull_mAmins() * PACK_MINV; //this is in uWmins scale
00428 
00429     //Divide uWmins by uW and you get minutes.
00430     cap = cap / presentrate;
00431   }
00432   else
00433   {
00434     cap = GetChgUntilFull_mAmins() / rate;      // (mAmins / mA) = minutes
00435   }
00436 
00437   return (unsigned int) cap;
00438 }
00439 
00440 
00441 
00442 
00443 
00444 
00445 
00446 /* =====================================================================================
00447    =====================================================================================
00448    ===================================================================================== */
00449 
00450 
00451 //Local 'helper' functions (not exposed in analog.h)
00452 
00453 
00456 #define FAST_RC_CAL    (sig_array[0x01])
00457 #define SLOW_RC_LO_CAL (sig_array[0x06])
00458 #define SLOW_RC_HI_CAL (sig_array[0x07])
00459 #define BG_C_CAL       (sig_array[0x09])
00460 #define CELL1_LO_CAL   (sig_array[0x10])
00461 #define CELL1_HI_CAL   (sig_array[0x11])
00462 #define CELL2_LO_CAL   (sig_array[0x12])
00463 #define CELL2_HI_CAL   (sig_array[0x13])
00464 #define CELL3_LO_CAL   (sig_array[0x14])
00465 #define CELL3_HI_CAL   (sig_array[0x15])
00466 #define CELL4_LO_CAL   (sig_array[0x16])
00467 #define CELL4_HI_CAL   (sig_array[0x17])
00468 #define ADC0_LO_CAL    (sig_array[0x18]) // currently not used
00469 #define ADC0_HI_CAL    (sig_array[0x19]) // currently not used
00470 #define VPTAT_LO_CAL   (sig_array[0x1A])
00471 #define VPTAT_HI_CAL   (sig_array[0x1B])
00472 
00473 // Old defines (for rev.D), not fully compatible with new routines
00474 /*
00475 #define FAST_RC_CAL  (sig_array[0x01])
00476 #define SLOW_RC_CAL  (sig_array[0x03])
00477 #define BG_C_CAL     (sig_array[0x09])
00478 #define BG_R_CAL     (sig_array[0x0B])
00479 #define VPTAT_HI_CAL (sig_array[0x0D])
00480 #define VPTAT_LO_CAL (sig_array[0x0F])
00481 #define CELL1_CAL    (sig_array[0x11])
00482 #define CELL2_CAL    (sig_array[0x13])
00483 #define CELL3_CAL    (sig_array[0x15])
00484 #define CELL4_CAL    (sig_array[0x17])
00485 */
00486 
00487 
00488 void ReadFactoryCalibration(void)
00489 {
00490   unsigned char __flash * ptr = 0;
00491   char i;
00492   char flags = SREG;
00493   char temp;
00494   unsigned char sig_array[0x1C];   //Array with signature/fab.calibration bytes
00495 
00496 
00497   __disable_interrupt();
00498   for(i=0; i< 0x1C; i++)
00499   {
00500     SPMCSR = (1<<SIGRD) | (1<<SPMEN);
00501     temp = *ptr++;
00502     sig_array[i] = temp;
00503   }
00504 
00505   if(flags & 0x80)
00506     __enable_interrupt();
00507 
00508   //Now apply the Cal values we just read out.
00509 
00510 //  SlowRCCal = SLOW_RC_CAL;
00511   SlowRCCal = (SLOW_RC_HI_CAL << 8)|(SLOW_RC_LO_CAL);
00512   FastRCCal = FAST_RC_CAL;
00513 
00514   //Calibrate bandgap (fab. or battery factory)
00515   BGCCRCal = BG_C_CAL| 0x80;
00516   if (ReadVrefCalibration()) {
00517         calibration_state &= ~CAL_VREF_MASK;
00518         calibration_state |=  CAL_VREF_OK;
00519   } else {
00520     calibration_state &= ~CAL_VREF_MASK;
00521   }
00522 
00523   //Save the Cell1-Cell4 gain values
00524   ADCgain[0] = (CELL1_HI_CAL << 8)|(CELL1_LO_CAL);
00525   ADCgain[1] = (CELL2_HI_CAL << 8)|(CELL2_LO_CAL);
00526   ADCgain[2] = (CELL3_HI_CAL << 8)|(CELL3_LO_CAL);
00527   ADCgain[3] = (CELL4_HI_CAL << 8)|(CELL4_LO_CAL);
00528 
00529   //On-chip temperature sensor calibration value
00530   VTgain = (VPTAT_HI_CAL<<8) | VPTAT_LO_CAL;    //on-chip temp sensor
00531 
00532   // Calibration values for CCoffset and CCIoffset?
00533   if (ReadCCOffsetCalibration()) {
00534         calibration_state &= ~CAL_CC_MASK;
00535         calibration_state |=  CAL_CC_OK;
00536   } else {
00537     calibration_state &= ~CAL_CC_MASK;
00538   }
00539 }
00540 
00541 
00542 
00543 unsigned char ReadVrefCalibration(void)
00544 {
00545   unsigned char temp;
00546 
00547   while(EECR & (1<<EEPE));
00548   EEAR = EESTORAGE_BGCCR;
00549   EECR = (1<<EERE);     //read
00550   temp = EEDR;
00551   if (temp != 0xFF) {
00552     BGCCR = temp;
00553     while(EECR & (1<<EEPE));
00554     EEAR = EESTORAGE_BGCRR;
00555     EECR = (1<<EERE);   //read
00556     BGCRR = EEDR;
00557     return(1);
00558   } else {
00559     BGCRR = 0x0F;
00560     BGCCR = BGCCRCal;           // signature value (with Band gap enabled)
00561     return(0);
00562   }
00563 }
00564 
00565 
00566 unsigned char ReadCCOffsetCalibration(void)
00567 {
00568   signed char temp;
00569 
00570   while(EECR & (1<<EEPE));
00571   EEAR = EESTORAGE_CC_valid;
00572   EECR = (1<<EERE);     //read
00573   temp = EEDR;
00574   if (temp != -1) {
00575     do {} while(EECR & (1<<EEPE));
00576     EEAR = EESTORAGE_CCoffset;
00577     EECR = (1<<EERE);   //read
00578     temp = EEDR;
00579     CCoffset = (signed int)temp;
00580     do {} while(EECR & (1<<EEPE));
00581     EEAR = EESTORAGE_CCIoffset;
00582     EECR = (1<<EERE);   //read
00583     temp=EEDR;
00584     CCIoffset = (signed long)temp;
00585     return(1);
00586   } else {
00587     CCIoffset = 0;
00588     CCoffset  = 0;
00589     return(0);
00590   }
00591 }
00592 
00593 /* =====================================================================================
00594    =====================================================================================
00595    ===================================================================================== */
00596 
00597 
00598 /* Calibration routines for VREF and CC/CCIoffset
00599 */
00600 
00601 
00602 
00603 unsigned char CalibrateVREF(void)         // calibration of VREF
00604 {
00605   long int V_error;                       // Variable to keep error after conversion
00606   long int old_V_error;                   // Value used to keep old error value during linear scan
00607   unsigned char ratio_counter;            // Value used to count up the BGCCR value
00608   unsigned char loopcount;                // Value used during temperature calibration
00609   volatile unsigned int vui_temp;         // Dummy variable used to ensure correct reading of VADC
00610 
00611   BGCRR = 0x0f;                                                   // Load Factory calibration
00612   BGCCR = BGCCRCal;                               // Load Factory calibration (with Band gap enabled)
00613   VADMUX = CAL_CHANNEL;                   // Select terminal input defined in calibration.h
00614   VADCSR = (1 << VADEN);                  // Enable the VADC
00615   vui_temp = VADC;                        // Dummy read(s) to ensure proper operation
00616   loopcount = CAL_WAIT;                               // Wait for error from CellBalancing to cancel
00617   while (loopcount--) {
00618     VADCSR |= (1 << VADSC) | (1 << VADCCIF);// Start a VADC conversion and clear any pending interrupts
00619     do {} while(!(VADCSR & (1 << VADCCIF)));// Wait while conversion in progress
00620     vui_temp = VADC;                      // Dummy read(s) to ensure proper operation
00621   }
00622   VADCSR |= (1 << VADSC) | (1 << VADCCIF);// Start a VADC conversion and clear any pending interrupts
00623   do {} while(!(VADCSR & (1 << VADCCIF)));// Wait while conversion in progress
00624   V_error = VADC;                         // Calculate the error of the measured value
00625   V_error = V_error * CAL_GAIN;
00626   V_error = V_error - Vcalibration_value;
00627   loopcount = 8;                                                  // Check for all different test limits
00628   while (V_error < vcalibration_level[loopcount]) {
00629     loopcount--;
00630     if (!loopcount) {
00631       break;
00632     }
00633   }
00634   BGCRR = tempcal[loopcount];             // Set BGCRR to correct setting
00635 
00636   V_error = vcalibration_level[7];        // Init old_V_errror
00637   ratio_counter = (1 << BGEN) - 1;        // Reset ratio counter (keep Band gap enabled), -1 because of increment
00638   do                                      // Repeat until VADC value becomes larger than reference
00639   {
00640     ratio_counter++;                      // Select next BGCCR value;
00641     BGCCR = ratio_counter;                // Set new BGCCR value
00642     old_V_error = V_error;                // Calulate the error of the measured value
00643     if(ratio_counter & 0x40)              // Ratio_counter bit 6 set means calibration failed
00644     {
00645       BGCRR = 0x0f;                                               // Load Factory calibration
00646       BGCCR = BGCCRCal;                               // Load Factory calibration (with Band gap enabled)
00647       return 0;                           // Return with fail flag
00648     }
00649     vui_temp = VADC;                      // Dummy read to ensure proper operation
00650     VADCSR |= (1 << VADSC) | (1 << VADCCIF);// Start a VADC conversion and clear any pending interrupts
00651     do {} while(!(VADCSR & (1 << VADCCIF)));// Wait while conversion in progress
00652     V_error = VADC;                       // Calulate the error of the measured value
00653     V_error = V_error * CAL_GAIN;
00654     V_error = V_error - Vcalibration_value;
00655   } while(V_error > 0);                   // Loop until VADC output is larger than the reference setting
00656   if(old_V_error < (-V_error)) {          // Select the best value out of the last two BGCCR values
00657     ratio_counter--;                      // Value below the reference setting is closest to the reference
00658   }
00659   BGCCR = ratio_counter;                  // Set the correct BGCCR value
00660 
00661   do {} while(EECR & (1<<EEPE));
00662   do {} while(SPMCSR & (1<<SPMEN));
00663   EEAR = EESTORAGE_BGCRR;
00664   EEDR = tempcal[loopcount];
00665   EECR = (0<<EEPM1) | (0<<EEPM0) | (0<<EERIE) | (1<<EEMPE) | (0<<EEPE) | (0<<EERE);     //arm
00666   EECR = (0<<EEPM1) | (0<<EEPM0) | (0<<EERIE) | (1<<EEMPE) | (1<<EEPE) | (0<<EERE);     //go
00667 
00668   do {} while(EECR & (1<<EEPE));
00669   EEAR = EESTORAGE_BGCCR;
00670   EEDR = ratio_counter;
00671   EECR = (0<<EEPM1) | (0<<EEPM0) | (0<<EERIE) | (1<<EEMPE) | (0<<EEPE) | (0<<EERE);     //arm
00672   EECR = (0<<EEPM1) | (0<<EEPM0) | (0<<EERIE) | (1<<EEMPE) | (1<<EEPE) | (0<<EERE);     //go
00673 
00674   return 1;
00675 }
00676 
00677 
00678 
00679 unsigned char CalibrateCCoffset(void)         // calibration of CC offset
00680 {                                       // assumes 0 current through shunt for minimum 1 second before calibration
00681   unsigned char index = CCindex;
00682   volatile signed long cc_temp;         // for dummy reads
00683 
00684   index--;
00685   index &= 63;
00686   CCIoffset = CCarray[index];
00687 
00688   ChangePowerMode(POWERMODE_ACTIVE,0);
00689   while (CC_delay_acc--) {  // check if we need to discard samples
00690     do {} while (!(CADCSRB & (1<<CADACIF)));
00691     cc_temp = CADAC;
00692     CADCSRB = (1<<CADACIE) | (1<<CADICIE) | (1<<CADACIF) | (1<<CADRCIF) | (1<<CADICIF);
00693   }
00694   CCoffset = CADAC;
00695 
00696   if ((CCoffset > CCoffset_limit) || (CCIoffset > CCIoffset_limit) ||
00697       (CCoffset < -CCoffset_limit) || (CCIoffset < -CCIoffset_limit)) {  // Max offset limits to avoid wrong calibration
00698     CCoffset = 0;
00699     CCIoffset = 0;
00700     return(0);
00701 
00702   } else {
00703     do {} while(EECR & (1<<EEPE));
00704     do {} while(SPMCSR & (1<<SPMEN));
00705     EEAR = EESTORAGE_CC_valid;
00706     EEDR = 0x00;
00707     EECR = (0<<EEPM1) | (0<<EEPM0) | (0<<EERIE) | (1<<EEMPE) | (0<<EEPE) | (0<<EERE);   //arm
00708     EECR = (0<<EEPM1) | (0<<EEPM0) | (0<<EERIE) | (1<<EEMPE) | (1<<EEPE) | (0<<EERE);   //go
00709 
00710     do {} while(EECR & (1<<EEPE));
00711     EEAR = EESTORAGE_CCIoffset;
00712     EEDR = (unsigned char)CCIoffset;
00713     EECR = (0<<EEPM1) | (0<<EEPM0) | (0<<EERIE) | (1<<EEMPE) | (0<<EEPE) | (0<<EERE);   //arm
00714     EECR = (0<<EEPM1) | (0<<EEPM0) | (0<<EERIE) | (1<<EEMPE) | (1<<EEPE) | (0<<EERE);   //go
00715 
00716     do {} while(EECR & (1<<EEPE));
00717     EEAR = EESTORAGE_CCoffset;
00718     EEDR = (unsigned char)CCoffset;
00719     EECR = (0<<EEPM1) | (0<<EEPM0) | (0<<EERIE) | (1<<EEMPE) | (0<<EEPE) | (0<<EERE);   //arm
00720     EECR = (0<<EEPM1) | (0<<EEPM0) | (0<<EERIE) | (1<<EEMPE) | (1<<EEPE) | (0<<EERE);   //go
00721     return(1);
00722   }
00723 }
00724 
00725 
00726 
00727 /* =====================================================================================
00728    =====================================================================================
00729    ===================================================================================== */
00730 
00731 
00732 
00733 /* General description of Coulomb Counter usage:
00734  *
00735  * The Coulomb Counter must be running whenever the chip is not
00736  * in deep sleep mode.  The interrupt is used to perform software
00737  * accumulation & must also handle analog offset voltage correction
00738  * for the input stage.
00739  *
00740 */
00741 
00742 void CCmode(unsigned char mode)
00743 {
00744   switch (mode)
00745   {
00746     default:
00747     case CC_DISABLED:
00748       CCSR = 0;                                 //switch to the Slow RC Oscillator to save power
00749       CCarray_Init();
00750       CADCSRA = 0;
00751       break;
00752 
00753     case CC_ACCUMULATE:                         //enable both Accumulate and Instantaneous
00754       if(CCSR != ((1<<XOE)|(1<<ACS)))           //if the 32KHz xtal isn't already in use, start it up.
00755       {
00756         Timer32KHz = 9;
00757         CCSR = (1<<XOE);
00758       }
00759       BGCCR |= (1<<BGEN);                       //ensure that the bandgap is enabled
00760       CADCSRB = (1<<CADACIE) | (1<<CADICIE) | (1<<CADACIF) | (1<<CADRCIF) | (1<<CADICIF);
00761       while ( CADCSRA & (1<<CADUB) );           //wait for clock domain sync
00762       CADCSRA = (1<<CADEN) | ACCUM_CONV_TIME;   
00763       CC_delay_acc  = 4;                        //must ignore the first 4 readings!
00764       CC_delay_inst = 4;                        //must ignore the first 4 readings!
00765       CCarray_Init();
00766       break;
00767 
00768     case CC_REGULAR:                            //enable ONLY Regular mode is enabled
00769       if(CCSR != ((1<<XOE)|(1<<ACS)))           //if the 32KHz xtal isn't already in use, start it up.
00770       {
00771         Timer32KHz = 9;
00772         CCSR = (1<<XOE);
00773       }
00774       CCarray_Init();
00775       BGCCR |= (1<<BGEN);                       //ensure that the bandgap is enabled
00776       CADRDC = -(ACTIVE_CURRENT_THRESHOLD + (CCIoffset>>4));
00777       CADCSRB = (1<<CADRCIE) | (1<<CADACIF) | (1<<CADRCIF) | (1<<CADICIF);
00778       while ( CADCSRA & (1<<CADUB) );           //wait for clock domain sync
00779       CADCSRA = (1<<CADSE) | REG_CONV_TIME;     
00780       break;
00781   }
00782 }
00783 
00784 
00785 void CCinit(void)
00786 {
00787   CCmode(CC_DISABLED);
00788 }
00789 
00790 
00791 
00792 /* =====================================================================================
00793    =====================================================================================
00794    ===================================================================================== */
00795 
00796 
00797 
00798 
00799 
00800 //Call this at startup to ensure the buffer is cleared.
00801 void CCarray_Init(void)
00802 {
00803   CCvalidsamples = 0;                   //reset the # of valid samples
00804   CCindex = 0;                          //reset the insertion index
00805 }
00806 
00807 
00808 //This inserts a new sample into the circular buffer.
00809 void CCarray_AddSample(signed int newsample)
00810 {
00811   if(CC_delay_inst)
00812   {
00813     CC_delay_inst--;
00814     return;
00815   }
00816 
00817   CCarray[CCindex++] = newsample;
00818   CCindex &= 63;
00819 
00820   if(CCvalidsamples < 64)
00821     CCvalidsamples++;
00822 }
00823 
00824 
00825 //This retrieves the most recent sample out of the circular buffer.
00826 signed int Current1Sec(void)    
00827 {
00828   unsigned char temp = CCindex;
00829 
00830   temp--;
00831   temp &= 63;
00832   return CCarray[temp];
00833 }
00834 
00835 
00836 
00837 
00838 
00839 //Note: the data source here is INSTANTANEOUS CURRENT readings, not the Accumulator.
00840 //  Although it is not as precise as the Accum, it is accurate within 0.5% of full-scale.
00841 signed int CCarray_Average(void)        //this will accurately reflect up to 64 seconds of data.
00842 {                                       
00843   signed long avg = 0;
00844   unsigned char ctr = CCvalidsamples;   //grab a local copy since CCvalidsamples is modified by an INTERRUPT
00845   unsigned char temp;
00846   signed int * ptr = CCarray;           //the same as saying  &CCarray[0]
00847 
00848   if(0 == ctr)
00849     return 0;
00850 
00851   temp = ctr;
00852   while(temp--)                         //add up ONLY the valid samples!
00853     avg += *ptr++;
00854 
00855   if(64 == ctr)
00856     return (signed int) (avg >> 6);     //use this optimized version after the first 64 seconds
00857   else
00858   if(1 == ctr)
00859     return (signed int) (avg);
00860   else
00861     return (signed int) (avg / ctr );   //this takes longer due to division.
00862 }
00863 
00864 
00865 
00866 /* ******************************************************************** *
00867  * Coulomb Counter, Instantaneous conversion complete ISR
00868  *
00869  * This interrupt fires after every conversion, at 3.9ms intervals.
00870  * Every 256 interrupts is therefore 1 second.
00871  *
00872  * Each count of the result is 53.7uV, which, into a 5mOhm sense R,
00873  * indicates 10.74mA flowing.  This is quick-responding and can therefore
00874  * be used in conjunction with cell voltage measurements to calculate
00875  * cell impedance by watching the delta-V on the cells when compared to
00876  * any available delta-I.  There obviously must be sufficient delta-I
00877  * to permit a reasonably accurate division result, otherwise the
00878  * calculation could be way off.  Note that the CC ADC is separate
00879  * hardware from the 12-bit general-purpose ADC and can therefore do a
00880  * current measurement in parallel with one cell voltage measurement.
00881  * There is a global variable, LatestCCI, that always contains the most
00882  * recent value.  The intent is that this value would be captured along
00883  * with the individual cell voltage at a given instant.
00884  *
00885  * ( NOTE: if you use LatestCCI, remember that it is NOT SCALED TO mA! )
00886  *
00887  * We also use this as the basis of the required 1-minute averaged current.
00888  * We play some tricks to get this value without doing ugly math.
00889  * Since the values need to be available in milliamps, we must accumulate
00890  * lots of samples to make up for the fact there's over a 10X scale delta.
00891  * Specifically, we get 1 count for every 10.74mA.  If we accumulate
00892  * 10.74 x 16 = 172 samples, then we can simply divide by 16 to get a
00893  * 1mA/step average result. We will thus take 172 samples per second.
00894  * (Note: this value could be modified SLIGHTLY to effect a 'calibration'
00895  *  without adding additional math processing burden.)
00896  *
00897  * The next problem is to spread these samples out evenly over the sampling
00898  * period (1 second).  To do this, we use a simple first-order sigma-delta
00899  * software modulator; if its 1-bit output is a '1', then we accumulate the
00900  * sample that's available at this moment.
00901  *
00902  * ******************************************************************** */
00903 
00904 
00905 
00906 #define CCI_CAL 172     /* # of samples we need to accumulate in a second */
00907 
00908 #pragma vector = CCADC_vect
00909 __interrupt void CC_Instantaneous_ISR(void)
00910 {
00911   static unsigned char timer = 0;       //256 interrupts = 1 second
00912   static signed long sl = 0;            //averaging accumulator
00913   static unsigned char mod_remainder;   //used by sigma-delta modulator
00914   signed int temp,temp2;
00915 
00916 
00917 
00918   if(PowerMode == POWERMODE_IDLE)       //do Regular Current mode instead.
00919   {
00920     temp = CADIC;
00921     temp <<= 3;                         //note: CADIC is actually 13 bits incl. sign
00922     RunningAcc += (signed long) temp;   //update the charge state
00923     RunningAcc += (signed long) temp;   //update the charge state
00924     RunningAcc += (signed long) temp;   //update the charge state
00925     RunningAcc += (signed long) temp;   //must do it 4 times (Instantaneous * 32 = Accumulate)
00926 
00927     //Note: it is POSSIBLE to overflow here, but not likely unless pushing lots of current.
00928     temp2 = CADIC;
00929     temp = temp2;
00930     temp += temp2;
00931     temp += temp2/2;                    //10.5x, a good approximation of 10.7x
00932     temp -= CCIoffset;                  //subtract the offset calibration value
00933     CCarray_AddSample(temp);            //update the 64-second average
00934     return;
00935   }
00936 
00937   //If get here, we're running in Active mode, so we use this only for 1-second avg current reading.
00938   if(0 == ++timer)                      //one per second, pull out the accumulated
00939   {                                     // result and update the 64-second list.
00940     sl /= 16;                           //divide result by 16, per our math.
00941     sl -= CCIoffset;        //subtract the offset calibration value
00942     CCarray_AddSample((signed int) sl); //extract the properly scaled current value.
00943     sl = 0;                             //clear the accumulator
00944   }
00945 
00946   // Now, accumulate SOME of the available samples, specifically, (10.74 x 16) of them,
00947   //  to ultimately yield a 1mA-per-bit current measurement in the accumulator variable "sl".
00948 
00949   temp = mod_remainder;                 //(this auto-clears the upper byte, by promotion.)
00950   temp += CCI_CAL;                      //if this addition causes a CARRY, then USE this sample.
00951   mod_remainder = (unsigned char) temp; //Re-save.
00952 
00953   if(temp>>8)   //was a CARRY generated?
00954   {
00955     temp = CADIC;                       //grab the result
00956 //    temp -= CCIoffset;     // remove offset from each sample, different value than the one currently used/calibrated
00957     LatestCCI = temp;                   //update the globally-available value
00958     sl += temp;
00959   }
00960   else
00961     LatestCCI = CADIC;                  //update the globally-available value
00962 }
00963 
00964 
00965 
00966 
00967 /* =====================================================================================
00968    =====================================================================================
00969    ===================================================================================== */
00970 
00971 
00972 
00973 
00974 
00975 
00976 /* ******************************************************************** *
00977  * Coulomb Counter, Regular Current ISR
00978  *
00979  * This interrupt is tripped when the discharge current exceeds the amount
00980  * specified in the CADRDC register (set up by the CCmode() function).
00981  * When this fires, it indicates that the system needs to switch back to
00982  * Active Mode.
00983  *
00984  * ******************************************************************** */
00985 
00986 #pragma vector = CCADC_REG_CUR_vect
00987 __interrupt void CC_RegularCurrent_ISR(void)
00988 {
00989   ChangePowerMode(POWERMODE_ACTIVE,0);
00990 }
00991 
00992 
00993 
00994 
00995 
00996 
00997 
00998 
00999 /* =====================================================================================
01000    =====================================================================================
01001    ===================================================================================== */
01002 
01003 
01004 /* ********************************************************************
01005  * Coulomb Counter, Accumulator ISR
01006  *
01007  *! \todo
01008  * This interrupt is the main mechanism for collecting charge estimates
01009  * for the 'fuel gauge' function.  Note that CCADC offset error must also
01010  * be corrected here, and temperature should be taken into account in
01011  * subsequent calculations as well.
01012  *
01013  * ******************************************************************** */
01014 
01015 //unsigned char ccindex = 0;
01016 //long ccarray[256];
01017 
01018 #pragma vector = CCADC_ACC_vect
01019 __interrupt void CC_Accumulator_ISR(void)
01020 {
01021   union {signed long acc; unsigned char byte[4];} lastCCreading;
01022 
01024 /*
01025   lastCCreading.byte[0] = CADAC0;       //grab the ACC value
01026   lastCCreading.byte[1] = CADAC1;       //grab the ACC value
01027   lastCCreading.byte[2] = CADAC2;       //grab the ACC value
01028   lastCCreading.byte[3] = CADAC3;       //grab the ACC value
01029 */
01030   lastCCreading.acc = CADAC;  // Only works with IAR 4.10 and later
01031 
01032   lastCCreading.acc -= (signed long) CCoffset;  //although the CC's offset is temperature-dependent
01033                                 // we have NOT implemented thermal compensation.
01034 
01035   if(CC_delay_acc)
01036   {
01037     CC_delay_acc--;
01038     return;
01039   }
01040 
01041   RunningAcc += lastCCreading.acc;      //merge this sample with the main accumulator.
01042 
01043 }
01044 
01045 
01046 
01047 
01048 
01049 /* =====================================================================================
01050    =====================================================================================
01051    ===================================================================================== */
01052 
01053 
01054 
01055 
01056 
01057 
01058 /* General description of ADC usage:
01059  *
01060  * The ADC handles 10 different inputs.  We trigger a periodic
01061  * scan through all the channels back-to-back by a timer.
01062  * Then, when desired, the user can request a particular measurement,
01063  * according to its function group, and will get back a properly
01064  * scaled and calibrated value.  For instance, reading a temperature
01065  * will scale the ADC result including gain and offset; when reading
01066  * a particular cell's voltage, it will be corrected according to the
01067  * offset and gain for that particular channel.
01068  *
01069  * When reading cell voltages, we must ensure that cell balancing FETs
01070  * are disabled.
01071  *
01072  * The time required for fully charging a cell's input bypass cap to
01073  * within 1/4000th of full-scale (about 1mV error, max.) is approx.
01074  * 8 time-constants (not 9, since the cap starts at cellV/2 rather
01075  * than at 0.00V).  In the configuration we use, we have a 0.1uF with
01076  * a 500ohm resistor, giving a TC of 50uS.  400uS is therefore required
01077  * before commencing a reading of any cell's ADC channel.  We accomplish
01078  * this delay without cost by reading the other channels first, THEN
01079  * reading the cell inputs.
01080  *
01081 */
01082 
01083 
01084 
01085 
01086 
01087 
01088 void ADCinit(void)
01089 {
01090 //  char i;
01091 
01092   ReadFactoryCalibration();
01093 
01094   // Note: SLOW_RC_CAL is used in calculations on the Wakeup timer to
01095   // calculate the elapsed time, rather than to adjust its oscillator.
01096 }
01097 
01098 
01099 
01100 
01101 //This routine starts a scan of all 10 ADC channels back-to-back.
01102 // The parameter specifies which Thermistor input to read (0-3).
01103 void StartAdc(unsigned char select)
01104 {
01105   unsigned char temp = (1<<select);
01106 
01107 
01109   DIDR0 = 0x0F;                                         //disable ADC0-3 digital input buffer
01110 
01111   PORTA &= 0xE0;
01112   PORTA |= (1<<4);              //drive PA4 high!
01113   DDRA  &= 0xE0;
01114   DDRA  |= (1<<4) | temp;       //make PA4 and the selected signal be Outputs
01115 
01116   VADMUX = 5;                                           //set up for channel 5 first.
01117   VADCSR = (1<<VADEN) | (1<<VADCCIF);                   //clear any pending int
01118   VADCSR = (1<<VADEN) | (1<<VADSC) | (1<<VADCCIE);      //Start the first conversion & ena int's
01119 }
01120 
01121 
01122 
01123 // Automatically scan through all ADC channels.
01124 // When all 10 are done, this interrupt disables itself.
01125 #pragma vector = VADC_vect
01126 __interrupt void ADC_INT(void)
01127 {
01128   unsigned char temp;
01129   static char counter;  //added to handle Rev E silicon errata.
01130 
01131   temp = VADMUX;
01132 
01133   ADCbuffer[temp-1] = VADC;     //save the result
01134 
01135   if((temp <= 4) && (temp >= 1))        //just read a Cell?
01136     cell_current[temp-1] = LatestCCI;   //save its associated CCI reading for impedance.
01137 
01138   if(temp == 5) {                       // init counters before getting there during scan.
01139     counter = VPTAT_READINGS; // per errata for Rev E silicon
01140     temp++;
01141   }
01142 
01143   if((temp == 6)||(temp == 7)) {  //
01144     if(--counter == 0) {                  // wait for VPTAT to stabilize, per errata for rev.E
01145       temp++;
01146       counter = ADC0_READINGS;    // wait for VREF to stabilize after VPTAT readings
01147     }
01148   } else {
01149     temp++;
01150     if(10 == temp) {         // earliest possible 8, latest 10, if earlier use 6/7 ifs above
01151       DisableCellBalancing();   //if more than 519uS is needed, we can do this earlier than 10!
01152     } else {
01153       if(10 < temp) {                   // continue with cell1-4?
01154         temp = 1;
01155       } else {
01156         if(5 == temp) {   //have we scanned ALL channels now?
01157           SetADCScanDone;               //flag Mainline that we have new samples!
01158           EnableCellBalancing();
01159           VADCSR = 0;           //disable this ADC and its Interrupt.
01160           return;
01161         }
01162       }
01163     }
01164   }
01165 
01166   VADMUX = temp;
01167   VADCSR |= (1<<VADSC);         //start next conversion now.
01168 }
01169 
01170 
01171 
01172 
01173 //This should be called from main().
01174 //After an ADC scan, this will calculate new results.
01175 //This needs to know the state from the thermistor state machine.
01176 void CalculateADCresults(void)
01177 {
01178   unsigned long calc;
01179   unsigned char index;
01180 //  unsigned int fixedV;
01181   unsigned int thermV;
01182 
01183   //Calculate new cell voltages
01184   for(index = 0; index < PACKSTACK; index++)
01185   {
01186     calc = ADCbuffer[index]<<2;
01187     calc = calc * ADCgain[index];
01188     calc >>= 16;
01189     CellV[index] = (unsigned int)(calc);
01190     if((unsigned int)calc < CELL_TOOLITTLEV)
01191       DoShutdown(SHUTDOWN_REASON_UNDERVOLTAGE);         //go to Shutdown Mode if cell is drained!
01192     if((unsigned int)calc > CELL_TOOMUCHV)
01193       DoShutdown(SHUTDOWN_REASON_OVERVOLTAGE);          //go to Shutdown Mode if cell is too full!
01194   }
01195 
01198 
01199   //Calculate new ADC4 (PA4) reading due to its scale factor of ~0.2X
01200   calc = ADCbuffer[4];
01201   calc = calc * 344;
01202   VPA4 = (unsigned int) (calc >> 8);
01203 
01204 
01205   //Calculate on-chip temperature. There may be ways to optimize this,
01206   // such as pre-scaling VTgain up by 2.5X, then scaling ADCbuffer[6] by 4X,
01207   // so that when multiplied you get 10X. Can then shift the result UP by 2,
01208   // then grab the upper two bytes as the result. Not sure if VTgain can be
01209   // scaled by 2.5X and still fit in an INT.  Doing this optimization would save a long div.
01210   calc = ADCbuffer[5];
01211   calc = calc * VTgain;                 //this gives 'K * 2^14
01212   OnChipTemp = (unsigned int) (calc / 1638);    //divide by (2^14 / 10) to get 0.1'K
01213 
01214 
01215   //Update the result for the thermistor that was just checked.
01216   index = ThermistorSelect;     //ThermistorSelect says which output was low.
01217   index++;                      //Go to next channel & use that reading as midpoint.
01218   index &= 0x03;
01219   thermV = ADCbuffer[index+7];
01220   Thermistor[ThermistorSelect] = thermV;
01221 }
01222 
01223 
01224 
01225 
01226 // Returns on-chip temperature in 0.1 degrees Kelvin.
01227 // Returns Thermistor resistance reading directly in Ohms (change this as needed).
01228 // channel: 0=on-chip; 1-4 = PA0-3 thermistors
01229 unsigned int ReadTemperature(unsigned char channel)
01230 {
01231 
01232   if(0 == channel)
01233     return OnChipTemp;
01234 
01235 //  temperature = ADCbuffer[6] * VTgain;
01236 //  temperature = temperature / 1638;           // =16384/10
01237 //    temperature -= 2731;                      //convert from 'K to 'C (0'C = 273.15K)
01238 //    return (unsigned int) temperature;
01239 
01240 
01241   if(channel > 5)
01242     return 0;                           //choose what error value you want.
01243 
01245   return Thermistor[channel-1];         //for now, just return the VADC result
01246 
01247 }
01248 
01249 
01250 
01251 //Read the cell voltage in millivolts (always positive). (cell = 1-4)
01252 unsigned int ReadCell(char cell)
01253 {
01254   if((cell == 0) || (cell > 4))
01255     return 0;
01256 
01257   return CellV[cell-1];
01258 }
01259 
01260 
01261 void DisableCellBalancing(void)
01262 {
01263   CBCR = 0;
01264 }
01265 
01266 
01267 void EnableCellBalancing(void)  //if CellToBalance = 0, no balancing req'd.
01268 {
01269   if(CellToBalance)
01270   {
01271     CBCR = (1<<(CellToBalance-1));
01272   }
01273   else
01274     CBCR = 0;
01275 }
01276 
01277 
01278 
01279 

Generated on Mon Nov 12 15:59:58 2007 for AVR453 Smart Battery Reference Design by  doxygen 1.5.3