#REM Li-Ion Battery Management System Master Module. By Peter Perkins Picaxe 28X1 - PIC16F886 - 150908 - www.150mpg.co.uk - V18 Video Beta **************************** General Information ****************************** The BMS modules carry no warranty or guarantee of any kind! They are used at your own risk, and I make no claims as to their suitability for a particular function. Prospective users must evaluate the system before using it, and no liability will be entertained by myself in any shape or form whatsoever. The modules and software have been produced at low cost for the benefit of the EV & electronic community. The software is available free via the internet. Users may modify or adapt the system as they see fit. If you are not fully competent to work on potentially lethal battery systems and high voltages, then do not experiment with or use this system. Be aware that vehicle modifications can lead to invalidated insurance and warranty issues. You the end user remain fully liable for any modifications made to your vehicle. ************************** Master Picaxe 28X1 Pinout ************************** Top _____ (Pic Reset) Reset -01| ^ |28- Output 7 (Drive Inhibit Out) (Current Sensor In) Adc 0 -02| |27- Output 6 (Charger Relay Out) (Spare Adc In) Adc 1 -03| |26- Output 5 (Video Display Out) (Spare Adc In) Adc 2 -04| |25- Output 4 (Controller Cutback Out) (Spare Adc In) Adc 3 -05| P |24- Output 3 (WatchDog Led Out) (Program In) Rxd -06| 2 |23- Output 2 (Slave Data Bus Out) (Program Out) Txd -07| 8 |22- Output 1 (Charger Cutback Out) (- Supply) Gnd -08| X |21- Output 0 (Audible Alarm Out) (Resonator In) Osc 1 -09| 1 |20- +Ve (+ Supply) (Resonator In) Osc 2 -10| |19- Gnd (- Supply) (Button A In) Input 0 -11| |18- Input 7 (Master Data Bus In) (Button B In) Input 1 -12| |17- Input 6 (Interlocks In) (Temp Sensors In) Input 2 -13| |16- Output c5 (Charging Led 2 Out) (Speed Sensor In) Input 3 -14| |15- Output c4 (Dash Led 1 Out) ----- ************************ Other IC's On Master Pcb ***************************** *********************** Watchdog Picaxe 08M Pinouts *************************** Top _____ (+ Supply) +Ve -1| ^ |8- Gnd (- Supply) (Program In) Rxd -2| 0 |7- Txd (Program Out) (Watchdog Led Out) Output 4 -3| 8 |6- Output 1 (Audible Alarm Out) (Pulse Count In) Input 3 -4| M |5- Output 2 (Master Reset Out) ----- ****************** SV2000 Serial to Composite Video IC ************************ Top _____ (Sync Out) Syn -1| ^ |8- +Ve (+ Supply) (Video Out) Vid -2| V |7- Nc (No Connection) (Serial Data In) Rxd 4 -3| I |6- Inv (Serial Data Invert Mode) (- Supply) Gnd 3 -4| D |5- Txd (No Connection) ----- ************************ Master Module Specification ************************** Board Supply Voltage 8.00-20.00V DC or as limited by 5.00V 78L05 regulator CPU Supply Voltage 5.00V CPU Speed 8mhz with internal resonator (Pic limit 20mhz with external res) Average Board Supply Current at 12.00v <100ma Maximum Serial Bus data rate 4800 baud (Picaxe 08M limit at 8mhz) Maximum Cell Capacity 65ah (65535) (Limit of 16bit Word Variable) Maximum Pack Voltage 650v (65535) (Limit of 16bit Word Variable) Maximum Charge/Discharge rate 100A (Allegro Current Sensor limit) Maximum 128 Slave Modules per Master Module (Pic Scratchpad Ram limit) Battery Pack Temp Sensor Range (0 to 127C) Composite Video Display data rate 9600 baud (SV2000 Serial to Video Chip) Composite RCA Video Monitor Output 1V 75ohm PAL/NTSC Charger relay output 5.00v at 500ma Opto Isolated Charger/Controller Cutback outputs max 25ma ******************************************************************************* ********************* Program Size 1551 out of 4096 Bytes ********************* ******************************************************************************* #ENDREM `Variables Constants and I/O definitions `Variables 16bit (Word) symbol FlagsWarnings= w0 ;w0 (b0,b1) = Flags and Warnings Word (b0,b1 bytes) (16 bits = 2 x 8bits) symbol CellVoltage = w1 ;w1 (b2,b3) = Cell Voltage 0-1023 10bit (0-5v) symbol PackVoltage = w2 ;w2 (b4,b5) = Calculated pack voltage (Voltage of all Cells added together) symbol Soc = w3 ;w3 (b6,b7) = Calculated pack capacity (Soc State of Charge) resolution 10ma symbol BatCurrent = w4 ;w4 (b8,b9) = Current Sensor ADC value 0-1023 10bit Approx 200ma resolution symbol DutyCycle = w5 ;w5 (b10,b11) = Charger & Controller HPWM Duty Cycle 0 - 100% (0-1023) (+/- 10 = 1%) symbol Charge = w6 ;w6 (b12,b13) = Accumulated Charge current for last minute symbol Discharge = w7 ;w7 (b14,b15) = Accumulated Discharge current for last minute symbol Speed = w8 ;w8 (b16,b17) = Vehicle calculated speed in mph/kph symbol Distance = w9 ;w9 (b18,b19) = Distance (feet) travelled accumulator (5280ft = 1 mile) symbol WhMile = w10 ;w10(b20,b21) = Watt Hours per mile (Power Consumed) symbol TempSoc = w11 ;w11(b22,b23) = Word var split into two bytes (Temp & Soc Counter) symbol CountW = w12 ;w12(b24,b25) = General 0-65535 16bit Word Counter/Local Variable (Note CountC & D) symbol CountX = w13 ;w13(b26,b27) = General 0-65535 16bit Word Counter/Local Variable (Note CountA & B) `Variables 8bit (Byte) symbol CountA = b27 ;b27 = General 0-255 8bit Byte Counter or General/Local Variable (Note CountX) symbol CountB = b26 ;b26 = General 0-255 8bit Byte Counter or General/Local Variable (Note CountX) symbol CountC = b25 ;b25 = General 0-255 8bit Byte Counter or General/Local Variable (Note CountW) symbol CountD = b24 ;b24 = General 0-255 8bit Byte Counter or General/Local Variable (Note CountW) symbol PackTemp = b23 ;b23 = Pack Temperature 1C resolution (0 to 127C) symbol SocCounter = b22 ;b22 = SocCounter increments each time current measured to calc av 1 min current symbol VoltageData = b2 ;b2 = Voltage data byte (8bit value) (Received from Slave via serial link) symbol Warnings = b1 ;b1 = Warning Bits {8 bits} (To clear all Warning bits set b1=0) symbol Flagbits = b0 ;b0 = Flag Bits {8 bits} (To clear all Flags set b0=0) `Note the 16 Bit flags use Byte variables (b0) & (b1) which in turn use Word (w0) `Note to clear all sixteen Flags & Bits in one go set (FlagsWarnings or w0 to Zero) `General Flags x8 1bit (b0) `Note to clear all eight Flags below in one go set (Flagbits or b0 to Zero) symbol WatchFlag = bit0 ;bit0 = Watchdog Flag pulses dash led every other time through Main loop symbol PosNegFlag = bit1 ;bit1 = Pos/Neg Flag used to indicate Charge/Discharge (0=Charge+) (1=Discharge-) symbol ChargeFlag = bit2 ;bit2 = ChargeFlag indicates charging in progress (0=Not Charging) (1=Charging) symbol IlockFlag = bit3 ;bit3 = IlockFlag indicates readiness to drive (0=Ready) (1=Not Ready) Interlocks symbol BalanceFlag = bit4 ;bit4 = BalanceFlag indicates cells are undergoing bypass/balancing (0=Off) (1=On) symbol Unused5 = bit5 ;bit5 = symbol Unused6 = bit6 ;bit6 = symbol Unused7 = bit7 ;bit7 = `Warning Bits x 8 1bit (b1) `Note to clear all eight Warning Bits below in one go set (Warnings or b1 to Zero) symbol Warn1 = bit8 ;bit8 = Warning Bit (Cell over AbsMax V) symbol Warn2 = bit9 ;bit9 = Warning Bit (Cell under AbsMin V) symbol Warn3 = bit10 ;bit10 = Warning Bit (Cell over Max V) symbol Warn4 = bit11 ;bit11 = Warning Bit (Cell under Min V) symbol Warn5 = bit12 ;bit12 = Warning Bit (Cell data serial transfer timeout error) symbol Warn6 = bit13 ;bit13 = Warning Bit (Battery Pack over AbsMax Temp) symbol Warn7 = bit14 ;bit14 = Warning Bit (Battery Pack over Max Temp) symbol Warn8 = bit15 ;bit15 = Warning Bit (Soc < SocMin = less than 5% remaining) `***** EEPROM Data Storage 0-255 bytes used for SOC, Odometer, Wh and other as yet undefined ideas! **** `Data Store Constants symbol SocStore = 0 ;Address in EEPROM at which SOC readings are stored each minute symbol DistStore = 2 ;Address in EEPROM at which Distance readings are stored symbol OdoStore = 4 ;Address in EEPROM at which Odometer readings are stored symbol WhStore = 6 ;Address in EEPROM at which WhMile readings are stored each minute `*** Special Compiler Variable Notes *** `Timer = Internal timer variable and set to 1 second ticks (t1s_8) `ptr = Scratchpad Ram Variable data pointer (Scratchpad is 128 bytes) `@ptrinc = Scratchpad Ram pointer with automatic increment after execution. `Constants symbol Cells = 5 ;Number of cells in the battery pack (5) (Max is 128 cells) symbol MaxPackVoltage = 19000 ;Maximum pack voltage = 190v (19,000 as 16 bit value) Res 10mv (Max 650v) symbol MinPackVoltage = 10000 ;Minimum pack voltage = 100v (10,000 as 16 bit value) Res 10mv symbol AbsMaxCellVoltage = 375 ;Absolute Maximum permitted cell voltage = (3.75V) (Alarm & Shutdown point) symbol AbsMinCellVoltage = 230 ;Absolute Minimum permitted cell voltage = (2.40V) (Alarm & Shutdown point) symbol MaxCellVoltage = 370 ;Normal Maximum permitted cell voltage = (3.70V) (Charger/Regen cutback point) symbol MinCellVoltage = 250 ;Normal Minimum permitted cell voltage = (2.60V) (Controller/Assist cutback point) symbol CutInV = 360 ;Balancing load/bypass cut in Voltage symbol CutOutV = 355 ;Balancing load/bypass cut out Voltage symbol AbsMaxPackTemp = 55 ;Absolute Maximum permitted pack temperature = (55C) (Alarm & Shutdown point) symbol MaxPackTemp = 45 ;Maximum permitted pack temperature = (45C) (Warning & Cutback point) symbol CellCapacity = 40000 ;Nominal cell capacity = 40ah (40,000 as 16 bit value) Res 1ma (Max 65A) symbol SocMin = 2000 ;Minimum cell capacity = 2ah (2000 as 16 bit value) 5% of CellCapacity symbol Delay = 10 ;Interrupt and data delay in milliseconds (10ms) symbol DiscardLow = 175 ;Cell correction value 175 or 1.75V added to CellVoltage to recreate correct V symbol DiscardHigh = 430 ;Cell correction value 430 or 4.30V (Not currently used in Master Software) symbol TimeOut = 100 ;Serial Data Receive Timeout value 100ms symbol PwmFreq = 199 ;Frequency for HPWM outputs (199 = 5khz) (99 = 10khz) (49 = 20khz) symbol PulsePerMile = 455 ;Pulses from speed sensor per mile (4550) / 10 to fit into integer maths `BaudRate constants for 8mhz symbol Baud1200 = T600 ;Baud rate 1200 at 8mhz symbol Baud2400 = T1200 ;Baud rate 2400 at 8mhz symbol Baud4800 = T2400 ;Baud rate 4800 at 8mhz symbol Baud9600 = T4800 ;Baud rate 9600 at 8mhz (Note Baud9600 is reserved for the Video output chip) `Pins used for I/O and designations `*** Digital high/low Outputs on port b - Outputs 0-7 *** symbol Alarm = 0 ;Audible Alarm warning on Output 0 symbol ChargerPWM = 1 ;Charger Cutback PWM output on Output 1 (Opto conducts when cell V > 3.70V) symbol SlaveBus = 2 ;Slave Data Bus Output Baud4800 on Output 2 symbol WatchDogLed = 3 ;Watchdog flashing Green Led on Output 3 (Flashes every other program loop) symbol ControllerPWM = 4 ;Controller Cutback PWM Output on Output 4 (Opto conducts when cell V < 2.20V) symbol Video = 5 ;Dashboard Video display Baud4800 (9600) on Output 5 symbol ChargerOnOff = 6 ;Charger Relay On/Off control on Output 6 (5.00v Max 500ma) symbol DriveInhibit = 7 ;Drive inhibit Opto on Output 7 `*** Extra Digital high/low Outputs on port c 4-5 *** symbol Led1 = 4 ;Dashboard Alarm Led 1 on Output portc 4 symbol Led2 = 5 ;Dashboard Charging Led 2 on Output portc 5 `*** Digital high/low Inputs on port c - Inputs 0-3 & 6-7 *** symbol ButtonA = pin0 ;Dashboard A button on Input 0 symbol ButtonB = pin1 ;Dashboard B button on Input 1 symbol TempSensor = 2 ;I2C DS18B20 Battery Pack Temp Sensors on Input 2 (-55 to +125C) symbol SpeedSensor = 3 ;Speed Sensor 5.00V pulse counter input on Input 3 symbol Interlocks = pin6 ;Interlocks and additional safety switch inputs on Input 6 symbol MasterBus = 7 ;Master Data Bus Input Baud4800 on Input 7 `*** Analogue ADC Inputs *** symbol CurrentSensor = 0 ;Battery Current Sensor 0-5V ADC on Input 0 (+100 to -100A) 2.5v = 0 Amps ;************************************************************************************************************* `*** Program Eprom Data Storage 0-255 bytes used for LCD messages *** `*** Note Display is 16x9 and Messages should be broken up accordingly *** TABLE 0, (27,67) ; 0-1 bytes Store data in eprom table (Clear Screen Command Sequence) TABLE 2, ("*BMS Master V18*") ; 2-17 bytes Store data in eprom table TABLE 18, ("By Peter Perkins") ; 18-33 bytes Store data in eprom table TABLE 34, ("www.150mpg.co.uk") ; 34-50 bytes Store data in eprom table TABLE 51, (" ") ; 51-67 bytes Store data in eprom table `TABLE 68, (" ") ; 68-84 bytes Store data in eprom table `TABLE 85, (" ") ; 85-101 bytes Store data in eprom table `TABLE 102,(" ") ; 102-117 bytes Store data in eprom table `TABLE 118,(" *Pack Low Soc* ") ; 118-133 bytes Store data in eprom table `TABLE 134,(" Cell > AbsMaxV ") ; 134-150 bytes Store data in eprom table `TABLE 151,(" Cell < AbsMinV ") ; 151-167 bytes Store data in eprom table `TABLE 168,(" Cell > MaxV ") ; 168-184 bytes Store data in eprom table `TABLE 185,(" Cell < MinV ") ; 185-201 bytes Store data in eprom table `TABLE 202,("Cell Data Error!") ; 202-217 bytes Store data in eprom table `TABLE 218,(" Pack > AbsMaxT ") ; 218-233 bytes Store data in eprom table `TABLE 234,(" Pack > MaxT ") ; 234-250 bytes Store data in eprom table `TABLE 251,(" ") ; 251-255 bytes Store data in eprom table (End of table 255 bytes) ;************************************************************************************************************* Start: ;Initialise Program. Start Timer, Load Variables, Start Hardware PWM setfreq m8 ;Setfreq CPU Freq to 8mhz serout Video,Baud9600,(" ") ;Clear Display Buffer (Reset Display chip to receive data) for CountA = 0 to 50 ;Start a display loop readtable CountA,CountB ;Read value from data table serout Video,Baud9600,(CountB) ;Transmit data to Video Display next CountA ;Loop to next character pause 2500 ;Wait 1.25 Seconds (8mhz) read SocStore, WORD Soc ;Load last saved Soc reading from eeprom storage if Soc = 0 then ;Test if stored SOC is 0, if it is then reset SOC to CellCapacity Soc = CellCapacity ;Set Initial SOC (State of charge) to CellCapacity (40ah = 40,000) endif gosub CheckTemp ;Gosub CheckTemp routine to get initial battery pack temperature ` read DistStore, WORD Distance ;Load last saved Distance (Feet) reading from eeprom storage ` read WhStore, WORD WhMile ;Load last saved WhMile reading from eeprom storage settimer t1s_8 ;Set internal (timer) variable to 1 second ticks at 8mhz ` hpwm 0,0,%1100,PwmFreq,0 ;Start internal HPWM outputs 1 & 4 at 0% duty cycle serout Video,Baud9600,(27,67,27,70) ;Video Display Clear Screen Command Sequence ;************************************************************************************************************* ;************************************************************************************************************* MainLoop: ;Main program loop If WatchFlag = 0 then ;If WatchFlag = 0 then turn on Led high WatchDogLed ;Turn on Green Led WatchFlag = 1 ;Set WatchFlag to 1 else ;If WatchFlag = 1 then turn off led low WatchDogLed ;Turn off Green Led WatchFlag = 0 ;Set WatchFlag to 0 end if Warnings = 0 ;Reset Warnings Bits gosub CheckCells ;Gosub CheckCells routine to collect Cell V data gosub CheckCurrent ;Gosub CheckCurrent routine to accumulate charge/discharge data ` gosub CheckInterlocks ;Gosub CheckInterlocks routine to evaluate readiness to drive ` gosub CheckSpeed ;Gosub CheckSpeed routine to calculate/speed distance if timer >59 then ;if SocTimer > 59 (1 Minute has elapsed) gosub CheckTemp ;Gosub CheckTemp routine to get battery pack temp at 1 min intervals gosub CalcSoc ;Gosub CalcSoc routine to calculate running Soc at 1 min intervals endif gosub DisplayA ;Gosub Display BMS Data on Video Monitor routine gosub Warning ;Gosub Warning Bits Routine and act on Warnings as reqd If ButtonA = 1 then gosub StartChg ;Gosub Start Charging Routine If ButtonB = 1 then gosub DisplayB ;Goto DisplayB individual Cell Data goto mainloop ;Goto main program loop ;************************************************************************************************************* ;************************************************************************************************************* CheckCells: ;Check Cells subroutine, receive data to calculate pack voltage ptr = 0 ;Reset Scratchpad pointer to 0 (Start of 128 byte Scratchpad Ram) CountC = 0 ;Reset CountC to 0 (Used to accumulate highest Cell V) CountD = 255 ;Reset CountD to 255 (Used to accumulate lowest Cell V) high SlaveBus ;Turn on Message waiting signal (SlaveBus) pause Delay ;Hold Message waiting signal high until Slave has time to respond low SlaveBus ;Turn off Message waiting signal (SlaveBus) for CountB = 1 to Cells ;Start for/next loop to store cell data in 128 byte Scratchpad Ram serin [TimeOut,DataError],MasterBus,Baud1200,@ptrinc ;Receive Data on opto bus into Scratchpad Ram and inc pointer next CountB ;Increment for/next loop and move to next Cell PackVoltage = 0 ;Reset PackVoltage Total to zero = 0V ptr = 0 ;Reset Scratchpad pointer to 0 (Start of 128 byte Scratchpad Ram) for CountB = 1 to Cells ;Start for/next loop to calculate Pack voltage and check cell data/voltage CellVoltage = 0 ;Clear CellVoltage Variable VoltageData = @ptrinc ;Load VoltageData (b2) with value from Scratchpad Ram and inc data pointer CellVoltage = CellVoltage + DiscardLow ;CellVoltage (w1) = CellVoltage (w1) (1-255 0.01-2.55V) + (175 1.75V) if CountC < VoltageData then ;If VoltageData > CountC then (If Current Cell > Highest Cell) CountC = VoltageData ;CountC = VoltageData (Store Current Cell V in CountC) endif if CountD > VoltageData then ;If VoltageData < CountD then (If Current Cell < Lowest Cell) CountD = VoltageData ;CountD = VoltageData (Store Current Cell V in CountD) endif if CellVoltage > AbsMaxCellVoltage then ;If cell V > AbsMaxCellVoltage then set Warning Bit Warn1 = 1 ;Set Warn1 Bit to 1 (Indicates Cell over AbsMax Voltage condition) endif if CellVoltage < AbsMinCellVoltage then ;If cell V < AbsMinCellVoltage then set Warning Bit Warn2 = 1 ;Set Warn2 Bit to 1 (Indicates Cell under AbsMin Voltage condition) endif if CellVoltage > MaxCellVoltage then ;If cell V > MaxCellVoltage then set Warning Bit Warn3 = 1 ;Set Warn3 Bit to 1 (Indicates Cell over Max Voltage condition) endif if CellVoltage < MinCellVoltage then ;If cell V < MinCellVoltage then set Warning Bit Warn4 = 1 ;Set Warn4 Bit to 1 (Indicates Cell under Min Voltage condition) endif PackVoltage = PackVoltage + CellVoltage ;Add Cell voltage to accumulated Pack voltage next CountB ;Increment for/next loop and move to next cell CountX = CountC + DiscardLow ;Highest Cell V Correction factor CountW = CountD + DiscardLow ;Lowest Cell V Correction factor w5 = CountX - CountW ;Take lowest Cell V from highest Cell V (Result in w5 is cell difference) if CountX > CutInV then ;If CountX (Highest Cell V) > Load Cut In V then BalanceFlag = 1 ;Turn on Cells Balancing Flag & Display endif if CountX < CutOutV then ;If CountX (Highest Cell V) < Load Cut Out V then BalanceFlag = 0 ;Turn off Cells Balancing Flag & Display endif return ;Return to main program loop DataError: ;Serial Slave Data Receive TimeOut error routine. ;If no Data received within (TimeOut = 100ms) then execution jumps here high portc led1 ;Activate dash Led1 serout Video,Baud9600,(27,83,0,8," Cell ",#CountB," Error! ") ;Video Display Warn5 = 1 ;Set Warn5 Bit to 1 (Indicates Cell Data Timeout Error) return ;Return to main program loop ;************************************************************************************************************** CheckTemp: ;Check Battery Pack temperature routine readtemp TempSensor, PackTemp ;Read Battery Pack temperature into variable (PackTemp) (+127C to -127C) if PackTemp > 127 then ;Test for Value >127 = Temp <0C PackTemp = 0 ;Set Temp to 0C if less than 0C endif if PackTemp > AbsMaxPackTemp then ;If PackTemp > AbsMaxPackTemp set Warning Bit Warn6 = 1 ;Set Warn6 Bit to 1 (Indicates Pack over AbsMax Temp condition) endif if PackTemp > MaxPackTemp then ;If PackTemp > MaxPackTemp set Warning Bit Warn7 = 1 ;Set Warn7 Bit to 1 (Indicates Pack over Max Temp condition) endif return ;Return to main program loop ;************************************************************************************************************** `CheckInterlocks: ;Check Interlocks Routine for readiness to drive ` ` If Interlocks = 1 then ;If Interlocks pin = 1 (High) then set IlockFlag ` IlockFlag = 1 ;Set IlockFlag to 1 (Indicates Interlock Set condition = Not ready to drive) ` High DriveInhibit ;Set Drive Inhibit output high to disable controller (Prevent Driving) ` else ` IlockFlag = 0 ;Set IlockFlag to 0 (Indicates Interlock Clear condition = Ready to drive) ` Low DriveInhibit ;Set Drive Inhibit output low to enable controller (Allow Driving) ` endif ` ` return ;Return to main program loop ;************************************************************************************************************** CheckCurrent: ;Accumulate (Current in Amps) charge/discharge data readadc10 CurrentSensor, BatCurrent ;Read present charge/discharge current (0-1023 10bit)(-100A to +100A 0-5V) if BatCurrent >512 then ;If BatCurrent is >512 means system is Charging BatCurrent = BatCurrent - 504 ;Subtract sensor offset to get a positive number (0-512 = 0-100A+) ` CountW = BatCurrent * 100 // 512 ;Get second part of decimal value (After decimal point) BatCurrent = BatCurrent * 100 / 504 ;Convert sensor charge rate to charge rate in Amps ` CountW = CountW / 10 ;Discard last digit of result (After decimal point) Charge = Charge + BatCurrent ;Add Latest sensor Current reading to running 1 minute Charge total PosNegFlag = 0 ;Set display indicator flag to (0 = +) Display Ascii Character (43 Decimal) goto ExitCurrent endif if BatCurrent <512 then ;If BatCurrent is <512 means system is Discharging BatCurrent = 512 - BatCurrent ;Subtract sensor offset to get a positive number (0-512 = 0-100A-) ` CountW = BatCurrent * 100 // 512 ;Get second part of decimal value (After decimal point) BatCurrent = BatCurrent * 100 / 512 ;Convert sensor charge rate to charge rate in Amps ` CountW = CountW / 10 ;Discard last digit of result (After decimal point) Discharge = Discharge + BatCurrent ;Add Latest sensor Current reading to running 1 minute Discharge total PosNegFlag = 1 ;Set display indicator flag to (1 = -) Display Ascii Character (45 Decimal) goto ExitCurrent endif ExitCurrent: inc SocCounter ;SocCounter = SocCounter + 1 return ;Return to main program loop ;************************************************************************************************************** CalcSoc: ;Use 1 min accumulated sensor current data to calculate Soc if Charge > 0 then ;If no Charge in last minute jump over Charge calculations Charge = Charge / SocCounter ;Calculate average sensor charge rate for last minute `REM Charge = Charge * 100 / 6 ;This line may be needed with values that exceed 65,000 integer maths Charge = Charge * 1000 / 60 ;Calculate Amount of power/current added in last minute Soc = Soc + Charge ;Add amount of power generated in last minute to (Soc) PackCapacity Charge = 0 ;Reset Charge Counter to 0 endif if Discharge > 0 then ;If no Discharge in last minute jump over Discharge calculations Discharge = Discharge / SocCounter ;Calculate average sensor discharge rate for last minute `REM Discharge = Discharge * 100 / 6 ;This line may be needed with values that exceed 65,000 integer maths Discharge = Discharge * 1000 / 60 ;Calculate Amount of power/current used in last minute Soc = Soc - Discharge ;Subtract amount of power used in last minute from (Soc) PackCapacity Discharge = 0 ;Reset Discharge Counter to 0 endif write SocStore, WORD Soc ;Write Soc reading to eeprom for storage when Master Off ` write DistStore, WORD Distance ;Write Distance reading to eeprom for storage when Master Off ` write WhStore, WORD WhMile ;Write WhMile reading to eeprom for storage when Master Off if Soc < SocMin then ;If Soc < SocMin then less than 10% capacity remaining warn8 = 1 ;Set Warn8 Bit to 1 (Indicates Pack < 10% capacity remaining) else warn8 = 0 ;Set Warn8 Bit to 0 (Indicates Pack > 10% capacity remaining) endif SocCounter = 0 ;Reset SocCounter to 0 timer = 0 ;Reset timer variable to 0 (Start another 1 minute timing loop) return ;Return to main program loop ;************************************************************************************************************** `CheckSpeed: ` CountW = 0 ;Set Local Variable CountW to 0 (Zero) ` Count SpeedSensor,250,CountW ;Measure speed by counting VSS pulses for 250ms (Vehicle Speed Sensor) ` Speed = CountW * 1440 / PulsePerMile ;Calculate Speed in mph ` CountW = CountW * 2112 / PulsePerMile ;Calculate Feet travelled in last second ` Distance = Distance + CountW ;Add distance travelled in last second to running total ` If Distance >= 5280 then ;5280ft per mile / Tyre circumference 5.8ft = 910 revs per mile ` read OdoStore, WORD CountW ;Load last saved Odometer (Mile) reading from eeprom storage ` inc CountW ;Increment Odometer by 1 mile ` write OdoStore, WORD CountW ;Write current Odometer reading to eeprom for storage when Master Off ` Distance = Distance - 5280 ;Subtract (5280ft 1mile) from Distance and start accumulating again ` endif ` return ;Return to main program loop ;************************************************************************************************************** Warning: ;Warnings/Alarms turns on led/audible alarms and turns down charger etc ;Action taken depends on Warning Bits (8 different warnings max!) If Warn1 = 1 then ;bit8 = Warning Bit 1 (Cell over AbsMax V) high DriveInhibit ;Set drive inhibit opto output high high Alarm ;Activate audible alarm high portc led1 ;Activate dash Led1 low ChargerOnOff ;Turn off Charger main relay ChargeFlag = 0 ;Set ChargeFlag to 0 indicates Charging Off serout Video,Baud9600,(27,83,0,8," Cell > AbsMaxV ") ;Video Display pause 2000 ;Pause for one second at 8mhz else low DriveInhibit ;Set drive inhibit opto output low low Alarm ;Deactivate audible alarm low portc led1 ;Deactivate dash Led1 endif If Warn2 = 1 then ;bit9 = Warning Bit (Cell under AbsMin V) high DriveInhibit ;Set drive inhibit opto output high high Alarm ;Activate audible alarm high portc led1 ;Activate dash Led1 serout Video,Baud9600,(27,83,0,8," Cell < AbsMinV ") ;Video Display pause 2000 ;Pause for one second at 8mhz else low DriveInhibit ;Set drive inhibit opto output low low Alarm ;Deactivate audible alarm low portc led1 ;Deactivate dash Led1 endif If Warn3 = 1 then ;bit10 = Warning Bit (Cell over Max V) ` if DutyCycle <= 1013 then ;Check to see if DutyCyle <= 1013 and can be Incremented (Max 1023) ` DutyCycle = DutyCycle + 10 ;Increment DutyCycle by 10 to increase opto conduction by 1% ` endif high portc led1 ;Activate dash Led1 low ChargerOnOff ;Turn off Charger main relay ChargeFlag = 0 ;Set ChargeFlag to 0 indicates Charging Off serout Video,Baud9600,(27,83,0,8," Cell > MaxV! ") ;Video Display pause 2000 ;Pause for one second at 8mhz else low portc led1 ;Deactivate dash Led1 endif If Warn4 = 1 then ;bit11 = Warning Bit (Cell under Min V) high portc led1 ;Activate dash Led1 serout Video,Baud9600,(27,83,0,8," Cell < MinV! ") ;Video Display pause 2000 ;Pause for one second at 8mhz else low portc led1 ;Deactivate dash Led1 endif If Warn5 = 1 then ;bit13 = Warning Bit (Cell data serial transfer timeout error) high DriveInhibit ;Set drive inhibit opto output high high Alarm ;Activate audible alarm high portc led1 ;Activate dash Led1 low ChargerOnOff ;Turn off Charger main relay ChargeFlag = 0 ;Set ChargeFlag to 0 indicates Charging Off else low DriveInhibit ;Set drive inhibit opto output low low Alarm ;Deactivate audible alarm low portc led1 ;Deactivate dash Led1 endif If Warn6 = 1 then ;bit13 = Warning Bit 6 (Battery Pack over Abs Max Temp) high DriveInhibit ;Set drive inhibit opto output high high Alarm ;Activate audible alarm high portc led1 ;Activate dash Led1 low ChargerOnOff ;Turn off Charger main relay ChargeFlag = 0 ;Set ChargeFlag to 0 indicates Charging Off serout Video,Baud9600,(27,83,0,8,"Cell>AbsMaxTemp!") ;Video Display pause 2000 ;Pause for one second at 8mhz else low DriveInhibit ;Set drive inhibit opto output low low Alarm ;Deactivate audible alarm low portc led1 ;Deactivate dash Led1 endif If Warn7 = 1 then ;bit14 = Warning Bit (Battery Pack over Max Temp) high portc led1 ;Activate dash Led1 serout Video,Baud9600,(27,83,0,8," Cell> Max Temp ") ;Video Display pause 2000 ;Pause for one second at 8mhz else low portc led1 ;Deactivate dash Led1 endif If Warn8 = 1 then ;bit15 = Warning Bit (Battery Pack < 5% capacity remaining) high portc led1 ;Activate dash Led1 serout Video,Baud9600,(27,83,0,8," Soc < 5% Left! ") ;Video Display pause 2000 ;Pause for one second at 8mhz else low portc led1 ;Deactivate dash Led1 endif If Warnings = 0 then serout Video,Baud9600,(27,83,0,8," System Nominal ") endif ` hpwmduty DutyCycle ;Set updated HPWM Charger/Controller DutyCycle to adjust output/limit return ;Return to main program loop ;************************************************************************************************************** ;************************************************************************************************************** DisplayA: ;General BMS Data Display Routines serout Video,Baud9600,(27,72) ;Video Display - (Cusror Home Command Sequence) if PosNegFlag = 0 then ;Check Amps (+/-) Display flag and set character to display CountB = 43 ;Set Character (+) to be displayed if charging else CountB = 45 ;Set Character (-) to be displayed if discharging endif CellVoltage = PackVoltage / Cells ;Average Cell Voltage = Total Pack Voltage / Number of Cells (50) serout Video,Baud9600,("** BMS Master **") `****************************************** Battery Current **************************************************** serout Video,Baud9600,("Current ",CountB,#BatCurrent,"A ",10,13) `****************************************** Pack Temperature *************************************************** serout Video,Baud9600,("Temp ",#PackTemp,"C ",10,13) `******************************************** Pack Voltage ***************************************************** CountW = PackVoltage / 100 ;Get first part of decimal value (Before decimal point) CountX = PackVoltage // 100 ;Get second part of decimal value (After decimal point) CountX = CountX / 10 ;Discard last digit of result (After decimal point) serout Video,Baud9600,("Voltage ",#CountW,".",#CountX,"V ",10,13) `**************************************** Average Cell Voltage ************************************************* CountW = CellVoltage / 100 ;Get first part of decimal value (Before decimal point) serout Video,Baud9600,("Average ",#CountW,".") CountX = CellVoltage // 100 ;Get second part of decimal value (After decimal point) If CountX <10 then serout Video,Baud9600,("0") endif serout Video,Baud9600,(#CountX,"V ",10,13) `******************************************* State of Charge *************************************************** CountW = Soc / 1000 ;Get first part of decimal value (Before decimal point) CountX = Soc // 1000 ;Get second part of decimal value (After decimal point) CountX = CountX / 100 ;Discard last two digits of result (After decimal point) serout Video,Baud9600,("Capacity ",#CountW,".",#CountX,"Ah",10,13) `************************************** Cell Voltage Difference ************************************************ serout Video,Baud9600,("Vd .0",#w5,"V ") `************************************************ Speed ******************************************************** ` serout Video,Baud9600,("Mph ",#Speed," ",10,13) `****************************************** Balance On/Off ***************************************************** If BalanceFlag = 1 then serout Video,Baud9600,(27,83,0,7,"Bal On!") else serout Video,Baud9600,(27,83,0,7,"Bal Off") endif `****************************************** Charging On/Off *************************************************** if ChargeFlag = 1 then ;If ChargeFlag = 1 then display mains charging message serout Video,Baud9600,(27,83,9,7,"Chg On!") else serout Video,Baud9600,(27,83,9,7,"Chg Off") endif return ;Return to main program loop ;************************************************************************************************************** DisplayB: ;Display Extended Cell BMS Data ptr = 0 ;Reset Scratchpad pointer to 0 (Start of 128 byte Scratchpad Ram) CountC = 0 ;Reset CountC to 0 (Used to accumulate highest Cell V) CountD = 255 ;Reset CountD to 255 (Used to accumulate lowest Cell V) serout Video,Baud9600,(27,67) ;Video Display Clear Screen Command Sequence for CountA = 1 to Cells ;Start for/next loop to read cell data from 128 byte Scratchpad Ram CellVoltage = 0 ;Clear CellVoltage Variable VoltageData = @ptrinc ;Load VoltageData (b2) with value from Scratchpad Ram and inc data pointer if CountC < VoltageData then ;If VoltageData > CountC then (If Current Cell > Highest Cell) CountC = VoltageData ;CountC = VoltageData (Store Current Cell V in CountC) endif if CountD > VoltageData then ;If VoltageData < CountD then (If Current Cell < Lowest Cell) CountD = VoltageData ;CountD = VoltageData (Store Current Cell V in CountD) endif CellVoltage = CellVoltage + DiscardLow ;CellVoltage (w1) = CellVoltage (w1) (1-255 0.01-2.55V) + (175 1.75V) w2 = CellVoltage / 100 ;Get first part of decimal value (Before decimal point) w4 = CellVoltage // 100 ;Get second part of decimal value (After decimal point) serout Video,Baud9600,("Cell ",#CountA," ",#w2,".",#w4,"V ",10,13) pause 1000 ;Pause for half a second at 8mhz Loop1: If ButtonB = 0 then Loop1 ;If Button B is not pressed goto Loop1 next CountA ;Increment for/next loop and move to next cell CountX = CountC + DiscardLow ;Highest Cell V Correction factor w2 = CountX / 100 ;Get first part of decimal value (Before decimal point) w4 = CountX // 100 ;Get second part of decimal value (After decimal point) serout Video,Baud9600,("Highest ",#w2,".",#w4,"V ",10,13) ;Video Display with Line Feed CountX = CountD + DiscardLow ;Lowest Cell V Correction factor w2 = CountX / 100 ;Get first part of decimal value (Before decimal point) w4 = CountX // 100 ;Get second part of decimal value (After decimal point) serout Video,Baud9600,("Lowest ",#w2,".",#w4,"V ",10,13) ;Video Display with Line Feed pause 2000 ;Pause for one second serout Video,Baud9600,("Press A to Exit ") ;Video Display with Line Feed Loop2: If ButtonA = 0 then Loop2 ;If Button A is not pressed goto Loop2 pause 2000 ;Pause for one second serout Video,Baud9600,(27,67) return ;Return to main program loop ;************************************************************************************************************** ;************************************************************************************************************** StartChg: ;Start Charging Cycle Routine If Warn1 = 1 or Warn6 = 1 or Warn5 = 1 then ;If Warn 1,5,or 6 means Cell> AbsMaxV or AbsMaxTemp & charge start aborted serout Video,Baud9600,(27,83,0,8," Charge Error! ") pause 2000 ;Pause for one second at 8mhz return ;Return to main loop, charging aborted due to relevant error Bit set endif ` DutyCycle = 0 ;Allow charger to start at maximum output Set PWM to 0% ` hpwmduty DutyCycle ;Set updated HPWM Charger/Controller DutyCycle to adjust output/limit ChargeFlag = 1 ;Set ChargeFlag to 1 indicates Charging in progress high ChargerOnOff ;Turn on Charger main relay high portc led2 ;Activate dash Led2 Mains Charging Indicator return ;Return to main program loop