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

I think that the gate-source capacitors of the MOSFETs are 10nF, not 100nF as wrtitten in the schematic.

Simply because 100nF seems to be way too high.

More, according to Adweb, the KT63 has 10nf caps.

Hope this helps.

Thierry
 
casainho said:
Also I keep seeing the Throttle/PAS/Torque sensor as "speed+torque". Using the torque sensor, your controller would control the motor speed and the motor current max:
- read motor_get_motor_speed_erps() to get motor speed and control the motor speed by adjusting the value of motor_set_pwm_duty_cycle_target()
- adjust linearly the motor_set_current_max() from the torque sensor value
stancecoke, how I see Throttle/PAS/Torque sensor, all of them working as "speed+torque" and for instance: at 50% of "speed+torque", the motor would run at 50% of max current and 50% of max speed.
- throttle: outputs a value from 0 to 100% (reading the ADC signal)
- PAS: outputs a value from 0 to 100% (reading the cadence)
- Torque sensor: outputs a value from 0 to 100% (considering the torque)
 
ThierryGTLTS said:
I think that the gate-source capacitors of the MOSFETs are 10nF, not 100nF as wrtitten in the schematic.

Simply because 100nF seems to be way too high.

More, according to Adweb, the KT63 has 10nf caps.
Can you please interact directly on the issues?? that would save me precious time :)

https://github.com/KingQueenWong/bmsbattery_s06s_controller_hardware/issues/
 
When I have the motor code working (just missing motor max current control due to some ADC reading issues), which I hope to be this week, I will promote the project on Arduino world as then everyone will be able to control a brushless motor from Arduino, using UART commands :)

Any ideas how to best promote in Arduino world?? I would go to Arduino forums....
 
casainho said:
stancecoke, how I see Throttle/PAS/Torque sensor, all of them working as "speed+torque" and for instance: at 50% of "speed+torque", the motor would run at 50% of max current and 50% of max speed.
- throttle: outputs a value from 0 to 100% (reading the ADC signal)
- PAS: outputs a value from 0 to 100% (reading the cadence)
- Torque sensor: outputs a value from 0 to 100% (considering the torque)

I can't follow your argumentation at the moment.
I'll try to explain my understanding of the different ride modes:

1. Throttle mode: Duty cycle is set directly to throttle value (can be combined with PAS detection, just check if pedals are turning for legality in Germany, no processing of cadence-information).

2. Speed Levels: a constant duty cycle is applied per level, if movement of pedals is recognized by the PAS. If you ride from the plain to a hill, you will get slower. With more power from the riders pedaling, current is decreasing. That's the way, very cheap bikes from the discounter have done it in former times.

3. Power Levels ("torque simulation"): motor current is held constant per level by a control loop (with cadence-dependent ramp up). With more power from the riders pedaling, the bike gets faster. Thats the way most conversion-sets and newer discounter bikes (can) do it.

4. Torque-Sensor-Mode: Motor power is proportional to human power. Motor current is controlled to keep Pmotor = k*Phuman, U*I = k*cadence*torque@crank, where k can be the assistLevel. Thats the way most "more expensive" bikes do it.

In my recent code, in Torque-Sensor-Mode I don't control the motor current, I'm just setting the duty cycle to factor*cadence*torque@crank, I'm quite satisfied with this solution for many years now. I don't know if you can feel a difference to current controlled Torque-Sensor mode in real life....

casainho said:
Any ideas how to best promote in Arduino world?? I would go to Arduino forums....
I don't believe, that the arduino community is interested, as the STM8 processors are not supported by arduino. It would be much more interesting, if we would merge to the Lishui controllers as the STM32 processors are supported, see stm32duino. The arduino-specific UART communication with the well known China-Displays is published already.

Regards
stancecoke
 
Ok, we can go with the standard well tested ways but I also would like to test the new "torque+speed" (but this is not a priority):



