Alternate Free Software Firmware and Tools for DFRobot's DFR0592 DC Motor Driver Hat
Overview
This project provides an alternate, open-source firmware for the DFRobot DFR0592 DC motor driver hat. It has been developed for use in a Télécom Paris mandatory project for all first year students. The alternate firmware enables students to pilot custom-designed robots using a Raspberry Pi (RPi) and the DFR0592 hat, managing the motors and the encoders through an integrated PID controller.
Features
1. Alternate Software for DFR0592
- Replaces the original firmware on the DFR0592 DC motor driver hat.
- Compatible with Raspberry Pi to control robot motors with encoder feedback.
2. I²C Bootloader
- Includes an I²C bootloader as the hat's STM32F103C8/APM32F103C8 microcontroller lacks a built-in bootloader.
- Facilitates easy firmware updates directly from a Raspberry Pi.
3. Firmware Updater
- Allows the firmware to be updated from the RPi without needing to reconnect a SWD probe.
- Ensures seamless integration and ease of maintenance in student projects.
4. Python Module and Rust Crate
- Python Module: Enables interaction with the firmware using Python scripts on a Raspberry Pi.
- Rust Crate (tele0592): Available on crates.io, allows interaction with the firmware from Rust programs.
Authors
The firmware has been developed by Samuel Tardieu and Tarik Graba. Karim Ben Kalaia and Tarik Graba have designed the probe interface allowing to connect a SWD probe to the hat test pins for the initial bootloader programming.
License
This software is released under the terms of the GNU General Public License version 3 or (at your option) any later version. See the COPYING file for the full license text.
Initial Setup
Programming the Bootloader
- The initial bootloader programming must be performed using the SWD interface.
- Any SWD probe like JLink can be used.
- The test points available on the DFR0592 hat should be used for this purpose.
Configuring the Raspberry Pi for correct I²C operation
⚠️ Important: I²C uses clock stretching, so care must be taken when using a Raspberry Pi whose SOC is bogus. Using 400kHz instead of the default 100kHz seems to mask the problem if not outputting too much debug information. Using a software I²C solves the issue. This problem happens both on Raspberry Pi 3 and Raspberry Pi 4 variants.
In boot/config.txt
, the i2c_arm=on
line must be commented out if
present, in order to free the pins to communicate with the hat.
Software I²C which does not suffer from the aforementioned bug must be enabled by adding:
dtoverlay=i2c-gpio,i2c_gpio_sda=2,i2c_gpio_scl=3,bus=8
After rebooting the Raspberry Pi, software I²C will be used to drive the I²C bus.
Flashing the firmware
On the Raspberry Pi, run:
$ firmware-updater flash controller-firmware.elf
Usage Scenarios
Telecom Paris students will develop and pilot robots using the Raspberry Pi and DFR0592 hat:
- Design Custom Robots: Students design their robots with motor control capabilities.
- RPi Integration: Use a Raspberry Pi to control motor operations and gather feedback from encoders.
- Firmware Management: Easily update the motor driver firmware through the built-in I²C bootloader.
Python Module and Rust Crate
A Python and Rust interface are provided by default.
Python Module
Using the module:
# Example Python usage
import controller
# Connect to the DC motor driver hat
c = controller.Controller()
# Set the auto shutdown timeout to 4 seconds
c.set_motor_shutdown_timeout(4)
# Set motor speed and direction
c.set_motor_speed(40, -40) # Turn without moving, each motor at 40 ticks per 100th of second
Rust Crate (tele0592)
Available on crates.io, provides a safe and efficient interface to the firmware.
// Example Rust usage
use rppal::i2c::I2c; // I²C on a Raspberry Pi
use std::time::Duration;
use tele0592::controller::{Controller, Device};
fn main() -> anyhow::Result<()> {
// Initialize the motor driver
let mut controller = I2c::with_bus(8)?;
// Check that the controller runs a firmware compatible with this API
controller.check_firmware_version()?;
// Set the auto shutdown timeout to 4 seconds
controller.set_motor_shutdown_timeout(Duration::from_secs(4.0))?;
// Turn without moving, each motor at 40 ticks per 100th of second
c.set_motor_speed(40, -40)?;
Ok(())
}
Hardware
Board DC Motor Driver Hat DFRobot DFR0592
Microcontroller STM32F103C8T6 (medium density)
Note that newest boards sold by DFRobot include an APM32F103C8T6 in place of the original STM32F103C8T6.
Useful onboard peripherals
Peripheral | N |
---|---|
Flash | 64kiB |
RAM | 20kiB |
Timers (general purpose) | |
Timers (advanced control) | |
CPU frequency | 72MHz |
I2C |
The I2C2
is used (PB10
/SCL
, PB11
/SDA
) to communicate with the TB6612FNG motor driver.
Flash-memory organization
- Main memory: 64 pages of 1KiB
- Page erasable
- Programmable by half-words
-
VTOR
must be aligned with the number of exception entries or 128 bits, whichever is largest. To use all DMA channels (DMA2_Channel4_5
is at position 59), an alignment of 256 is required. In any case, aligning it on a page boundary will work.
Driver IC TB6612FNG for dual DC motor
Encoders FIT0450
Led pattern
The led pattern on the hat communicates in Morse code.
Code | Meaning | Pattern |
---|---|---|
I | Regular mode | .. |
LP | Low power (Vdd is below 2.9V) | .-.. .--. |
WW | Last reset was due to the window watchdog trigerring | .-- .-- |
Bootloader
A I²C bootloader is available so that the firmware can be reprogrammed
from the host (typically a Raspberry Pi) using the firmware-updater
program.
When the bootloader is used, the flash is split into two parts:
- 24KiB for the bootloader
- 40KiB for the firmware
The firmware will be compiled with a layout compatible with the
bootloader if the bootloader
feature is activated, which is the
default. It can also be compiled as a standalone program using the
standalone
feature.
Motor controller command set
I²C address: 0x57
Multibyte values are exchanged in little endian format.
0x08 Firmware version (R)
- 3 bytes containing the major, minor and patch version numbers
derived from
Cargo.toml
.
0x0F Who am I? (R)
- The I²C address (1 byte)
0x10 PWM frequency (R/W)
+- Frequency in Hz on 3 bytes, from 1 to 100_000 (default: 20_000)
0x20-0x22 PID coefficients (R/W)
The PID will be working at 100Hz.
P, I and D are expressed as 32 bit signed numbers with a 24 bit integer part and a 8 bit fractional part.
- 0x20: P (4 bytes). Default value is 0x0000_0040 (0.25)
- 0x21: I (4 bytes). Default value is 0x0000_0000 (0.0)
- 0x22: D (4 bytes). Default value is 0x0000_0000 (0.0)
Changing the coefficients will take effect only when the controlled mode is off, for example by setting the motors to standby mode.
0x28 Motor shutdown timeout (R/W)
- Number of tenths of seconds in before the motor shut down (1 byte). The minimum is 1 (0.1 seconds) and the maximum is 100 (10 seconds).
0x30 Raw motor speed (R/W)
- Left and right motor speed, between -127 and 127 each (1 byte each):
- ±127 corresponds to the full speed
- -128 on both engines mean standby
- -128 on one of the engines does not modify its speed
- This is the raw speed sent to the motor, no PID is computed.
- If written while in controlled mode, the robot switches to manual mode
0x31 Controlled motor speed (R/W)
- Left and right motor speed, beween -32767 and 32767 each (2 bytes each). The unit is the number of ticks per 100th of seconds.
- Each motor will be controlled by a separate PID with shared coefficients.
- Using a speed of (0, 0) will exit the controlled mode and switch to raw mode. This allows the PID coefficients to be updated if needed.
- Speed will be returned as (-32768, -32768) if controlled mode is not active.
0x32 Encoder ticks (R)
- Left and right encoder counters since the last time they were read, between -32768 and 32767 (2 bytes each).
0x33 Raw encoder ticks (R) (since version 1.1.0)
- Left and right encoder counters as an absolute value. Each encoder counter is a 16 bit unsigned value in wraparound arithmetic (2 bytes each).
0x36 Status byte (R)
- Bit 0: moving (1) or idle (0)
- Bit 1: controlled mode (1) or raw mode (0)
0xE0 Reset (W)
- Reset the device. Used mainly for testing.
0xE1 Reset to bootloader (W) [only if bootloader feature is enabled]
- Reset the device in bootloader mode. Used when reprogramming.
0xF0 Unique device ID (R)
- Unique device ID as found in addresses 0x1FFFF7E8-0x1FFFF7EF (8 bytes)
0xF1 Device family (R)
- Identity code code (1 byte)
- Continuation code (1 byte)
If they cannot be read, they are returned as zeroes.
0xF2 MCU kind (R)
- Device ID from the
IDCODE
field of theDBGMCU
register (1 byte) - Revision ID from the
IDCODE
field of theDBGMCU
register (1 byte)
It might happen that an incorrect value is returned on STM32 devices when no SWD or JTAG probe is connected.
0xF3 Flash size (R)
- Internal flash size as declared by the microcontroller (2 bytes)
0xFE Firmware features byte (R)
- Bit 0: bootloader feature is activated
Bootloader command set
If a firmware is present, the bootloader will start it unless the firmware has explicitly chosen to reboot in bootloader mode.
I²C address: 0x57
The note about clock stretching also applies for the bootloader.
0x08 Firmware version (R)
- 3 bytes containing the major, minor and patch version
numbers derived from
Cargo.toml
.
0x0F Who am I? (R)
- The I²C address with the highest bit set: 0xd7 (1 byte)
0x10 Programming status (R)
Returns 2 bytes:
- One set of flags ORed together:
- 0x01: system is in programming mode
- 0x02: at least one error was detected since the last time the system has been put in programming mode
- 0x04: a program is currently present in flash
- A XORed version of all data received so far since the programming address was last set, either by entering programming mode, or by using the SET PROGRAMMING ADDRESS command.
0x20 Change programming mode (W)
- [0x00]: leave programming mode without marking the application as succesfully written [programming mode only]
- [0x01]: leave programming mode and mark the application as succesfully written [programming mode only]
- [0x17, 0x27, 0x65, 0x40]: enter programming mode
0x24 Erase pages (W) [programming mode only]
- First byte: index of the first 1kB page in the application area
- Second byte: number of pages to erase (up to the end of the application area)
- Third byte: XOR of the first two bytes, as a safety
The memory will be erased. The active programming address is then set to the beginning of the erased area.
Even if the last page of the application does not belong to the erased page set, it will be erased with the first erase request of the current session (or after having being set again) to ensure that the application is no longer considered valid.
Designating address outside the application area, or having a wrong checksum, will be an error.
0x28 Set programming address (W) [programming mode only]
- 4 bytes containing the next address to program, relative to the application start
- 1 byte XORing the previous ones
0x2c Program data (W) [programming mode only]
- 2, 4, 6, or 8 data bytes
- 1 byte XORing the previous ones
In case of error, the bootloader leaves programming mode immediately.
The programming address will advance with the right number of bytes.
0x30 Read memory (R) [programming mode only]
- Read 8 consecutive bytes in memory from the programming address (relative to the application start), and advance it by 8.
0 will be returned outside the application memory area.
0x34 Set checksum (W) [programming mode only]
- Set the checksum value to the given byte.
0xE0 Reset (W)
- Reset the bootloader in automatic mode
0xE1 Reset to the bootloader (W)
- Reset the bootloader in bootloader mode
0xE2 Reset to the application (W)
- Reset the bootloader in application mode
0xF0 Unique device ID (R)
- Unique device ID as found in addresses 0x1FFFF7E8-0x1FFFF7EF (8 bytes)
0xF1 Device family (R)
- Identity code code (1 byte)
- Continuation code (1 byte)
If they cannot be read, they are returned as zeroes.
0xF2 MCU kind (R)
- Device ID from the
IDCODE
field of theDBGMCU
register (1 byte) - Revision ID from the
IDCODE
field of theDBGMCU
register (1 byte)
It might happen that an incorrect value is returned on STM32 devices when no SWD or JTAG probe is connected.
0xF3 Flash size (R)
- Internal flash size as declared by the microcontroller (2 bytes)