laszlodaniel / SmartBatteryHack

Arduino based hacking tool for smart batteries using SMBus.
GNU General Public License v3.0
149 stars 54 forks source link

what is 0x50 0x51 0x52 command? #4

Closed youxiaojie closed 4 years ago

youxiaojie commented 4 years ago

Appendix B Writing and Reading Data Flash B.1 Writing to Data Flash With Command 0x50 B.2 Reading from Data Flash With Commands 0x51 and 0x52 B.3 Reading/Writing Data Flash With the SMB Block Protocol B.4 For the bq2083 and bq2085 Writing to Data Flash With Command 0x50 There are two methods to write and read flash. The simplest method uses commands 0x50, 0x51 and 0x52 as described below. This covers the complete range of the data flash for the bq2083 and bq2085. However, the bq2084 contains many data flash locations outside of this range (above address 0xff). The preferred technique, for both types of devices is to read and write entire blocks. This is generally faster if there are several locations to read and/or write. This technique also allows full programming of the bq2084 data flash constants. The user may use Command 0x50 to write individual bytes up through 0xff only. The first parameter is the data byte, the second is the address. For example, writing a 16-bit integer requires two writes: lError = WriteSMBusWord(&H50, yDataMS, yAddress) lError = WriteSMBusWord(&H50, yDataLS, yAddress + 1) The user may use Command 0x51 to set the address (up to 0xff only) of the flash byte to read. Then use Command 0x52 to read the byte. When setting the address, the first byte is a dummy. For example, to read an integer: ‘set flash address for ms data byte lError = WriteSMBusWord(&H51, yDummy, yAddress) 'read ms byte lError = ReadSMBusWord(&H52, yDummy, yDataMS) 'set flash address for ls data byte lError = WriteSMBusWord(&H51, yDummy, yAddress + 1) 'read ls byte lError = ReadSMBusWord(&H52, yDummy, yDataLS) lDataWord = (256 * CLng(yDataMS)) + yDataLS The flash data can be read or written with the following read/write page commands which use the SMB Block protocol. In order to write, however, the entire block must first be read, then edited and written back. 0x58 64 bytes Reads/Writes data flash locations 0x0000-0x003f 0x59 64 bytes Reads/Writes data flash locations 0x0040-0x007f 0x5a 64 bytes Reads/Writes data flash locations 0x0080-0x00bf 0x5b 42 bytes Reads/Writes data flash locations 0x00c0-0x00e9 Writing and Reading Data Flash 6 SLVA148A–October 2003–Revised September 2004 www.ti.com B.5 For the bq2084 For the bq2084 Or use: 0x60 16 bytes Reads/Writes data flash locations 0x0000-0x000f 0x61 16 bytes Reads/Writes data flash locations 0x0010-0x001f 0x62 16 bytes Reads/Writes data flash locations 0x0020-0x002f 0x63 16 bytes Reads/Writes data flash locations 0x0030-0x003f 0x64 16 bytes Reads/Writes data flash locations 0x0040-0x004f 0x65 16 bytes Reads/Writes data flash locations 0x0050-0x005f 0x66 16 bytes Reads/Writes data flash locations 0x0060-0x006f 0x67 16 bytes Reads/Writes data flash locations 0x0070-0x007f 0x68 16 bytes Reads/Writes data flash locations 0x0080-0x008f 0x69 16 bytes Reads/Writes data flash locations 0x0090-0x009f 0x6a 16 bytes Reads/Writes data flash locations 0x00a0-0x00af 0x6b 16 bytes Reads/Writes data flash locations 0x00b0-0x00bf 0x6c 16 bytes Reads/Writes data flash locations 0x00c0-0x00cf 0x6d 16 bytes Reads/Writes data flash locations 0x00d0-0x00df 0x6e 10 bytes Reads/Writes data flash locations 0x00e0-0x00e9 For the bq2084 0x58 64 bytes Reads/Writes data flash locations 0x0000-0x003f 0x59 64 bytes Reads/Writes data flash locations 0x0040-0x007f 0x5a 64 bytes Reads/Writes data flash locations 0x0080-0x00bf 0x5b 64 bytes Reads/Writes data flash locations 0x00c0-0x00ff 0x5c 52 bytes Reads/Writes data flash locations 0x0100-0x133 Or use: 0x60 16 bytes Reads/Writes data flash locations 0x0000-0x000f 0x61 16 bytes Reads/Writes data flash locations 0x0010-0x001f 0x62 16 bytes Reads/Writes data flash locations 0x0020-0x002f 0x63 16 bytes Reads/Writes data flash locations 0x0030-0x003f 0x64 16 bytes Reads/Writes data flash locations 0x0040-0x004f 0x65 16 bytes Reads/Writes data flash locations 0x0050-0x005f 0x66 16 bytes Reads/Writes data flash locations 0x0060-0x006f 0x67 16 bytes Reads/Writes data flash locations 0x0070-0x007f 0x68 16 bytes Reads/Writes data flash locations 0x0080-0x008f 0x69 16 bytes Reads/Writes data flash locations 0x0090-0x009f 0x6a 16 bytes Reads/Writes data flash locations 0x00a0-0x00af 0x6b 16 bytes Reads/Writes data flash locations 0x00b0-0x00bf 0x6c 16 bytes Reads/Writes data flash locations 0x00c0-0x00cf 0x6d 16 bytes Reads/Writes data flash locations 0x00d0-0x00df 0x6e 16 bytes Reads/Writes data flash locations 0x00e0-0x00ef 0x6f 16 bytes Reads/Writes data flash locations 0x00f0-0x00ff 0x70 16 bytes Reads/Writes data flash locations 0x0100-0x10f 0x71 16 bytes Reads/Writes data flash locations 0x0110-0x011f 0x72 16 bytes Reads/Writes data flash locations 0x0120-0x012f 0x73 4 bytes Reads/Writes data flash locations 0x0130-0x133