I think I will implement the speed controller as being part of the motor controller module, because this speed controller is needed to control the motor max speed for legal reasons, safety (don't want my kid to run over 20km/h for instance) or for future implementation of that "torque+speed".

stancecoke said:
casainho said:
Any ideas how to best promote in Arduino world?? I would go to Arduino forums....
I don't believe, that the arduino community is interested, as the STM8 processors are not supported by arduino. It would be much more interesting, if we would merge to the Lishui controllers as the STM32 processors are supported, see stm32duino. The arduino-specific UART communication with the well known China-Displays is published already.
Not officially but there is: Sduino: Porting the most important features of the Arduino API to the STM8S.
https://tenbaht.github.io/sduino/

But my idea was to present a cheap generic brushless motor controller to be controlled by UART. I forgot that this controllers c (with original firmware) can be controlled using the throttle signal and also some controls over UART. Still, I think this project add value on the flexibility to control the motor over UART, like control everything over UART (including regen current). Even this generic controller can fast change the motor rotation direction that is needed for balance things and that original firmware can't do it.
I think I will got to the Arduino forum.
 
casainho said:
I think I will implement the speed controller as being part of the motor controller module, because this speed controller is needed to control the motor max speed for legal reasons

Speed Limiting is already implemented in my code!

Regards
stancecoke
 
stancecoke said:
casainho said:
I think I will implement the speed controller as being part of the motor controller module, because this speed controller is needed to control the motor max speed for legal reasons
Speed Limiting is already implemented in my code!
Yes, thanks!! But I want to have it isolated in one module and also being calling on the slow loop. Throttle/PAS/Torque Sensor module will then be able just to define the motor speed as now happens to motor current. I will reuse your code much as possible.

There is also a big reason why I see motor speed as need to protect the controller: my Q85 seems to ask much current peaks at higher speeds when the sinewave have less than 50 points... that should increase the mosfets controller temperature and I think I will want to limit the max speed of my motor, like what seems the Lishui controller does, were it simple fails/shuts down on that situation!!
 
Gives your function "ui16_adc_read_motor_total_current" the filtered value from Pin AI8?

Regards
stancecoke
 
stancecoke said:
Gives your function "ui16_adc_read_motor_total_current" the filtered value from Pin AI8?
No, just the raw ADC value.
 
casainho said:
No, just the raw ADC value.

OK, can you please add a function, that reads from AI8?

We have another collision of our codes: Interrupt on Port D is triggerd by PAS and by OverCurrent. We have to check which source has triggerd the interrupt in the handler (by masking PortB-Byte?!)

Regards
stancecoke
 
Motor branch now have code for the motor (tested only with Q85) that implements the following interface:
Code:
/***************************************************************************************/
// Motor interface
void hall_sensor_init (void); // must be called before using the motor
void motor_init (void); // must be called before using the motor
void motor_set_mode_coast (void); // disable PWM output
void motor_set_mode_run (void); // enable PWM output
void motor_set_pwm_duty_cycle_target (uint8_t value);
void motor_set_current_max (uint8_t value); // steps of 0.5A each step
void motor_set_regen_current_max (uint8_t value); // steps of 0.5A each step
void motor_set_pwm_duty_cycle_ramp_inverse_step (uint8_t value); // each step = 64us
uint16_t motor_get_motor_speed_erps (void);
/***************************************************************************************/
Stancecoke, please adopt this code. On my Q85 it does seem to work well. Next step I want to test with a battery :)
 
stancecoke said:
casainho said:
No, just the raw ADC value.

OK, can you please add a function, that reads from AI8?
Code:
uint8_t ui8_adc_read_motor_total_current (void)
{
// 0x53E0 + 2*8 = 0x53F0
  return *(uint8_t*)(0x53F0);
}
It is from AIN8, and it is not filtered on firmware, just on hardware :)
That address (// 0x53E0 + 2*8 = 0x53F0) is base address for ADC buffered registers + 2*channel_number.

stancecoke said:
We have another collision of our codes: Interrupt on Port D is triggerd by PAS and by OverCurrent. We have to check which source has triggerd the interrupt in the handler (by masking PortB-Byte?!)
Yes, go ahead!
 
casainho said:
stancecoke said:
We have another collision of our codes: Interrupt on Port D is triggerd by PAS and by OverCurrent. We have to check which source has triggerd the interrupt in the handler (by masking PortB-Byte?!)
Yes, go ahead!
Seems that PAS pin (PD0) also can be used as TIM1 BREAK interrupt but that would fire instead TIM1_UPD_OVF_TRG_BRK_IRQHandler that is already used as PWM/motor fast loop. I think using instead the PortD pin change interrupt may be better.
 
Now I will need to clean the ground or my girlfriend will kill me :)

