Skip to content

Guppy16/pico-tts

Repository files navigation

Pico DShot

This repo is being developed to use a RPi Pico to send dshot commands to ESCs. This is a work in progress.

Setting up the repo

  • git clone repo
  • git submodule init
  • git submodule update
  • cd lib/extern/pico-sdk; git submodule update --init <-- This was required for TinyUSB This repo is being developed to use a RPi Pico to send dshot commands to ESCs. This is a work in progress.

Running the Unit Tests

We use Unity Test as the framework for our tests. The CMake file was inspired from the Rainer Poisel, Throw The Switch and Testing with CMake and CTest. Assuming all the submodules have been setup

mkdir test/build && cd $_
cmake ..
cmake --build .
ctest --verbose

Code Overview

|-- lib
    |-- dshot
    |-- config
    |-- tts
    |-- shoot
    |-- utils
|-- src
    | -- main.cpp
|-- test
    | -- test_dshot.cpp
  • lib/ contains the libraries used to implement dshot on the pico

    • dshot/ is a standalone module that is used to convert a dshot command to a frame that can be sent using pwm hardware
    • config/ a header file for dshot globals, which are used to configure the hardware in tts/
    • tts/ contains the hardware implementations for dma, pwm, alarm pool
    • shoot/ implements sending dshot commands using the pwm hw as well as a repeating timer. A future merge request will add telem over uart in this module
    • utils/ just an led flashing for debugging
  • src/ contains main.cpp which uses serial input to send dshot commands

  • test/ contains a unit test for dshot/ as well as a cmake config file

Dependency Graph:

|-- shoot
|   |-- tts
|   |   |-- config
|   |-- dshot
|-- utils

To Do

  • Convert dshot/ module to just a header file using static inlines (make sure to check it works with the unit tests)
  • Move config.h to tts/dshotglobals.h . Wrap variables in a namespace and remove prefix DSHOT_ (a bit difficult because dshot namespace already exists)
  • tts.h can be renamed dshothw.h?
  • Maybe change / clarify the use of code and command

Backlog

  • attempt proper arm sequence
  • Try: DSHOT_SPEED = DEBUG ? 0.008 : 1200 // kHz (this may get rid of some other ternaries on DEBUG)
  • Add validation to ensure PWM, DMA, repeating timer have been setup correctly. MCU_CLK could take in a measured reading. Use lib/extern/pico-sdk/src/rp2_common/hardware_clocks/scripts/vcocalc.py to find valid sys clock frequencies.
  • Currently dma writes to a PWM counter compare. This actually writes to two dma channels (because upper / lower 16 bits are separate counters). Hence we render one dma channel useless. Is it possible to implement this in a better way?
  • Do we need to use the Arduino framework? Or can we just use the Pico SDK and import libraries 3rd party libs if necessary? If the latter, we could either consider Wiz IO or check out this post.
  • Transfer print_*_setup functions to logging / utils? Maybe check refactor branch. Maybe include the serial printf lib. There is an interesting post on printable classes
  • Scheme to represent DShot codes. enum is possible for special codes, but shouldn't be used for throttle values?
  • Explore the idea of using / generating a look up table to convert dshot to pwm counter values (and vice versa for bidir dshot). Can this be hooked up with Programmable IO? Memory usage: 2^16 command x 32 bit word = 32k x 64 bit word (that might be too much).
  • I believe that the uart and serial are initialised in stdio_init_all() (called in main.cpp). Perhaps we should use stdio_usb_init() and stdio_uart_init() separately. For telemetry (not yet committed), Peter used stdio_uart_init() after stdio_init_all() and didn't have errors.

DShot Protocol

A flight controller (Pico) sends commands to the ESC to control the motor. DShot is a digital protocol which typically sends commands using PWM hardware (note that we are not sending PWM frames, but are rather piggy backing off of its hardware to send dshot frames!). DShot is has discretised resolution, where as PWM was analogue on FC side (albeit discretised on the ESC due to hw limitations), however the advantages in accuracy, precision, resolution and communication outweigh this.

A Dshot command is constructed as follows:

  • 11 bit number representing the code (0 to 2047)
  • 1 bit telemetry flag
  • 4 bit CRC

To transmit this command we construct a frame by converting the bits to a voltage signal. Each bit is represented as a duty cycle on pwm hw

  • 0 = < 33%
  • 1 = > 75%
  • Note that a prefix and suffix 0 duty cycle (not a 0 bit!) is required in transmission to tell each frame apart

The period of each bit (i.e. the period set in the PWM hw) is determined by by the DShot freq. i.e. DShot150 sets the PWM hw freq to 150 kHz

Dshot Codes: 0 - Disarm (though not implemented?) 1 - 47: reserved for special use 48 - 2047: Throttle (2000 steps of precision)

For example, to make the motors beep the DShot frame is constructed as follows:

Code = 1, Telemetry = 1

→ First 12 bits of the command 0x003

CRC = 0 XOR 0 XOR 3 = 3

Command = 0x0033

→ Transmitted from left to right, the frame is: LLLL LLLL LLHH LLHH

(Please note that this nomenclature is not standard, but I believe it is clear)

Interesting Aside

It takes some time to calculate and send a dshot frame. To mitigate the effect of calculation, the implementation can use two buffers to store the DMA frame: a main buffer to store the current frame being sent by DMA, and a second buffer to store the next frame. This second buffer is only used if DMA is still sending the current frame, otherwise we default to storing the next frame in the main buffer. Further optimisation can be done by checking if the current dshot code and telem are the same as the next one, in which case there is no need to recalculate the frame.


Thrust Test Stand

Aim: Gather data on motor performance

  • linear ramp test (how long do we ramp for?)

  • step response

  • code a pid loop, and see it's effect on motor performance

  • Interestingly, since the number of dshot values is discretised, it might be possible to create a table characterising the response from one throttle value to another!?

  • In send_dshot_frame(), there could be a safety measure to see if the motors are responding, and only then send a command. If not, don't send a command, and the ESC will disarm itself.

Possible pico telemetry:

  • Count number of dshot frames sent
  • Measure how long each main loop takes
  • Measure min and max dshot update freq

Sources

USB Driver

Docs and Sample Implementation

Explanations of DShot

Other

NOTE: (Although we don't use this functionality), a common implmentation measuring timer uses 32 bit time (note that 64 bit is possible using timer_hw->timerawl but more effort..)

  • timer_hw->timerawl returns a 32 bit number representing the number of microseconds since power on
  • This will overflow at around 72 hrs, so must assume that this longer than the operation timer of the pico
  • This has been the cause of many accidental failures in the past :)

Bidirectional DShot

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published