'------ Li-Ion Battery Management System Master by Peter Perkins Module ------- '------ Picbasic Pro Compiler version PIC16F886 - 010710 - V0.06 Alpha ------- '------ This code requires Master board V2. Please report errors or problems. '------------------------------------------------------------------------------ 'Note this code is for the Pic Basic Pro v2.60+ 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 ' '******************************************************************************* '****************** Program Size 6409 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) AlarmCount var Byte 'Global = Alarm Counter Byte (0-255 number of times an Alarm has occured) VoltageData var CellVoltage.LowByte 'Local = Voltage data byte (8 bit value) (Rxd from Slave via serial link) 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] ID var Byte[8] 'Local = [Array storage variable for I2C DS18B20 Temp Sensor 64-bit ROM code] 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 '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 Cells con 1 'Number of cells in the battery pack (Max is 255 cells) 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 3 'Interrupt and data delay (Pause 3 = 3ms at 8mhz) 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 AlarmActivate con 2 'Number of times Alarm must occur before Alarm activates VoltageDrop con 50 'Voltage Drop = 50 Pack must drop (500mv) when charging before charger cuts off FailSafeV con 335 'Fail Safe Minimum Load voltage Cut off (<3.35V) Cell resting voltage Deg con 223 'Data to display Deg ° symbol 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 ******************************************* '************************************** 255 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 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 @ DEVICE PIC16F886,FCMEN_OFF @ DEVICE PIC16F886,IESO_OFF @ DEVICE PIC16F886,CPD_OFF @ DEVICE PIC16F886,MCLR_ON @ DEVICE PIC16F886,WDT_OFF @ DEVICE PIC16F886,INTRC_OSC_NOCLKOUT @ DEVICE PIC16F886,PROTECT_OFF 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 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) high Alarm ;Activate audible alarm pause 1000 ;Pause for 1 second (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!" ;Splash Screen pause 2000 ;Pause for 2 seconds DEBUG 27,67 ;Clear Screen 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 toggle WatchDogLed ;Keeps watchdog happy, flashes every other time through loop gosub CheckVoltage ;Gosub CheckVoltage routine to collect/display Cell V data gosub CheckCurrent ;Gosub CheckCurrent routine to calculate/display charge/discharge data ' gosub CheckAnalogTemp ;Gosub Analog Temp routine to collect/display battery temperature gosub CheckI2CTemp ;Gosub I2C Temp routine to collect/display battery temperature gosub CheckSpeed ;Gosub CheckSpeed routine to calculate/display speed/distance gosub MenuButtons ;Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in VarC2 if VarC2 = 4 then gosub SubMenu1 ;If Menu button pressed gosub SubMenu1 if AlarmByte > 0 then AlarmCount = AlarmCount + 1 ;Increment Alarm Counter else AlarmCount = 0 ;Reset Alarm Counter endif gosub TxdData ;Gosub TxdData routine Txd data to remote display & store in Eeprom if AlarmCount >= AlarmActivate and AlarmByte > 0 then ;If AC >= AA & AB > 0 then gosub Alarms Display gosub DisplayAlarms ;Gosub DisplayAlarms endif AlarmByte = 0 ;Reset AlarmByte to 0 (Clears any Alarms) QSeconds = QSeconds + 1 ;Increment 1/4 Seconds if QSeconds = 40 then ;Ten seconds have elapsed so store data 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 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) 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 pause 50 ;Pause between Commands (50ms) 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) '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 serin MasterBus, N9600, TimeOut, DataError, VoltageData ;Rxd 9600 Data on MasterBus (T=True Idle High)(N=Inverted Idle Low) if VarB2 < VoltageData then ;If VoltageData > VarB2 then (Current Cell > Highest Cell) VarB2 = VoltageData ;VarB2 = VoltageData (Store Current High Cell V in VarB2) HighCell = VarA1 ;Store High V Cell number in HighCell Variable endif if VarB1 > VoltageData then ;If VoltageData < VarB1 then (Current Cell < Lowest Cell) VarB1 = VoltageData ;VarB1 = VoltageData (Store Current Low Cell V in VarB1) LowCell = VarA1 ;Store Low V Cell number in LowCell Variable 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) 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 '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) '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) VarB = 0 ;Set VarB to 0 VarA = 0 ;Set VarA to 0 ADCON0 = %01001001 'SETUP ADC CONVERTER MODULE FOSC/8 & ENABLE ADC MODULE on AD2 for VarC2 = 1 to 10 ;10 x Temperature Oversampling 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) VarA = VarA + VarD ;Accumulate 10 temp readings in VarA next VarC2 ;Next VarC2 ADCON0 = %01001101 'SETUP ADC CONVERTER MODULE FOSC/8 & ENABLE ADC MODULE on AD3 for VarC2 = 1 to 10 ;10 x Temperature Oversampling ADCON0.1 = 1 'Start ADC conversion while ADCON0.1 = 1 'Wait for ADC DONE wend VarE.highbyte = ADRESH 'Move HIGH byte of result to Variable (0-1023 10bit) VarE.lowbyte = ADRESL 'Move LOW byte of result to Variable (0-1023 10bit) VarB = VarB + VarE ;Accumulate 10 temp readings in VarB next VarC2 ;Next VarC2 VarB = VarB / 10 ;Get average temp for last 10 readings VarA = VarA / 10 ;Get average temp for last 10 readings Temp1Data = (VarA - 560) / 2 ;Calculate Temp1 in C from average Adc reading Temp2Data = (VarB - 560) / 2 ;Calculate Temp2 in C from average Adc reading if Temp1Data > AbsMaxTemp or Temp2Data > AbsMaxTemp then ;If Temp > AbsMaxTemp set AlarmByte AlarmByte = 4 ;Set AlarmByte to 4 (Indicates Temp over AbsMaxTemp condition) endif 'Display Temperatures DEBUG 27,83,0,3,"Tem F",#VarA1," R",#VarB1," ",248,"C " ;Display Temps 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,$28,$D6,$2D,$16,$01,$00,$00,$E6,$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,$28,$D6,$2D,$16,$01,$00,$00,$E6,$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,$28,$B9,$11,$16,$01,$00,$00,$A4,$BE] ;Match ROM & Send lsb,msb gosub calculatetemp ;Gosub Calculate Temperature routine DEBUG VarB1,#VarD1,".",#VarD2," " ;Display Temp Sensor 2 owout DigitalTemp,1,[$55,$28,$B9,$11,$16,$01,$00,$00,$A4,$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 MIN 99 ;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 MIN 99 ;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: '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 MIN 15000 ;Increment Soc % by one unit (2ma/ah) Maximum 15,000 (150%) 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 MAX 1 ;Decrement Soc % by one unit (2ma/ah) Minimum 1 (0.01%) Discharge = Discharge - SocUnit ;Subtract (SocUnit) from Discharge WEND ;Repeat loop until Discharge < SocUnit 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 MIN 9999 ;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 MIN 999 ;Calculate wh/mile MAX 999 Tenths = 0 ;Reset (Tenths) counter endif Distance = Distance - 52800 ;Subtract (528ft 1/10th mile) from Distance WEND ;Repeat loop until Distance < 52800 '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 " Charge End! Voltage Drop " ;Video Display endif AlarmByte = 0 ;Reset AlarmByte to 0 (Clears any Alarm Flags) AlarmCount = 0 ;Reset AlarmCount to 0 (Clears any Alarm Flags) 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 ;************************************************************************************************************** SetAlarm: ;Set Test Alarm conditon AlarmByte = 5 ;Set AlarmByte to 5 return ;Return to main program ;************************************************************************************************************** SetSoc: ;Set Soc to 100% Soc = 10000 ;Set Soc to 10,000 (100.00%) write 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 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 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 = L",_ "Reset 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,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 = L",_ "Tx BMS Data = DTemp Rom = RExit = 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 ExitMenu ;If varC2 = 4 then Exit Sub Menu goto MenuLoop2 ;Goto Menu Loop 2 ;************************************************************************************************************** 'SubMenu3: ;Sub Menu 3 routine for extended BMS functions ' Debug 27,67,"* Sub Menu 3 *",10,13,"Cell Voltage = USlave Update = L"_"Tx BMS Data = DTemp Rom = 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 DisplayB ;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 SensorSerial ;If varC2 = 3 then goto Discover I2C Sensor Serial Number ' if VarC2 = 4 then ExitMenu ;If varC2 = 4 then Exit Sub Menu ' goto MenuLoop3 ;Goto Menu Loop 3 ;************************************************************************************************************** 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 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",10,10,13,"Be Patient!" ;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 ;Video Display Clear Screen Command Sequence 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 serin MasterBus, N9600, TimeOut, DataError, VoltageData ;Rxd 9600 Data on MasterBus (T=True Idle High)(N=Inverted Idle Low) if VarA1 = VarA2 then DisplayCellV ;If cell being read is equal to cell to be displayed then next VarA1 DisplayCellV: ;Display individual cell voltage CellVoltage = CellVoltage + 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 ",#VarA2," ",#VarB,".0",#VarC,"V ",10,13 ;Video Display else DEBUG "Cell ",#VarA2," ",#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 VarA2 = VarA2 + 1 ;Increment Cell Counter and move to next cell if VarA2 < Cells then ReadCellV ;If last Cell has not been displayed then got ReadCellV Pause 500 ;Pause for 0.5 seconds DEBUG 10,13,"Any Key To Exit!" ;Video Display Pause 1000 ;Pause for 1 second Loop3: ;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 Loop3 ;If no button pressed goto loop3 DEBUG 27,95 ;Video Display Restore Current Screen from SV2000 Flash return ;Return to main program loop ;************************************************************************************************************** ;*********************************************** Slave Commands *********************************************** ; Command 01 = Send Cell Voltage on Master Bus (0.1ms pulse) ; Command 02 = Turn Off Slave Load (0.2ms pulse) ; Command 03 = Turn On Slave Loads as Required (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 (Flashes Led) (0.9ms pulse) ; Command 10 = Set Baud rate to 9600bps (1.0ms pulse) Note not supported in Pbpro Master ; Command 11 = Set Baud rate to 2400bps (1.1ms pulse) Note not supported in Pbpro Master ; Command 12 = Report Slave Software Version Number (1.2ms pulse) 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 for VarA1 = 1 to Cells ;Start for/next loop to receive data serin MasterBus, N9600,1000,CommError, VarC2 ;Receive Data at 9600 Baud into VarC2 (T=True Idle High)(N=Inverted Idle Low) if VarC1 = 12 then ;If Command sent was 12 (report slave software version number) then DEBUG 10,13,"Slave ",#VarA1," V",#VarC2," " ;Video Display pause 500 ;Pause for 1/4 second goto CheckLoop ;Goto CheckLoop endif if VarC1 <> VarC2 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 pause 1000 ;Pause for 1 second ExitUpdate: ;Jump here to exit updates 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,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! *" ;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 I2C DS18B20 Temp Sensor Rom Codes OWOUT DigitalTemp, 1, [$33] 'Issue Read ROM command OWIN DigitalTemp, 0, [STR ID\8] 'Read 64-bit device data into the 8-byte array "ID" DEBUG 27,67,"Family = ",HEX2 ID[0],"h",10,10,13 'Display Device Family ID DEBUG HEX2 ID[1]," ",HEX2 ID[2]," ",HEX2 ID[3],"h",10,13 DEBUG HEX2 ID[4]," ",HEX2 ID[5]," ",HEX2 ID[6],"h" 'Display Serial No DEBUG 10,10,13,"CRC Val = ",HEX2 ID[7],"h" 'Display Device CRC Value pause 2000 ;Pause for 2 seconds goto ExitUpdate ;Goto Exit Update