KT motor controllers -- Flexible OpenSource firmware for BMSBattery S/Kunteng KT motor controllers (0.25kW up to 5kW)

casainho said:
daffy99 said:
FWIW, and that's just a remark, because I don't know anything, I do notice a very pronounced absence of the C keyword "volatile" in the firmware C source code (git repo search). I keep reading about interrupts and global variables all the time, though, which we would indicate that volatile would rather be expected ...
Must say that I just use local/static local or global variables. I don't know why the need of volatile, need to read about that.

And the optimizations, I have them turned off on the makefile options inside. I need no optimizations to be able to better debug the code.

daffy99 has a good point. A non-volatile variable can be optimized to be the whole time in a register; if, let's say, that's in the main loop and you also use that same variable in an interrupt, changes in the main won't be seen in the interrupt. Like you updating a speed value in the main loop and it (not) being seen in an interrupt. The volatile keyword will force the variable's memory to always be updated (at an execution speed penalty cost, since it's slower than updating a CPU register only).
 
casainho said:
I don't know why the need of volatile

volatile is all about making sure that the "right" content is found in variables whenever they are read.

This applies, for instance, whenever data is written in an interrupt routine, while it is being read elsewhere, too. (The inverse is true as well) And that's where registers and optimization come into play - having no optimization in place often makes software behave almost as if every single variable was declared with "volatile".

https://en.wikipedia.org/wiki/Memory_ordering looks at this more from a hardware perspective, most of which probably does not apply to the rather simple and straight-forward STM8 CPU.
 
stancecoke said:
I think I got the solution now :D

I took the SDCC installation from the original source:
https://sourceforge.net/projects/sdcc/files/
This leads to different compiler output.

Now I tried the SDCC version from the STM8 binutils:
https://sourceforge.net/projects/stm8-binutils-gdb/files/cygwin/x86/release/sdcc/

Are these the exact same _version_ of the compiler?

It would be very helpful to note in the README the _exact_ version of the compiler which is known to work. In case the same version of the compiler shows different behaviour, the tool chain that built that compiler has a problem. In which case the tool chain / provenance of the compiler binary would also be very helpful to know about.

I had a look at the state of sdcc a few weeks ago, and there seemed to have been some material movement related to the STM8.
 
It's 3.6.0 #9615 in both cases in the head of the asm code. The only difference is cygwin/minGW64...

file.php


regards
stancecoke
 

Attachments

  • Difference Cygwin_Mingw64.JPG
    Difference Cygwin_Mingw64.JPG
    41.9 KB · Views: 1,920
stancecoke said:
It's 3.6.0 #9615 in both cases in the head of the asm code. The only difference is cygwin/minGW64

Hm. This is consistent with https://sourceforge.net/p/sdcc/code/10108/log/?path=/tags/sdcc-3.6.0/sdcc

So cygwin has the "working" version? In this case the best way to proceed is to have a Windows build environment completely derived from cygwin?

If you are certain that your mingw environment had been correctly set up (no intermingling of mingw / cygwin), then the mingw maintainers possibly would be happy to hear about your experience.
 
Still strange. The .asm files are identical now, except the local path entries.
But the behavior is quite the same as yesterday. The motor runs for 1 or 2 seconds after powering up the system, then it stops. A difference now is, that the UART still works after the motor stopped and the throttle value is displayed correctly.
@casainho: can you test my attached main.bin, please? It's compiled from the recent master branch.
Can you upload your .hex file? Then I'll try yours, and we can compare the binaries.

Thank you
stancecoke
 

Attachments

  • main.zip
    24.8 KB · Views: 26
Today I prepared the ebike so I could go for a ride. I did about 10km up to a friend workshop of electric motorbikes and ebikes.
On the firmware, I implemented the speed and current controller and I drove using it -- the throttle defined the speed and current and the lowest of both values are applied to duty_cycle. I was happy with the results.
Code:
  // throttle will setup motor current from 5A to 12A
  ui16_temp = (uint16_t) (map ((int32_t) ui8_ADC_throttle, ADC_THROTTLE_MIN_VALUE, ADC_THROTTLE_MAX_VALUE, 16, 96));
  motor_controller_set_current (ui16_temp);

  // throttle will setup motor speed from 0 to 29km/h (in 26 inch wheel, Q85 328RPM)
  ui16_temp = map (ui8_ADC_throttle, ADC_THROTTLE_MIN_VALUE, ADC_THROTTLE_MAX_VALUE, 0, MOTOR_OVER_SPEED_ERPS);
  motor_controller_set_speed_erps (ui16_temp);
Code:
  ui8_current_pwm_duty_cycle = pwm_get_duty_cycle ();
  ui8_pwm_duty_cycle_a = motor_current_controller (ui8_current_pwm_duty_cycle);
  ui8_pwm_duty_cycle_b = motor_speed_controller (ui8_current_pwm_duty_cycle);
  // apply the value that is lower
  motor_set_pwm_duty_cycle_target (ui8_min (ui8_pwm_duty_cycle_a, ui8_pwm_duty_cycle_b));

At my friends workshop, we started to play with the wires of hall sensors and soon we saw some smoke coming from the controller... luck I that he had a new Kunteng sinewave controller (as some others non Kunteng) and we exchanged for that new one. Was good because I didn't had any with original firmware and so I did the ride back with the original controller / firmware to understand the differences.

The issue still present on the firmware is that transition to sinewave sometimes seems to fail, on the original never fails. What I could quick see on the original:
- sinewave commutation seems to happen at about ~30 erps, even if the motor has or not a load
- after entering in sinewave, will stay in that mode until motor stops, which may happens at very low speeds like 10 or less erps

My ebike have some problems that I need to resolve and also I need to setup again all the wiring to my development setup, may take some days...





 
Njay said:
casainho said:
daffy99 said:
FWIW, and that's just a remark, because I don't know anything, I do notice a very pronounced absence of the C keyword "volatile" in the firmware C source code (git repo search). I keep reading about interrupts and global variables all the time, though, which we would indicate that volatile would rather be expected ...
Must say that I just use local/static local or global variables. I don't know why the need of volatile, need to read about that.

And the optimizations, I have them turned off on the makefile options inside. I need no optimizations to be able to better debug the code.

daffy99 has a good point. A non-volatile variable can be optimized to be the whole time in a register; if, let's say, that's in the main loop and you also use that same variable in an interrupt, changes in the main won't be seen in the interrupt. Like you updating a speed value in the main loop and it (not) being seen in an interrupt. The volatile keyword will force the variable's memory to always be updated (at an execution speed penalty cost, since it's slower than updating a CPU register only).
So for me is clear that current firmware will not work without optimizations. I started to use volatile but I think it needs a deep thinking to understand which variables need or not need to volatile. It is also good to know that with optimizations, the PWM interrupt code processing time may be improved.
 
I just recorded some oscilloscope images of the original firmware phase B current signal and took some notes. I think original also does FOC but the angle is "advanced" a bit, meaning it should be bale to run motors a bit fast at expense of more current:

https://opensourceebikefirmware.bitbucket.io/Motor_controllers--BMSBattery_S_series--BMSBattery_S06S--Phase_B_current_signal.html
 
stancecoke said:
Still strange. The .asm files are identical now, except the local path entries.
But the behavior is quite the same as yesterday. The motor runs for 1 or 2 seconds after powering up the system, then it stops. A difference now is, that the UART still works after the motor stopped and the throttle value is displayed correctly.
@casainho: can you test my attached main.bin, please? It's compiled from the recent master branch.
Can you upload your .hex file? Then I'll try yours, and we can compare the binaries.
Your file works for me and have speed limited to 27km/h.

My file in attachment.

I was able to repair my controller, had to exchanged 2 mosfets in short circuit.
 

Attachments

  • BMSBattery_S_controllers_firmware.zip
    123.4 KB · Views: 26
casainho said:
I was able to repair my controller, had to exchanged 2 mosfets in short circuit.
:D then we have the same score now!

casainho said:
Your file works for me and have speed limited to 27km/h.
My file in attachment.
Hmm, interesting, that my code works for you. So there must be a difference in the controllers or in the motors. Have you changed anything in the option bytes of your controller?!

Your file doesn't work for me, I just see the display communication but the motor doesn't move at all :-(

regards
stancecoke
 
This are the 4th mosfets, that was my 2nd repair to this controller.

I didn't changed anything.

Try to run using only the throttle, and with th 6 steps only, it must work!!
 
casainho said:
This are the 4th mosfets, that was my 2nd repair to this controller.
OK, you are two in front :wink:

casainho said:
I didn't changed anything.
Try to run using only the throttle, and with th 6 steps only, it must work!!
The motor says just nothing, I tried it several times. Not even the jump at powering the system...

I've even compiled the recent master branch with linux, it's the same result: UART working, motor not moving.
(perhaps you should add a hint on bitbucket, that you have to install the texinfo package to set up the toolchain, I needed some time to figure that out...)

regards
stancecoke
 
stancecoke said:
casainho said:
This are the 4th mosfets, that was my 2nd repair to this controller.
OK, you are two in front :wink:

casainho said:
I didn't changed anything.
Try to run using only the throttle, and with th 6 steps only, it must work!!
The motor says just nothing, I tried it several times. Not even the jump at powering the system...

I've even compiled the recent master branch with linux, it's the same result: UART working, motor not moving.
(perhaps you should add a hint on bitbucket, that you have to install the texinfo package to set up the toolchain, I needed some time to figure that out...)

regards
stancecoke
So if you have Linux working, go and install the debug -- that way you can go and see the program running and see what he is doing, that way you will even understand better the code for improving your future developments!!
 
casainho said:
So if you have Linux working, go and install the debug
I get the error
Code:
Makefile:2799: die Regel für Ziel „src/svf/svf.lo“ scheiterte
when running the make command on openocd-0.10.0...
I have no desire to spend hours on trying to get the debugger running with linux. I now got the proof, that not the compiling with windows is the problem... Perhaps I'll try OpenOCD on windows. But to be honest I'm thinking on doing the further development on the J-LCD branch, as this is the best working version for me so far.

regards
stancecoke
 
casainho said:
So for me is clear that current firmware will not work without optimizations.

The current C source code might not be defined well enough to work _with_ compiler optimizations turned _on_.

You committed a change to github where you added volatile to one variable; that may be one step into the right direction.

casainho said:
understand which variables need or not need to volatile.

That's quite simple, actually. Checklist:

1.) is there any means that one subtask may read from a given variable, while another subtask writes to it? Interrupt routines, as little as I know about the STM8, seem to be the only scenario where the "while" condition might hold true.

If yes
2.) is it of critical importance that the true value of the variable is always being read? In some cases, it simply is not material that the true value is being read. One example would be some function writing to a global variable, with some other (concurrent) function reading the value for display, and only for display, every 100ms. Using "volatile" would be correct, yet a waste of resources, as delaying reporting by, say, 100ms is probably immaterial.

What you do not want at all, ever, is to introduce synchronization / serialization into this heavily resource-constrained platform. You only want to have consistency in operations, no more, no less.

Variables on the stack very, very rarely are volatile (you'd have to have some concurrent task update something on the stack, which would require some C gymnastics (pointer to variable being passed around), which I do not remember having seen in the C source code on github). Simply ignore that for the moment, and make as many variables as possible local in scope.
 
Original firmware
Throttle seems to control the PWM duty_cycle + motor current.

PWM duty_cycle
When I increase the throttle, the motor runs fast (keeping the same motor load). If I increasing the motor load and keep the same throttle, the speed decreases.

Current control
If I block the wheel with my hand, the motor keeps doing force/torque and I can read the current value on the power supply. The current is low at the begin of throttle and increases as I increase the throttle.

The current control, at least while the motor is blocked, must be done using AN8 (motor total current) because phase B current signal may be 0 at that situation.

With the motor blocked, it tries to rotate (doing force/torque) but after some seconds it stops.
 
Correction about how original firmware works:

Throttle control depends of P3 parameter:

P3 = 1 → speed control: throttle controls the PWM duty_cycle, motor will run up to the max defined speed and up to motor max defined current.
Changing the assist level seems to have no effect.

P3 = 0 → imitation torque control: throttle controls the speed and the torque/max current of the motor.
Max assist level means that motor will run up to max defined speed and up to motor max defined current. Choosing different assist levels will make the motor running up to fractions of those values.

NOTE: each throttle position seams refer to always the same speed value, where the max throttle position may be equivalent to 72km/h (max defined speed possible on LCD5).

How max current control works

If I block the wheel with my hand, the motor keeps doing force/torque and I can read the current value on the power supply. The current is low at the begin of throttle and increases as I increase the throttle.

The current control, at least while the motor is blocked, must be done using AN8 (motor total current) because phase B current signal may be 0 at that situation.

With the motor blocked, it tries to rotate (doing force/torque) but after some seconds it stops.

----

Seems that P3 = 0 → imitation torque control: is what I already implemented in the firmware. Also I see torque sensor as the throttle, where the output value will define the motor speed + torque/current values:

Code:
void throttle_pas_torque_sensor_controller (void)
{
  // only throttle implemented for now
  ui8_ADC_throttle = ui8_adc_read_throttle ();

  // throttle will setup motor current from 0A to 12A
  ui16_temp = (uint16_t) (map ((int32_t) ui8_ADC_throttle, ADC_THROTTLE_MIN_VALUE, ADC_THROTTLE_MAX_VALUE, 0, 96));
  motor_controller_set_current (ui16_temp);

  // throttle will setup motor speed from 0 to 29km/h (in 26 inch wheel, Q85 328RPM)
  ui16_temp = map (ui8_ADC_throttle, ADC_THROTTLE_MIN_VALUE, ADC_THROTTLE_MAX_VALUE, 0, MOTOR_OVER_SPEED_ERPS);
  motor_controller_set_speed_erps (ui16_temp);
}

void motor_controller_high_level (void)
{
  ui8_current_pwm_duty_cycle = pwm_get_duty_cycle ();
  ui8_pwm_duty_cycle_a = motor_current_controller (ui8_current_pwm_duty_cycle);
  ui8_pwm_duty_cycle_b = motor_speed_controller (ui8_current_pwm_duty_cycle);
  // apply the value that is lower
  motor_set_pwm_duty_cycle_target (ui8_min (ui8_pwm_duty_cycle_a, ui8_pwm_duty_cycle_b));
}
 
I've forked the project now, to work on the J-LCD branch.
I've added a function to disable the speed limit by a "Morse-Code" with the break lever :wink:

so working in this fork is now:
- sine wave commutation with simplified FOC
- speed limit
- battery current limit
- throttle mode
- throttle + PAS mode
- torquesensor-mode
- cheat for disabling speed limit
- Overcurrent emergency stop
- J-LCD Display / Forerider App for smartphone

- Graphic user interface (Java Tool) for easy use of the firmware (has to be updated with the latest features)

I hope we can switch back to one common project some day, but at the moment I can't do anything with the recent master branch.

Regards
stancecoke
 
Thank you daffy99.

stancecoke said:
I've forked the project now, to work on the J-LCD branch.
Did you ride with your version?

I am not happy with my motor code, because sometimes it fails. I am pretty sure original firmware is doing only 60º degrees interpolation. And that seems more robust if I use on the firmware, maybe I will try to avoid 360º interpolation.

Also they do FOC but the sync is not on the hall sensor transition but a bit after - that seems to be the reason why their firmware uses less energy: Vbat = 28V | Current = 3.7A; Speed = 45km/h VERSUS Vbat = 28V | Current = 4.2A; Speed = 45km/h.

Next weekend I will have a ride of 90km (with 3 stops to charge, every 30km) with others, I would like to take my EBike with Q100 rear motor and use a S12S with our firmware. For that I need:
1. stable firmware for the motor, that always start and never blocks/short circuit (TODO)
2. throttle with PWM duty_cycle control or speed + current control (DONE)
3. LCD display showing battery state of charge and speed (DONE)
4. LCD display that can configure max speed and assist levels (TODO)

Let's see if I can do 1. and 4. on next week and test on the EBike.
 
casainho said:
Did you ride with your version?
Not yet, but I tested it several hundred times on the test bench with the battery and loads bigger than 250W, and found no problems.
The efficiency is exactly the same as with the orinial Kunteng firmware.

casainho said:
Also they do FOC but the sync is not on the hall sensor transition but a bit after - that seems to be the reason why their firmware uses less energy: Vbat = 28V | Current = 3.7A; Speed = 45km/h VERSUS Vbat = 28V | Current = 4.2A; Speed = 45km/h.
In the "shift of angles" branch I had to decrease the margins for the FOC-control, to get back the best efficiency.

Code:
if (ui16_current_array[4]>>2 > 122)
		{
		  i8_position_correction_value++;
		}
		else if (ui16_current_array[4]>>2 < 120)
		{
		  i8_position_correction_value--;
}

regards
stancecoke
 
stancecoke said:
casainho said:
Also they do FOC but the sync is not on the hall sensor transition but a bit after - that seems to be the reason why their firmware uses less energy: Vbat = 28V | Current = 3.7A; Speed = 45km/h VERSUS Vbat = 28V | Current = 4.2A; Speed = 45km/h.
In the "shift of angles" branch I had to decrease the margins for the FOC-control, to get back the best efficiency.

Code:
if (ui16_current_array[4]>>2 > 122)
		{
		  i8_position_correction_value++;
		}
		else if (ui16_current_array[4]>>2 < 120)
		{
		  i8_position_correction_value--;
}
Great, this means it that seems correct what I am seeing. You changed the Y value to be 121 while the zero cross should happen at 127. I want to make the code so we can adjust on X values / angle degrees.

I think I didn't mention before but the code I did recently with current control, uses the motor current and the 10 bits ADC -- I had to correct the code of ADC because reading 10 bits had a bug. You can now use the ADC code as 8 and 10 bits now works well and the fastest possible, I believe.
 
Alan B said:
It seems odd that they would delay the timing from the hall transition event. Usually earlier timing (advance) is desired as RPM increases. Unless the halls are in the wrong place. The sensorless back EMF transition occurs much earlier (30 degrees) than the halls and commutation event.
I am not sure if is a delay or an advance. See:

Original firmware:
89-1.png


Our OpenSource firmware (hall sensor is not the real signal but inverted by microcontroller):
91-1.png
 
Finally found the issue that were provoking short circuit sometimes when starting the motor:
Code:
    pwm_init_6_steps ();
    // put the phases to a valid state
    pwm_phase_a_disable (ui8_duty_cycle);
    pwm_phase_b_disable (ui8_duty_cycle);
    pwm_phase_c_disable (ui8_duty_cycle);
    ui8_hall_sensors_last = 0; // this way we force execution of hall sensors code
Before I wasn't putting the phases at a valid state after initialize the PWM for 6 steps / block commutation!!

Now the motor always start as it should -- now seems I have a robust motor startup and transition to/from block commutation <-> sinewave.
 
Back
Top