application-notes.digchip.com/001/1-1474.pdf

is it this program can send it?

laszlodaniel commented 4 years ago

Yes, it can do it but you don't want to write to data flash. You only need to change EEPROM. In the Sanyo firmware these commands start at 0x40

define SetROMAddress 0x40 -> 0x51

define PeekROMByte 0x42 -> 0x52

define PeekROMBlock 0x43 -> no register for block read

These work similarly like the description in the .pdf.

Use the read_rom_byte() and read_rom_block() functions in the Arduino code with modified registers if you want to try it out. There are jumps in these functions because the Sanyo firmware is funny as hell. You may need to remove them.

youxiaojie commented 4 years ago

what is the read rom function? [RX->] ROM block data received 3D 00 24 84 05 00 00 32 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 20

[INFO] ROM address: 00 00; Data: 32 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F 9F

[RX->] Device is ready 3D 00 03 80 01 00 84

seems watchdog is working. click by byte:

[RX->] OK 3D 00 03 8F 00 00 92

[<-TX] Read ROM byte by byte 3D 00 02 04 04 0A

[RX->] ROM byte data received 3D 00 84 84 04 00 00 24 24 24 24 24 FF FF 01 7E 46 00 00 08 64 23 7D 7D FF FF FF 00 38 E7 A9 C0 5C 31 7D A7 A7 A7 A7 03 06 04 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 00 1A 17 16 16 16 16 16 16 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 83

[INFO] ROM address: 00 00; Data: 24 24 24 24 24 FF FF 01 7E 46 00 00 08 64 23 7D 7D FF FF FF 00 38 E7 A9 C0 5C 31 7D A7 A7 A7 A7 03 06 04 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 00 1A 17 16 16 16 16 16 16 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A

[RX->] ROM byte data received 3D 00 84 84 04 00 80 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 8C .............. [INFO] ROM address: 87 80; Data: B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0 B0

[RX->] OK 3D 00 03 8F 00 00 92

laszlodaniel commented 4 years ago

The read_rom_block() functions blocks your code because the fuel gauge IC doesn't respond with a 20-byte block. With the default registers this function won't work for you. Again this code you're playing with is for Sanyo-firmware. You need to make changes for TI-firmware compatibility.

