• Hello ES! We could use some help to get us past the finish line on building the new knowledgebase for the forum.
    Can you donate? Please see our fundraising page. Thank you!

TSDZ8 OSF (open source firmware)

That's exactly the same with the STM32F103 that is very popular for BLDC FOC controllers.
No FPU, only two ADCs. You could reuse a lot of the code of the EBiCS firmware.

You don't need a third phase current as the sum of the three currents is always zero. See the Clarke function, it has only two currents as input.

The angle extrapolation from the hall signals already exists in your firmware, so no need to use a PLL. You don't have to deal with CORDIC yourself, that's all done by the functions in the math library. There is an integer and a floating point implementation. EBiCS uses not a single floating point operation, it's all done in integer values.
I had a short look at EBICS. I am not sure XMC02 could support it. It seems that XMC1302 is about 4X slower than STM32F103X6. In Infineon implementation, even with all tricks being used, it seems that the FOC update uses about 2/3 of the CPU time (at 19kz)
 
Not four times. The Lishui controllers don't use an external oszillator, so the MCU runs with 64 MHz, compared to the 32 MHz of the XMC1302.
I asked chatgpt to compare the 2 mcu. It seems that STM32 is a cortex M3 instead of a cortex M0 for infineon and so, it is more efficient in different domains (pieline, ...) even if they should have the same clock. Chatgpt estimates that the difference could be in a ratio up to 1/5. Probably that in practice it would be about 1/3 - 1/4.
 
I asked chatgpt
Hm. I don't give anything on such queries.
The Infinion code is able to do FOC sensorless. For that, much more math is necessary, in comparison to hall sensing for the rotor position, as you have to update observer each PWM cycle.
So it should be possible to do sensored FOC with this "slow" processor without problems...
As the hardware has three phase current shunts, the original Tongsheng firmware will use "real" FOC.
 
Last edited:
The hardware has 3 phase current shunts. I do not plan to do real FOC because this seems me too difficult and thre would be so much changes compared to TSDZ2 version. It makes future maintenance to difficult. There should also be changes in javaconfigurator and 860C firmware.
I would like to try:
- first to calculate the correct foc angle (which requires to know R and L)
- second to measure the 3 phases currents and so calculate Id
- third, I would add a PID to apply corrections to the calculated foc angle in order to keep average Id = 0.
I'm currently playing around with compiling my own tweaks and messing around with foc angles. So far
Field Weakening (FW) logic
Replaced the “FW only when duty=100%” with a hysteresis + decay scheme:
FW offset ramps up if duty ≥ ~92%.
FW offset ramps back down slowly if duty ≤ ~88%.
Purpose: let FW “stick” a bit under load instead of flicking on/off at the exact 100% edge.
Throttle + FW interaction
Removed the old guard (&& (!ui8_adc_throttle_assist)), so FW logic works even when throttle is active.
Before, FW was effectively disabled under throttle.

Fixed warnings for ui16_hall_counter_total_previous and ui8_foc_angle_accumulated (declared but unused).
Hooked in extern uint16_t ui16_motor_speed_erps; so the FOC scheduling could “see” motor speed. I was getting compile errors until I did this.

From my findings it feels like after the 50 pwm mark foc doesn't advance enough which looses torque there but increasing foc multi helps a lil then it advances too much after that which makes the motor out of sync which is causing the shudders. I will do some more experimenting and report back
 
I'm currently playing around with compiling my own tweaks and messing around with foc angles. So far
Field Weakening (FW) logic
Replaced the “FW only when duty=100%” with a hysteresis + decay scheme:
FW offset ramps up if duty ≥ ~92%.
FW offset ramps back down slowly if duty ≤ ~88%.
Purpose: let FW “stick” a bit under load instead of flicking on/off at the exact 100% edge.
Throttle + FW interaction
Removed the old guard (&& (!ui8_adc_throttle_assist)), so FW logic works even when throttle is active.
Before, FW was effectively disabled under throttle.

Fixed warnings for ui16_hall_counter_total_previous and ui8_foc_angle_accumulated (declared but unused).
Hooked in extern uint16_t ui16_motor_speed_erps; so the FOC scheduling could “see” motor speed. I was getting compile errors until I did this.

