'------ Li-Ion Battery Management System Master by Peter Perkins Module ------- '------ Picbasic Pro Compiler version PIC16F886 - 130710 - V0.06 Alpha ------- '------ This code requires Master board V2. Please report errors or problems. '------------------------------------------------------------------------------ 'Note this code is for the Pic Basic Pro v2.60A+ internal PM compiler not MPASM! '**************************** 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 PIC 16F886 Pinout ************************** ' Top ' _____ '(Pic Reset) MCLR -01| ^ |28- RB7 ICSP Data & (433mhz Txd Out) '(Current Sensor Adc In) RA0 -02| |27- RB6 ICSP Clk & (Chg Relay Out) '(Buttons Adc In) RA1 -03| |26- RB5 (Video Display Out) '(Analog Temp1 Adc In) RA2 -04| |25- RB4 (Controller Cutback Out) '(Analog Temp2 Adc In) RA3 -05| 1 |24- RB3 (WatchDog Led Out) '(Spare) RA4 -06| 6 |23- RB2 (Slave Data Bus Out) '(Spare) RA5 -07| F |22- RB1 (Charger Cutback Out) '(- Supply) Gnd -08| 8 |21- RB0 (Audible Alarm Out) '(Spare) RA6 -09| 8 |20- +Ve (+5V Supply) '(Spare) RA7 -10| 6 |19- Gnd (- Supply) '(Interlocks) RC0 -11| |18- RC7 (Master Data Bus In) '(VSS Speed Sensor In) RC1 -12| |17- RC6 (Dash Led 1 Out) '(I2C Temp Sensor In) RC2 -13| |16- RC5 (Dash Led 2 Out) '(I2C Eeprom Clock) RC3 -14| |15- RC4 (I2C Eeprom Data) ' ----- '************************ Other IC's On Master Pcb ***************************** '************************* Watchdog PIC 12F683 Pinouts ************************* ' Top ' _____ '(+ Supply) +Ve -1| ^ |8- -Ve (- Supply) '(Not Used) Output 5 -2| 6 |7- Output 0 (Not Used) '(Watchdog Led Out) Output 4 -3| 8 |6- Output 1 (Audible Alarm Out) '(Pulse Count In) Input 3 -4| 3 |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 -4| D |5- Txd (No Connection) ' ----- '******************* AT24C512B 64K Byte I2C Serial EEprom IC ******************* ' Top ' _____ '(Address A0) A0 -1| ^ |8- +Ve (+5V Supply) '(Address A1) A1 -2| M |7- WP (Write Protect) '(Address A2) A2 -3| E |6- Scl (I2C Clock) '(- Supply) Gnd -4| M |5- Scd (I2C Data) ' ----- '************************ Master Module Specification ************************** 'Board Supply Voltage 8.00-30.00V DC or as limited by 5.00V 78L05 regulator 'CPU Supply Voltage 5.00V 'Master Pic 16F886 CPU Speed 8mhz with internal resonator 'Watchdog Pic 12F683 CPU Speed 4mhz with internal resonator (Timeout 65 Seconds) 'Average Board Supply Current at 12.00v <100ma 'Serial Master Bus data rate 9600 baud 'Maximum Cell Capacity 650ah but adjustment will be reqd for Soc routines. 'Maximum Pack Voltage 655.35v (65535) (Limit of 16bit Word Variable) 'Maximum Charge/Discharge rate 100A (Allegro Current Sensor limit) (This can be increased to 650A) 'Maximum 255 Slave Modules per Master Module 'Maximum OdoMeter Reading 65535 Miles 'Maximum TripMeter Reading 6553.5 Miles to 1/10th Mile 'DS18B20 I2C Digital Battery Pack Temp Sensor Range (-55 to +127C) 'LM335 Analog Pack Temp Sensor Range (0 to +100C) 'LM35 Analog Pack Temp Sensor Range (-20 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 (Normally this will be about 12V) 'Opto Isolated Charger/Controller Cutback outputs max 50ma 50v 'Master Command pulses are 0.1ms units in length '************************ Slave Commands ************************************** ' Command 01 = Send Cell Voltage on Master Bus (0.1ms pulse) ' Command 02 = Turn Off Slave Load (Enhanced Mode Only) (0.2ms pulse) ' Command 03 = Turn On Slave Loads as Reqd (Enhanced Mode Only) (0.3ms pulse) ' Command 04 = Increase Load CutIn Voltage by 10mv (0.4ms pulse) ' Command 05 = Decrease Load CutIn Voltage by 10mv (0.5ms pulse) ' Command 06 = Increase Load CutOut Voltage by 10mv (0.6ms pulse) ' Command 07 = Decrease Load CutOut Voltage by 10mv (0.7ms pulse) ' Command 08 = Set Slave Load CutIn/CutOut Voltage Defaults (0.8ms pulse) ' Command 09 = Turn On Slave Load for 0.5 seconds (Flash Led) (0.9ms pulse) ' Command 10 = Set Baud rate to 9600bps (1.0ms pulse) ' Command 11 = Set Baud rate to 2400bps (1.1ms pulse) ' Command 12 = Report Slave Software Version Number (1.2ms pulse) ' Command 13 = Set Slave to Normal mode (1.3ms pulse) ' Command 14 = Set Slave to Enhanced mode (1.4ms pulse) ' Command 15 = Increase Delay Time by 1ms (Max 10ms) (1.5ms pulse) ' Command 16 = Decrease Delay Time by 1ms (Min 0ms) (1.6ms pulse) ' Command 17 = Set Default Delay Time (5ms) (1.7ms pulse) ' Command 18 = Increase FailSafe Cut Off Voltage by 5mv (1.8ms pulse) ' Command 19 = Decrease FailSafe Cut Off Voltage by 5mv (1.9ms pulse) ' Command 20 = Set FailSafe Cut Off Voltage to Default 3.40V (2.0ms pulse) ' Command 21 = Set ADC Output Voltage to 3.70V (Test Mode) (2.1ms pulse) ' Command 22 = Set ADC Output Voltage to Cell Voltage (Default) (2.2ms pulse) '******************************************************************************* '****************** Program Size 7915 Words out of 8192 Words ****************** '******************************************************************************* '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) CellVoltage var Word 'Local = Cell Voltage 0-1023 10bit (0-5v) PackVoltage var Word 'Local = Calculated pack voltage (Voltage of all Cells added together) BatCurrent var Word 'Local = Current Sensor ADC value 0-1023 10bit (0-5v) Approx 200ma resolution I2CAddress var Word 'Global = I2CAddress byte (0-65535) address of data byte to be written WhMile var Word 'Global = Watt Hours per mile (Power Consumed) Odometer var Word 'Global = Vehicle odometer miles travelled (0-65535) TripMeter var Word 'Global = Vehicle trip meter miles travelled (0-6553.5) Soc var Word 'Global = Calculated pack capacity (Soc State of Charge) Distance var Word 'Global = Distance (feet) travelled (5280ft = 1 mile) HighPackV var Word 'Global = High Pack voltage PreLoad var Word 'Global = Timer Preload Word Variable Charge var Word 'Global = [Charge] Discharge var Word 'Global = [Discharge] DistRem var Word 'Global = [DistRem] VarA var Word 'Local = General 0-65535 16bit Word Variable VarB var Word 'Local = General 0-65535 16bit Word Variable VarC var Word 'Local = General 0-65535 16bit Word Variable VarD var Word 'Local = General 0-65535 16bit Word Variable VarE var Word 'Local = General 0-65535 16bit Word Variable R_Temp var Word 'Local = RAW Temperature readings TempC var Word 'Local = Temp in deg C Float var Word 'Local = Holds remainder for + temp C display 'Variables 8bit (Byte) AlarmByte var Byte 'Global = Alarm Type Byte (0-255 number of different Alarms) VoltageData var CellVoltage.LowByte 'Local = Voltage data byte (8 bit value) (Rxd from Slave via serial link) Cells VAR byte 'Global = Number of cells in the battery pack (Max 95) ErrCell var Byte 'Global = [Error Cell] Cell with error ChgFlag var Byte 'Global = [ChgFlag] Charge in progress (0=Not Charging 1=Charging) HighCell var Byte 'Global = [HighCell] Cell with Highest Voltage LowCell var Byte 'Global = [LowCell] Cell with Lowest Voltage QSeconds var Byte 'Global = [QSeconds] Stores number of elapsed 1/4 seconds (250ms) Temp1Data var Byte 'Global = [Temp1] Temperature data Temp2Data var Byte 'Global = [Temp2] Temperature data Tenths var Byte 'Global = [Tenths of a mile] VoltsData var Byte 'Global = [PackVoltage] SocData var Byte 'Global = [Soc] AmpSign var Byte 'Global = [AmpSign + or -] AlarmData var Byte 'Global = [AlarmData] Dummy var Byte 'Local = [Dummy for Div32 temp conversion] Control var Byte 'Global = [BMS Control Byte 8 bits used for BMS Configuration] BD var BYTE[50] 'Define BD as a byte array (50 Bytes) (BD = Battery Data) I2C1 VAR BYTE[8] 'Define I2C1 as a byte array (8 Bytes) (I2C = I2C Temp sensors) I2C2 VAR BYTE[8] 'Define I2C2 as a byte array (8 Bytes) (I2C = I2C Temp sensors) VarA1 var VarA.LowByte 'Local = General 0-255 8bit Byte Variable VarA2 var VarA.HighByte 'Local = General 0-255 8bit Byte Variable VarB1 var VarB.LowByte 'Local = General 0-255 8bit Byte Variable VarB2 var VarB.HighByte 'Local = General 0-255 8bit Byte Variable VarC1 var VarC.LowByte 'Local = General 0-255 8bit Byte Variable VarC2 var VarC.HighByte 'Local = General 0-255 8bit Byte Variable VarD1 var VarD.LowByte 'Local = General 0-255 8bit Byte Variable VarD2 var VarD.HighByte 'Local = General 0-255 8bit Byte Variable 'Variables 1bit (Bit) TMR1IF var PIR1.0 'Global = Overflow flag bit of Timer1 Busy var BIT 'Local = I2C Temp conversion Busy Status-Bit Cold_Bit var R_Temp.Bit11 'Local = Sign-Bit for +/- Temp. 1 = Below 0 deg C TempBit var Control.Bit0 'Global = Temperature Routine (1 = I2C Sensors 0 = Analog Sensors) SuperBit var Control.Bit1 'Global Super Slaves Bit (1 = Normal Slaves 0 = Super Slaves) '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) 'Constants MaxPackVoltage con 18000 'Maximum pack voltage = 180v (18,000 as 16 bit value) Res 10mv (Max 650v) MinPackVoltage con 12000 'Minimum pack voltage = 120v (12,000 as 16 bit value) Res 10mv AbsMaxCellVoltage con 380 'Abs Maximum permitted cell voltage = (3.80V) (Alarm & Shutdown point) AbsMinCellVoltage con 220 'Abs Minimum permitted cell voltage = (2.20V) (Alarm & Shutdown point) MaxCellVoltage con 365 'Normal Maximum permitted cell voltage = (3.65V) (Charger/Regen cutback point) MinCellVoltage con 230 'Normal Minimum permitted cell voltage = (2.30V) (Controller/Assist cutback point) CutInVD con 370 'Balancing load/bypass default cut in Voltage (Must match value set in Slaves) CutOutVD con 365 'Balancing load/bypass default cut out Voltage (Must match value set in Slaves) AbsMaxTemp con 50 'Abs Maximum permitted pack temperature = (50C) (Alarm & Shutdown point) SocMax con 10000 'Max cell capacity = 100% (10000 as 16 bit value) SocMin con 1000 'Min cell capacity = 10% (1000 as 16 bit value) SocUnit con 20 'SocUnit constant for Soc calculations. for 20ah cells (1 Unit = 20) Delay con 2 'Interrupt and data delay (Pause 2 = 2ms) DiscardLow con 175 'Cell correction value 175 or 1.75V added to CellVoltage to recreate correct V TimeOut con 100 'Serial Data Receive Timeout value 100ms VoltageDrop con 50 'Voltage Drop = 50 Pack must drop (500mv) when charging before charger cuts off FailSafeV con 340 'Fail Safe Minimum Load voltage Cut off (<3.40V) Cell resting voltage AT24C512B con %10100000 'Control byte for AT24C512B eeprom %1010ddd0 (ddd) = device select bits 'Pins used for I/O and designations '*** Digital high/low Outputs on PORT B - Outputs 0-7 *** Alarm var PORTB.0 'Audible Alarm warning on Output 0 Charger var PORTB.1 'Charger Cutback output on Output 1 (Opto conducts when cell V > 3.75V) SlaveBus var PORTB.2 'Slave Data Bus Output on Output 2 WatchDogLed var PORTB.3 'Watchdog flashing Green Led on Output 3 (Flashes every other program loop) Controller var PORTB.4 'Controller Cutback output on Output 4 (Opto conducts when cell V < 2.40V) Video var PORTB.5 'Dashboard Video Display on Output 5 ChargerOnOff var PORTB.6 'Charger Relay On/Off control on Output 6 (5.00v Max 500ma) DriveInhibit var PORTB.7 'Drive inhibit Opto on Output 7 '*** Digital high/low Inputs & Outputs on PORT C *** Interlocks var PORTC.0 'Interlocks and additional safety/security switch inputs on Input 0 SpeedSensor var PORTC.1 'VSS Speed sensor pulse sensor on Input 1 (Measures length of a single pulse) DigitalTemp var PORTC.2 'I2C DS18B20 Battery Pack Temp Sensor on Input 2 (-55 to +127C) Sckeeprom var PORTC.3 'I2C EEprom Clock (Symbol not used in program added for completeness only) Scdeeprom var PORTC.4 'I2C EEprom Data (Symbol not used in program added for completeness only) Led2 var PORTC.5 'Dashboard Led 2 on Output portc 5 Led1 var PORTC.6 'Dashboard Led 1 on Output portc 6 MasterBus var PORTC.7 'Master Data Bus Input on Input 7 '*** Analogue ADC Inputs on PORT A *** CurrentSensor var PORTA.0 'Battery Current Sensor 0-5V ADC on Input 0 (+100 to -100A) 2.5v = 0 Amps ButtonsADC var PORTA.1 'Menu Buttons ADC Input 0-5V ADC on Input 1 (0-5v = 1 of 6 values) (0,1,2,3,4,5) AnalogTemp1 var PORTA.2 'Analog Temp1 Sensor LM335 ADC Input (-55 to +150C) on Input 2 AnalogTemp2 var PORTA.3 'Analog Temp2 Sensor LM335 ADC Input (-55 to +150C) on Input 3 'Unallocated4 var PORTA.4 'Spare Output 'Unallocated5 var PORTA.5 'Spare Output 'Unallocated6 var PORTA.6 'Spare Output 'Unallocated7 var PORTA.7 'Spare Output '************************************ Memory Allocations for 16F886 ****************************************** '************************************************************************************************************* '************************************** 368 bytes of Ram available ******************************************* '************************************** 256 Bytes Eeprom [0 to 255] ****************************************** '************************************************************************************************************* '(Peek & Poke used to access RAM) (Read & Write used for EEprom) '****************** Eeprom Data Storage 0-255 bytes (This does not affect program memory) ******************* '*** Eeprom addresses constants Cells0 con 242 'EEprom address Byte data [Cells0] (Dec 242) Control0 con 243 'EEprom address Byte data [Control] (Dec 243) PrevOdo0 con 244 'EEprom address Word data [PrevOdo] (Dec 244) PrevOdo1 con 245 'EEprom address Word data [PrevOdo] (Dec 245) PrevSoc0 con 246 'EEprom address Word data [PrevSoc] (Dec 246) PrevSoc1 con 247 'EEprom address Word data [PrevSoc] (Dec 247) SocStore0 con 248 'EEprom address Word data [SOC] (Dec 248) Stored every 10 seconds SocStore1 con 249 'EEprom address Word data [SOC] (Dec 249) Stored every 10 seconds DistStore0 con 250 'EEprom address Word data [Distance] (Dec 250) Stored every 10 seconds DistStore1 con 251 'EEprom address Word data [Distance] (Dec 251) Stored every 10 seconds OdoStore0 con 252 'EEprom address Word data [Odo] (Dec 252) Stored every 10 seconds OdoStore1 con 253 'EEprom address Word data [Odo] (Dec 253) Stored every 10 seconds TripStore0 con 254 'EEprom address Word data [Trip] (Dec 254) Stored every 10 seconds TripStore1 con 255 'EEprom address Word data [Trip] (Dec 255) Stored every 10 seconds '************************************************************************************************************* @ DEVICE PIC16F886,LVP_OFF 'Turn off low voltage programming @ DEVICE PIC16F886,FCMEN_OFF @ DEVICE PIC16F886,IESO_OFF @ DEVICE PIC16F886,CPD_OFF @ DEVICE PIC16F886,MCLR_ON 'Turn on Master Clear @ DEVICE PIC16F886,WDT_OFF 'Turn off internal Watchdog timer @ DEVICE PIC16F886,INTRC_OSC_NOCLKOUT 'Set internal oscillator with no clock out pins @ DEVICE PIC16F886,PROTECT_OFF 'Turn off code protect Define OSC 8 'Set PicBasic Pro processor speed to 8 Mhz (Must match oscillator value) OSCCON = %01110101 'Internal 8 mhz Osc and stable CM1CON0 = 0 'Comparator Off CM2CON0 = 0 'Comparator Off T1CON = %00111001 'Enable Timer1 prescale 1:8 internal clock source 262ms at 8mhz TRISA = %11111111 'SET PORTA AS INPUTS TRISB = %00000000 'SET PORTB AS OUTPUTS TRISC = %10011111 'SET PORTC AS INPUTS EXCEPT PORTS C5,C6 ANSEL = %00001111 'SET INPUTS AN0-AN3 AS ANALOG INPUTS ANSELH = %00000000 'Disable PortB AD ADCON0 = %01000001 'SETUP ADC CONVERTER MODULE FOSC/8 & ENABLE ADC MODULE on AD0 ADCON1 = %10000000 'SETUP ADC RIGHT JUSTIFY SET REFV to VDD & VSS 'Debug is used as it is the fastest and smallest serial routine to drive the video display DEFINE DEBUG_REG PORTB 'Set Debug pin port DEFINE DEBUG_BIT 5 'Set Debug pin bit DEFINE DEBUG_BAUD 9600 'Set Debug baud rate DEFINE DEBUG_MODE 0 'Set Debug mode: 0 = true, 1 = inverted DEFINE PULSEIN_MAX 20000 'Set PULSIN maximum count to 20,000 (100ms) (Range 0-65535 x 5us units) include "modedefs.bas" 'Allows the use of the serin/serout command '************************************************************************************************************* Start: 'Initialise Program. Start Timer, Load Variables CLEAR 'Clear all variables to 0 PORTB = %00100000 'Set PortB outputs to off except PortB.5 PORTC.5 = 0 'Set PortC.5 output to off PORTC.6 = 0 'Set PortC.6 output to off read Cells0, Cells 'Load Cells Byte from eeprom storage (Number of Cells in pack) if Cells = 255 then 'Set's default number of cells to 50 if nothing stored in eeprom Cells = 50 'Default number of Cells (50) WRITE Cells0, Cells 'Load Cells Byte into eeprom storage (Number of cells in pack) endif read Control0, Control 'Load BMS Control Byte from eeprom storage read OdoStore0, Odometer.Byte0 'Load last saved OdoMeter (Miles) reading from eeprom storage read OdoStore1, Odometer.Byte1 'Load last saved OdoMeter (Miles) reading from eeprom storage read TripStore0, Tripmeter.Byte0 'Load last saved TripMeter (10ths Mile) reading from eeprom storage read TripStore1, Tripmeter.Byte1 'Load last saved TripMeter (10ths Mile) reading from eeprom storage read DistStore0, Distance.Byte0 'Load last saved Distance (Feet) reading from eeprom storage read DistStore1, Distance.Byte1 'Load last saved Distance (Feet) reading from eeprom storage read SocStore0, Soc.Byte0 'Load last saved SOC (0-100% = 10,000) reading from eeprom storage read SocStore1, Soc.Byte1 'Load last saved SOC (0-100% = 10,000) reading from eeprom storage write PrevOdo0, Odometer.Byte0 'Write Initial Odometer reading to eeprom storage (Used for wh/m Calc) write PrevOdo1, Odometer.Byte1 'Write Initial Odometer reading to eeprom storage (Used for wh/m Calc) write PrevSoc0, Soc.Byte0 'Write Initial Soc reading to eeprom storage (Used for wh/m Calc) write PrevSoc1, Soc.Byte1 'Write Initial Soc reading to eeprom storage (Used for wh/m Calc) 'Load 2 x I2C Temp sensor data from first 16 bytes of AT24C512B External EEPROM VarA = 0 I2CREAD Scdeeprom,Sckeeprom,AT24C512B,VarA,[STR I2C1\8] 'Read data from I2c eeprom pause 10 'Pause for 10ms VarA = 8 I2CREAD Scdeeprom,Sckeeprom,AT24C512B,VarA,[STR I2C2\8] 'Read data from I2c eeprom 'Start initial I2C temperature conversion to avoid start up over temp alarm condition owout DigitalTemp,1,[$55,STR I2C1\8,$44] 'Match ROM & Convert Temperature owout DigitalTemp,1,[$55,STR I2C2\8,$44] 'Match ROM & Convert Temperature high Alarm 'Activate audible alarm pause 500 'Pause for 500ms (Wait for display screen to be ready) low Alarm 'Deactivate audible alarm 'Display Splash Screen, Software Version, CPU Speed & Number of Cells in System. DEBUG 27,70,27,46,27,67,"BMS Master V0.06By Peter Perkinswww.150mpg.co.uk",10,13,"8mhz (",#Cells,") Cells" pause 4000 'Pause for 4 seconds DEBUG 27,67 'Clear Screen I2CAddress = 16 'Set start address to avoid overwriting I2C temp sensor data PreLoad = 3035 'Preload Timer with 3035 to get 250ms ticks TMR1L = PreLoad.LowByte 'Load the Timer with preload value TMR1H = PreLoad.HighByte 'Load the Timer with preload value TMR1IF = 0 'Clear TMR1 int flag '************************************************************************************************************* '************************************************************************************************************* MainLoop: 'Main program loop AlarmByte = 0 'Reset AlarmByte to 0 (Clears any Alarm Flags) 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 If TempBit = 0 then gosub CheckAnalogTemp 'Gosub Analog Temp routine to collect/display battery temperature If TempBit = 1 then gosub CheckI2CTemp 'Gosub I2C Temp routine to collect/display battery temperature gosub CheckSpeed 'Gosub CheckSpeed routine to calculate/display speed/distance if AlarmByte > 0 then gosub DisplayAlarms 'Gosub DisplayAlarms gosub MenuButtons 'Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in VarC2 if VarC2 = 4 then gosub SubMenu1 'If Menu button pressed gosub SubMenu1 gosub TxdData 'Gosub TxdData routine Txd data to remote display & store in Eeprom QSeconds = QSeconds + 1 'Increment 1/4 Seconds if QSeconds = 40 then 'Ten seconds have elapsed so store data write Control0, Control 'Write BMS Control Byte to eeprom storage write OdoStore0, Odometer.Byte0 'Write OdoMeter reading to eeprom storage write OdoStore1, Odometer.Byte1 'Write OdoMeter reading to eeprom storage write TripStore0, Tripmeter.Byte0 'Write TripMeter reading to eeprom storage write TripStore1, Tripmeter.Byte1 'Write TripMeter reading to eeprom storage write DistStore0, Distance.Byte0 'Write Distance reading to eeprom storage write DistStore1, Distance.Byte1 'Write Distance reading to eeprom storage write SocStore0, Soc.Byte0 'Write SOC reading to eeprom storage write SocStore1, Soc.Byte1 'Write SOC reading to eeprom storage QSeconds = 0 'Reset 10 second counter endif lOOP0: IF TMR1IF = 0 THEN lOOP0 'If 250ms has not elapsed then goto Loop0 TMR1L = PreLoad.LowByte 'Load the timer with preload value TMR1H = PreLoad.HighByte 'Load the timer with preload value TMR1IF = 0 'Clear TMR1 int flag and restart 250ms timer 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 low SlaveBus 'Turn off Message waiting signal (SlaveBus) pause 1 'Pause for 1ms pulsout SlaveBus, 40 'Send Command 2 (40 x 5us units at 8mhz = 0.2ms) (Turn off all Loads) if SuperBit = 0 then SuperSlaves 'Jump over standard slaves collision test for VarA1 = 1 to Cells 'Start for/next loop to wait until previous command complete serin MasterBus, N9600, TimeOut, DataError, VoltageData 'Check Commands If VoltageData <> 2 then DataError 'If Command is not 2 then error! next VarA1 'repeat loop SuperSlaves: 'jump here if using super slaves (No Command 2 & 3 acknowledge) 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) pause 1 'Pause for 1ms pulsout SlaveBus, 20 'Send Command 1 (20 x 5us units at 8mhz = 0.1ms) (Report Cell Voltages) 'Get cell data into BD byte array for VarA1 = 1 to Cells 'Start for/next loop to wait until previous command complete serin MasterBus, N9600, TimeOut, DataError, BD[VarA1] 'Check Commands next VarA1 'repeat loop 'Turn on Slave loads as reqd 'Sends Slave Command 3 to turn on loads as required 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) pause 1 'Pause for 1ms pulsout SlaveBus, 60 'Send Command 3 (60 x 5us units at 8mhz = 0.3ms) (Turn on Loads if Reqd) 'Evaluate returned cell data 'Evaluates and calculates cell voltages for VarA1 = 1 to Cells 'Start for/next loop to receive cell CellVoltage = 0 'Clear CellVoltage Variable if VarB2 < BD[VarA1] then 'If VoltageData > VarB2 then (Current Cell > Highest Cell) VarB2 = BD[VarA1] 'VarB2 = VoltageData (Store Current High Cell V in VarB2) HighCell = VarA1 'Store High V Cell number in HighCell Variable endif if VarB1 > BD[VarA1] then 'If VoltageData < VarB1 then (Current Cell < Lowest Cell) VarB1 = BD[VarA1] 'VarB1 = VoltageData (Store Current Low Cell V in VarB1) LowCell = VarA1 'Store Low V Cell number in LowCell Variable endif CellVoltage = BD[VarA1] + 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) ErrCell = VarA1 'Store Error Cell number in ErrCell Variable 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) ErrCell = VarA1 'Store Error Cell number in ErrCell Variable 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 (XXXmv Variable) means charge has 'terminated or battery/bms fault so switches off charger relay if ChgFlag = 1 then 'If Charger is operating (1) then Chg End & Batt Chg Fault Detection if PackVoltage < HighPackV then 'If Pack Voltage is less than stored Pack Voltage then HighPackV = HighPackV - PackVoltage 'Calculate Voltage Drop result stored in HighPackV if HighPackV > VoltageDrop then 'If Pack Voltage Drop > than permitted Voltage Drop then AlarmByte = 6 'Set Alarmbyte to 6 (Indicates Pack Voltage dropping) endif else HighPackV = PackVoltage 'Load HighPackV 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) VoltsData = VarA1 'Store VarA1 in VoltsData Variable (Note 0-255V Max) DEBUG 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 Led1 'Activate dash Over-V Led1 else low 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) VarA1 = HighCell 'Load VarA1 with data from HighCell Variable DEBUG 27,83,0,1,"HiV [" 'Video Display if VarA1 < 10 then 'If Leading zero reqd in display due to Value <10 then DEBUG "0" 'Video Display endif if VarD <10 then 'Check if leading zero reqd in display DEBUG #VarA1,"] ",#VarC,".0",#VarD,"V " 'Video Display else DEBUG #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 Led2 'Activate dash Under-V Led2 else low Controller 'De-Activate Controller pull down opto low 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) VarA1 = LowCell 'Load VarA1 with data from LowCell Variable DEBUG 27,83,0,2,"LoV [" 'Video Display if VarA1 < 10 then 'If Leading zero reqd in display due to Value <10 then DEBUG "0" 'Video Display endif if VarD <10 then 'Check if leading zero reqd in display DEBUG #VarA1,"] ",#VarC,".0",#VarD,"V " 'Video Display else DEBUG #VarA1,"] ",#VarC,".",#VarD,"V " 'Video Display endif return 'Return to main program loop DataError: 'Serial Slave Data Rxd TimeOut, If no Data within (TimeOut = 50ms) ErrCell = VarA1 'Store Error Cell number in ErrCell Variable AlarmByte = 3 'Set AlarmByte to 3 (Indicates Cell Serial Data Timeout Error) return 'Return to main program loop '************************************************************************************************************** CheckAnalogTemp: 'Check Analog Temperature Sensors Routine (LM335 10mv/K Sensors) return 'Return to main program loop '************************************************************************************************************** '************************************************************************************************************** CheckI2CTemp: 'Check Battery Pack temperature routine (Multiple DS18B20 I2C Sensors) 'The owout lines become ($55,xx,xx,xx,xx,xx,xx,xx,xx,$44) where xx = decimal serial no's for the I2C chips DEBUG 27,83,0,3,"Tem " 'Setup Display for Temp Sensors 'Sensor 1 (Front Battery Block) owout DigitalTemp,1,[$55,STR I2C1\8,$BE] 'Match ROM & Send lsb,msb gosub calculatetemp 'Gosub Calculate Temperature routine DEBUG 27,83,0,3,"Tem ",VarB1,#VarD1,".",#VarD2," " 'Display Temp Sensor 1 owout DigitalTemp,1,[$55,STR I2C1\8,$44] 'Match ROM & Convert Temperature Temp1Data = VarD1 'Store Front Pack Temp in Temp1Data (Note 0-100C Max) 'Sensor 2 (Rear Battery Block) owout DigitalTemp,1,[$55,STR I2C2\8,$BE] 'Match ROM & Send lsb,msb gosub calculatetemp 'Gosub Calculate Temperature routine DEBUG VarB1,#VarD1,".",#VarD2," " 'Display Temp Sensor 2 owout DigitalTemp,1,[$55,STR I2C2\8,$44] 'Match ROM & Convert Temperature Temp2Data = VarD1 'Store Rear Pack Temp in Temp2Data (Note 0-100C Max) return 'Return to main program loop CalculateTemp: owin DigitalTemp, 2, [VarA.Lowbyte, VarA.Highbyte] 'Read two bytes / end comms 'Convert 12bit DS18B20 value to a useable Temperature range -55 to +127C VarB1 = 43 'Display + (43) IF VarA > 64655 THEN '- 55 degrees = 64656 VarB1 = 45 'Display - (45) VarA = - VarA 'if - ie w1=100 display 10.0 -c ENDIF VarA = VarA * 5 / 8 '+ ie w1=850 display 85.0c VarD1 = VarA / 10 'Calculate Temp prior to decimal point VarD2 = VarA // 10 'Calculate Temp after decimal point if VarD1 > AbsMaxTemp then 'If Temp > AbsMaxTemp set AlarmByte AlarmByte = 4 'Set AlarmByte to 4 (Indicates Temp over AbsMaxTemp condition) endif return 'Return to CheckTemp '************************************************************************************************************** '************************************************************************************************************** CheckCurrent: 'Accumulate (Current in Amps) charge/discharge data VarB = 0 'Reset Current oversampling accumulator to 0 (Zero) ADCON0 = %01000001 'SETUP ADC CONVERTER MODULE FOSC/8 & ENABLE ADC MODULE on AD0 for VarA2 = 1 to 10 '10 x Current Oversampling ADCON0.1 = 1 'Start ADC conversion while ADCON0.1 = 1 'Wait for ADC DONE wend BatCurrent.highbyte = ADRESH 'Move HIGH byte of result to Variable (0-1023 10bit)(-100A to +100A 0-5V) BatCurrent.lowbyte = ADRESL 'Move LOW byte of result to Variable (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 <507 then 'If BatCurrent is <507 means system is Charging BatCurrent = ((510 - BatCurrent) / 3) 'Subtract sensor offset to get a positive number (0-510 = 0-100A+) Maximum 99 AmpSign = 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-) Maximum 99 AmpSign = 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) AmpSign = 61 'Set ascii Character (=) to be displayed if no charge/discharge ExitCurrent: if BatCurrent > 99 then 'Limits BatCurrent to 99 Amps BatCurrent = 99 endif 'Display Battery Amps & Motor Power Kw VarA = (PackVoltage / 100) * BatCurrent VarB = (VarA // 1000) / 100 VarA = VarA / 1000 DEBUG 27,83,0,4,"Amp ",AmpSign,#BatCurrent," K ",#VarA,".",#VarB," " 'Video Display CalculateSoc: 'Calculate Soc Routine '1A charge/discharge = 2.7 per second when 1A = 100 '20A charge/discharge = 55.55 per second when 20A = 2000 '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 8ah Cell 100% Soc is represented by 10,000 which equals 0.8mah per unit. (8 = 1 unit = 0.8mah) 'For a 20ah Cell 100% Soc is represented by 10,000 which equals 2mah per unit. (20 = 1 unit = 2mah) '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 (8 for 8ah cells) (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 AmpSign = 43 then 'If AmpSign = "+" then Charge calculations Charge = Charge + BatCurrent 'Add latest Current data to running Charge total while Charge >= SocUnit 'If Charge >= SocUnit then execute code in loop Soc = Soc + 1 'Increment Soc % by one unit (2ma/ah) Maximum 10,000 (100%) Charge = Charge - SocUnit 'Subtract (SocUnit) from Charge WEND 'Repeat loop until Charge < SocUnit endif if AmpSign = 45 then 'If AmpSign = "-" then Discharge calculations Discharge = Discharge + BatCurrent 'Add latest Current data to running Discharge total while Discharge >= SocUnit 'If Discharge >= SocUnit then execute code in loop Soc = Soc - 1 'Decrement Soc % by one unit (2ma/ah) Minimum 1 (0.01%) If Soc < 1 then 'Limit Soc to 1 (00.01%) Soc = 1 endif Discharge = Discharge - SocUnit 'Subtract (SocUnit) from Discharge WEND 'Repeat loop until Discharge < SocUnit endif If Soc > 10000 then 'Limit Soc to 10,000 (100%) Soc = 10000 endif 'Display Battery State of Charge SOC in % VarC = Soc / 100 'Get first part of value (Before decimal point) SocData = VarC1 'Store Soc % in SocData Ram Byte (Note 0-100%) VarA = Soc // 100 'Get second part of value (After decimal point) DEBUG 27,83,0,5,"Soc ",#VarC,"." 'Video Display if VarA < 10 then 'If Leading zero reqd in display due to Value <10 then DEBUG "0",#VarA," % " 'Video Display else DEBUG #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 100ms) 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 DEBUG 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 while Distance >= 52800 'If distance travelled > 528ft 1/10th mile then execute code in loop Tenths = Tenths + 1 'Increment by 1 /10th Mile Tripmeter = Tripmeter + 1 'Increment TripMeter by 1/10th mile Max 999.9 miles if VarA1 = 10 then 'If VarA1 = 10 one mile has been travelled Odometer= Odometer + 1 'Increment OdoMeter by 1 mile 'Calculate Miles Remaining read PrevOdo0, VarA.Byte0 'Load last saved PrevOdo (Miles) reading from eeprom storage read PrevOdo1, VarA.Byte1 'Load last saved PrevOdo (Miles) reading from eeprom storage read PrevSoc0, VarC.Byte0 'Load last saved PrevSoc (Miles) reading from eeprom storage read PrevSoc1, VarC.Byte1 'Load last saved PrevSoc (Miles) reading from eeprom storage 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) DistRem = VarD 'Load DistRem from VarD 'Calculate wh/mile WhMile = ((VarC * 4) * 16) / 100 'Calculate wh/mile MAX 999 if WhMile > 999 then 'Limit WhMile to 999 WhMile = 999 endif Tenths = 0 'Reset (Tenths) counter endif Distance = Distance - 52800 'Subtract (528ft 1/10th mile) from Distance WEND 'Repeat loop until Distance < 52800 If Tripmeter > 9999 then 'Limit tripmeter to 999.9 miles Tripmeter = 0 endif If Odometer > 9999 then 'Limit Odometer to 9999 miles Odometer = 0 endif 'Display Odometer, Tripmeter and Charge Flag Status DEBUG 27,83,0,7,"Odo ",#Odometer," Rem ",#DistRem," " 'Video Display VarA = Tripmeter / 10 'Get first part of decimal value (Before decimal point) VarB = Tripmeter // 10 'Get second part of decimal value (After decimal point) DEBUG 27,83,0,8,"Tri ",#VarA,".",#VarB," Chg ",#ChgFlag," " '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!) DEBUG 27,94 'Video Display Save Current Screen to SV2000 Flash pause 100 'pause for 100ms at 8mhz DEBUG 27,33,27,67," * BMS Alarms * ",10,13 'Display Negative, Clear Screen & Display Message 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 ChgFlag = 0 'Set ChgFlag to 0 indicates Charging Off If AlarmByte = 1 then '(Cell over AbsMax V) DEBUG "Cell ",#ErrCell," >AMaxV" 'Video Display endif If AlarmByte = 2 then '(Cell under AbsMin V) DEBUG "Cell ",#ErrCell," AbsMaxTemp" 'Video Display endif ' If AlarmByte = 5 then '(Alarm test condition set) ' DEBUG "Alarm Test" 'Video Display ' endif If AlarmByte = 6 then '(Pack Voltage Dropped) DEBUG "Voltage Drop" 'Video Display endif pause 2000 'Pause for 2 seconds low Alarm 'Deactivate audible alarm DEBUG 27,95 'Video Display Restore Current Screen from SV2000 Flash return 'Return to main program loop '************************************************************************************************************** '************************************************************************************************************** MenuButtons: 'Decodes button Inputs and returns 6 values (0,1,2,3,4,5) in VarC2 ADCON0 = %01000101 'SETUP ADC CONVERTER MODULE FOSC/8 & ENABLE ADC MODULE on AD1 pause 10 'Pause 10ms until ADC Module ready ADCON0.1 = 1 'Start ADC conversion while ADCON0.1 = 1 'Wait for ADC DONE wend VarD.highbyte = ADRESH 'Move HIGH byte of result to Variable (0-1023 10bit) VarD.lowbyte = ADRESL 'Move LOW byte of result to Variable (0-1023 10bit) VarD = VarD / 4 'Convert 10 bit to 8 bit result VarD = VarD + 6 VarD = VarD / 42 VarD = VarD - 1 VarC2 = VarD1 'Loads Returned button value into VarC2 return 'Return to main program '************************************************************************************************************** '************************************************************************************************************** SetTrip: 'Set Trip Meter to Zero Tripmeter = 0 'Set Tripmeter to Zero write TripStore0, Tripmeter.Byte0 'Write TripMeter reading to eeprom for storage when Master Off write TripStore1, Tripmeter.Byte1 'Write TripMeter reading to eeprom for storage when Master Off goto ExitMenu 'Return to main program via restore screen routine '************************************************************************************************************** SetOdo: 'Set Odo Meter to Zero Odometer = 0 'Set Odometer to Zero write OdoStore0, Odometer.Byte0 'Write OdoMeter reading to eeprom for storage when Master Off write OdoStore1, Odometer.Byte1 'Write OdoMeter reading to eeprom for storage when Master Off goto ExitMenu 'Return to main program via restore screen routine '************************************************************************************************************** SetSoc: 'Set Soc to 100% Soc = 10000 'Set Soc to 10,000 (100.00%) write SocStore0, Soc.Byte0 'Write Soc reading to eeprom for storage when Master Off write SocStore1, Soc.Byte1 'Write Soc reading to eeprom for storage when Master Off goto ExitMenu 'Return to main program via restore screen routine '************************************************************************************************************** '************************************************************************************************************** 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 high Alarm 'Activate audible alarm If ChgFlag = 0 then 'If Charge Flag is not set then set Charge Flag 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 HighPackV = 0 'Load HighPack Voltage data with 0 else 'If Charge Flag is set then clear Charge Flag ChgFlag = 0 'Set ChgFlag = 0 low ChargerOnOff 'Turn off Charger main relay endif pause 250 'Pause for 250ms at 8mhz low Alarm 'Deactivate audible alarm goto ExitMenu 'Return to main program via restore screen routine '************************************************************************************************************** SubMenu1: 'Sub Menu 1 routine for extended BMS functions Debug 27,94 'Video Display Save Current Screen to SV2000 Flash pause 100 'Pause for 100ms at 8mhz Debug 27,67,"* Sub Menu 1 *",10,13,"Start Charge = UReset Soc = LReset Trip = DReset Odo = RSub Menu 2 = C" pause 2000 'Pause for 2 seconds MenuLoop1: 'Loop here to wait for button press gosub MenuButtons 'Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in VarC2 if VarC2 = 0 then StartChg 'If varC2 = 0 then goto Start Charge Routine if VarC2 = 1 then SetSoc 'If varC2 = 1 then goto Reset Soc to 100% if VarC2 = 2 then SetTrip 'If varC2 = 2 then goto Reset Tripmeter to 0 miles if VarC2 = 3 then SetOdo 'If varC2 = 3 then goto Reset Odometer to 0 miles if VarC2 = 4 then SubMenu2 'If varC2 = 4 then goto Sub Menu 2 goto MenuLoop1 'Goto Menu Loop 1 ExitMenu: Debug 27,67,27,95 'Video Display Restore Current Screen from SV2000 Flash return 'Return to main program '************************************************************************************************************** SubMenu2: 'Sub Menu 2 routine for extended BMS functions Debug 27,67,"* Sub Menu 2 *",10,13,"Cell Voltage = USlave Update = LTx BMS Data = DTemp Rom = RSub Menu 3 = C" pause 2000 'Pause for 2 seconds MenuLoop2: 'Loop here to wait for button press gosub MenuButtons 'Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in VarC2 if VarC2 = 0 then Display 'If varC2 = 0 then goto Display Cell Voltages if VarC2 = 1 then UpdateSlaves 'If varC2 = 1 then goto Update Slaves if VarC2 = 2 then DumpData 'If varC2 = 2 then goto Dump BMS Data if VarC2 = 3 then TempRom 'If varC2 = 3 then goto Discover I2C Sensor Serial Number if VarC2 = 4 then SubMenu3 'If varC2 = 4 then goto Sub Menu 3 goto MenuLoop2 'Goto Menu Loop 2 '************************************************************************************************************** SubMenu3: 'Sub Menu 3 routine for extended BMS functions Debug 27,67,"* Sub Menu 3 *",10,13,"Analog Temp = UDigital Temp = LNum of Cells = DReserved = RExit = C" pause 2000 'Pause for 2 seconds MenuLoop3: 'Loop here to wait for button press gosub MenuButtons 'Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in VarC2 if VarC2 = 0 then 'If varC2 = 0 then set AnalogTemp mode TempBit = 0 'Set Analog temperature sensor mode Debug 10,13,"Analog Temp Set!" pause 2000 'Pause for 2 seconds goto ExitMenu 'Goto Exit Sub menu endif if VarC2 = 1 then 'If varC2 = 1 then goto DigitalTemp TempBit = 1 'Set Digital temperature sensor mode Debug 10,13,"Digital Temp Set" pause 2000 'Pause for 2 seconds goto ExitMenu 'Goto Exit Sub menu endif if VarC2 = 2 then CellNumber 'If varC2 = 2 then goto Cell Number if VarC2 = 3 then ExitMenu 'If varC2 = 3 then Reserved if VarC2 = 4 then ExitMenu 'If varC2 = 4 then Exit Sub Menu goto MenuLoop3 'Goto Menu Loop 3 '************************************************************************************************************** CellNumber: 'Set number of cells in pack routine (Max 95) pause 500 'Pause for 500ms second Loop1: 'Loop here to wait for button press gosub MenuButtons 'Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in VarC2 DEBUG 27,83,0,8,"Cells ",#Cells," " 'Video Display pause 200 'Pause for 200ms if VarC2 = 5 then Loop1 'If no button pressed goto Loop1 if VarC2 = 0 then 'If Button pressed is up then increment cells Cells = Cells + 1 'Increment Cells Variable endif If VarC2 = 2 then 'If Button pressed is down then decrement cells Cells = Cells - 1 'Decrement Cells Variable endif If VarC2 <> 4 then Loop1 'If Centre button not pressed then goto loop1 WRITE Cells0, Cells 'Load Cells Byte into eeprom storage (Number of cells in pack) goto ExitUpdate 'Goto Exit Update '************************************************************************************************************** TxdData: 'Sends 17 bytes of data to remote display via 433mhz rf link 'Txd sends 5 byte preamble to lock with Rxd, then qualifier "bms" 'then Control code (0) followed by 8 bytes of data I2CWRITE Scdeeprom,Sckeeprom,AT24C512B,I2CAddress,[AlarmByte] 'Write data to I2c eeprom starting at I2CAddress I2CAddress = I2CAddress + 1 'Increment I2CAddress word variable serout DriveInhibit, N2400, [85, 85, 85, 85, 85] 'Txd 5 byte preamble to clear Rxd pause 10 'Pause for 10ms to allow serial rxd to clear serout DriveInhibit, N2400, ["bms",0,AlarmByte,ErrCell] 'Txd Control code (0) and data (2400 baud at 8mhz) I2CWRITE Scdeeprom,Sckeeprom,AT24C512B,I2CAddress,[ErrCell] 'Write data to I2c eeprom starting at I2CAddress I2CAddress = I2CAddress + 1 'Increment I2CAddress word variable if I2CAddress > 65532 then 'Check if AT24C512B external eeprom full I2CAddress = 16 'Set start address to avoid overwriting I2C temp sensor data endif return 'Return to main program '************************************************************************************************************** DumpData: 'Sends Stored EEPROM BMS data to remote display and/or PC Logger 'Txd sends 5 byte preamble to lock with Rxd, then qualifier "bms" 'followed by control code and then data. Data rate is limited to '2400 baud and 64kbyte may take upto 4 minutes to transmit DEBUG 27,67,"Txd ",#I2CAddress," Bytes" 'Video Display serout DriveInhibit, N2400, [85, 85, 85, 85, 85] 'Txd 5 byte preamble to clear Rxd pause 5 'Pause for 5ms to allow serial rxd to clear serout DriveInhibit, N2400, ["bms",1] 'Txd Qualifier and control code (1) (2400 baud at 8mhz) pause 5 'Pause for 5ms to allow serial rxd to clear for VarA = 0 to I2CAddress 'Start loop to transmit stored data I2CREAD Scdeeprom,Sckeeprom,AT24C512B,I2CAddress,[VarB1] 'Read data from I2c eeprom starting at I2CAddress serout DriveInhibit, N2400, [VarB1] 'Txd BMS Data byte via 443mhz Tx 2400 baud toggle WatchDogLed 'Keeps watchdog happy, Prevents alarm during data transmision next VarA 'Repeat loop until all eeprom data sent serout DriveInhibit, N2400, [255] 'Txd BMS Data end flag (255) via 443mhz Tx 2400 baud goto ExitUpdate 'Goto Exit Update '************************************************************************************************************** '************************************************************************************************************** Display: 'Display Extended Cell BMS Data DEBUG 27,67,"Scroll Cells = D",10,13 'Video Display VarA2 = 1 'Set Cell counter to first Cell (1) ReadCellV: 'Read Cell Voltages 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) pause 1 'Pause for 1ms pulsout SlaveBus, 20 'Send Command 1 (20 x 5us units at 8mhz = 0.1ms) (Report Cell Voltages) for VarA1 = 1 to Cells 'Start for/next loop to receive cell data CellVoltage = 0 'Clear CellVoltage Variable CellVoltage = BD[VarA1] + DiscardLow 'CellVoltage (w1) = CellVoltage (w1) (1-255 0.01-2.55V) + (175 1.75V) VarB = CellVoltage / 100 'Get first part of decimal value (Before decimal point) VarC = CellVoltage // 100 'Get second part of decimal value (After decimal point) 'Display Cell Voltage if VarC < 10 then 'If Leading zero reqd in display due to Value <10 then DEBUG "Cell ",#VarA1," ",#VarB,".0",#VarC,"V ",10,13 'Video Display else DEBUG "Cell ",#VarA1," ",#VarB,".",#VarC,"V ",10,13 'Video Display endif Loop4: gosub MenuButtons 'MenuButtons gets current button data (0,1,2,3,4,5) value in VarC2 If VarC2 <> 2 then Loop4 'If Down Button is not pressed goto Loop4 pause 150 'Pause for 150ms next VarA1 'Display Next Cell goto ExitUpdate 'Goto ExitUpdate '************************************************************************************************************** '*********************************************** Slave Commands *********************************************** UpdateSlaves: 'Update Slaves in situ (Non Picaxe Serial command slaves only) VarC1 = 9 'Set variable VarC1 to Command 9 (Turn on Load for 0.5 seconds, flash Led) DEBUG 27,67,"* Slave Update * Use Up/Down to Select Command " 'Video Display DEBUG 10,13," Centre Push to Send Command!!" 'Video Display pause 1000 'Pause for 1 second Loop5: 'Loop here to wait for button press gosub MenuButtons 'Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in VarC2 DEBUG 27,83,0,7," Command ",#VarC1," ! " 'Video Display pause 500 'Pause for 500ms if VarC2 = 5 then Loop5 'If no button pressed goto Loop5 if VarC2 = 0 then 'If Button pressed is up then increment command varC1 = varC1 + 1 'Increment Variable VarC1 endif If VarC2 = 2 then 'If Button pressed is down then decrement command varC1 = varC1 - 1 'Decrement Variable VarC1 endif If VarC2 <> 4 then Loop5 'If Centre button not pressed then goto loop5 DEBUG 10,10,13,"Confirm (Y/N) ? Up=Yes Down=No" 'Video Display pause 1000 'Pause for 1 second Loop6: 'Loop here to wait for button press gosub MenuButtons 'Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in VarC2 If VarC2 = 0 then ConfirmUpdate 'If Button pressed is up then update slaves If VarC2 = 2 then ExitUpdate 'If Button pressed is down then cancel update Goto Loop6 'Goto Loop6 ConfirmUpdate: DEBUG 27,67,"Command ",#VarC1," Sent" 'Video Display VarD = VarC1 * 20 'Calculate pulse length value (20 x 5us units at 8mhz = 0.1ms) 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) pause 1 'Pause for 1ms at 8mhz pulsout SlaveBus, VarD 'Send Command (VarD x 5us units at 8mhz) SlaveCheck: 'Checks Slaves have received and acknowledged commands 'Get cell data into BD byte array for VarA1 = 1 to Cells 'Start for/next loop to wait until previous command complete serin MasterBus, N9600, 1000, CommError, BD[VarA1] 'Check Commands next VarA1 'repeat loop for VarA1 = 1 to Cells 'Start for/next loop to receive data if VarC1 = 12 then 'If Command sent was 12 (report slave software version number) then DEBUG 10,13,"Slave ",#VarA1," V",#BD[VarA1]," " 'Video Display pause 250 'Pause for 1/4 second goto CheckLoop 'Goto CheckLoop endif if VarC1 <> BD[VarA1] then 'If bytes don't match then command error DEBUG 10,13,"Slave ",#VarA1," Error!" 'Video Display high Alarm 'Activate audible alarm pause 3000 'Pause for 3 seconds low Alarm 'Deactivate audible alarm goto ExitUpdate 'Goto ExitUpdate endif CheckLoop: 'Slave Commands Check Loop label next VarA1 'Increment for/next loop and move to next cell DEBUG 10,13,"Acknowledged OK" 'Video Display ExitUpdate: 'Jump here to exit updates pause 500 'Pause for 500ms DEBUG 10,13,"Any Key To Exit" 'Video Display Loop7: 'Loop here to wait for button press gosub MenuButtons 'Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in VarC2 If VarC2 = 5 then Loop7 'If no button pressed goto loop7 DEBUG 27,67,27,95 'Video Display Restore Current Screen from SV2000 Flash return 'Return to main program loop CommError: 'Command serial comms error DEBUG 10,13,"* Comms Error! *",10,13," Cell ",#VarA1," " 'Video Display high Alarm 'Activate audible alarm pause 3000 'Pause for 3 seconds low Alarm 'Deactivate audible alarm goto ExitUpdate 'Goto Exit Update '**************************************************************************** TempRom: 'Read & Store 2 x I2C DS18B20 Temp Sensor Rom Codes debug 27,67,"Ins 1st sensor",10,13,"Any key to scan" 'Video Display pause 2000 'Pause for 2 seconds LoopA: 'Loop here to wait for button press gosub MenuButtons 'Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in VarC2 If VarC2 = 5 then LoopA 'If no button pressed goto loopA OWOUT DigitalTemp, 1, [$33] 'Issue Read ROM command OWIN DigitalTemp, 0, [STR I2C1\8] 'Read 64-bit device data into the 8-byte array "I2C1" debug 27,67,"Ins 2nd sensor",10,13,"Any key to scan",10,13 'Video Display pause 2000 'Pause for 2 seconds LoopB: 'Loop here to wait for button press gosub MenuButtons 'Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in VarC2 If VarC2 = 5 then LoopB 'If no button pressed goto loopB OWOUT DigitalTemp, 1, [$33] 'Issue Read ROM command OWIN DigitalTemp, 0, [STR I2C2\8] 'Read 64-bit device data into the 8-byte array "I2C2" 'Save 2 x I2C Temp sensor data into first 16 bytes of AT24C512B External EEPROM VarA = 0 I2CWRITE Scdeeprom,Sckeeprom,AT24C512B,VarA,[STR I2C1\8] 'Load data into I2c eeprom pause 10 'Pause for 10ms VarA = 8 I2CWRITE Scdeeprom,Sckeeprom,AT24C512B,VarA,[STR I2C2\8] 'Load data into I2c eeprom For VarA = 0 to 15 'Load I2C1 data from eeprom I2CREAD Scdeeprom,Sckeeprom,AT24C512B,VarA,[VarB1] 'Read data from I2c eeprom DEBUG HEX2 VarB1 Next VarA goto ExitUpdate 'Goto Exit Update