So, again:

define SetROMAddress 0x40 -> 0x51

define PeekROMByte 0x42 -> 0x52

define PeekROMBlock 0x43 -> no register for block read

Rename SetROMAddress in the read_rom_byte() function to 0x51 and PeekROMByte to 0x52.

youxiaojie commented 4 years ago

aha, your code is for sanyo-specific-firmware. the sanyo changes alot! the rom is the eeprom(data flash)?

youxiaojie commented 4 years ago

how to distinguish sanyo-firmware? which register record it?

youxiaojie commented 4 years ago

thanks your patience!!

laszlodaniel commented 4 years ago

Well, if common commands don't work then it's Sanyo. Just kidding! :'D Block read the 0x20 register (ManufacturerName) and if it reads SANYO then the fuel gauge IC has probably Sanyo-firmware. I don't know exactly how to query firmware-type.

ROM = data flash = the code running to calculate battery health. EEPROM = settings, parameters, everything you want to change is here, I don't know how to access it, though.

youxiaojie commented 4 years ago
SLUS640A–JUNE 2005–REVISED JUNE 2005
DATA FLASH ADDRESS                                                           DATA
                                        NAME              LI-ION EXAMPLE
HIGHBYTE LOWBYTE                                                             MSB LSB
0x32 0x33                       Design Capacity 7200 mAh            1c 20
0x36 0x37                 Full Charge Capacity 7200 mAh           1c 20
0x0c 0x0d                      Cycle Count         0                          00 00

this is I want to change.
 The bq2084-V133 saves the
cycle count value to Cycle Count (DF 0x0c-0x0d) after an update to CycleCount().

directly write to this reg is not work.so I hope to try this command

youxiaojie commented 4 years ago

and I am caring your battery. if you buy and solder a new chip from market , will it working?

laszlodaniel commented 4 years ago

Well then, you don't have to use functions, just use the write section of the GUI. But before that read the bytes and make a backup of the original data, in case of something goes wrong (wrong checksum somewhere...):

Design Capacity: Write: 51 0032 Read: 52 -> DC_HB Write: 51 0033 Read: 52 -> DC_LB

Full Charge Capacity: Write: 51 0036 Read: 52 -> FCC_HB Write: 51 0037 Read: 52 -> FCC_LB

Cycle Count: Write: 51 000C Read: 52 -> CC_HB Write: 51 000D Read: 52 -> CC_LB

"The user may use Command 0x50 to write individual bytes up through 0xff only. The first parameter is the data byte, the second is the address. For example, writing a 16-bit integer requires two writes:

lError=WriteSMBusWord(&H50,yDataMS,yAddress) lError=WriteSMBusWord(&H50,yDataLS,yAddress+1)"

Design Capacity: Write: 50 1C32 Write: 50 2033

Full Charge Capacity: Write: 50 1C36 Write: 50 2037

Cycle Count: Write: 50 000C Write: 50 000D

Read these back to confirm success. And of course you need to be in unsealed mode (or full access) to be effective. I'm not sure about byte-order so use these commands with caution, double-triple check what you're doing.

youxiaojie commented 4 years ago

ok, I try to rewrite and compile again first.

define SetROMAddress 0x51 // word write only

define PeekROMByte 0x52

ok?

take this Cycle Count: Write: 50 000C Write: 50 000D as example, in gui, Tools-> Register choose (50), write fill in 000c and click byte button?

define SafetyAlert 0x50

define SafetyStatus 0x51

define PFAlert 0x52

how your program tell it is reg 50 or command 50?

laszlodaniel commented 4 years ago

You don't need to mess with the defines. Only thing to do is to select register, fill in data and click on the Word button. You always write words. Register and command mean the same thing in this context.

youxiaojie commented 4 years ago

great!!!! thanks.

youxiaojie commented 4 years ago

success with 50 command!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

laszlodaniel commented 4 years ago

Alrighty then! Is it permanent? Can you write down the steps here from the beginning?

youxiaojie commented 4 years ago