From my findings it feels like after the 50 pwm mark foc doesn't advance enough which looses torque there but increasing foc multi helps a lil then it advances too much after that which makes the motor out of sync which is causing the shudders. I will do some more experimenting and report back
Thanks for the feedback.
On my side I imagine to let OSF automatically try to find best foc angle.
I already prepared code to:
- measure 3 current shunt at 19 khz
- calculate Id at 19 khz
- at 100Hz calculate Id average (over last 190 values) and use a PID with Id set point = 0 in order to adapt lead angle with in a limited range; store the amplitude (=abs(avg_Id in an array.
- at 5 hz, apply an optimiser: calculate the average of the abs(avg Id). Use it to check if a small increase/decrease an lead angle offset has positieve or negatieve effect. This update slowly a lead angle offset. If the variance of abs(avg id) is above a thresehold, then step of increase/decrease test is reduced.

If you want, I can yet share the code but I did not yet tested it and I know it still require to fine tune some ratio/parameters (e.g. conversion from/to Q15)

Neaw lead angle is apply at 100hz based on PID output + optimiser lead angle offset.
 
Thanks for the feedback.
On my side I imagine to let OSF automatically try to find best foc angle.
I already prepared code to:
- measure 3 current shunt at 19 khz
- calculate Id at 19 khz
- at 100Hz calculate Id average (over last 190 values) and use a PID with Id set point = 0 in order to adapt lead angle with in a limited range; store the amplitude (=abs(avg_Id in an array.
- at 5 hz, apply an optimiser: calculate the average of the abs(avg Id). Use it to check if a small increase/decrease an lead angle offset has positieve or negatieve effect. This update slowly a lead angle offset. If the variance of abs(avg id) is above a thresehold, then step of increase/decrease test is reduced.

If you want, I can yet share the code but I did not yet tested it and I know it still require to fine tune some ratio/parameters (e.g. conversion from/to Q15)

Neaw lead angle is apply at 100hz based on PID output + optimiser lead angle offset.
That would be awesome!
 
Hello.

I'm looking to upgrade my display from an EKD01 to an 860C, as I've run into the functional limitations of the EKD01.

However, I'm having trouble finding a model with the exact specifications I need. My requirements are as follows:
  • Model: 860C Display
  • Connector: 5-pin male connector
  • Compatibility: Must support a TSDZ8 motor running Open Source Firmware (OSF).
I've searched extensively on AliExpress and other online stores, but I have not been able to find a product that has both a 5-pin male connector and is guaranteed to be OSF-compatible.

Does anyone know of a reliable place to purchase this specific display?

If buying it directly isn't possible, is there a known method to modify a different version? I would be willing to try it if a detailed guide or a resource with clear instructions is available.

Any help would be greatly appreciated. Thank you!
 
Dear colleagues, please advise which version of OSF firmware for TSDZ8 should be installed to work with SW102 display? Because in the instructions here I only see versions for VLCD5 or EKD01 etc. (with JavaConfigurator)...
Should I just flash TSDZ2-v20.1C.5-update-1-860C(and SW102) from emmebrusa (the one for TSDZ2) but with J-Link v9 for TSDZ8? I've read through tons of instructions on GitHub and now I'm completely confused))
 
Last edited:
Dear colleagues, please advise which version of OSF firmware for TSDZ8 should be installed to work with SW102 display? Because in the instructions here I only see versions for VLCD5 or EKD01 etc. (with JavaConfigurator)...
Should I just flash TSDZ2-v20.1C.5-update-1-860C(and SW102) from emmebrusa (the one for TSDZ2) but with J-Link v9 for TSDZ8? I've read through tons of instructions on GitHub and now I'm completely confused))
There are 2 different repositories on github for OSF TSDZ8:
- for VLCD5 ( and others using the original protocol) : GitHub - mstrens/OSF.
- for 860C and SW102 : GitHub - mstrens/OSF_860C

So in you case, you must use the second one.
Read the Readme section.
You have to flash one hex file (from folder files to flash) in the TSDZ8 controller using J-link.

To use OSF for 860C, the display it self must be flashed with a software provided by mbrusa (see link in the readme section). There is no specific version of the display firmware for TSDZ8. So you must follow the instructions provided for the TSDZ2.
 
Hello.

I'm looking to upgrade my display from an EKD01 to an 860C, as I've run into the functional limitations of the EKD01.

However, I'm having trouble finding a model with the exact specifications I need. My requirements are as follows:
  • Model: 860C Display
  • Connector: 5-pin male connector
  • Compatibility: Must support a TSDZ8 motor running Open Source Firmware (OSF).
I've searched extensively on AliExpress and other online stores, but I have not been able to find a product that has both a 5-pin male connector and is guaranteed to be OSF-compatible.

Does anyone know of a reliable place to purchase this specific display?

If buying it directly isn't possible, is there a known method to modify a different version? I would be willing to try it if a detailed guide or a resource with clear instructions is available.

Any help would be greatly appreciated. Thank you!
Ebikestuff.eu
 
Knowing the internal resistance R and inductance L of TSDZ8 should normally allow to calculate the best foc angle for different speeds and currents

