Building New Firmware
For those wanting to change the firmware in their chargers, this is the post with the meat in it. I'm sorry it took so long to find the time to get this together.
The original firmware was written in C. I have snippets of old versions of the code, but nowhere near enough to generate a functioning firmware. So for now, we have to settle for assembling from source code that has been disassembled from the original. Worse than that, we have to keep the code aligned with the original code, because we might have have separated code from data properly, so some bytes that should be relocated perhaps aren't, and some that should not be might be. By keeping the code largely in its original locations, we avoid all these problems; pointers still point to the code or data where they are meant to point.
Unfortunately at this point, it is necessary to use the
Keil uVision compiler / assembler / linker / debugger tool. This software is expensive, and we only need a fraction of its full power. Hopefully one day, I'll figure out a way of using open source tools for this, but that could be a long way off.
[ Edit: see
later post re IAR Embedded Workbench. ]
The good news is that for the most part, we only want to change some data, not executable code. For this we could also use assembly language, except that alas the assembler involved doesn't handle floating point constants, and we need a lot of these. They are a royal pain to maintain by hand, so we actually hand translate the data to C, and get the C compiler to generate the data for us in the correct format.
But now we have two source code files, and we need to generate a hex file for programming into the microcontroller. So these source code files are assembled or compiled to object code, and the linker is used to combine these into an "executable" file. This file gets converted to a hex file using some command line tools. To make matters worse, there are a few edits along the way.
If this sounds complicated and scary, well, it is a bit. So this is not for everyone. But if you know a little about bits and bytes, and aren't scared by having to edit a few files and execute a few scripts, then read on.
Let's start with the most important part, the "curve" data. Here is the start of such a file; the complete contents are available as attachment 2500W_master_144V_4stage.zip:
// 144 V one stage curves
#pragma src // Generate assembler
typedef unsigned char byte; // U8
typedef unsigned int word; // U16
byte code charge_curve = 20; // Version 2.0 data
byte code user_sel = 9; // User has chosen the third capacity (index=2)
float code Ah_capacities[10] = {
180.0, 180.0, 180.0,
180.0, 180.0, 180.0,
180.0, 180.0, 180.0,
180.0};
byte code cell_num[10] = {
42, 43, 44,
45, 46, 47,
48, 49, 50,
51
};
byte code master_info_send_en = 1; // Leave as 1 to talk to the slave if needed
byte code listen_info_send_en = 1; // Leave as 1 to emit useful data packet
float code output_line_res = 12.5e-3; // 12.5 mR line resistance (others have 25 mR)
// 51 x 3.65 = 186.15 V; call it 186V? Same at full and half power since one stage
word code vol_need_full_power = 1860; // Voltage needed at full power, times 10, as integer
word code vol_need_half_power = 1860; // Voltage needed at half power, times 10, as integer
// 51 * 3.8 V = 193.8
float code unused1 = 193.8; // Unused; make it the same as below
float code vol_need_limit_max = 193.8; // Maximum voltge ever needed?
float code ac110v_power_max = 1350.0; // Maximum power to be delivered when powered from 110/120 V
float code ac220v_power_max = 2420.0; // Maximum power to be delivered when powered from 220/240 V
float code s0_batter_vol_start = 1.50; // Minimum volts per cell before charger will turn on
float code s0_batter_vol_over = 3.70; // Voltage per cell above which the battery is considered overcharged
byte code stateForOverTemp = 8; // State to go to if the battery becomes overheated
// NOTE: this may mean what to do when the voltage on pin 1 of the 7-pin connector
// does something (too high in voltage?)
// State 8 is the special "all finished, LED is solid green" state
float code s1_7_temp_over_protect_vol = 0; // ?
word code max_time_any_state = 0; // or e.g. 30 * 24 * 60;
// Maximum amount of time (minutes) in any state. 0 means unlimited
// Have seen 43200 = 30 days
float code code_dd3 = 3.20; // ?
I can't remember what the keyword
code is needed for; I think it has to do with the fact that the 8051 processor has about 4 address spaces, and here we need the "code" address space (flash memory).
After a few definitions, there is the declaration of the Ah_capacities array. There are ten "user selections", which can be selected with the switch at the front panel; the details are described elsewhere. This is the capacities part of the user selections, there is also the number of cells selection. In this file, we're using 180 Ah for all selections. (Sometimes the ten selections are split between a few combinations of capacity and number of cells). cell_num is the array for the number of cells. So user selection one is 180 Ah and 42 cells; user selection 10 is 180 Ah and 51 cells.
Not far down is a pair of variables called vol_need_full_power and vol_need_half_power . It's not totally clear what these should exactly be set to; for this example they are set the same. Unlike many of the variables here, this one is in the form of an integer number, representing tenths of a volt. So 186.0 V is represented by 1860; 123.4 V would be represented by 1234 in these variables. The charger will check these values against values stored in EEPROM, representing the maximum voltage the charger is capable of. If it fails this test, the charger will end up in an infinite loop apparently doing nothing. So these are a sort of desperation sanity check.
The variables ac110v_power_max and ac110v_power_max set the maximum power that the charger will attempt to use with 110/120 V and 220/240 V power respectively. You could adjust these if you had a limited supply available.
s0_batter_vol_start sets the voltage (per cell, average) below which the charger considers the battery to be too low to charge safely. Similarly, if it sees higher than
s0_batter_vol_over volts per cell average, it will refuse to charge it also. You can see that these are values appropriate to LiFePO4 cells, not lead acid or any of the higher voltage lithium chemistries. Only the total voltage is really important, but you may as well get these right to make it easier on yourself. These have "s0" in the name, indicating that they are accessed in state zero, which is the special state the charger begins in, before it closes the output relay and does actual charging.
Next part of the file:
byte code s0_led_r[5] = { 0x11, 0x44, 0, 0, 0};
// The LED pattern for state 0 (waiting to connect to the bat
tery at the very
// start of charging). First byte is for the red LED, se
cond is for green;
// others seem to be provision for more LEDs in the future
// LSB comes out first, e.g. with 0x1 0x00 0 0 you would see the red LED on for
// one second, then off for 7 seconds.
byte code s8_led_r[5] = {0, 0xFF, 0, 0, 0}; // LED pattern for state 8; solid green
byte code s8_SOC_percent = 100; // SOC at state 8 is 100%
byte code unused2 = 0;
float code extemp_sensor_modify = 3.0;
float code intemp_sensor_modify = 2.0;
float code intemp_sensor_use_low = 15.0;
float code intemp_sensor_use_high = 30.0;
float code bat_temp_pro_high = 50.0;
float code bat_temp_pro_high_hy = 40.0;
float code s0_batter_temp_protect_low = -20.0;
float code batter_temp_curve_base = 10.0; // Intercept and slope of temperature compensation line?
float code batter_temp_curve_lv = 0.0;
float code new_unknown1 = 10.0;
float code new_unknown2 = 0.0;
When in state zero, the dual-color LED will be flashing according to the pattern proscribed by the
s0_led_r array. Only the first two bytes are used. The 0x11 (binary 00010001) and 0x44 (binary 01000100) set the patterns for the red and green LEDs respectively. The least significant bit (last written) are accessed first. So you will see red (1 and 0), then black (0 and 0) then green (0 and 1) then black again (0 and 0). These are repeated again with the upper four bits, and the whole cycle is repeated.
Similarly, the LED pattern for special state 8 (end of charge) is set by array s8_led_r. With the second byte set to 0xFF (binary 11111111), the green LED is on solid, no flashing.
Continued next post.