Hello everyone,
I'm new to esp32 and I can not find simple examples that could help me to generate a dshot signal using DMA to GPIO transfers.
The dshot protocol is digital protocol to comunicate with ESC used to drive quadcopter motors for example:
And some technical infos about this protocol:
https://blck.mn/2016/11/dshot-the-new-kid-on-the-block/
DMA transfers seem to be the way to go to generate this signal for 4 motors.
With my understanding of DMA to GPIO process, the idea is to use a timer that would trig 3 DMA channels to operate this scheme:
I only found some examples involving I2S transfers using DMA but I do not have any idea of how to do DMA to GPIO transfer of a bits array to GPIO registers (GPIO_OUT_W1TS_REG ?).
Any help would be apreciated because I'm lost , thanks in advance!
DMA to GPIO for generating dshot signal
Re: DMA to GPIO for generating dshot signal
Looks like a job for RMT peripheral!
-
- Posts: 10
- Joined: Thu Sep 07, 2017 8:39 pm
Re: DMA to GPIO for generating dshot signal
Hi ESP_igrr,
thx for your reply! It looks like a great feature that I didn't know... perhaps because of its name^^. I'll try too investigate this way!
thx for your reply! It looks like a great feature that I didn't know... perhaps because of its name^^. I'll try too investigate this way!
-
- Posts: 10
- Joined: Thu Sep 07, 2017 8:39 pm
Re: DMA to GPIO for generating dshot signal
Hello,
I used RMT to generate the signal and the result is strange:
-Signal frames are generated precisly and correctly -> Good
-Unexplicable 35us delay between frames -> Not good
-Huge delay of 140 ms that seems like a reset or something like that... -> Not good at all
Here is the code (I use arduino IDE):
Here is the result with dezooming progressively:
With a 5us division, we see the signal is OK: With 20us division, we see that we have 35us between frames. I expected less than 10 (delay of 1us and the computing takes 7us). It can be reduced to 20 with setting wait_tx_done to 0 for rmt_write_items :
With 200ms division, we see a huge delay of 144ms! :
So the questions I have this time are:
-What is causing that delay between frames and the huge delay of 140ms?
-Is it OK if I put wait_tx_done to 0 for rmt_write_items?
-What is the impact of carrier_freq_hz for RMT? (I tried to change it without visible impact)
Thanks in advance!
I used RMT to generate the signal and the result is strange:
-Signal frames are generated precisly and correctly -> Good
-Unexplicable 35us delay between frames -> Not good
-Huge delay of 140 ms that seems like a reset or something like that... -> Not good at all
Here is the code (I use arduino IDE):
Code: Select all
void setup() {
config.rmt_mode = RMT_MODE_TX;
config.channel = RMT_CHANNEL_0;
config.gpio_num = (gpio_num_t)0;
config.mem_block_num = 1;
config.tx_config.loop_en = 0;
config.tx_config.carrier_en = 0;
config.tx_config.idle_output_en = 1;
config.tx_config.idle_level = (rmt_idle_level_t)0;
config.tx_config.carrier_duty_percent = 50;
config.tx_config.carrier_freq_hz = 1000000;
config.tx_config.carrier_level = (rmt_carrier_level_t)1;
config.clk_div = 2;
rmt_config(&config);
rmt_driver_install(config.channel, 0, 0);
}
Code: Select all
void loop() {
/* Computing Signal Timing to feed RMT */
rmt_write_items(config.channel, Times_Array,
16, /* Number of items */
1 /* wait till done */);
delayMicroseconds(1);
}
With a 5us division, we see the signal is OK: With 20us division, we see that we have 35us between frames. I expected less than 10 (delay of 1us and the computing takes 7us). It can be reduced to 20 with setting wait_tx_done to 0 for rmt_write_items :
With 200ms division, we see a huge delay of 144ms! :
So the questions I have this time are:
-What is causing that delay between frames and the huge delay of 140ms?
-Is it OK if I put wait_tx_done to 0 for rmt_write_items?
-What is the impact of carrier_freq_hz for RMT? (I tried to change it without visible impact)
Thanks in advance!
Last edited by Weskrauser on Sun Sep 24, 2017 9:18 am, edited 1 time in total.
Re: DMA to GPIO for generating dshot signal
You may want to let the RMT keep transmitting same data until you decide to change it. That is called "loop mode" in RMT driver:
http://esp-idf.readthedocs.io/en/latest ... channel_tb
That would be equivalent to saying "keep sending this until i change the data". Then you only need to call rmt_write_items when new data is available from the application. I can't immediately tell what happens during these 140ms delay periods, it could be that the main task (i.e. the task in which "loop" runs) is being preempted by something else. Do you have something else in your application, or is it just this rmt_write_items call?
Regarding carrier frequency: it is used only if you set carrier_en to "true":
http://esp-idf.readthedocs.io/en/latest ... arrier_enE
When carrier is enabled, the signal will be modulated at the given carrier frequency and duty cycle. This feature is mostly used when transmitting and receiving IR remote control signals, which are modulated at ~40kHz.
http://esp-idf.readthedocs.io/en/latest ... channel_tb
That would be equivalent to saying "keep sending this until i change the data". Then you only need to call rmt_write_items when new data is available from the application. I can't immediately tell what happens during these 140ms delay periods, it could be that the main task (i.e. the task in which "loop" runs) is being preempted by something else. Do you have something else in your application, or is it just this rmt_write_items call?
Regarding carrier frequency: it is used only if you set carrier_en to "true":
http://esp-idf.readthedocs.io/en/latest ... arrier_enE
When carrier is enabled, the signal will be modulated at the given carrier frequency and duty cycle. This feature is mostly used when transmitting and receiving IR remote control signals, which are modulated at ~40kHz.
-
- Posts: 10
- Joined: Thu Sep 07, 2017 8:39 pm
Re: DMA to GPIO for generating dshot signal
Hello,
thx again for your answer ESP_igrr, I tried the loop mode, it now works a lot better without the 144ms delay and with a correct delay of 6us between frames which is excellent. This is exacttly what I need for my use!
I've got only one problem remaining:
-With random frequency, frames longer than 16 bits are visibles. Perhaps a kind of concatenation of several parts of 16 bits frames (it can be 24 or more bits). Maybe I 'm not calling rmt_write_items at the right time or something like that. The frequency of these longer frames depends on the delayMicroseconds I use in my loop() fonction so it seems like a synchronization problem:
...more in detail:
I posted the code I use. The only thing it does is converting the throttle 1983 into a compatible 16 bit dshot frame. It then compute an timing array for RMT and write it:
Part1 with loop and init:
Part2 with functions that compute timings array for RMT_write
In fact, I would like to use the loop mode like you proposed and to inject a new updated value "at the right time" not to disturb the frame that will be updated at the next TX
Thanks in advance
thx again for your answer ESP_igrr, I tried the loop mode, it now works a lot better without the 144ms delay and with a correct delay of 6us between frames which is excellent. This is exacttly what I need for my use!
I've got only one problem remaining:
-With random frequency, frames longer than 16 bits are visibles. Perhaps a kind of concatenation of several parts of 16 bits frames (it can be 24 or more bits). Maybe I 'm not calling rmt_write_items at the right time or something like that. The frequency of these longer frames depends on the delayMicroseconds I use in my loop() fonction so it seems like a synchronization problem:
...more in detail:
I posted the code I use. The only thing it does is converting the throttle 1983 into a compatible 16 bit dshot frame. It then compute an timing array for RMT and write it:
Part1 with loop and init:
Code: Select all
// DSHOT600 mais peut-être modifié pour DSHOT1200
#include <driver/rmt.h>
/*RMT definition*/
#define DIVIDER 2 /* Diviseur du timer*/
#define DURATION 12.5 /* flash 80MHz => minimum time unit */
#define PULSE_T0H ( 625 / (DURATION * DIVIDER));
#define PULSE_T1H ( 1250 / (DURATION * DIVIDER));
#define PULSE_T0L ( 1045 / (DURATION * DIVIDER));
#define PULSE_T1L ( 420 / (DURATION * DIVIDER));
#define PULSE_T0H_RAW 625;
#define PULSE_T1H_RAW 1250;
#define PULSE_T0L_RAW 1045;
#define PULSE_T1L_RAW 420;
rmt_config_t config;
uint16_t Throttle = 1983;
uint16_t Throttlecut = Throttle;
uint8_t Decalcut = 0;
byte Telem = 0;
uint8_t Checksum = 0;
uint16_t ThrottleTelem = 0;
uint16_t DshotBinFrame = 0;
long t_now = 0;
void setup() {
config.rmt_mode = RMT_MODE_TX;
config.channel = RMT_CHANNEL_0;
config.gpio_num = (gpio_num_t)0;
config.mem_block_num = 1;
config.tx_config.loop_en = 0;
config.tx_config.carrier_en = 0;
config.tx_config.idle_output_en = 1;
config.tx_config.idle_level = (rmt_idle_level_t)0;
config.tx_config.carrier_duty_percent = 50;
config.tx_config.carrier_freq_hz = 10000;
config.tx_config.carrier_level = (rmt_carrier_level_t)1;
config.clk_div = 2;
rmt_config(&config);
rmt_driver_install(config.channel, 0, 0);
rmt_set_tx_loop_mode((rmt_channel_t)0, 1);
}
void loop() {
ThrottleTelem = ComputeThrottleTelem(Throttle , Telem);
Checksum = ComputeDshotChecksum(ThrottleTelem);
DshotBinFrame = ComputeFullDshotFrame(ThrottleTelem , Checksum);
rmt_item32_t *Times_Array = ComputeDshotBuffer_RMT_ESP32(DshotBinFrame);
delayMicroseconds(25);
rmt_write_items(config.channel, Times_Array,
16, /* Number of items */
1 /* wait till done */);
rmt_wait_tx_done(config.channel);
free(Times_Array);
}
Code: Select all
/* Compute Dshot checksum */
uint8_t ComputeDshotChecksum(uint16_t ThrottleTelem) { //Coupe la valeur de 12 bits en 3 parties de 4 bits et les "somme" avec XOR.
uint8_t Checksum = 0;
for (int i = 0; i < 3; i++) {
Decalcut = 12 - (i + 1) * 4; // On commence par la partie de 4 bits coté bit dominant, donc on décale de 8, 4 et 0 bits
Throttlecut = (ThrottleTelem >> Decalcut) & 0b1111; // Maintenant on ne garde que les 4 premiers chiffres de l'uint8
Checksum ^= Throttlecut; // On somme avec XOR
}
return Checksum;
}
/* Compute Complete ThrottleTelem Value */
uint16_t ComputeThrottleTelem(uint16_t Throttle , byte Telem) { //Rassemble le throttle et le bit de telem en une valeur de 12 bit
uint16_t ThrottleTelem = 0;
ThrottleTelem = (Throttle << 1) + Telem;
return ThrottleTelem;
}
/* Compute Full Dshot frame */
uint16_t ComputeFullDshotFrame(uint16_t ThrottleTelem , uint8_t Checksum) { //Concatene 11 bits throttle, 1 bit telem, et 4 bits de checksum
uint16_t DshotBinFrame = 0;
DshotBinFrame = (ThrottleTelem << 4) + (Checksum & 0b1111);
return DshotBinFrame;
}
// Fonction utilisée générant le véritable standard pour esp32
rmt_item32_t *ComputeDshotBuffer_RMT_ESP32(uint16_t DshotBinFrame) { // Put timings in a table to use for RMT (1 = High during 1250ns, 0 = High during 625ns). Fonction qui renvoit un pointeur qui pointe vers le tableau cf http://www.dinduks.com/cpp-retourner-un-tableau-dans-une-fonction-ou-methode/;
rmt_item32_t *Times_Array = new rmt_item32_t[16]; // Allocation dynamique c++ ~ malloc, calloc
for (int i = 0; i < 16 ; i++) {
if ( ((DshotBinFrame >> i) & 0b1) == 1 ) { //Prend les bits du signal entier un par un par décalage. Si ce bit vaut un, on met les durées du timing haut, sinon timing bas en fonction du divider rmt
Times_Array[(15 - i)].duration0 = PULSE_T1H;
Times_Array[(15 - i)].level0 = 1;
Times_Array[(15 - i)].duration1 = PULSE_T1L;
Times_Array[(15 - i) ].level1 = 0;
}
else {
Times_Array[(15 - i)].duration0 = PULSE_T0H;
Times_Array[(15 - i)].level0 = 1;
Times_Array[(15 - i)].duration1 = PULSE_T0L;
Times_Array[(15 - i) ].level1 = 0;
}
}
Times_Array[15].duration0 = PULSE_T0H;
Times_Array[15].level0 = 1;
Times_Array[15].duration1 = 250;
Times_Array[15].level1 = 0;
return Times_Array;
}
Thanks in advance
Re: DMA to GPIO for generating dshot signal
Were you able to get this working with the tx loop enabled?
I've been following a similar path using the rmt feature for dshot signals but I'm running into some troubles getting 4 ESCs to arm properly. One servo seems to work just fine using rmt tx at 1ms intervals, but maybe the timing is marginal and breaks with more channels. I'm planning on trying the loop mode next to see if that gives more consistent results.
I've been following a similar path using the rmt feature for dshot signals but I'm running into some troubles getting 4 ESCs to arm properly. One servo seems to work just fine using rmt tx at 1ms intervals, but maybe the timing is marginal and breaks with more channels. I'm planning on trying the loop mode next to see if that gives more consistent results.
Re: DMA to GPIO for generating dshot signal
How did you go getting this working?
Im also looking at dshot with RMT but would also like to receive the dshot telemetry. My post below has working code that sends dshot and Im having trouble figuring out the receiving component.
http://bbs.esp32.com/viewtopic.php?f=19 ... 6d97af393e
Im also looking at dshot with RMT but would also like to receive the dshot telemetry. My post below has working code that sends dshot and Im having trouble figuring out the receiving component.
http://bbs.esp32.com/viewtopic.php?f=19 ... 6d97af393e
Who is online
Users browsing this forum: Basalt and 44 guests