The video shows the motor running using my lab power supply (that can handles max of 10A). Firmware was limiting the motor max current to be about 8A. Motor max speed is 45km/h when the wheel is on the air.

[youtube]H1GZXHkMK7M[/youtube]

Next I will want to test with a battery and ride the bicycle. Or maybe before riding, I will install the LCD and then ride :)
 
stancecoke said:
We have another collision of our codes: Interrupt on Port D is triggerd by PAS and by OverCurrent. We have to check which source has triggerd the interrupt in the handler (by masking PortB-Byte?!)

Can it work this way? I'n not so familiar to bit manipulation....

Code:
void EXTI_PORTD_IRQHandler(void) __interrupt(EXTI_PORTD_IRQHANDLER)
{
  uint8_t uint8_PortD_value;
  uint8_PortD_value = *(uint8_t*)(0x5010); //input register adress of port D
  if(!uint8_PortD_value & 0x01){ //Mask Bit 0 for PAS Pin
  ui8_PAS_Flag = 1; //just setting flag in interrupt handler
  }
  if(!uint8_PortD_value>>7){ //Mask Bit 7 for OC Pin
      motor_set_mode_coast ();
          while (1) ; // infinite loop, user will need to reset the system
    }
}

Regards
stancecoke
 
stancecoke said:
Can it work this way? I'n not so familiar to bit manipulation....
Take the example of Brake signal: brake_is_set ():
Code:
// Brake signal
void EXTI_PORTA_IRQHandler(void) __interrupt(EXTI_PORTA_IRQHANDLER)
{
  if (brake_is_set())
  {
(...)

BitStatus brake_is_set (void)
{
  if (GPIO_ReadInputPin(BRAKE__PORT, BRAKE__PIN) == 0)
    return 1;
  else
    return 0;
}
 
My pitch on Arduino forum: https://forum.arduino.cc/index.php?topic=505223.0
 
I have merged the motor branch to the J-LCD branch now. The motor runs, current limiting works, the value of 79 for ADC_MOTOR_CURRENT_MAX_ZERO_VALUE leads to about 2.4A Amax. The motor start is a little sticky. Do I have to adjust the MOTOR_ROTOR_DELTA_PHASE_ANGLE_RIGHT value?

[youtube]FvpOQvanXvY[/youtube]

regards
stancecoke
 
stancecoke said:
I have merged the motor branch to the J-LCD branch now. The motor runs, current limiting (with the update_setpoint function) works. The motor start is a little sticky. Do I have to adjust the MOTOR_ROTOR_DELTA_PHASE_ANGLE_RIGHT value?
Can't see your video, like if is not public or do not exist.

I had to find the minimum eRPS value after the system transition from 6 steps to sinewave interpolation 60 degres and after to interpolation 360 degrees.
For Q85, 125 would be the minimum to leave the 6 steps. You may try to increase or decrease that values. Start by guarantee that 6 steps works well first, by commenting the code that enables next states and them go step by step on that 3 different states:

Code:
      if (ui16_motor_speed_erps > 250)
      {
	ui8_motor_interpolation_state = INTERPOLATION_360_DEGREES;
	ui8_motor_state = MOTOR_STATE_RUNNING;
      }
      else if (ui16_motor_speed_erps > 150)
      {
	if (ui8_motor_interpolation_state == NO_INTERPOLATION_60_DEGREES)
	{
	    pwm_init_bipolar_4q ();
	}

	ui8_motor_interpolation_state = INTERPOLATION_60_DEGREES;
	ui8_motor_state = MOTOR_STATE_RUNNING;
      }
      else
      {
	if (ui8_motor_interpolation_state != NO_INTERPOLATION_60_DEGREES)
	{
	  pwm_init_6_steps ();
	}

	ui8_motor_interpolation_state = NO_INTERPOLATION_60_DEGREES;
      }
 
stancecoke said:
The motor runs, current limiting works, the value of 72 for ADC_MOTOR_CURRENT_MAX_ZERO_VALUE leads to about 2.4A Amax.
That is strange because on my hardware the value of 79 equals to about zero: #define ADC_MOTOR_CURRENT_MAX_ZERO_VALUE 79

That is why: void motor_set_current_max (uint8_t value); // steps of 0.5A each step
Code:
void motor_set_current_max (uint8_t value)
{
  ui8_ADC_motor_current_max = value;
}
Then on PWM duty_cycle controller:
Code:
  // verify motor max current limit
  ui8_temp = ui8_adc_read_motor_total_current ();
  if (ui8_temp > (ADC_MOTOR_CURRENT_MAX_ZERO_VALUE + ui8_ADC_motor_current_max))  // motor max current, reduce duty_cycle
  {
    if (ui8_duty_cycle > 0)
    {
      ui8_duty_cycle--;
    }
  }
  // verify motor max regen current limit
  else if (ui8_temp < (ADC_MOTOR_CURRENT_MAX_ZERO_VALUE - ui8_ADC_motor_regen_current_max))  // motor max current, increase duty_cycle
  {
    if (ui8_duty_cycle < 255)
    {
      ui8_duty_cycle++;
    }
  }
 
casainho said:
stancecoke said:
The motor runs, current limiting works, the value of 72 for ADC_MOTOR_CURRENT_MAX_ZERO_VALUE leads to about 2.4A Amax.
That is strange because on my hardware the value of 79 equals to about zero: #define ADC_MOTOR_CURRENT_MAX_ZERO_VALUE 79
If you printf the ui8_adc_read_motor_total_current (); while motor is stopped, you will find the zero value -- can you please test and tell me what is that value?
 
The video should run now.

the "72" was a typing error, "79" was ment.

I've just grilled my controller with setting ADC_MOTOR_CURRENT_MAX_ZERO_VALUE to 200 and using an unfused battery :shock: I have produced quite lot of magic smoke.
I don't know, if it's just a burned mosfet or if there is more damage.... The shunt on the board was red glowing, before I was able to cut the voltage....

At which part of your code do you set the ui8_ADC_motor_current_max value?! I only found values = 0?!
I just updated my recent code to the TORQUESENSOR EXPERIMENTING branch, perhaps you can take a look, what went wrong....

Regards
stancecoke
 
stancecoke said:
The video should run now.

the "72" was a typing error, "79" was ment.

I've just grilled my controller with setting ADC_MOTOR_CURRENT_MAX_ZERO_VALUE to 200 and using an unfused battery :shock: I have produced quite lot of magic smoke.
I don't know, if it's just a burned mosfet or if there is more damage.... The shunt on the board was red glowing, before I was able to cut the voltage....

At which part of your code do you set the ui8_ADC_motor_current_max value?! I only found values = 0?!
I just updated my recent code to the TORQUESENSOR EXPERIMENTING branch, perhaps you can take a look, what went wrong....
Video is ok now. I can't understand what happens with your motor. You would need to enable only 6 steps to see if that mode works ok, since it is the first mode and used for startup.

Maybe your hardware have a different value for zero current. I had a code that did read at startup the firmware, the current value and considered that value as zero value... maybe I need to recover that code if we find that are expressive differences between hardwares.

Well, at least that seems important for us to learn. Can you please tell if your battery has a BMS, because if so, it should act as a fuse and should avoid that situation.

On main:
Code:
  enableInterrupts();

  motor_init ();
  motor_set_current_max (8); // 1 --> 0.5A
  motor_set_regen_current_max (2); // 1 --> 0.5A
  motor_set_pwm_duty_cycle_ramp_inverse_step (2); // each step = 64us

  hall_sensors_read_and_action (); // needed to start the motor

I will be able to give a look just tomorrow. I want to start using your code, but with the Kunteng LCD, that is the one I have and I also think we should go with.
 
stancecoke said:
I've just grilled my controller with setting ADC_MOTOR_CURRENT_MAX_ZERO_VALUE to 200 and using an unfused battery :shock: I have produced quite lot of magic smoke.
I don't know, if it's just a burned mosfet or if there is more damage.... The shunt on the board was red glowing, before I was able to cut the voltage....
That also means that OC Over Current code did not work/protect :-(
 
Back
Top