| /* ************************************************************************* *
 *   Individual handlers for SMBus WRITE-type commands
 *
 ************************************************************************* */
 
 
 unsigned char SMBW_MfrAccess(void)        //  0
 {
 unsigned char temp = TW_RxBuf[TW_RxBufIndex++];
 SMBvar_int[SMBV_MfrAccess] = temp | (TW_RxBuf[TW_RxBufIndex]<<8);
 SMBvariables[SMBV_BattStatus][lobyte] &= 0xF0;  //since this cmd is OK, clear Error bits per sbdat110, 4.3.2
 return 0;
 }
 
 
 unsigned char SMBW_RemCapAlm(void)        //  1
 {
 unsigned char temp = TW_RxBuf[TW_RxBufIndex++];
 SMBvar_int[SMBV_RemCapAlm] = temp | (TW_RxBuf[TW_RxBufIndex]<<8);
 SMBvariables[SMBV_BattStatus][lobyte] &= 0xF0;  //since this cmd is OK, clear Error bits per sbdat110, 4.3.2
 return 0;
 }
 
 
 unsigned char SMBW_RemTimeAlm(void)        //  2
 {
 unsigned char temp = TW_RxBuf[TW_RxBufIndex++];
 SMBvar_int[SMBV_RemTimeAlm] = temp | (TW_RxBuf[TW_RxBufIndex]<<8);
 SMBvariables[SMBV_BattStatus][lobyte] &= 0xF0;  //since this cmd is OK, clear Error bits per sbdat110, 4.3.2
 return 0;
 }
 
 
 unsigned char SMBW_BattMode(void)          //  3
 {
 unsigned char tempH = TW_RxBuf[TW_RxBufIndex+1];
 unsigned char tempL;
 
 tempL = SMBvariables[SMBV_BattMode][lobyte] & 0xF0;
 
 if(tempH & 0x1C)
 return SMBerr_AccessDenied;                //attempt to write to reserved bits!
 
 
 if(~(tempL & INTERNAL_CHARGE_CONTROLLER))
 {
 tempH &= ~CHARGE_CONTROLLER_ENABLED;  //feature not present, don't let it get turned on.
 }
 
 
 if(tempH & PRIMARY_BATTERY)
 {
 ;        //let the Host do what it wants with this flag; we ignore it.
 }
 
 //if(tempH & ALARM_MODE)
 //  SetAlarmMode;        //this DISABLES sending alarm msgs for 60 secs
 
 
 if(tempH & CHARGER_MODE)
 {
 ;        //Allow Host to enable us to send Master-mode Charger-Voltage/Current msgs to the Charger
 }
 
 
 if(tempH & CAPACITY_MODE)
 {
 ;        //Host must be allowed to control this bit (report in mAH or 10mWH)
 }
 
 
 SMBvar_int[SMBV_BattMode] = tempL | (tempH<<8);        //write the modified bits.
 
 return 0;
 }
 
 
 
 unsigned char SMBW_AtRate(void)             //  4
 {
 unsigned char temp = TW_RxBuf[TW_RxBufIndex++];
 SMBvar_int[SMBV_AtRate] = temp | (TW_RxBuf[TW_RxBufIndex++]<<8);
 SMBvariables[SMBV_BattStatus][lobyte] &= 0xF0;  //since this cmd is OK, clear Error bits per sbdat110, 4.3.2
 return 0;
 }
 
 unsigned char SMBW_CalibV(void)               // 0x1E
 {
 
 // unsigned char temp = TW_RxBuf[TW_RxBufIndex++];
 //unsigned int tempI = temp|(TW_RxBuf[TW_RxBufIndex] << 8);
 
 return 0; // Return OK.
 }
 
 unsigned char SMBW_CalibC(void)               // 0x1F
 {
 //unsigned int temp = TW_RxBuf[TW_RxBufIndex++];
 //temp = (signed int)(temp | (TW_RxBuf[TW_RxBufIndex]<<8));
 //__disable_interrupt();
 //CalibrateCCoffset_1A((signed int)temp);
 //__enable_interrupt();
 return 0;
 }
 
 unsigned char SMBW_Opt6(void)               // 0x2e  3 addrh addrl optlen
 {
 unsigned int temp = TW_RxBuf[3];
 if(UsePEC == 0)
 return(1);
 Flash_Addr = (temp<<8)|(unsigned int)TW_RxBuf[4];
 if(TW_RxBuf[2] == 3)
 {
 Flash_Cnt = TW_RxBuf[5];
 if((TW_RxBuf[5] == 0xff)&&(Flash_Addr>0x5fff)) // 12 ~ 15 扇区
 {
 EA = 0;
 FlashErase(Flash_Addr);  //0x0000 ~ 0x7fff
 EA = 1;
 }
 }
 //FillWriteStr(&SMBstring[0][0]);
 return 0;
 }
 
 unsigned char SMBW_Opt5(void)               // 0x2F
 {
 unsigned char j=0;
 //__disable_interrupt();
 if(UsePEC == 0)
 return(1);
 EA = 0;
 //MCUSR = 0x1F;                         //clear all reset sources before jumping to bootloader code.
 //  __watchdog_reset;                                        //
 
 //asm("jmp 0x9000");                                        // Byte adress
 
 //  __watchdog_reset;                                                // reset watchdog
 
 
 for(j=0;j<TW_RxBuf[2];j++)
 {
 Flash_Write((Flash_Addr + j), TW_RxBuf[3+j]);
 }
 EA = 1;
 return(0);                                                        // to avoid compiler warning, should never be reached
 
 //  return SMBerr_ReservedCommand; // <-- Compiler generates warning if return statement is missing.
 }
 
 unsigned char SMBW_Opt4(void)               // 0x3C
 {
 //calibration_state_req = TW_RxBuf[TW_RxBufIndex++];
 //calibration_state_req |= (unsigned int)TW_RxBuf[TW_RxBufIndex++] << 8; // Store request details.
 SetCalibRequest; // Set action flag so that main loop starts calibrating.
 return 0; // Return OK.
 }
 
 
 unsigned char SMBW_Opt3(void)               // 0x3D
 {
 return SMBerr_ReservedCommand;
 }
 
 unsigned char SMBW_Opt2(void)               // 0x3E
 {
 return SMBerr_ReservedCommand;
 }
 
 unsigned char SMBW_Opt1(void)               // 0x3F
 {
 return SMBerr_ReservedCommand;
 }
 
 unsigned char SMBW_Invalid(void)        //This should never execute, if error is caught early!
 {
 return SMBerr_AccessDenied;
 }
 
 
 //Table of pointers to functions, indexed from the received SMBus Command byte.
 ptr2funcUC_V SMB_WriteCmd[HIGHEST_SMB_CMD+1] =
 {
 SMBW_MfrAccess,        //  0
 SMBW_RemCapAlm,        //  1
 SMBW_RemTimeAlm,        //  2
 SMBW_BattMode,          //  3
 SMBW_AtRate,             //  4
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,                //0x0F
 
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,                //0x1F
 
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Opt6,
 SMBW_Opt5,               // 0x2F
 
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Invalid,
 SMBW_Opt4,               // 0x3C
 SMBW_Opt3,               // 0x3D
 SMBW_Opt2,               // 0x3E
 SMBW_Opt1               // 0x3F
 };
 
 /* *************************************************************************
 *
 *   Utilities for SMBus commmunications
 *
 ************************************************************************* */
 code const unsigned char crctable[16] =  {0,0x07,0x0E,0x09, 0x1c,0x1b,0x12,0x15, 0x38,0x3F,0x36,0x31, 0x24,0x23,0x2A,0x2D};
 code const unsigned char crctable2[16] =  {0,0x70,0xE0,0x90, 0xC1,0xB1,0x21,0x51, 0x83,0xF3,0x63,0x13, 0x42,0x32,0xA2,0xD2};
 unsigned char FastCRC(unsigned char LastCRC, unsigned char newbyte)
 {
 unsigned char idata index;
 
 index = newbyte;
 index ^= LastCRC;
 index >>= 4;
 LastCRC &= 0x0F;
 LastCRC ^= crctable2[index];
 
 index = LastCRC;
 index ^= newbyte;
 index &= 0x0F;
 LastCRC &= 0xF0;
 LastCRC ^= crctable[index];
 
 return(LastCRC);
 }
 void SMBvariablesFlash(void)
 {
 unsigned char index;
 unsigned char wbuffer[2];
 unsigned char flag;
 
 flag =1;
 while(flag)
 {
 if(1 == flag)
 {
 FlashErase(0x7000);  //0x0000 ~ 0x7fff
 flag = 2;
 //delay();
 }
 Flash_Write((SMB_DataFlashAddr + index*2), SMBvariables[index][hibyte]);
 Flash_Write((SMB_DataFlashAddr + 1 + index*2), SMBvariables[index][lobyte]);
 wbuffer[hibyte] = Flash_Read(SMB_DataFlashAddr + index*2); //扇区14
 wbuffer[lobyte] = Flash_Read(SMB_DataFlashAddr + 1 + index*2); //扇区14
 if((SMBvariables[index][hibyte] != wbuffer[hibyte])||(SMBvariables[index][lobyte] != wbuffer[lobyte]))
 {
 flag = 1;
 index = 0;
 continue;
 }
 if(index > SMBV_SerialNo)
 break;
 //if()
 
 
 }
 
 
 }
 
 //! \todo  This can be modified to load defaults from EEPROM rather than fixed values to the SMB variables.
 // This sets power-up defaults.
 void InitSMBvariables(void)
 {
 unsigned char i;
 
 for(i=0; i<0x20; i++)
 {
 SMBvariables[i][hibyte] = Flash_Read(SMB_DataFlashAddr + i*2); //扇区14
 SMBvariables[i][lobyte] = Flash_Read(SMB_DataFlashAddr + 1 + i*2); //扇区14
 }
 
 if((SMBvariables[SMBV_PackStackWidth] [hibyte]> 4)||(SMBvariables[SMBV_PackStackWidth][hibyte] < 2))
 SMBvariables[SMBV_PackStackWidth ][hibyte] = 3;        //
 if((SMBvariables[SMBV_PackStackWidth][lobyte] > 5)||(SMBvariables[SMBV_PackStackWidth][lobyte] < 1))
 SMBvariables[SMBV_PackStackWidth ] [lobyte]= 2;        //
 if((SMBvar_int[SMBV_DesignCap] > 10000)||(SMBvar_int[SMBV_DesignCap] < 2000))
 SMBvar_int[SMBV_DesignCap ] = 4400;        //
 
 
 //SMBvar_int[SMBV_MfrAccess] = 0x4060;
 if((SMBvar_int[SMBV_RemCapAlm] > 1200)||(SMBvar_int[SMBV_RemCapAlm] < 200))
 SMBvar_int[SMBV_RemCapAlm] = (SMBvar_int[SMBV_DesignCap ] / 10);        // per sbdat110, 4.4.1
 if((SMBvar_int[SMBV_RemTimeAlm] > 20)||(SMBvar_int[SMBV_RemTimeAlm] < 3))
 SMBvar_int[SMBV_RemTimeAlm] = 10;
 SMBvar_int[SMBV_BattMode  ] = 0x0000;
 SMBvar_int[SMBV_AtRate    ] = 0x0000;
 /* For testing with no calcs
 SMBvar_int[SMBV_AtRateTTF ] = 0x0000;        //
 SMBvar_int[SMBV_AtRateTTE ] = 0x0000;        //
 SMBvar_int[SMBV_AtRateOK  ] = 0x0000;        //
 
 SMBvar_int[SMBV_Temperature] = 0x0000;        //
 SMBvar_int[SMBV_Voltage    ] = 0x0000;        //
 SMBvar_int[SMBV_Current    ] = 0x0000;        //
 SMBvar_int[SMBV_AvgCurrent ] = 0x0000;        //
 SMBvar_int[SMBV_MaxError   ] = 0x0000;        //
 SMBvar_int[SMBV_RelSOC     ] = 0x0000;        //
 SMBvar_int[SMBV_AbsSOC     ] = 0x0000;        //
 SMBvar_int[SMBV_RemCap     ] = 0x0000;        //
 
 SMBvar_int[SMBV_FullChgCap] = 0x0000;        //
 SMBvar_int[SMBV_RunTTE    ] = 0x0000;        //
 SMBvar_int[SMBV_AvgTTE    ] = 0x0000;        //
 SMBvar_int[SMBV_AvgTTF    ] = 0x0000;        //
 SMBvar_int[SMBV_ChgCurrent] = 0x0000;        //
 SMBvar_int[SMBV_ChgVoltage] = 0x0000;        //
 */
 if((SMBvar_int[SMBV_ChgCurrent] > 6000)||(SMBvar_int[SMBV_ChgCurrent] < 800))
 SMBvar_int[SMBV_ChgCurrent] = CELL_CCCV_C*SMBvariables[SMBV_PackStackWidth][lobyte];
 if((SMBvar_int[SMBV_ChgVoltage] > 17000)||(SMBvar_int[SMBV_ChgVoltage] < 8000))
 SMBvar_int[SMBV_ChgVoltage] = CELL_CCCV_V*SMBvariables[SMBV_PackStackWidth ][hibyte];
 
 SMBvar_int[SMBV_BattStatus] = 0x0080;        //per sbdat110, 4.4.1
 if(SMBvar_int[SMBV_CycleCount] > 300)
 SMBvar_int[SMBV_CycleCount] = 0x0000;        //per sbdat110, 4.4.1
 
 if((SMBvar_int[SMBV_DesignVolt] > 16800)||(SMBvar_int[SMBV_DesignVolt] < 7000))
 SMBvar_int[SMBV_DesignVolt] = 11100;        //
 
 if((SMBvar_int[SMBV_SpecInfo] != 0x0021)||(SMBvar_int[SMBV_SpecInfo] != 0x0031))
 SMBvar_int[SMBV_SpecInfo  ] = 0x0031;        // no scaling of I or V; we support PEC, and we're V1.1
 if(SMBvar_int[SMBV_MfrDate] > ((2050-1980)<<9)+(12<<5)+(31))
 SMBvar_int[SMBV_MfrDate   ] = ((2015-1980)<<9)+(8<<5)+(31);        //! \todo Fill in current year, month, day. Values are octal
 /* For testing with no calcs
 SMBvar_int[SMBV_SerialNo  ] = 12345;        // arbitrary...
 */
 
 SMBvar_int[SMBV_Current    ] = 0x0000;        //
 SMBvar_int[SMBV_AvgCurrent ] = 0x0000;        //
 SMBvar_int[SMBV_MaxError   ] = 0x0001;        //
 SMBvar_int[SMBV_RelSOC     ] = 0x0000;        //
 SMBvar_int[SMBV_AbsSOC     ] = 0x0000;        //
 SMBvar_int[SMBV_RemCap     ] = 1000;        //
 
 if((SMBvar_int[SMBV_FullChgCap] > SMBvar_int[SMBV_DesignCap ]*2)||(SMBvar_int[SMBV_FullChgCap] < SMBvar_int[SMBV_DesignCap ]/2))
 SMBvar_int[SMBV_FullChgCap] = SMBvar_int[SMBV_DesignCap ];
 
 
 //Note that for the Block-Read variables, we copy those into a RAM buffer only as needed.
 // These are MfrName, DeviceName, DeviceChem, and MfrData.
 
 SetMaxTopAcc((long)SMBvar_int[SMBV_DesignCap ]*3600);   //! \todo for testing, initialized value before reaching fully charged, 6600mAh * 10727
 RunningAcc = (long)1000*3600;    //for testing, to start at other point than 0, 1000 * 10727
 
 }
 
 
 
 |