Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

DCMotor: two-input H-bridges brake when going backwards; add a mode with PWM on both inputs

Open
#2,617 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
35/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
csharp
Domain
embedded-iot

Research direction

The proposal identifies DCMotor.cs and ExplorerHat/Motors.cs, and notes that work should start after the PR for #2613. First read that PR and the existing DCMotor2PinNoEnable implementation to understand the factory and channel lifecycle. The issue leaves API shape, braking behavior, and whether ExplorerHat should switch open for discussion; completion depends on those decisions and tests for both motor directions.

Written by the indexing model from the issue text.

Description

untriaged

Problem: backwards is much slower than forwards with two-input H-bridges

With two-input H-bridges such as the DRV8833 (also L9110S, MX1508, DRV8871...), DCMotor.Create(speedPin, directionPin) (DCMotor2PinNoEnable) turns the motor much slower backwards than forwards at the same |Speed|:

Speed Forwards (pulses/s) Backwards (pulses/s) Backwards / forwards
0.4 44.3 15.9 ~36 %
0.6 52.3 29.8 ~57 %
0.8 57.7 46.7 ~81 %
1.0 61.5 61.4 100 %

Measured on a Raspberry Pi 3 B+ with the Pimoroni Explorer HAT Pro (DRV8833; Iot.Device.ExplorerHat, which uses DCMotor.Create(19, 20) and DCMotor.Create(21, 26)), Iot.Device.Bindings 4.2.0, two TT gear motors with the wheels in the air, and an LM393 slotted speed sensor on a 20-slot disc on each wheel (1 s to reach the speed, 3 s counting; both wheels gave similar values, the table shows the average).

The duty cycle math is right (this was discussed in #844): the motor gets voltage for the same fraction of the cycle in both directions. What changes is the state of the H-bridge in the off part of the cycle:

  • Forwards: the direction pin is low and the PWM pin goes high/low, so in the off part both inputs are low and the DRV8833 lets the motor coast.
  • Backwards: the direction pin is high and the PWM is inverted (1.0 + val), so in the off part both inputs are high and the DRV8833 brakes the motor (it shorts its terminals: dynamic braking).

I checked it by sampling the pins with pinctrl while the motors were running. At Speed 0.4, forwards: 43 % driving, 57 % coasting; backwards: 43 % driving, 57 % braking.

A higher PWM frequency does not help. With my own SoftwarePwmChannel (both wheels, pulses per second):

Frequency 0.4 forwards / backwards 0.6 forwards / backwards
50 Hz (DCMotor default) 43 / 17 52 / 31
100 Hz 43 / 14 52 / 34
200 Hz 41 / 18 52 / 34
1000 Hz 45 / 29 44 / 29

Up to 200 Hz, the off time is much longer than the electrical time constant of the motor, so it brakes the same at any frequency. From ~500 Hz on, SoftwarePwmChannel on the Pi 3 no longer follows the duty cycle (at 1000 Hz, 0.4 and 0.6 give the same speed), and only a hardware PWM of several kHz would change the result.

Proposal: a DC motor with a PWM channel on each input

A DC motor mode with a PWM channel on each input, as the Pimoroni Python library for the Explorer HAT does (GPIO.PWM at 100 Hz on both pins): forwards, PWM on the first input and the second one low; backwards, PWM on the second input and the first one low. The motor coasts in the off part of the cycle in both directions. DCMotor2PinNoEnable can't do this because it only gets one PWM channel, so it would be a new factory method, for example:

// New: one PWM channel on each input of a two-input H-bridge (DRV8833, L9110S...)
public static DCMotor Create(PwmChannel forwardChannel, PwmChannel backwardChannel, bool shouldDispose = true);
  • Speed > 0: forwardChannel.DutyCycle = Speed, backwardChannel.DutyCycle = 0.
  • Speed < 0: forwardChannel.DutyCycle = 0, backwardChannel.DutyCycle = -Speed (the channel of the other direction is set to 0 first).
  • Speed = 0: both 0 (coast).

The direction is the same as DCMotor.Create(speedPin, directionPin) when forwardChannel is on speedPin, so existing wiring keeps working.

I tried this in my robot with two SoftwarePwmChannel at 100 Hz per motor (the DCMotor2PinNoEnable column comes from the first table):

Speed Forwards Backwards, DCMotor2PinNoEnable Backwards, PWM on both inputs
0.4 41.5 15.9 (~36 %) 42.4 (102 %)
0.6 51.2 29.8 (~57 %) 47.7 (93 %)
0.8 56.7 46.7 (~81 %) 56.5 (100 %)
1.0 60.5 61.4 (100 %) 60.5 (100 %)

With the pins sampled again, the inputs were never both high. The cost: with software PWM there are two threads per motor instead of one (on the Pi 3 B+, the process went from ~15 % to ~20–25 % of one core with both motors running).

Then Iot.Device.ExplorerHat could use it in Motors, as the Pimoroni library does. That changes its behavior (backwards becomes as fast as forwards, as users would expect) and needs two more PWM threads, so I'd like to know if you agree before changing it. It touches the same files as #2613 (DCMotor.cs and ExplorerHat/Motors.cs), so I would start after that PR.

Questions:

  1. Is the factory method above a good shape for the API, or do you prefer another one (for example, a public class or an option on an existing method)?
  2. Should it also allow the braking variant (both inputs high in the off part, "slow decay")? Some users may prefer it, but then both directions should brake, not only backwards.
  3. Should Iot.Device.ExplorerHat switch to the new mode?
Dominant language
C#
Stars
2.4k
Forks
630
Avg merge
6d 17h
Merged PRs (30d)
10

Getting set up

  • No Dockerfile or Docker Compose file
  • Has a pull request template
  • No contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from dotnet/iot

All issues in dotnet/iot

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.