Is someone able to measure the internal resistance and inductance of one phase of the TSDZ8.
This suppose to remove the controller and to disconnect the 3 wires (3 big connectors). It is then possible to measure between 2 wires. This should provide the R and L values for 2 phases and so the values for one phase should be 1/2.
Note that the values are probably quite small (R about 0,1 Ohm, L about 150uH).
To measure R you need probably a special tool or you have to force a direct current with a known value (e.g. 0.5 or 1 A to avoid damaging the motor) and measure the voltage with a multimeter. V ; R = V/I
To measure L, it is easiest if you have the tool. Otherwise it is not so easy. For L, please note that the measured value depends probably on the position of the rotor. So you should have to make several measures for different rotor positions and then take the average between the min and the max values.

I do not have those tools and that is why I ask for.
I can measure internal resistance. Any additional condition that I have to follow during measuring?
 
I can measure internal resistance. Any additional condition that I have to follow during measuring?
To measure internal resistance, there is no special conditions. Disconnect the 3 wires between motor and controller, mesasure between 2 wires from the motor. Divide by 2 to get the resistance of one phase. Measurement can be done 3 times (once for each combination of the 3 wires). The 3 measures should provide similar values.
 
I've made some weird discovery. I'm messing around tuning the firmware source. I clamped foc to stop it shuddering and for a safety guard and increased foc multi on my display in steps all the way to 50, I managed to make motor rpm increase way over it's usual while riding. I went from 15mph to19, motor rpm shot to 370, felt like pressing a boost button lol. It happended when load increased. Like pulling brake or going up hill
 
I do not understand what you mean with "I clamped foc to stop it shuddering". Did you assign to foc angle a fixed value or did you limit the min and max to some ranges? Which value/limits did you used?
 
I do not understand what you mean with "I clamped foc to stop it shuddering". Did you assign to foc angle a fixed value or did you limit the min and max to some ranges? Which value/limits did you used?
Here's the exact sed I applied to the source files
Might be different for you since I've already made some modifications
sed -i '/uint8_t[[:space:]]\+foc_tmp[[:space:]]*=/,/ui8_g_foc_angle[[:space:]]*=[[:space:]]*foc_tmp[[:space:]]*;'/c'\
uint8_t foc_tmp = (uint8_t)((((uint16_t)ui8_adc_battery_current_filtered * ui8_foc_angle_multiplicator) + 128) >> 8);\
/* HAZZA HYBRID FOC: bias + hybrid trim + cap + slew */\
const uint16_t ERPS_KNEE = (uint16_t)(MOTOR_SPEED_FIELD_WEAKENING_MIN);\
/* treat as "real load" only if duty high OR batt amps a few counts */\
uint8_t high_load = ((ui8_g_duty_cycle >= (uint8_t)(PWM_DUTY_CYCLE_MAX * 70 / 100)) || (ui8_adc_battery_current_filtered >= 6));\
/* ---------- ERPS-based trim (only when high_load) ---------- */\
uint8_t scale_pct = 100;\
if (high_load && (ui16_motor_speed_erps > ERPS_KNEE)) {\
uint16_t span = ui16_motor_speed_erps - ERPS_KNEE;\
if (span > ERPS_KNEE) span = ERPS_KNEE;\
/* 100% → 60% across ERPS_KNEE..2*ERPS_KNEE; floor 70% for stability */\
scale_pct = (uint8_t)(100 - (40 * span) / ERPS_KNEE);\
if (scale_pct < 70) scale_pct = 70;\
}\
/* ---------- low-speed base bias: extra punch with low multiplier ---------- */\
const uint16_t ERPS_BOOST_LO = (uint16_t)(ERPS_KNEE * 3 / 10); /* full bias ≤ ~30% of knee */\
const uint16_t ERPS_BOOST_HI = (uint16_t)(ERPS_KNEE * 9 / 10); /* fades to 0 by ~90% of knee */\
const uint8_t BASE_BIAS_MAX = 7; /* try 6–8 to taste */\
uint8_t base_bias = 0;\
if (high_load) {\
if (ui16_motor_speed_erps <= ERPS_BOOST_LO) base_bias = BASE_BIAS_MAX;\
else if (ui16_motor_speed_erps < ERPS_BOOST_HI) {\
uint16_t span2 = ui16_motor_speed_erps - ERPS_BOOST_LO;\
uint16_t width2 = ERPS_BOOST_HI - ERPS_BOOST_LO;\
base_bias = (uint8_t)(BASE_BIAS_MAX - (BASE_BIAS_MAX * span2) / (width2 ? width2 : 1));\
}\
}\
/* apply trim then add bias to existing foc_tmp */\
foc_tmp = (uint8_t)(((uint16_t)foc_tmp * scale_pct) / 100);\
foc_tmp = (uint8_t)(foc_tmp + base_bias);\
/* ---------- low cap + tiny slew limiter ---------- */\
uint8_t cap = 18; /* bump to 19–20 if too tame up top */\
if (foc_tmp > cap) foc_tmp = cap;\
static uint8_t hazza_prev_angle = 0;\
int8_t hazza_df = (int8_t)foc_tmp - (int8_t)hazza_prev_angle;\
if (hazza_df > 1) foc_tmp = (uint8_t)(hazza_prev_angle + 1); /* slow rise */\
else if (hazza_df < -2) foc_tmp = (uint8_t)(hazza_prev_angle - 2); /* faster pull-down */\
ui8_g_foc_angle = foc_tmp;\
hazza_prev_angle = foc_tmp;' $f
I'm going to experiment with it more see if I can make it more efficient as when it was in this go I'll wack mode I saw motor amps at 20 (not sure if this is accurate reading or)
 
