4-cell BMS

From openinverter.org wiki
Jump to navigation Jump to search
v5.3 slave board with annotations

The 4-cell BMS owes its name to the fact that every cell module can monitor and balance up to for Lithium cells. It can be used for LiFePO4 (3.3V) or NMC (3.7V) cell chemistries.

Its features are:

  • High resolution (1mV) voltage measurement around the typical cell voltage 3.1-3.4 for LFP, 3.6-4V NMC
  • Low resolution (10mV) voltage measurement outside the typical cell voltage
  • Low resolution (+/- 5°C) cell group temperature (sensor embedded in controller)
  • 20mA balancing current via a 2.4V (LFP) or 3.3V(NMC) LED. This prevents full cell drain in case of faults in the balancing software or circuitry
  • 2mA balanced cell drain during operation
  • 50µA balanced cell drain in sleep mode
  • 1-wire UART current mode communication, 10kbit/s
  • Very low cost due to generic component use and low component count
BMS Master Unit
BMS Master Unit

The communication is terminated/translated via at the master module. Its output connects the the first cell-groups positive terminal via a 220 Ohm resistor (Pin 3 of the 4-cell module) and the communication input. Its input also connects to the last cell groups positive terminal and the communication output.

All communication that goes into the first module is forwarded in a daisy-chain manner out to the last module. Each cell module can see the communication stream of the modules BEFORE it but not the modules after it. Exception: address mode, discussed below.

The master unit has two CAN busses that can be used for system integration. It continuously estimates vital parameters like SoC and SoH, calculates power limits etc. In electric vehicles it can control fuel gauges via a 2 channel analog output. Finally a relay can be used for basic charge or discharge control.

Wiring Diagram

Wiring Diagram

Operation

When installed there are 4 modes of operation:

  • After power up, i.e. when the module is connected the first time to the cell terminals, it is in wait-address mode
  • When address is assigned, it changes to run-mode
  • When there is no communication for about 30s it changes to sleep mode
  • When an update command is received, it changes to firmware update mode

Address mode

When the 4-cell module is in address mode it disables forwarding of the communication stream to the next module. This makes it possible to only communicate with a single module, the first one. As soon as it receives a "set address X" command it assigns address "X" to itself, enables communication forwarding and sends a "set address X+1" command to the next module. This continues for the entire chain until the isolator receives the address of the last module + 1. Thereby it knows how many modules there are.

Run mode

In run mode the module accepts the following commands

  • "Get Data X" : module X sends its 4 cell voltages and the temperature within the controller (cell group temperature)
  • "Shunt X Y": module X enables shunts Y (bitmask). Returns Y for plausibility checking
  • "Get version X": module X sends its hardware and firmware version
  • "Reset address": All modules reset their address and enter address mode
  • "Update": All modules change to firmware update mode

Update mode

This allows updating the modules firmware via the existing 1-wire communication bus. The current version with 4k of flash memory expects 56 pages of 64 bytes each, prepended with the page number and terminated with the pages CRC16. The protocol is implemented in the master unit and the cell module firmware is embedded into the master units firmware. If any module discovers a CRC error it sends a pulse onto the bus. The master then has to resend the page.

As we are planning to switch to the Attiny84 this would have to be changes to 120 pages. (The boot loader takes up 8 pages).

Isolator Command line interface - deprecated!

As already known from the inverter the isolator is controlled via a simple command line interface to carry out above operations. Since there can be more then 1 isolator on the RS485 bus each isolator is automatically assigned an address that has to be supplied to each command. This results in a hierarchical bus structure where the 1st module on the 1st isolator has address "1.1" and the yth module on the xth isolator has address "x.y". Com speed is 115200.

get x.y

Instruct isolator x to send the "Get Data y" command in order to obtain measurements from cell module y. The values are NOT printed out to allow get commands to all existing isolators without bus collision. The values are printed with:

print x

Print the values obtained by the last get command. Looks like this:

val x.y:U1,U2,U3,U4,T,CRC16

U1 to U4 are mV, T is °C. CRC16 is the X-Modem CRC of the string up until the last ",". (_crc_xmodem_update() from avrlib)

shunt x.y z

Sets shunt threshold of module x.y to z mV. z can be from 3000 to 5000mV. 3000mV is considered "no shunting". The shunt command times out (not sure how long it takes, say 1 minute) and has to be resent in order to continue shunting. When the module enters sleep mode shunting is also stopped.

mmuver x

Print firmware version of isolator X (used to be called module management unit)

cmuver x.y

Print firmware version of cell modules x.y

setadr x

Isolator x sends "set address 1" command to the first attached module

resetadr x

All cell modules on isolator x enter wait-address mode

calib x.y chan delta

Add delta to calibration channel chan of cell module x.y. Prints out the new calibration value (calib x.y: val) or "Calib command failed" on invalid response from cell module.

calib x.y 31 0

Lock calibration data of cell module x.y

cmupdate
import serial
import crc16
import time

PAGE_SIZE_BYTES=64
MAX_PAGES=56

ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=0.01)
ser.flushInput()
ser.flushOutput()
updateFile = open("bms-tiny.bin", "rb")
data = bytearray(updateFile.read())
updateFile.close()

numBytes = min(len(data), PAGE_SIZE_BYTES * MAX_PAGES)
numPages = (numBytes + PAGE_SIZE_BYTES - 1) / PAGE_SIZE_BYTES

#Pad unused space with 0xff
while len(data) < (PAGE_SIZE_BYTES * MAX_PAGES):
    data.append(0xff)

print ("File length is %d bytes/%d pages" % (numBytes, numPages))
print ("Entering update mode...")

ser.write("\ncmupdate\n")
time.sleep(1)
page = 0

def sendPage(ser, data, page):
    binData = bytearray(PAGE_SIZE_BYTES + 3)
    binData[0] = page
    
    crc = crc16.crc16xmodem(chr(binData[0]))
    
    for i in range(0, PAGE_SIZE_BYTES):
        binData[i+1] = data[page * PAGE_SIZE_BYTES + i]
        crc = crc16.crc16xmodem(chr(binData[i+1]), crc)
    
    binData[1 + PAGE_SIZE_BYTES] = crc & 0xff
    binData[2 + PAGE_SIZE_BYTES] = (crc >> 8)

    print ("Sending page %d, crc %x" % (page, crc))
    
    for i in range(0, PAGE_SIZE_BYTES + 3):
        ser.write(chr(binData[i]))
        
    return crc

while page < MAX_PAGES:
    crc = sendPage(ser, data, page)
    
    # Allow 110ms for the page to be sent to the CMUs, 40ms
    # for the page to be written to flash and 20ms for the MMU replies
    time.sleep(0.17)
    response = ser.read(100)
    print response
    
    if response == "":
        page = page + 1

All cell modules on all isolators enter update mode. Afterwards the isolators expect binary data on the console. 1 byte page number, 64 bytes page data, 2 bytes CRC16 xmodem over page num and page. After sending a page wait 170ms. If there is no response from any isolator continue with the next page. If there is, resend the page. After 56 pages the cell modules exit update mode and jump to the new firmware.