#REM Li-Ion Battery Management System Master Module. By Peter Perkins Picaxe 28X1 Firmware A3+ PIC16F886 - 240809 - www.150mpg.co.uk - V1.37 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 (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 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 (65335) (Limit of 16bit Word Variable) Maximum Pack Voltage 650v (65335) (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) Maximum OdoMeter Reading 6553.5 Miles to 1/10th Mile (Can be changed to 65535 whole miles) Battery Pack Temp Sensor Range (-55 to +127C) 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 matrix. 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 1676 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 (AlarmBytes b0 & AlarmCount b1 bytes) 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 PackTemp = w4 ;w4 Global (b8,b9) = Battery Block Temperatures symbol LoopCounter = w5 ;w5 Global (b10,b11) = LoopCounter incs each time main loop executes for 1 min averages symbol Charge = w6 ;w6 Global (b12,b13) = Accumulated Charge current for last minute symbol Discharge = w7 ;w7 Global (b14,b15) = Accumulated Discharge current for last minute symbol Soc = w8 ;w8 Global (b16,b17) = Calculated pack capacity (Soc State of Charge) resolution 10ma symbol Distance = w9 ;w9 Global (b18,b19) = Distance (feet) travelled (5280ft = 1 mile) symbol WhMile = w10 ;w10 Global (b20,b21) = Watt Hours per mile (Power Consumed) symbol CountV = w11 ;w11 Local (b22,b23) = General 0-65335 16bit Word Counter/Local Variable (Note CountE & F) symbol CountW = w12 ;w12 Local (b24,b25) = General 0-65335 16bit Word Counter/Local Variable (Note CountC & D) symbol CountX = w13 ;w13 Local (b26,b27) = General 0-65335 16bit Word Counter/Local Variable (Note CountA & B) `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 CountF = b22 ;b22 General 0-255 8bit Byte Counter Local Variable (Note CountV) symbol CountE = b23 ;b23 General 0-255 8bit Byte Counter Local Variable (Note CountV) symbol CountD = b24 ;b24 General 0-255 8bit Byte Counter Local Variable (Note CountW) symbol CountC = b25 ;b25 General 0-255 8bit Byte Counter Local Variable (Note CountW) symbol CountB = b26 ;b26 General 0-255 8bit Byte Counter Local Variable (Note CountX) symbol CountA = b27 ;b27 General 0-255 8bit Byte Counter Local Variable (Note CountX) `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 `*** 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) 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 = 375 ;Normal Maximum permitted cell voltage = (3.75V) (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 = 40000 ;Max cell capacity = 40ah (40000 as 16 bit value) (Max 65A) symbol SocMin = 4000 ;Min cell capacity = 4ah (4000 as 16 bit value) 10% of SocMax 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 = 2 ;Number of times Alarm must occur before Alarm activates symbol InitialSoc = 40000 ;Initial Soc setting, set Eeprom saved Soc to this (Used if program updated) symbol InitialOdo = 0 ;Initial OdoMeter setting, set Eeprom saved Odo to this (Used if program updated) symbol InitialTrip = 0 ;Initial TripMeter setting, set Eeprom saved Trip to this (Used if program updated) `BaudRate constants for 8mhz symbol Baud1200 = N600 ;Low baud rate 1200 at 8mhz (T=True Idle High)(N=Inverted Idle Low) symbol Baud2400 = N1200 ;Med 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 ;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 AvgSpeed = 84 ;Ram address Word data [AvgSpeed] One minute accumulator (Dec 84,85) `*** 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) ;****************** Eeprom Data Storage 0-255 bytes (This does not affect program memory) ******************* `*** Eeprom addresses symbol WhStore = 246 ;EEprom address Word data [WhMile] (Dec 246 + 247) Updated each minute symbol SocStore = 248 ;EEprom address Word data [SOC] (Dec 248 + 249) Updated each minute symbol DistStore = 250 ;EEprom address Word data [Distance] (Dec 250 + 251) Updated each loop symbol OdoStore = 252 ;EEprom address Word data [Odo] (Dec 252 + 253) Updated every 1/10th mile symbol TripStore = 254 ;EEprom address Word data [Trip] (Dec 254 + 255) Updated every 1/10th mile ;************************************************************************************************************* Start: ;Initialise Program. Start Timer, Load Variables setfreq m8 ;Setfreq CPU Freq to 8mhz high Video ;Set Video Output high to enable T9600 Serial Comms with Video Chip 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, set SOC to InitialSoc Soc = InitialSoc ;Set SOC/Capacity (State of charge) to InitialSoc write SocStore, WORD Soc ;Write SOC reading to eeprom for storage when Master Off endif read OdoStore, WORD CountW ;Load last saved OdoMeter reading from eeprom storage if CountW = 0 then ;Test if stored Odo reading is 0, if it is, set Odo to InitialOdo CountW = InitialOdo ;Set Initial Odo reading to the value in constant InitialOdo write OdoStore, WORD CountW ;Write OdoMeter reading to eeprom for storage when Master Off endif read TripStore, WORD CountW ;Load last saved TripMeter reading from eeprom storage if CountW = 0 then ;Test if stored Trip reading is 0, if it is, set Trip to InitialTrip CountW = InitialTrip ;Set Initial Trip reading to the value in constant InitialTrip write TripStore, WORD CountW ;Write TripMeter reading to eeprom for storage when Master Off endif 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 `Display Splash Screen, Software Version Number & Number of Cells in System. serout Video,Baud9600,(27,46,27,67," BMS Master V37 By Peter Perkinswww.150mpg.co.uk") ;Video Display serout Video,Baud9600,(10,13," ** ",#Cells," Cells **") ;Video Display Number Of Cells pause 10000 ;Pause for 5 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 accumulate/display charge/discharge data gosub CheckTemp ;Gosub CheckTemp routine to collect/display battery pack temperature gosub CheckSpeed ;Gosub CheckSpeed routine to calculate/display speed/distance if timer >59 then ;if Timer > 59 (1 Minute has elapsed) gosub CheckSoc ;Gosub CheckSoc calculate [Ah] used/added in last minute gosub CheckDistance ;Gosub CheckDistance calculate [Distance] travelled in last minute LoopCounter = 0 ;Reset LoopCounter to 0 for one minute averages timer = 0 ;Reset timer variable to 0 (Start another 1 minute timing loop) endif gosub MenuButtons ;Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in CountE on CountE gosub StartChg, DisplayB, ResetScreen, SetAlarm, 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 then DisplayAlarms ;If AlarmCounter >= AlarmActivate goto Alarms Display AlarmByte = 0 ;Reset AlarmByte to 0 (Clears any Alarms) goto mainloop ;Goto main program loop ;************************************************************************************************************* ;************************************************************************************************************* CheckVoltage: ;Check Voltage 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 Scratchpad Ram serin [TimeOut,DataError],MasterBus,Baud4800,@ptrinc ;Rxd Data on Master 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 if CountC < VoltageData then ;If VoltageData > CountC then (Current Cell > Highest Cell) CountC = VoltageData ;CountC = VoltageData (Store Current High Cell V in CountC) poke HighCell, CountB ;Store High V Cell number in HighCell Ram Byte endif if CountD > VoltageData then ;If VoltageData < CountD then (Current Cell < Lowest Cell) CountD = VoltageData ;CountD = VoltageData (Store Current Low Cell V in CountD) poke LowCell, CountB ;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, CountB ;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, CountB ;Store Error Cell number in ErrCell Ram Byte endif PackVoltage = PackVoltage + CellVoltage ;Add Cell voltage to accumulated Pack voltage next CountB ;Increment for/next loop and move to next cell `Display Pack Voltage PackVoltage = PackVoltage / 100 ;Get first part of decimal value (Before decimal point) poke VoltsData, b4 ;Store PackVoltage in VoltsData Ram Byte (Note 0-255V Max) serout Video,Baud9600,(27,83,0,0,"Pack Volts ",#PackVoltage," V") ;Video Display `Display Highest Cell Voltage CountX = CountC + DiscardLow ;Highest Cell V Correction factor if CountX > MaxCellVoltage then ;If CountX > MaxCellVoltage then activate Charger & Controller Optos high Charger ;Activate Charger pull down opto high portc led1 ;Activate dash Over-V Led1 endif w2 = CountX / 100 ;Get first part of decimal value (Before decimal point) w4 = CountX // 100 ;Get second part of decimal value (After decimal point) peek HighCell, CountB ;Load CountB with data from HighCell Ram byte serout Video,Baud9600,(27,83,0,1,"HiV [C",#CountB,"] ",27,83,10,1,#w2,".") ;Video Display if w4 <10 then ;Check if leading zero reqd in display serout Video,Baud9600,("0",#w4," V") ;Video Display else serout Video,Baud9600,(#w4," V") ;Video Display endif `Display Lowest Cell Voltage CountX = CountD + DiscardLow ;Lowest Cell V Correction factor if CountX < MinCellVoltage then ;If CountX < 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 w2 = CountX / 100 ;Get first part of decimal value (Before decimal point) w4 = CountX // 100 ;Get second part of decimal value (After decimal point) peek LowCell, CountB ;Load CountB with data from LowCell Ram byte serout Video,Baud9600,(27,83,0,2,"LoV [C",#CountB,"] ",27,83,10,2,#w2,".") ;Video Display if w4 <10 then ;Check if leading zero reqd in display serout Video,Baud9600,("0",#w4," V") ;Video Display else serout Video,Baud9600,(#w4," V") ;Video Display endif return ;Return to main program loop DataError: ;Serial Slave Data Rxd TimeOut, If no Data within (TimeOut = 100ms) poke ErrCell, CountB ;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) CountW = 0 ;Set CountX to 0 CountX = 0 ;Set CountW to 0 for CountE = 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 CountW = CountW + w2 ;Accumulate 10 temp readings in CountW CountX = CountX + w4 ;Accumulate 10 temp readings in CountX next CountE ;Next CountE CountW = CountW / 10 ;Get average temp for last 10 readings CountX = CountX / 10 ;Get average temp for last 10 readings if CountW > 558 then ;Check for Temp < 0C CountD = CountW - 552 / 2 ;Temp over 0C so calculate Temp in C else CountD = 0 ;Temp <= 0C so set Temp to 0C endif if CountX > 558 then ;Check for Temp < 0C CountB = CountX - 556 / 2 ;Temp over 0C so calculate Temp in C else CountB = 0 ;Temp <= 0C so set Temp to 0C endif if CountD > AbsMaxTemp or CountB > AbsMaxTemp then ;If Temp > AbsMaxTemp set AlarmByte AlarmByte = 4 ;Set AlarmByte to 4 (Indicates Temp over AbsMaxTemp condition) endif `Display Temperatures poke Temp1Data, CountB ;Store Front Pack Temp in Temp1Data Ram Byte (Note 0-100C Max) poke Temp2Data, CountD ;Store Rear Pack Temp in Temp2Data Ram Byte (Note 0-100C Max) serout Video,Baud9600,(27,83,0,3,"Temp F",#CountB," R",#CountD," ",27,83,14,3,248,"C") ;Display Temps return ;Return to main program loop ;************************************************************************************************************** CheckCurrent: ;Accumulate (Current in Amps) charge/discharge data CountW = 0 ;Reset Current oversampling accumulator to 0 (Zero) for CountA = 1 to 10 ;Start x10 Current oversampling loop readadc10 CurrentSensor, BatCurrent ;Read present charge/discharge current (0-1023 10bit)(-100A to +100A 0-5V) CountW = CountW + BatCurrent ;Add Current to accumulated Current Next CountA ;Repeat loop until 10 samples obtained BatCurrent = CountW / 10 ;Divide CountW 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-) & ;Convert sensor charge rate to charge rate in Amps Charge = Charge + BatCurrent ;Add Latest sensor Current reading to running 1 minute Charge total CountE = 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+) & ;Convert sensor charge rate to charge rate in Amps Discharge = Discharge + BatCurrent ;Add Latest sensor Current reading to running 1 minute Discharge total CountE = 45 ;Set ascii Character 45 (-) to be displayed if discharging goto ExitCurrent endif BatCurrent = 0 ;If BatCurrent = 512 then set BatCurrent to 0 (Zero) CountE = 32 ;Set ascii Character 32 (Space) to be displayed if no charge/discharge ExitCurrent: inc LoopCounter ;LoopCounter = LoopCounter + 1 `Display Battery Current serout Video,Baud9600,(27,83,0,4,"Current ",CountE," ",#BatCurrent," ",27,83,15,4,"A") ;Video Display `Display Battery State of Charge SOC CountW = Soc / 1000 ;Get first part of decimal value (Before decimal point) poke SocData, b24 ;Store Soc in SocData Ram Byte (Note 0-255Ah Max) 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,(27,83,0,5,"Capacity ",#CountW,".",#CountX,27,83,14,5,"Ah") ;Video Display return ;Return to main program loop ;************************************************************************************************************** CheckSoc: ;Use 1 min accumulated current data to calculate Soc ;Maximum average 1 minute current is 650A if Charge > 0 then ;If no Charge in last minute jump over Charge calculations Charge = Charge / LoopCounter ;Calculate average charge current for last minute Charge = Charge * 100 / 6 ;Calculate Ah added in last minute Soc = Soc + Charge ;Add Ah in last minute to (Soc) Ah 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 / LoopCounter ;Calculate average discharge current for last minute Discharge = Discharge * 100 / 6 ;Calculate Ah used in last minute Soc = Soc - Discharge ;Subtract Ah used in last minute from (Soc) Ah PackCapacity Discharge = 0 ;Reset Discharge Counter to 0 endif write SocStore, WORD Soc ;Write Soc reading to eeprom for storage when Master Off ` write WhStore, WORD WhMile ;Write WhMile reading to eeprom for storage when Master Off return ;Return to main program loop ;************************************************************************************************************** CheckSpeed: ;Pulsin measures VSS pulse. In no pulse in the timeout, result will be 0 pulsin SpeedSensor,1,CountW ;Measure length of a VSS pulse in 5us (8mhz) units (Timeout 0.32768s) if CountW = 0 then JumpSpeed ;If CountW = 0 pulsin has timed out and vehicle is moving at < 1.5mph CountW = CountW / 11 ;Divide CountW by 11 to enable results to fit into 16 bit Integer Maths CountW = 7908 / CountW + 1 ;Returns speed in mph accurate to 1 mph Approx! JumpSpeed: ;Jump here to avoid calculation errors peek AvgSpeed, WORD CountX ;Read in current accumulated speed from RAM WORD AvgSpeed CountX = CountX + CountW ;Add latest speed reading to accumulated speed poke AvgSpeed, WORD CountX ;Store current accumulated speed in RAM WORD AvgSpeed `Display Speed serout Video,Baud9600,(27,83,0,6,"Speed ",#CountW," ",27,83,13,6,"Mph") ;Video Display read OdoStore, WORD CountW ;Load last saved OdoMeter reading from eeprom storage read TripStore, WORD CountV ;Load last saved TripMeter reading from eeprom storage `Display Distance w2 = CountW / 10 ;Get first part of decimal value (Before decimal point) w4 = CountW // 10 ;Get second part of decimal value (After decimal point) serout Video,Baud9600,(27,83,0,7,"OdoMeter ",#w2,".",#w4) ;Video Display w2 = CountV / 10 ;Get first part of decimal value (Before decimal point) w4 = CountV // 10 ;Get second part of decimal value (After decimal point) serout Video,Baud9600,(27,83,0,8,"Trip ",#w2,".",#w4) ;Video Display return ;Return to main program loop ;************************************************************************************************************** CheckDistance: ;Check Distance routine executes once per minute. ;528ft per 1/10th mile / Tyre circumference 5.8ft = 910 revs per mile peek AvgSpeed, WORD CountX ;Read accumulated speed into CountX from RAM WORD AvgSpeed CountX = CountX / LoopCounter ;Get average speed for last minute CountX = CountX * 88 ;Work out feet travelled in last minute (60mph = 88ft per second) Distance = Distance + CountX ;Add Distance travelled to any saved from previous minute read OdoStore, WORD CountW ;Load last saved OdoMeter reading from eeprom storage read TripStore, WORD CountV ;Load last saved TripMeter reading from eeprom storage do while Distance >= 528 ;If distance travelled > 528ft 1/10th mile then execute code in loop inc CountW ;Increment OdoMeter by 1/10th mile inc CountV ;Increment TripMeter by 1/10th mile Distance = Distance - 528 ;Subtract (528ft 1/10th mile) from Distance loop ;Repeat loop until Distance < 528 write DistStore, WORD Distance ;Save remaining Distance (Feet) reading into eeprom storage write OdoStore, WORD CountW ;Write current OdoMeter reading to eeprom for storage when Master Off write TripStore, WORD CountV ;Write current TripMeter reading to eeprom for storage when Master Off CountX = 0 ;Reset accumulated average speed to 0 poke AvgSpeed, WORD CountX ;Store current accumulated speed in RAM WORD AvgSpeed 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, CountB ;Retrieve CountB (CountB = 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 ",#CountB," >AMaxV ",10,13) ;Video Display endif If AlarmByte = 2 then ;(Cell under AbsMin V) serout Video,Baud9600,("Cell ",#CountB," AbsMaxTemp ") ;Video Display endif If AlarmByte = 5 then ;(Alarm test condition set) serout Video,Baud9600,(" Alarm Test! ") ;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 goto MainLoop ;Goto main program loop ;************************************************************************************************************** DisplayB: ;Display Extended Cell BMS Data serout Video,Baud9600,(27,94) ;Video Display Save Current Screen to SV2000 Flash pause 200 ;pause for 100ms at 8mhz 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 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) `Display Cell Voltage serout Video,Baud9600,("Cell ",#CountA," ",#w2,".",#w4,"V ",10,13) ;Video Display pause 250 ;Pause for 125ms Loop1: gosub MenuButtons ;MenuButtons routine gets current button data (0,1,2,3,4,5) value in CountE If CountE <> 1 then Loop1 ;If Button 2 is not pressed goto Loop1 next CountA ;Increment for/next loop and move to next cell Pause 1000 ;Pause for 0.5 seconds serout Video,Baud9600,(10,13,"Press 5 to Exit ") ;Video Display Loop2: gosub MenuButtons ;MenuButtons routine gets current button data (0,1,2,3,4,5) value in CountE Pause 1000 ;Pause for 0.5 seconds If CountE <> 4 then Loop2 ;If Button 5 (Menu) is not pressed goto Loop2 Pause 1000 ;Pause for 0.5 seconds serout Video,Baud9600,(27,95) ;Video Display Restore Current Screen from SV2000 Flash 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 high Alarm ;Activate audible alarm Pause 2000 ;Pause for 1 second at 8mhz low Alarm ;Deactivate audible alarm return ;Return to main loop, charging aborted due to relevant error Bit set endif peek ChgFlag, CountE ;Get Charge Flag Status (0 or 1) from Ram byte into CountE If CountE = 0 then ;If Charge Flag is not set (CountE = 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 else ;If Charge Flag is set (CountE = 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,CountE ;Read ButtonsADC input and get value into Local Byte Variable CountE CountE = CountE + 6 / 42 - 1 ;Convert ADC data to button number 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 Initial Soc Soc = InitialSoc ;Set Soc to Initial Soc write SocStore, WORD Soc ;Write Soc reading to eeprom for storage when Master Off return ;Return to main program ;************************************************************************************************************** ResetScreen: ;Reset Screen to Normal Mode serout Video,Baud9600,(27,46) ;Set Screen to Normal Mode return ;Return to main program ;************************************************************************************************************** TxdData: ;Sends 6 bytes of data to remote display and/or PC Logging unit ;Txd sends preamble to lock with Rxd, then sends qualifier "data" ;followed by 6 bytes of data #REM *** This is an extract from the remote Display unit code to show data txd/rxd order. symbol AlarmByte = b0 ;Global Alarm Type Byte (0-255 number of different Alarms) symbol AlarmCount = b1 ;Global Alarm Counter Byte (0-255 number of times an Alarm has occured) symbol Temp1 = b2 ;Temp1 Sensor LM335 (0-100C) symbol Temp2 = b3 ;Temp2 Sensor LM335 (0-100C) symbol PackVoltage = b4 ;Pack Voltage (0-255V) symbol Soc = b5 ;Pack Capacity (0-255Ah) Rounded down to nearest Ah symbol ErrCell = b6 ;Number of the Cell with any Error condition #ENDREM 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-255Ah Max) peek ErrCell, b6 ;Read Cell with Error from ErrCell Ram Byte serout DriveInhibit, Baud2400, (0x55, 0x55, 0x55) ;Txd preamble pause 10 ;Pause for 5ms to allow serial rxd to clear serout DriveInhibit, Baud2400, ("bms",b0,b1,b2,b3,b4,b5,b6) ;Txd data to remote display (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