am32-firmware / AM32

GNU General Public License v3.0
312 stars 111 forks source link

Dshot won't arm without a FC, and also direction is wrong in one case #123

Closed frank26080115 closed 4 days ago

frank26080115 commented 5 days ago

V2.16, building off of 28739ce3f4f9bfcb66318ddf4bb7a3bd7d26aa44 , I'm using a DYS GSPACE_F051 and my friend is using a Mamba HACKRC_KB_F421 as the ESC, this problem is not rooted in hardware though

Problem 1: AM32 simply will not arm when using Dshot, not in normal directional mode and not in bidirectional mode

Problem 2: the direction in bidirectional mode is wrong

NOTE: bidirectional rotation mode, not bidirectional communication

The original source code at this commit

if (dshot) {
    if (newinput > 1047) {
        if (forward == eepromBuffer.dir_reversed) {
            if (((commutation_interval > reverse_speed_threshold) && (duty_cycle < 200)) || stepper_sine) {
                forward = 1 - eepromBuffer.dir_reversed;
                zero_crosses = 0;
                old_routine = 1;
                maskPhaseInterrupts();
                brushed_direction_set = 0;
            } else {
                newinput = 0;
            }
        }
        adjusted_input = ((newinput - 1048) * 2 + 47) - reversing_dead_band;
    }
    if (newinput <= 1047 && newinput > 47) {
        if (forward == (1 - eepromBuffer.dir_reversed)) {
            if (((commutation_interval > reverse_speed_threshold) && (duty_cycle < 200)) || stepper_sine) {
                zero_crosses = 0;
                old_routine = 1;
                forward = eepromBuffer.dir_reversed;
                maskPhaseInterrupts();
                brushed_direction_set = 0;
            } else {
                newinput = 0;
            }
        }
        adjusted_input = ((newinput - 48) * 2 + 47) - reversing_dead_band;
    }
    if (newinput < 48) {
        adjusted_input = 0;
        brushed_direction_set = 0;
    }
}

The first problem is that the ESC never arms, ever, I've verified the DShot packets using a logic analyzer.

I conducted an experiment, adding armed = 1; to the bottom of the if (dshot) if statement. The immediately started running even when I was sending 1047. This was unexpected because in bidirectional mode I expected 1047 to be the "center". The motor responded as if the directional mode was single directional, the response was linear with the throttle input.

(the experiment here also confirms that the dshot packets are valid, the bits have valid timings, the bit order is correct, the CRC bits are correct, and the min and max are roughly correct)

This pointed to the problem being the line adjusted_input = ((newinput - 48) * 2 + 47) - reversing_dead_band; doesn't "flip" the adjusted_input value. (in contrast, the code meant for RC pulse input uses the map function to reverse the adjusted_input power level)

I'm trying to help a friend get Dshot working so I rushed out a solution that's very dirty:

if (dshot) {
    if (newinput > 1047) {
        if (forward == eepromBuffer.dir_reversed) {
            if (((commutation_interval > reverse_speed_threshold) && (duty_cycle < 200)) || stepper_sine) {
                forward = 1 - eepromBuffer.dir_reversed;
                zero_crosses = 0;
                old_routine = 1;
                maskPhaseInterrupts();
                brushed_direction_set = 0;
            } else {
                newinput = 1047;
            }
        }
        adjusted_input = map(newinput, 1047, 2047, 47, 2047);
    }
    if (newinput <= 1047 && newinput > 47) {
        if (forward == (1 - eepromBuffer.dir_reversed)) {
            if (((commutation_interval > reverse_speed_threshold) && (duty_cycle < 200)) || stepper_sine) {
                zero_crosses = 0;
                old_routine = 1;
                forward = eepromBuffer.dir_reversed;
                maskPhaseInterrupts();
                brushed_direction_set = 0;
            } else {
                newinput = 1047;
            }
        }
        adjusted_input = map(newinput, 48, 1047, 2047, 47);
    }
    if (newinput >= (1047 - (servo_dead_band << 1)) && newinput <= (1047 + (servo_dead_band << 1))) {
        adjusted_input = 0;
        brushed_direction_set = 0;
        armed = 1;
    }
}

and later for single-direction mode

} else {
    adjusted_input = newinput > 48 ? map(newinput, 48, 2047, 47, 2047) : 0;
    if (adjusted_input == 0) {
        armed = 1;
    }
}

It's a bit heavy handed, I'm copying code from the non-dshot parts of the code. The way it arms is more instantaneous. It also uses the deadband when it is probably inappropriate to.

For some reason, zero_input_count doesn't seem to increment, which leads me to believe that adjusted_input is never 0 (or another reason). I also tried adding zero_input_count++; myself but the result was that arming takes like 30 seconds.

NOTE: the device sending out the Dshot is a ELRS receiver built on a ESP32, so it has Dshot implemented by RMT. It is not capable of sending Dshot commands.

NOTE: BLHeli32 is fully working with this ELRS receiver.

Please investigate these issues and please tell me how to fix this properly. Thanks!

frank26080115 commented 4 days ago

closing, sending zero is an acceptable solution, and I misunderstood how the directions work in dshot 3D mode