#REM Li-Ion Battery Management System Master Module. By Peter Perkins Picaxe 28X1 Firmware A3+ PIC16F886 - 290909 - www.150mpg.co.uk - V1.68 No Scratch **************************** 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 (433mhz Txd Out) (Current Sensor Adc In) Adc 0 -02| |27- Output 6 (Charger Relay Out) (Buttons Adc In) Adc 1 -03| |26- Output 5 (Video Display Out) (Analog Temp1 Adc In) Adc 2 -04| |25- Output 4 (Controller Cutback Out) (Analog Temp2 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 (+5V Supply) (Resonator In) Osc 2 -10| |19- Gnd (- Supply) (I2C Temp Sensor In) Input 0 -11| A |18- Input 7 (Master Data Bus In) (I2C Temp Sensor In) Input 1 -12| 4 |17- Input 6 (Interlocks In) (I2C Temp Sensor In) Input 2 -13| |16- Output c5 (Dash Led 2 Out) (VSS Speed Sensor In) Input 3 -14| |15- Output c4 (Dash Led 1 Out) ----- ************************ Other IC's On Master Pcb ***************************** *********************** Watchdog Picaxe 08M Pinouts *************************** Top _____ (+5V 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 RCA Video IC ********************** Top _____ (Sync Out) Syn -1| ^ |8- +Ve (+5V 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) ----- *********** 16x9 Character Serial 8,N,1 9600 baud display layout ************** 0> > 1> > 2> > 3> > 4> > 5> > 6> > 7> > 8> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 15 ************************ Master Module Specification ************************** Board Supply Voltage 8.00-30.00V DC or as limited by 5.00V 78L05 regulator CPU Supply Voltage 5.00V Picaxe 28X1 CPU Speed 8mhz with internal resonator Picaxe 08M CPU Speed 4mhz with internal resonator Average Board Supply Current at 12.00v <100ma Maximum Serial Master Bus data rate 4800 baud (Picaxe 08M limit at 8mhz) Maximum Cell Capacity Unlimited but adjustment reqd for Soc routines. Maximum Pack Voltage 650v (65535) (Limit of 16bit Word Variable) Maximum Charge/Discharge rate 100A (Allegro Current Sensor limit) (This can be increased to 650A) Maximum 128 Slave Modules per Master Module (Picaxe 28X1 Scratchpad Ram limit) Maximum OdoMeter Reading 65535 Miles Maximum TripMeter Reading 6553.5 Miles to 1/10th Mile DS18B20 I2C Battery Pack Temp Sensor Range (-55 to +127C) LM35 Analaog Pack Temp Sensor Range (0 to +100C) Composite Video Display data rate 9600 baud (SV2000 Serial to Video Chip) Composite RCA Video Monitor Output 1V 75ohm PAL/NTSC The Main display is limited to 16x9 characters in a 0-15 & 0-8 format. The Remote display is limited to 16x2 characters in a 2 line format. Charger relay output is board supply voltage max 30v at 500ma (Note! not 5v) Opto Isolated Charger/Controller Cutback outputs max 50ma 50v ******************************************************************************* ********************* Program Size 1713 out of 4096 Bytes ********************* ******************************************************************************* #ENDREM #picaxe 28x1 ;Set Picaxe type 28X1 for compiler/editor `Variables Constants and I/O definitions `Note Global variables must be preserved throughout the program `Note Local variables may be used at will within subroutines `Variables 16bit (Word) symbol Alarms = w0 ;w0 Global (b0,b1) = Alarms Word (AlarmByte b0 & AlarmCount b1) symbol CellVoltage = w1 ;w1 Local (b2,b3) = Cell Voltage 0-1023 10bit (0-5v) symbol PackVoltage = w2 ;w2 Local (b4,b5) = Calculated pack voltage (Voltage of all Cells added together) symbol BatCurrent = w3 ;w3 Local (b6,b7) = Current Sensor ADC value 0-1023 10bit Approx 200ma resolution symbol Spare1 = w4 ;w4 Local (b8,b9) = Not Used symbol WhMile = w5 ;w5 Global (b10,b11) = Watt Hours per mile (Power Consumed) symbol Odometer = w6 ;w6 Global (b12,b13) = Vehicle odometer miles travelled (0-65535) symbol TripMeter = w7 ;w7 Global (b14,b15) = Vehicle trip meter miles travelled (0-6553.5) symbol Soc = w8 ;w8 Global (b16,b17) = Calculated pack capacity (Soc State of Charge) symbol Distance = w9 ;w9 Global (b18,b19) = Distance (feet) travelled (5280ft = 1 mile) symbol VarD = w10 ;w10 Local (b20,b21) = General 0-65535 16bit Word Counter/Local Variable (VarD1 & VarD2) symbol VarC = w11 ;w11 Local (b22,b23) = General 0-65535 16bit Word Counter/Local Variable (VarC1 & VarC2) symbol VarB = w12 ;w12 Local (b24,b25) = General 0-65535 16bit Word Counter/Local Variable (VarB1 & VarB2) symbol VarA = w13 ;w13 Local (b26,b27) = General 0-65535 16bit Word Counter/Local Variable (VarA1 & VarA2) `Variables 8bit (Byte) symbol AlarmByte = b0 ;b0 Global Alarm Type Byte (0-255 number of different Alarms) symbol AlarmCount = b1 ;b1 Global Alarm Counter Byte (0-255 number of times an Alarm has occured) symbol VoltageData = b2 ;b2 Local Voltage data byte (8 bit value) (Rxd from Slave via serial link) symbol VarD1 = b20 ;b20 General 0-255 8bit Byte Counter Local Variable (Note VarD) symbol VarD2 = b21 ;b21 General 0-255 8bit Byte Counter Local Variable (Note VarD) symbol VarC1 = b22 ;b22 General 0-255 8bit Byte Counter Local Variable (Note VarC) symbol VarC2 = b23 ;b23 General 0-255 8bit Byte Counter Local Variable (Note VarC) symbol VarB1 = b24 ;b24 General 0-255 8bit Byte Counter Local Variable (Note VarB) symbol VarB2 = b25 ;b25 General 0-255 8bit Byte Counter Local Variable (Note VarB) symbol VarA1 = b26 ;b26 General 0-255 8bit Byte Counter Local Variable (Note VarA) symbol VarA2 = b27 ;b27 General 0-255 8bit Byte Counter Local Variable (Note VarA) `AlarmByte (b0) Additional Information (255 possible Alarms) 0 = No Alarms `If AlarmByte = 0 then (No Alarms Set) `If AlarmByte = 1 then (Cell over AbsMax V) `If AlarmByte = 2 then (Cell under AbsMin V) `If AlarmByte = 3 then (Cell data serial transfer timeout error) `If AlarmByte = 4 then (Battery Pack over AbsMax Temp) `If AlarmByte = 5 then (Alarm Test Condition) Used to simulate Alarms `If AlarmByte = 6 then (End Charge & Battery Fault Condition) (Pack Voltage Drop) `*** Special Compiler Variable Notes *** `Timer = Internal timer variable and set to 1 second ticks (t1s_8) `ptr = Scratchpad Ram Variable data pointer (Picaxe 28x1 Scratchpad is 128 bytes) `@ptrinc = Scratchpad Ram pointer with automatic increment after execution. `Constants symbol Cells = 50 ;Number of cells in the battery pack (50) (Max is 128 cells for 28X1 Picaxe) symbol MaxPackVoltage = 18000 ;Maximum pack voltage = 180v (18,000 as 16 bit value) Res 10mv (Max 650v) symbol MinPackVoltage = 12000 ;Minimum pack voltage = 120v (12,000 as 16 bit value) Res 10mv symbol AbsMaxCellVoltage = 380 ;Abs Maximum permitted cell voltage = (3.80V) (Alarm & Shutdown point) symbol AbsMinCellVoltage = 240 ;Abs 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.50V) (Controller/Assist cutback point) symbol CutInV = 360 ;Balancing load/bypass cut in Voltage (This must match value set in Slaves) symbol CutOutV = 355 ;Balancing load/bypass cut out Voltage (This must match value set in Slaves) symbol AbsMaxTemp = 45 ;Abs Maximum permitted pack temperature = (45C) (Alarm & Shutdown point) symbol SocMax = 10000 ;Max cell capacity = 100% (10000 as 16 bit value) symbol SocMin = 1000 ;Min cell capacity = 10% (1000 as 16 bit value) symbol SocUnit = 40 ;SocUnit constant for Soc calculations. for 40ah cells (1 Unit = 40) symbol Delay = 10 ;Interrupt and data delay (Pause 10 = 5ms at 8mhz) symbol DiscardLow = 175 ;Cell correction value 175 or 1.75V added to CellVoltage to recreate correct V symbol TimeOut = 100 ;Serial Data Receive Timeout value 100ms symbol AlarmActivate = 3 ;Number of times Alarm must occur before Alarm activates symbol VoltageDrop = 50 ;Voltage Drop = 50 Pack must drop (500mv) when charging before charger cuts off `BaudRate constants for 8mhz symbol Baud2400 = N1200 ;Min baud rate 2400 at 8mhz (T=True Idle High)(N=Inverted Idle Low) symbol Baud4800 = N2400 ;Max baud rate 4800 at 8mhz (T=True Idle High)(N=Inverted Idle Low) symbol Baud9600 = T4800 ;Video baud rate 9600 at 8mhz (Baud9600 is reserved for 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 Charger = 1 ;Charger Cutback output on Output 1 (Opto conducts when cell V > 3.75V) 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 Controller = 4 ;Controller Cutback output on Output 4 (Opto conducts when cell V < 2.40V) symbol Video = 5 ;Dashboard Video Display Baud9600 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 Led 1 on Output portc 4 symbol Led2 = 5 ;Dashboard Led 2 on Output portc 5 `*** Digital high/low Inputs on port c - Inputs 0-3 & 6-7 *** symbol DigitalTemp1 = 0 ;I2C DS18B20 Battery Pack Temp Sensor on Input 0 (-55 to +125C) symbol DigitalTemp2 = 1 ;I2C DS18B20 Battery Pack Temp Sensor on Input 1 (-55 to +125C) symbol DigitalTemp3 = 2 ;I2C DS18B20 Battery Pack Temp Sensor on Input 2 (-55 to +125C) symbol SpeedSensor = 3 ;VSS Speed sensor pulse sensor on Input 3 (Measures length of a single pulse) symbol Interlocks = 6 ;Interlocks and additional safety/security switch inputs on Input 6 symbol MasterBus = 7 ;Master Data Bus Input 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 symbol ButtonsADC = 1 ;Menu Buttons ADC Input 0-5V ADC on Input 1 (0-5v = 1 of 6 values) (0,1,2,3,4,5) symbol AnalogTemp1 = 2 ;Analog Temp1 Sensor LM335 ADC Input (-55 to +150C) on Input 2 symbol AnalogTemp2 = 3 ;Analog Temp2 Sensor LM335 ADC Input (-55 to +150C) on Input 3 ;************************************* Memory Allocations for 28X1 ******************************************* ;************************************************************************************************************* ;*********************** 128 Bytes Scratchpad Ram Used by Serial data input routine ************************** ;****************** 95 Bytes Spare Ram [80 to 126] ($50 to $7E) & [192 to 239] ($C0 to $EF) ****************** ;*************************************** 255 Bytes Eeprom [0 to 255] ***************************************** ;************************************************************************************************************* ;************************** Information is stored in the ram/eeprom by various routines ********************** ;************************************************************************************************************* `(Peek & Poke used to access RAM) (Read & Write used for EEprom) (Get & Put used to access Scratchpad RAM) `*** Ram addresses symbol ErrCell = 80 ;Ram address Byte data [Error Cell] Cell with error (Dec 80) symbol ChgFlag = 81 ;Ram address Byte data [ChgFlag] Charge in progress (0=Not Charging 1=Charging) (Dec 81) symbol HighCell = 82 ;Ram address Byte data [HighCell] Cell with Highest Voltage (Dec 82) symbol LowCell = 83 ;Ram address Byte data [LowCell] Cell with Lowest Voltage (Dec 83) symbol Charge = 84 ;Ram address Word data [Charge] (Dec 84 & 85) symbol Discharge = 86 ;Ram address Word data [Discharge] (Dec 86 & 87) symbol Seconds = 88 ;Ram address Byte data [Seconds] Stores number of elapsed seconds (Dec 88) symbol Tenths = 89 ;Ram address Byte data [Tenths] (Dec 89) symbol DistRem = 90 ;Ram address Word data [DistRem] (Dec 90 & 91) symbol HighPackV = 92 ;Ram address Word data [HighPackV] (Dec 92 & 93) `*** Ram addresses to store data for wireless 433mhz transmission to remote display symbol Temp1Data = 192 ;Ram address Byte data [AnalogTemp1] (Dec 192) symbol Temp2Data = 193 ;Ram address Byte data [AnalogTemp2] (Dec 193) symbol VoltsData = 194 ;Ram address Byte data [PackVoltage] (Dec 194) symbol SocData = 195 ;Ram address Byte data [Soc] (Dec 195) symbol AmpSign = 196 ;Ram address Byte data [AmpSign] (Dec 196) symbol AmpData = 197 ;Ram address Byte data [AmpData] (Dec 197) symbol AlarmData = 198 ;Ram address Byte data [AlarmData] (Dec 198) ;****************** Eeprom Data Storage 0-255 bytes (This does not affect program memory) ******************* `*** Eeprom addresses symbol PrevOdo = 242 ;EEprom address Word data [PrevOdo] (Dec 242 + 243) symbol PrevSoc = 244 ;EEprom address Word data [PrevSoc] (Dec 244 + 245) symbol WhStore = 246 ;EEprom address Word data [WhMile] (Dec 246 + 247) Stored every 10 seconds symbol SocStore = 248 ;EEprom address Word data [SOC] (Dec 248 + 249) Stored every 10 seconds symbol DistStore = 250 ;EEprom address Word data [Distance] (Dec 250 + 251) Stored every 10 seconds symbol OdoStore = 252 ;EEprom address Word data [Odo] (Dec 252 + 253) Stored every 10 seconds symbol TripStore = 254 ;EEprom address Word data [Trip] (Dec 254 + 255) Stored every 10 seconds ;************************************************************************************************************* Start: ;Initialise Program. Start Timer, Load Variables setfreq m8 ;Setfreq CPU Freq to 8mhz with internal resonator high Video ;Set Video Output high to enable T9600 Serial Comms with Video Chip read OdoStore, WORD Odometer ;Load last saved OdoMeter (Miles) reading from eeprom storage read TripStore, WORD Tripmeter ;Load last saved TripMeter (10ths Mile) reading from eeprom storage 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 read SocStore, WORD Soc ;Load last saved SOC (0-100% = 10,000) reading from eeprom storage write PrevOdo, Word Odometer ;Write Initial Odometer reading to eeprom storage (Used for wh/m Calc) write PrevSoc, Word Soc ;Write Initial Soc reading to eeprom storage (Used for wh/m Calc) `Display Splash Screen, Software Version, CPU Speed & Number of Cells in System. serout Video,Baud9600,(27,46,27,67," BMS Master V68 By Peter Perkinswww.150mpg.co.uk") ;Video Display serout Video,Baud9600,(10,13,"8mhz (",#Cells,") Cells!") ;Video Display pause 8000 ;Pause for 4 seconds at 8mhz serout Video,Baud9600,(27,67) ;Clear Screen settimer t1s_8 ;Set internal (timer) variable to 1 second ticks at 8mhz ;************************************************************************************************************* ;************************************************************************************************************* MainLoop: ;Main program loop toggle WatchDogLed ;Keeps watchdog happy, flashes every other time through loop gosub CheckVoltage ;Gosub CheckVoltage routine to collect/display Cell V data gosub CheckCurrent ;Gosub CheckCurrent routine to calculate/display charge/discharge data gosub CheckTemp ;Gosub CheckTemp routine to collect/display battery temperature gosub CheckSpeed ;Gosub CheckSpeed routine to calculate/display speed/distance gosub MenuButtons ;Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in VarC2 on VarC2 gosub StartChg, SetCalcs, SetAlarm, SetTrip, SetSoc ;Gosub depending on button pressed if AlarmByte > 0 then inc AlarmCount ;Increment Alarm Counter else AlarmCount = 0 ;Reset Alarm Counter endif gosub TxdData ;Gosub TxdData routine Txd data to remote display if AlarmCount >= AlarmActivate and AlarmByte > 0 then ;If AC >= AA & AB > 0 then gosub Alarms Display gosub DisplayAlarms ;Gosub DisplayAlarms endif AlarmByte = 0 ;Reset AlarmByte to 0 (Clears any Alarms) peek Seconds, VarA1 ;Load seconds value into VarA1 from RAM byte inc VarA1 ;Increment VarA1 if VarA1 = 10 then ;Ten seconds have elapsed so store data write OdoStore, WORD Odometer ;Write OdoMeter reading to eeprom storage write TripStore, WORD Tripmeter ;Write TripMeter reading to eeprom storage write DistStore, WORD Distance ;Write Distance reading to eeprom storage write WhStore, WORD WhMile ;Write WhMile reading to eeprom storage write SocStore, WORD Soc ;Write SOC reading to eeprom storage VarA1 = 0 ;Reset 10 second counter endif poke Seconds, VarA1 ;Load seconds value into RAM byte from VarA1 Loop1: if timer <1 then Loop1 ;if Timer <1 (1 Second has not elapsed) timer = 0 ;Reset timer variable to 0 (Start another 1 second timing loop) goto mainloop ;Goto main program loop ;************************************************************************************************************* ;************************************************************************************************************* CheckVoltage: ;Check Voltage subroutine, receive data to calculate pack voltage VarB1 = 255 ;Reset VarB1 to 255 (Used to accumulate lowest Cell V) VarB2 = 0 ;Reset VarB2 to 0 (Used to accumulate highest Cell V) PackVoltage = 0 ;Reset PackVoltage Total to zero = 0V high SlaveBus ;Turn on Message waiting signal (SlaveBus) pause Delay ;Hold Message waiting signal high until Slave has time to respond for VarA1 = 1 to Cells ;Start for/next loop to read cell CellVoltage = 0 ;Clear CellVoltage Variable low SlaveBus ;Turn off Message waiting signal (SlaveBus) serin [TimeOut,DataError],MasterBus,Baud4800,VoltageData ;Rxd Data on Master Bus into VoltageData if VarB2 < VoltageData then ;If VoltageData > VarB2 then (Current Cell > Highest Cell) VarB2 = VoltageData ;VarB2 = VoltageData (Store Current High Cell V in VarB2) poke HighCell, VarA1 ;Store High V Cell number in HighCell Ram Byte endif if VarB1 > VoltageData then ;If VoltageData < VarB1 then (Current Cell < Lowest Cell) VarB1 = VoltageData ;VarB1 = VoltageData (Store Current Low Cell V in VarB1) poke LowCell, VarA1 ;Store Low V Cell number in LowCell Ram Byte endif CellVoltage = CellVoltage + DiscardLow ;CellVoltage (w1) = CellVoltage (w1) (1-255 0.01-2.55V) + (175 1.75V) if CellVoltage > AbsMaxCellVoltage then ;If Cell V > AbsMaxCellVoltage then set AlarmByte to 1 AlarmByte = 1 ;Set AlarmByte to 1 (Indicates Cell over AbsMax Voltage condition) poke ErrCell, VarA1 ;Store Error Cell number in ErrCell Ram Byte endif if CellVoltage < AbsMinCellVoltage then ;If Cell V < AbsMinCellVoltage then set AlarmByte to 2 AlarmByte = 2 ;Set Alarmbyte to 2 (Indicates Cell under AbsMin Voltage condition) poke ErrCell, VarA1 ;Store Error Cell number in ErrCell Ram Byte endif PackVoltage = PackVoltage + CellVoltage ;Add Cell voltage to accumulated Pack voltage next VarA1 ;Increment for/next loop and move to next cell `End Charge and Battery Fault Detection ;Detects end of charge by keeping record of highest pack voltage ;when voltage falls more than (300mv Variable) means charge has ;terminated or battery/bms fault so switches off charger relay peek ChgFlag, VarC2 ;Get Charge Flag Status (0 or 1) from Ram byte into VarC2 if VarC2 = 1 then ;If Charger is operating (1) then Chg End & Batt Chg Fault Detection peek HighPackV, WORD VarA ;Load stored HighPack Voltage data from RAM location into VarA if PackVoltage < VarA then ;If Pack Voltage is less than stored Pack Voltage then VarA = VarA - PackVoltage ;Calculate Voltage Drop result in VarA if VarA > VoltageDrop then ;If Pack Voltage Drop > than permitted Voltage Drop then AlarmByte = 6 ;Set Alarmbyte to 6 (Indicates Pack Voltage dropping) endif else poke HighPackV, WORD PackVoltage ;Load HighPackV data into RAM location from PackVoltage endif endif `Display Pack Voltage VarA = PackVoltage / 100 ;Get first part of decimal value (Before decimal point) VarC = PackVoltage // 100 / 10 ;Get second part of decimal value (After decimal point) poke VoltsData, VarA1 ;Store VarA1 in VoltsData Ram Byte (Note 0-255V Max) serout Video,Baud9600,(27,83,0,0,"Vol ",#VarA,".",#VarC," V ") ;Video Display `Display Highest Cell Voltage VarA = VarB2 + DiscardLow ;Highest Cell V Correction factor if VarA > MaxCellVoltage then ;If VarA > MaxCellVoltage then activate Charger Opto high Charger ;Activate Charger pull down opto high portc led1 ;Activate dash Over-V Led1 else low portc led1 ;De-Activate dash Over-V Led1 endif VarC = VarA / 100 ;Get first part of decimal value (Before decimal point) VarD = VarA // 100 ;Get second part of decimal value (After decimal point) peek HighCell, VarA1 ;Load VarA1 with data from HighCell Ram byte serout Video,Baud9600,(27,83,0,1,"HiV [") ;Video Display if VarA1 < 10 then ;If Leading zero reqd in display due to Value <10 then serout Video,Baud9600,("0") ;Video Display endif if VarD <10 then ;Check if leading zero reqd in display serout Video,Baud9600,(#VarA1,"] ",#VarC,".0",#VarD," V ") ;Video Display else serout Video,Baud9600,(#VarA1,"] ",#VarC,".",#VarD," V ") ;Video Display endif `Display Lowest Cell Voltage VarA = VarB1 + DiscardLow ;Lowest Cell V Correction factor if VarA < MinCellVoltage then ;If VarA < MinCellVoltage then activate Controller Opto high Controller ;Activate Controller pull down opto high portc led2 ;Activate dash Under-V Led2 else low Controller ;De-Activate Controller pull down opto low portc led2 ;De-Activate dash Under-V Led2 endif VarC = VarA / 100 ;Get first part of decimal value (Before decimal point) VarD = VarA // 100 ;Get second part of decimal value (After decimal point) peek LowCell, VarA1 ;Load VarA1 with data from LowCell Ram byte serout Video,Baud9600,(27,83,0,2,"LoV [") ;Video Display if VarA1 < 10 then ;If Leading zero reqd in display due to Value <10 then serout Video,Baud9600,("0") ;Video Display endif if VarD <10 then ;Check if leading zero reqd in display serout Video,Baud9600,(#VarA1,"] ",#VarC,".0",#VarD," V ") ;Video Display else serout Video,Baud9600,(#VarA1,"] ",#VarC,".",#VarD," V ") ;Video Display endif return ;Return to main program loop DataError: ;Serial Slave Data Rxd TimeOut, If no Data within (TimeOut = 100ms) poke ErrCell, VarA1 ;Store Error Cell number in ErrCell Ram Byte AlarmByte = 3 ;Set AlarmByte to 3 (Indicates Cell Serial Data Timeout Error) return ;Return to main program loop ;************************************************************************************************************** CheckTemp: ;Check Battery Pack temperature routine (LM335 Sensors) VarB = 0 ;Set VarB to 0 VarA = 0 ;Set VarA to 0 for VarC2 = 1 to 10 ;10 x Temperature Oversampling readadc10 AnalogTemp1, w2 ;Read R Analog Temp Sensor 1 (0-1023 10bit) into Local var w2 readadc10 AnalogTemp2, w4 ;Read F Analog Temp Sensor 2 (0-1023 10bit) into Local var w4 VarA = VarA + w2 ;Accumulate 10 temp readings in VarA VarB = VarB + w4 ;Accumulate 10 temp readings in VarB next VarC2 ;Next VarC2 VarB = VarB / 10 ;Get average temp for last 10 readings VarA = VarA / 10 ;Get average temp for last 10 readings if VarA > 557 then ;Check for Temp < 0C VarA1 = VarA - 552 / 2 ;Temp over 0C so calculate Temp in C else VarA1 = 0 ;Temp <= 0C so set Temp to 0C endif if VarB > 557 then ;Check for Temp < 0C VarB1 = VarB - 556 / 2 ;Temp over 0C so calculate Temp in C else VarB1 = 0 ;Temp <= 0C so set Temp to 0C endif if VarB1 > AbsMaxTemp or VarA1 > AbsMaxTemp then ;If Temp > AbsMaxTemp set AlarmByte AlarmByte = 4 ;Set AlarmByte to 4 (Indicates Temp over AbsMaxTemp condition) endif `Display Temperatures poke Temp1Data, VarA1 ;Store Front Pack Temp in Temp1Data Ram Byte (Note 0-100C Max) poke Temp2Data, VarB1 ;Store Rear Pack Temp in Temp2Data Ram Byte (Note 0-100C Max) serout Video,Baud9600,(27,83,0,3,"Tem F",#VarA1," R",#VarB1," ",248,"C ") ;Display Temps return ;Return to main program loop ;************************************************************************************************************** CheckCurrent: ;Accumulate (Current in Amps) charge/discharge data VarB = 0 ;Reset Current oversampling accumulator to 0 (Zero) for VarA2 = 1 to 10 ;10 x Current Oversampling readadc10 CurrentSensor, BatCurrent ;Read present charge/discharge current (0-1023 10bit)(-100A to +100A 0-5V) VarB = VarB + BatCurrent ;Add Current to accumulated Current Next VarA2 ;Repeat loop until 10 samples obtained BatCurrent = VarB / 10 ;Divide VarB by 10 to get average current from last 10 samples if BatCurrent <508 then ;If BatCurrent is <508 means system is Charging BatCurrent = 512 - BatCurrent / 3 ;Subtract sensor offset to get a positive number (0-512 = 0-100A-) & VarC2 = 43 ;Set ascii Character 43 (+) to be displayed if charging goto ExitCurrent endif if BatCurrent >512 then ;If BatCurrent is >512 means system is Discharging BatCurrent = BatCurrent - 512 / 3 ;Subtract sensor offset to get a positive number (0-512 = 0-100A+) & VarC2 = 45 ;Set ascii Character 45 (-) to be displayed if discharging goto ExitCurrent endif BatCurrent = 0 ;If BatCurrent = (508 to 512) then set BatCurrent to 0 (Zero) VarC2 = "=" ;Set ascii Character (=) to be displayed if no charge/discharge ExitCurrent: `Display Battery Amps & Motor Power Kw VarA = PackVoltage / 100 * BatCurrent VarB = VarA // 1000 / 100 VarA = VarA / 1000 serout Video,Baud9600,(27,83,0,4,"Amp ",VarC2,#BatCurrent," Kw ",#VarA,".",#VarB," ") ;Video Display poke AmpSign, VarC2 ;Store AmpsSign in AmpSign Ram Byte poke AmpData, b6 ;Store Amps in AmpData Ram Byte CalculateSoc: ;Calculate Soc Routine `1A charge/discharge = 2.7 per second when 1A = 100 `50A charge/discharge = 138.8 per second when 50A = 5,000 `100A charge/discharge = 277.7 per second when 100A = 10,000 `500A charge/discharge = 1388.8 per second when 500A = 50,000 `650A charge/discharge = 1805.5 per second when 650A = 65,000 `For a 40ah Cell 100% Soc is represented by 10,000 which equals 4mah per unit. (40 = 1 unit = 4mah) `For a 100ah Cell 100% Soc is represented by 10,000 which equals 10mah per unit. (100 = 1 unit = 10mah) `For a 200ah Cell 100% Soc is represented by 10,000 which equals 20mah per unit. (200 = 1 unit = 20mah) `SocUnit is a Constant (40 for 40ah cells) (100 for 100ah cells) (200 for 200ah cells) BatCurrent = BatCurrent * 100 / 36 ;Calculate Ah to be added/subtracted in last second if VarC2 = 43 then ;If VarC2 = "+" then Charge calculations peek Charge, WORD VarA ;Load Charge data from RAM location into VarA VarA = VarA + BatCurrent ;Add latest Current data to running Charge (VarA) total do while VarA >= SocUnit ;If VarA >= SocUnit then execute code in loop Soc = Soc + 1 MAX 10000 ;Increment Soc % by one unit (4ma/ah) MAX 10,000 (100%) VarA = VarA - SocUnit ;Subtract (SocUnit) from VarA loop ;Repeat loop until VarA < SocUnit poke Charge, WORD VarA ;Load Charge data VarA into RAM location endif if VarC2 = 45 then ;If VarC2 = "-" then Discharge calculations peek Discharge, WORD VarA ;Load Discharge data from RAM location into VarA VarA = VarA + BatCurrent ;Add latest Current data to running Discharge (VarA) total do while VarA >= SocUnit ;If Discharge >= SocUnit then execute code in loop Soc = Soc - 1 MIN 0 ;Decrement Soc % by one unit (4ma/ah) MIN 0 (0%) VarA = VarA - SocUnit ;Subtract (SocUnit) from VarA loop ;Repeat loop until VarA < SocUnit poke Discharge, WORD VarA ;Load Discharge data VarA into RAM location endif `Display Battery State of Charge SOC in % VarC = Soc / 100 ;Get first part of value (Before decimal point) poke SocData, VarC1 ;Store Soc % in SocData Ram Byte (Note 0-100%) VarA = Soc // 100 ;Get second part of value (After decimal point) serout Video,Baud9600,(27,83,0,5,"Soc ",#VarC,".") ;Video Display if VarA < 10 then ;If Leading zero reqd in display due to Value <10 then serout Video,Baud9600,("0",#VarA," % ") ;Video Display else serout Video,Baud9600,(#VarA," % ") ;Video Display endif return ;Return to main program loop ;************************************************************************************************************** CheckSpeed: ;Pulsin measures VSS pulse. If no pulse result will be 0 pulsin SpeedSensor,1,VarB ;Measure length of a VSS pulse in 5us (8mhz) units (Timeout 0.32768s) if VarB = 0 then JumpSpeed ;If VarB = 0 pulsin has timed out and vehicle is moving at < 1.5mph VarB = VarB / 11 ;Divide VarB by 11 to enable results to fit into 16 bit Integer Maths VarB = 7908 / VarB + 1 ;Returns speed in mph accurate to 1 mph Approx! JumpSpeed: ;Jump here to avoid calculation errors `Display Speed serout Video,Baud9600,(27,83,0,6,"Mph ",#VarB," wh/m ",#WhMile," ") ;Video Display `CheckDistance ;Check Distance routine executes once per minute. ;528ft per 1/10th mile / Tyre circumference 5.8ft = 910 revs per mile ;1mph = 1.4667ft per second. 1mile = 5280ft VarB = VarB * 146 ;Work out feet travelled in last second (1mph = 1.466ft) Distance = Distance + VarB ;Add Distance travelled to any saved from previous loop do while Distance >= 52800 ;If distance travelled > 528ft 1/10th mile then execute code in loop peek Tenths, VarA1 ;Load Tenths data from RAM location into VarA1 inc VarA1 ;Increment by 1 /10th Mile inc Tripmeter ;Increment TripMeter by 1/10th mile if VarA1 = 10 then ;If VarA1 = 10 one mile has been travelled inc Odometer ;Increment OdoMeter by 1 mile `Calculate Miles Remaining read PrevOdo, Word VarA ;Load PrevOdo reading into VarA read PrevSoc, Word VarC ;Load PrevSoc reading into VarC VarA = Odometer - VarA ;Subtract Previous Odo from Current Odo (VarA = Dist in whole miles) VarB = VarC - Soc ;Subtract Current Soc from Previous Soc (VarB = Cap Used) VarC = VarB / VarA ;Divide Capacity used by distance travelled (VarC = Cap/Dist) VarD = Soc / VarC ;Divide Soc by Cap/Dist (VarD = Miles Remaining) poke DistRem, Word VarD ;Load DistRem data into RAM location from VarD `Calculate wh/mile WhMile = VarC * 4 * 16 / 100 MAX 999 ;Calculate wh/mile VarA1 = 0 ;Reset VarA1 (Tenths) counter endif poke Tenths, VarA1 ;Load Tenths data into RAM location from VarA1 Distance = Distance - 52800 ;Subtract (528ft 1/10th mile) from Distance loop ;Repeat loop until Distance < 52800 `Display Odometer, Tripmeter and Charge Flag Status peek DistRem, Word VarD ;Load DistRem data from RAM location into VarD serout Video,Baud9600,(27,83,0,7,"Odo ",#Odometer," Rem ",#VarD," ") ;Video Display peek ChgFlag, VarC2 ;Get Charge Flag Status (0 or 1) from Ram byte into VarC2 VarA = Tripmeter / 10 ;Get first part of decimal value (Before decimal point) VarB = Tripmeter // 10 ;Get second part of decimal value (After decimal point) serout Video,Baud9600,(27,83,0,8,"Tri ",#VarA,".",#VarB," Chg ",#VarC2," ") ;Video Display return ;Return to main program loop ;************************************************************************************************************** DisplayAlarms: ;Alarms turns on led/audible alarms and turns off charger etc ;Action taken depends on AlarmByte (255 different Alarms max!) serout Video,Baud9600,(27,94) ;Video Display Save Current Screen to SV2000 Flash pause 200 ;pause for 100ms at 8mhz serout Video,Baud9600,(27,33,27,67," * BMS Alarms * ",10,13) ;Display Negative, Clear Screen & Display Message peek ErrCell, VarA1 ;Retrieve VarA1 (VarA1 = Cell Number) from ErrCell RAM Byte high Alarm ;Activate audible alarm high Charger ;Activate Charger pull down opto high Controller ;Activate Controller pull down opto low ChargerOnOff ;Turn off Charger main relay poke ChgFlag, 0 ;Set ChgFlag to 0 indicates Charging Off If AlarmByte = 1 then ;(Cell over AbsMax V) serout Video,Baud9600,("Cell ",#VarA1," >AMaxV ") ;Video Display endif If AlarmByte = 2 then ;(Cell under AbsMin V) serout Video,Baud9600,("Cell ",#VarA1," AbsMaxTemp ") ;Video Display endif If AlarmByte = 5 then ;(Alarm test condition set) serout Video,Baud9600,(" Alarm Test! ") ;Video Display endif If AlarmByte = 6 then ;(Pack Voltage Dropped) serout Video,Baud9600,(" Charge End! ") ;Video Display serout Video,Baud9600,(" Voltage Drop ") ;Video Display endif Alarms = 0 ;Reset Alarms Word (AlarmByte & AlarmCount to 0 (Clears any Alarm Flags) pause 8000 ;Pause for 4 seconds at 8mhz low Alarm ;Deactivate audible alarm serout Video,Baud9600,(27,95) ;Video Display Restore Current Screen from SV2000 Flash return ;Return to main program loop ;************************************************************************************************************** SetCalcs: ;Reset BMS Calculations inc wh/mile & dist remaining return ;Return to main program loop ;************************************************************************************************************** StartChg: ;Start Charging Cycle Routine If AlarmByte >0 then ;If any Alarms are set then charge start is aborted return ;Return to main loop, charging aborted due to error set endif peek ChgFlag, VarC2 ;Get Charge Flag Status (0 or 1) from Ram byte into VarC2 If VarC2 = 0 then ;If Charge Flag is not set (VarC2 = 0) then set Charge Flag poke ChgFlag, 1 ;Set ChgFlag = 1 low Charger ;De-activate Charger pull down opto low Controller ;De-activate Controller pull down opto high ChargerOnOff ;Turn on Charger main relay VarA = 0 ;Set VarA to 0 poke HighPackV, WORD VarA ;Load HighPack Voltage data into RAM location from VarA else ;If Charge Flag is set (VarC2 = 1) then clear Charge Flag poke ChgFlag, 0 ;Set ChgFlag = 0 low ChargerOnOff ;Turn off Charger main relay endif return ;Return to main program loop ;************************************************************************************************************** MenuButtons: ;Decodes button Inputs and returns 6 values (0,1,2,3,4,5) readadc ButtonsADC,VarC2 ;Read ButtonsADC input and get value into Local Byte Variable VarC2 VarC2 = VarC2 + 6 / 42 - 1 ;Convert ADC data to button number return ;Return to main program ;************************************************************************************************************** SetTrip: ;Set Trip Meter to Zero Tripmeter = 0 ;Set Tripmeter to Zero write TripStore, WORD Tripmeter ;Write TripMeter reading to eeprom for storage when Master Off return ;Return to main program ;************************************************************************************************************** SetAlarm: ;Set Test Alarm conditon AlarmByte = 5 ;Set AlarmByte to 5 return ;Return to main program ;************************************************************************************************************** SetSoc: ;Set Soc to 100% Soc = 10000 ;Set Soc to 10,000 (100.00%) write SocStore, WORD Soc ;Write Soc reading to eeprom for storage when Master Off return ;Return to main program ;************************************************************************************************************** TxdData: ;Sends 18 bytes of data to remote display and/or PC Logger ;Txd sends 5 byte preamble to lock with Rxd, then qualifier "bms" ;followed by 10 bytes of data peek Temp1Data, b2 ;Read Front Pack Temp from Temp1Data Ram Byte (Note 0-100C Max) peek Temp2Data, b3 ;Read Rear Pack Temp from Temp2Data Ram Byte (Note 0-100C Max) peek VoltsData, b4 ;Read PackVoltage from VoltsData Ram Byte (Note 0-255V Max) peek SocData, b5 ;Read Soc from SocData Ram Byte (Note 0-100%) peek ErrCell, b6 ;Read Cell with Error from ErrCell Ram Byte peek AmpSign, b7 ;Read AmpSign (+ or -) from AmpSign Ram Byte peek AmpData, b8 ;Read AmpData from AmpData Ram Byte peek WhStore, b9 ;Read Wh/Mile Data from WhStore EEprom Byte serout DriveInhibit, Baud2400, (0x55, 0x55, 0x55, 0x55, 0x55) ;Txd 5 byte preamble to clear Rxd pause 10 ;Pause for 5ms to allow serial rxd to clear serout DriveInhibit, Baud2400, ("bms",b0,b1,b2,b3,b4,b5,b6,b7,b8,b9) ;Txd data (2400 baud at 8mhz) return ;Return to main program ;************************************************************************************************************** #REM *** Odd Notes By measuring voltage as a function of current, you can work out the resistance of the battery. R = (V_0 - V_I)/I where V0 is the voltage with 0 current and VI is the terminal voltage when delivering current I to a load. 1 milliohm = 1/1000th of an Ohm #ENDREM