sure, I am. tomorrow I make a note. I did not change the battery,just using the old one. making fcc=design capacity. and clear cyclecounter. install into computer to test.and then change a bigger 18650 , and test again. without changing parameter, note the time. and changing the parameter again, and note time again. to show the parameter's effect. and test whether the paramater is missing when 18650 disconnect. I noticed changing 18650 maybe result in melt-fuse and permanent failure. so test later.

laszlodaniel commented 4 years ago

I've heard that the correct way of disassembly is to cut the wires one-by-one going from + to - and when inserting new cells solder the wires closest to - first and go up from there to +. I did this way and the fuse didn't blow and there was no permanent failure flag.

youxiaojie commented 4 years ago

however a weiry situation, I use batteryinfoview to show,it alway show 71040mwh = (23200mah)11.1v but I did not find any register for 3200.the design capacity read from this software is 4800mah. the datasheet show of sanyo ur18650f is 2400. interesting

youxiaojie commented 4 years ago

I've heard that the correct way of disassembly is to cut the wires one-by-one going from + to - and when inserting new cells solder the wires closest to - first and go up from there to +.thank!!!!very much!!!!

youxiaojie commented 4 years ago

Alrighty then! Is it permanent? Once in sealed mode, the part can never permanently return to Unsealed or Full Access modes. This is the datasheet, and I do change datasheet first and then disconnect battery is to try 1 whether PF bit will set,2 whether I will return unseal mode after power loss.

do you know 0x60 command to read write by block? and does it run once to read run twice to write?

laszlodaniel commented 4 years ago

I meant that the changes you made (DC, FCC, CC) are those permanent or did they revert to their original value? This is probably a silly question but one never knows what could really happen.

Unsealed and Full Access modes are not automatically enabled. The battery doesn't stay in those modes indefinitely. So they must be enabled for every diagnostic session. It's not a problem though, you know the keys.

You want to change the unseal key at the 0x60 register?

youxiaojie commented 4 years ago

For the bq2084 0x58 64 bytes Reads/Writes data flash locations 0x0000-0x003f 0x59 64 bytes Reads/Writes data flash locations 0x0040-0x007f 0x5a 64 bytes Reads/Writes data flash locations 0x0080-0x00bf 0x5b 64 bytes Reads/Writes data flash locations 0x00c0-0x00ff 0x5c 52 bytes Reads/Writes data flash locations 0x0100-0x133 Or use: 0x60 16 bytes Reads/Writes data flash locations 0x0000-0x000f 0x61 16 bytes Reads/Writes data flash locations 0x0010-0x001f ........................... 0x72 16 bytes Reads/Writes data flash locations 0x0120-0x012f 0x73 4 bytes Reads/Writes data flash locations 0x0130-0x133

In the function below, it is demonstrated how to write a word to flash using the 16 byte SMB block commands above. ReadSMBusByteArray( ) and WriteSMBusByte Array are low level communication functions that implement the SMBus block commands. g_fWRITE_DELAY is a delay value of 0.12 seconds to insure that the block write has completed. Function WriteFlashWord(iAddress As Integer, yDataMS As Byte, yDataLS As Byte) As Long Dim lerror As Long Dim yDtaRow(16) As Byte Dim yNextDtaRow(16) As Byte Dim bWrapAround As Boolean Dim iLen As Integer Dim iLenNext As Integer iLen = 16 iLenNext = 16 '//Is this a wrap-around write? If (iAddress Mod 16) = 15 Then bWrapAround = True '//Read the data flash row lerror = ReadSMBusByteArray(&h60 + (iAddress \ 16), yDtaRow(), iLen) '//Read the following row if needed If bWrapAround Then lerror = ReadSMBusByteArray(&h60 + (iAddress \ 16) + 1, yNextDtaRow(), iLenNext) End If '//Modify elements in first data row yDtaRow(iAddress Mod 16) = yDataMS If (False = bWrapAround) Then yDtaRow((iAddress Mod 16) + 1) = yDataLS Else yNextDtaRow(0) = yDataLS End If '//Write the row(s) back lerror = WriteSMBusByteArray(&h60 + (iAddress \ 16), yDtaRow(), iLen) If (True = bWrapAround) Then ‘ // wait for block write to finish DoDelay g_fWRITE_DELAY lerror = WriteSMBusByteArray(&h60 + (iAddress \ 16) + 1, yNextDtaRow(), iLenNext) Else '//wait for block write to finish DoDelay g_fWRITE_DELAY End If WriteFlashWord = lerror End Function

