0

Does anyone one know if it's possible to implement Dshot on Arduino? I want to interface the Arduino with ESCs (electric speed controllers). These ESCs use Dshot, (https://oscarliang.com/dshot/) a digital protocol. From what I read online, you can make PWM follow a duty cycle. However, I want to change the duty cycle after each pulse and not just repeat the same duty cycle. I understand this requires DMA (direct memory access) but it's a bit over my head.

  • There is no technical specification of "dshot" at your link, only an advertisement – Chris Stratton Aug 20 '17 at 16:28
  • The more important question however is how you would get any actual benefit - this is a high performance protocol for fast control loops run on fast modern processors, not slow, limited ATmegas. Pretty much any ESC that supports this supports other things too, either automatically or with a different build of the open firmware it is using to support this in the first place. – Chris Stratton Aug 20 '17 at 16:45
  • I mainly want dshot for everything except the performance aspect (send commands, beeping, telemetry, leds, parity bits). I don't mind using pwm but since I'm getting this esc anyway, why not use the functionality to the fullest. I think this is a better link: blog.seidel-philipp.de/dshot-digital-esc-signal – Melvin Foo Aug 21 '17 at 14:22
  • I would like to use flight controllers but I'm not using these escs for quadcopter usage. I can use PWM to interface with the flight controller (or are there other ways) but in that case I might as well connect directly to the escs. – Melvin Foo Aug 21 '17 at 14:36
1

you can make pwm follow a duty cycle.

single shot pwm would be difficult to do on an avr.

much better to use hardware spi. Looks like you can do use 3 spi bits for every dshot bit. So 6 bytes of spi transmission would enough for every dshot frame (16 dshot bits).

running hardware spi at 3*0.6Mhz = 1.8Mhz or where about on a 16MIPS avr would be quite doable.

  • Sounds promising! i want to connect 4 escs though, while the audrino only has 1 mosi. One idea is to use a demuxer. Any thoughts? – Melvin Foo Aug 19 '17 at 21:29
  • it will depend on how the esc firmware reacts to long periods of no (input) signal. if it requires the constant presence of an input signal, that wouldn't be a viable solution. – dannyf Aug 20 '17 at 13:12
  • "pause between frames of at least 2 microseconds to indicate a frame reset" and "PID loop this pause is actually considerably longer" . As mentioned here: blck.mn/2016/11/dshot-the-new-kid-on-the-block. This might actually work! – Melvin Foo Aug 21 '17 at 14:30
1

In short, I ended up creating a library capable to drive multiple DShot600 ESCs: DShot-Arduino It still need a lot more polish, but the bit-banging works really well.


SPI method

I tried the method @dannyf mentioned, which involved combining 3 SPI bytes to form 1 dShot bit and it actually works.

But there's a few problem with SPI:

  • It takes up the valuable SPI port, in theory you can still connect other SPI devices, but;
  • maximum 1 dShot ESC can be connected
  • During transmission of the packet, the CPU need to wait for the whole task, assume we are sending DShot300, each packet is about 54us long so CPU is halt for that amount of time. If you are doing this in 1000Hz, that is 5% CPU time.
  • The timing is not very accurate to the DShot protocol standard

e.g. for DShot300, each bit is

1.25us High - 1.25us H/L - 0.83 Low

for a 16MHz Arduino, 1.25us will be 20 CPU cycles which is not accurate to transmit with a SPI byte

I end up with the following codes to send a bit:

inline void dshot_0(){
  SPDR = 0xFF;
  NOP;
  while (!(SPSR & _BV(SPIF))) ;  
  SPDR = 0x60;
  NOP;
  while (!(SPSR & _BV(SPIF))) ;  
  SPDR = 0x00;
  NOP8;
  while (!(SPSR & _BV(SPIF))) ;
}

inline void dshot_1(){
  SPDR = 0xFF;
  NOP;
  while (!(SPSR & _BV(SPIF))) ;  
  SPDR = 0xFF;
  NOP;
  while (!(SPSR & _BV(SPIF))) ;  
  SPDR = 0xF0;
  NOP8;
  while (!(SPSR & _BV(SPIF))) ;  
}

Above code is for DShot150, the SPI was set to 4Mbps, to adhere to the correct timing, I cannot transmit a full DShot byte as all on or all off, here's how the calculation done:

4Mbit SPI, each bit 0.25us. Thus for 16M clock arduino:

  • DShot 0 sequence: 10 bits of 1, 14 bits of 0
  • DShot 1 sequence: 20 bits of 1, 4 bits of 0

Those NOPn are just macro for the number of NOPs. I avoided using SPI library code here since the overhead of making library call is quite a lot.

Pure software bit-banging

This is the method resulted in my library, since using hardware SPI won't give me any advantages of releasing CPU time, and doing some Math gives me that a DShot600 require total 27 CPU cycle on 16MHz UNO. Which the pattern is the following:

10 Cycle High -> 10 Cycle H/L (depending on bit 1/0) -> 7 cycles low

Which gives total of 1.6875us per bit or 27us per packet. To allow the very tight timing those are coded in assembly.

Also by applying bit-masking the library is capable of driving at most 8 ESCs on the same hardware port (on UNO, Digital pin 0-7, PORTD).

Update cycle

One last note about DShot is during my test, I found it is quite strict on the update frequency. It must be updated at exact time interval and I found the slowest update frequency will be around 500Hz. To provide the correct timing for that, the library used Timer 1 and by default update in 1000Hz frequency.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.