:eek: a simple link to the line of code in your GitHub fork would be much more helpful and readable ;)
I haven't updated the fork yet I just play around with local files in kinda noob with GitHub (only joined Linux about 6 months ago) 😂 here is a video of it happening. Looks like we found out missing power this is on my 4th gear I think that ones about 30-32t
 
Last edited:
Hello everyone
I Just bought a TSDZ8 with B02N-TS display.
Is it possible to use OSF on this combo so I can use street mode to limit speed/power when on road?
Thanks
 
I haven't updated the fork yet I just play around with local files in kinda noob with GitHub (only joined Linux about 6 months ago) 😂 here is a video of it happening. Looks like we found out missing power this is on my 4th gear I think that ones about 30-32t
In mspider versions (which I prefer and have more experience with) with good settings there was no problem with missing power and I have modified many sets with tsdz8 and none of them had such problems.
 
In mspider versions (which I prefer and have more experience with) with good settings there was no problem with missing power and I have modified many sets with tsdz8 and none of them had such problems.
I seem to be rpm limited , motor won't pull past 320rpm. So that's why I'm messing around with it
 
Is it really 320 rpm? This seems me so low. The motor is announced for 4700 rpm.
Going off technical screen with my 860c. It seems when I'm getting limited rpm foc isn't advancing at all. I'm trying to experiment with a punchy torque curve that advances with lower requirements. When I got home,
I added a new timing control block into the interrupt that runs every motor cycle. Inside it, the motor’s advance angle is calculated, but instead of letting it jump all over, the block now limits it to a cap of 23. When the motor is under load, either because duty cycle is above fifty percent or current is flowing, it latches onto that cap and holds it. It only releases once duty drops below forty percent for around twenty ticks, which stops the advance from flickering.
The block also smooths changes. If the angle wants to climb, it can only rise by one step per rotation, and if it wants to fall it can drop by two. That avoids sudden jolts in torque. If the normal calculation gives zero, it falls back to a stored stage value instead of stalling.
At higher RPM, another scaling rule trims back the advance gradually, so the motor doesn’t go unstable. No matter what, the angle is clamped at twenty-five. If throttle is closed, the motor phase current and timing are reset to zero so nothing is left floating.
I also cleaned up the code structure so there’s only one place where the motor angle actually gets written. I was getting loads of conflicts and the compiler errors. Huge headache with missing braces I finally managed to correct so the function closes properly, and the foc_flag reset was moved to after the update so the “once per rotation” check works as intended.
After finally passing the first compile stage i had missing functions motor_enable_pwm, motor_disable_pwm, and get_hall_pattern. I managed to restore them in from a backup. My goal is to have a boost-hold feature with a leash: it can advance and hold timing under load, it smooths out changes, it releases cleanly, and it won’t overshoot or stick. After finally succeeded at compiling, I flashed it and motor doesn't spin 😭 so I guess I need to wire in the functions properly. So back to the firmware I showed a video of. Seems to perform well under load but clamps down the foc advance too much of I'm cruising. I managed to hit 60 mph with no load in the highest gear tapping the back brake causing a surge. I got a external temp sensor strapped to the shell of the motor and didn't see over 22 degrees I think I should try sneak it inside for proper readings tho
 
Up to now, I tried mainly to transpose TSDZ2 logic to TSDZ8. I did not tried to really understand the concept of lead angle and field weakening. To day I tried to better understand using chatgpt when a full foc is not used (like currently). Chatgpt proposed to apply an angle correction like this:
- up to a base speed, this angle correction would go from 0 up to lead max (e.g. 20°) when speed and or current increases; both (speed and current) have an impact but the speed is the most important one. This is the lead angle.
- above this base speed, this angle correction would go down (e.g. going up up to -20°) when speed further increases. This is field weakening.

I am not sure that the TSDZ2/TSDZ8 logic respect this logic. As far I understand the code, it seems that when field weakening applies, the correction angle continue to increase instead of going down.
Any comments are welcome.
 
Back
Top