are you heard of and know this command ? after download some new chips datasheet, I find 80zxx sieries have different command to access data flash and different data flash format. which is the new command with new 80zxx chips?

youxiaojie commented 4 years ago

does "dump register function" dump every register with mode word? is there way to change to block mode when access some standard register such 0x20~0x3.

youxiaojie commented 4 years ago

I meant that the changes you made (DC, FCC, CC) are those permanent or did they revert to their original value? This is probably a silly question but one never knows what could really happen.

the value is kept. the first ,before changing fcc, I charged full (laptop show full),reading about 4000mah, and then after changing fcc , connected to laptop ,the computer shows nearly 80% full and continue charge. and then till 93% for nearly 20 minutes, jumps to show 100% stops charging. use this gui reading all value is kept.

youxiaojie commented 4 years ago

The read_rom_block() functions blocks your code because the fuel gauge IC doesn't respond with a 20-byte block. With the default registers this function won't work for you. Again this code you're playing with is for Sanyo-firmware. You need to make changes for TI-firmware compatibility.

So, again:

define SetROMAddress 0x40 -> 0x51

define PeekROMByte 0x42 -> 0x52

define PeekROMBlock 0x43 -> no register for block read

Rename SetROMAddress in the read_rom_byte() function to 0x51 and PeekROMByte to 0x52.

I guess the rom block read/write is cmd 0x58 and 0x60 with different block size. according to above demo , is it right?

laszlodaniel commented 4 years ago

which is the new command with new 80zxx chips?

I have no idea, this is out of my scope of knowledge.

does "dump register function" dump every register with mode word?

Yes.

is there way to change to block mode when access some standard register such 0x20~0x3.

It's possible. It would be better to use the registers with their intended purpose (some store words, some store blocks). A new function needs to be implemented.

the value is kept. the first ,before changing fcc, I charged full (laptop show full),reading about 4000mah, and then after changing fcc , connected to laptop ,the computer shows nearly 80% full and continue charge. and then till 93% for nearly 20 minutes, jumps to show 100% stops charging. use this gui reading all value is kept.

Cool!

I guess the rom block read/write is cmd 0x58 and 0x60 with different block size. according to above demo , is it right?

You're right, the Arduino code needs modification to adapt to other block lengths. Actually at first I determined block length from the first byte read from the register then just went with fixed 0x20 (32) bytes because it's always the same for Sanyo-firmware.

youxiaojie commented 4 years ago

My portC is burnt strangely:( using default code never connected!

youxiaojie commented 4 years ago

please take a look at my document. thanks. to see is there errors.

laszlodaniel commented 4 years ago

Does the GUI connect to the Arduino but there's no communication with the battery? Your documentation is cool! I merged it with my repo.

youxiaojie commented 4 years ago

I changed portB every thing is ok.

laszlodaniel commented 4 years ago

It would be good to know why PortC doesn't work, though.

youxiaojie commented 4 years ago

I forget to write we need add softi2cmaster lib into the proper directory. later I will add it.

youxiaojie commented 4 years ago

maybe hot plug? A5 could not be low always high! broken....so It will be a lessons! do everthing could not be lazy.

laszlodaniel commented 4 years ago

I forget to write we need add softi2cmaster lib into the proper directory. later I will add it.

That's not a requirement. Everyone should download it from its original place.

maybe hot plug? A5 could not be low always high! broken....so It will be a lessons! do everthing could not be lazy.

You mean hot-swap? It shouldn't be a problem, they are signal-wires. I2C is an open-collector bus, meaning you have to have external or internal pullup resistor on its pins all the time. Arduino pulls the pin low when necessary (0 bit) and lets it go (1 bit). A5 is the clock pin, perhaps there's a contact issue somewhere.