Sur un multirotor, le contrôleur de vol recalcule la consigne de chaque moteur plusieurs milliers de fois par seconde. Le lien entre le contrôleur de vol et les contrôleurs de moteur (ESC) est donc devenu un point critique : il doit être rapide, insensible aux parasites et, idéalement, capable de remonter des informations. Le DShot répond à ces trois besoins. Cet article décortique le protocole au niveau du bit, puis sa variante bidirectionnelle qui renvoie la vitesse de rotation des moteurs sur le même fil.
Un calculateur en fin d’article encode et décode les trames décrites ici, commande comme réponse bidirectionnelle. Le côté pratique (réglages Betaflight, câblage, choix de la variante) est traité sur La fabrique du drone.
Du PWM au DShot
Historiquement, une consigne d’actionneur RC est une impulsion dont la largeur code la valeur : 1 ms pour 0 %, 2 ms pour 100 %, répétée environ toutes les 20 ms (50 Hz). Ce codage analogique convient parfaitement à un servo ou au variateur d’un avion, dont la bande passante utile se limite à celle des doigts du pilote. Il pose en revanche trois problèmes sur un drone :
- Fréquence de rafraîchissement : même poussé à 400–490 Hz, le PWM reste très en dessous des boucles de régulation modernes (4 à 8 kHz) ;
- Précision : la valeur dépend de la mesure d’une durée par l’ESC, donc de la gigue des horloges et du bruit sur la ligne ; il faut en plus calibrer les bornes de gaz de chaque ESC ;
- Immunité au bruit : une impulsion déformée par un parasite donne une consigne fausse, sans aucun moyen de le détecter.
Les premières évolutions ont simplement raccourci les impulsions pour augmenter la cadence, sans changer de principe :
| Protocole | Codage | Plage d’impulsion | Cadence maximale typique | Calibration |
|---|---|---|---|---|
| PWM | analogique | 1000–2000 µs | ≈ 490 Hz | oui |
| OneShot125 | analogique | 125–250 µs | ≈ 4 kHz | oui |
| OneShot42 | analogique | 42–84 µs | ≈ 11 kHz | oui |
| Multishot | analogique | 5–25 µs | ≈ 32 kHz | oui |
| DShot300 | numérique | trame de 53 µs | ≈ 16 kHz (théorique) | non |
| DShot600 | numérique | trame de 27 µs | ≈ 32 kHz (théorique) | non |
OneShot et Multishot repoussent la limite de cadence mais gardent les défauts d’un signal analogique : plus l’impulsion est courte, plus quelques dizaines de nanosecondes d’erreur pèsent lourd. Le DShot (Digital Shot) transmet au contraire une valeur numérique protégée par une somme de contrôle : l’ESC reçoit exactement la valeur émise, ou rejette la trame.
Le DShot côté physique
Le DShot utilise le même fil que le PWM, avec les mêmes niveaux logiques (3,3 V ou 5 V selon le contrôleur). Le nombre qui suit le nom est le débit en kbit/s : le DShot600 transmet 600 kbit/s. Il n’y a pas d’horloge séparée : chaque bit commence par un front montant, et c’est la durée de l’état haut qui donne sa valeur.
Le rapport est de 1 à 2 entre les deux durées : environ 37,5 % de la période pour un 0, 75 % pour un 1. Les implémentations varient un peu autour de ces valeurs (Betaflight génère par exemple ses bits avec un timer et les ESC tolèrent un écart confortable), car le récepteur se contente de comparer la durée de l’état haut à un seuil. Ce principe est le même que celui des LED adressables WS2812, avec d’autres durées.
| Variante | Débit | T1H (µs) | T0H (µs) | Bit (µs) | Trame de 16 bits (µs) |
|---|---|---|---|---|---|
| DShot150 | 150 kbit/s | 5,00 | 2,50 | 6,67 | 106,7 |
| DShot300 | 300 kbit/s | 2,50 | 1,25 | 3,33 | 53,3 |
| DShot600 | 600 kbit/s | 1,25 | 0,625 | 1,67 | 26,7 |
| DShot1200 | 1200 kbit/s | 0,625 | 0,313 | 0,83 | 13,3 |
Entre deux trames, la ligne reste à l’état bas ; c’est ce silence qui permet à l’ESC de se resynchroniser sur le début de la trame suivante. La cadence d’envoi n’est pas imposée par le protocole : elle suit en général la boucle de régulation du contrôleur de vol.
Format de la trame
Une trame DShot fait toujours 16 bits, transmis poids fort en premier :
- 11 bits de valeur (0 à 2047) ;
- 1 bit de demande de télémétrie, qui demande à l’ESC de renvoyer ses mesures sur le fil de télémétrie série, s’il existe ;
- 4 bits de CRC, calculés sur les 12 bits précédents.
Valeurs de gaz et commandes
Les 11 bits de valeur ne codent pas que les gaz :
- 0 : moteur désarmé (arrêt) ;
- 1 à 47 : commandes spéciales, exécutées uniquement moteur arrêté ;
- 48 à 2047 : gaz, soit 2000 pas de 0 à 100 %. En mode 3D (moteur réversible), la plage est coupée en deux : 48–1047 dans un sens, 1048–2047 dans l’autre.
| Commande | Rôle | Remarque |
|---|---|---|
| 1 à 5 | bips de l’ESC | attendre au moins 260 ms avant la commande suivante |
| 6 | demande d’informations ESC | réponse sur la télémétrie |
| 7, 8 | sens de rotation 1 / 2 | à envoyer 6 fois |
| 9, 10 | mode 3D désactivé / activé | à envoyer 6 fois |
| 12 | sauvegarde des réglages | à envoyer 6 fois, puis attendre 35 ms |
| 13, 14 | télémétrie DShot étendue (EDT) activée / désactivée | à envoyer 6 fois |
| 20, 21 | sens de rotation normal / inversé | à envoyer 6 fois |
| 22 à 29 | LED 0 à 3 allumées puis éteintes | surtout BLHeli_32 |
Les commandes sensibles sont répétées pour qu’une trame isolée, même avec un CRC valide par hasard, ne puisse pas inverser un moteur. Betaflight les envoie avec le bit de télémétrie à 1.
Le CRC
Le « CRC » du DShot est en réalité un simple XOR des trois quartets de la valeur de 12 bits (11 bits de gaz + bit de télémétrie) :
crc = (v ^ (v >> 4) ^ (v >> 8)) & 0x0F
Sur l’exemple : 1046 décalé d’un bit avec le bit de télémétrie à 0 donne v = 0x82C. Les trois quartets 8, 2 et C donnent 8 ⊕ 2 ⊕ C = 6, d’où la trame 0x82C6. Ce contrôle détecte toute erreur sur un bit isolé, mais pas deux erreurs à la même position dans deux quartets différents : c’est rudimentaire, et suffisant pour une liaison courte répétée des milliers de fois par seconde. Une trame rejetée est simplement remplacée par la suivante.
/* Construit une trame DShot de 16 bits.
valeur : 0 = arrêt, 1..47 = commande, 48..2047 = gaz
telemetrie: bit de demande de télémétrie
bidir : true en DShot bidirectionnel (CRC inversé) */
uint16_t dshot_trame(uint16_t valeur, bool telemetrie, bool bidir)
{
uint16_t v = (uint16_t)((valeur << 1) | (telemetrie ? 1u : 0u));
uint16_t crc = (v ^ (v >> 4) ^ (v >> 8)) & 0x0F;
if (bidir)
crc = (~crc) & 0x0F;
return (uint16_t)((v << 4) | crc);
}
Le DShot bidirectionnel
À quoi sert la vitesse des moteurs ?
Le contrôleur de vol stabilise le drone à partir d’un gyroscope MEMS, très sensible aux vibrations. La principale source de vibrations est le balourd des hélices et des moteurs, dont la fréquence est exactement la vitesse de rotation de chaque moteur (et ses harmoniques, notamment celle du passage des pales). Si le contrôleur de vol connaît ces vitesses en temps réel, il peut placer des filtres réjecteurs (notch) qui suivent précisément chaque moteur : c’est le filtre RPM de Betaflight. Le filtrage est plus efficace et introduit moins de retard qu’un filtre passe-bas fixe ; un drone qui vibre reste pilotable.
Les ESC savaient déjà renvoyer leur vitesse par un fil de télémétrie série séparé (protocole KISS, à environ 115 kbit/s), mais trop lentement et sur un fil supplémentaire. Le DShot bidirectionnel renvoie la vitesse sur le fil de commande, après chaque trame.
Le principe de l’échange
Deux modifications de la trame de commande signalent à l’ESC qu’une réponse est attendue :
- le signal est inversé : la ligne est haute au repos et chaque bit commence par un front descendant ; c’est la durée de l’état bas qui code la valeur ;
- le CRC est inversé :
crc = ~(v ^ (v >> 4) ^ (v >> 8)) & 0x0F. La trame 0x82C6 devient ainsi 0x82C9.
Après sa trame, le contrôleur de vol passe la broche en entrée (avec résistance de tirage vers le haut). L’ESC attend environ 30 µs, puis répond avec une trame de 21 bits émise à 5/4 du débit de la commande : 750 kbit/s en DShot600. Le bidirectionnel n’existe qu’à partir du DShot300.
Contenu de la réponse
L’ESC ne transmet pas une vitesse mais la période électrique du moteur en microsecondes, sous une forme flottante très compacte :
- 3 bits d’exposant e et 9 bits de mantisse m : période = m × 2e µs ;
- 4 bits de CRC, inversé comme dans la commande : le XOR des quatre quartets de la trame vaut 0xF ;
- la valeur maximale (période de 65 408 µs) signifie « moteur arrêté ».
Le contrôleur de vol en déduit la vitesse électrique : eRPM = 60 000 000 / période. La vitesse mécanique s’obtient en divisant par le nombre de paires de pôles du moteur (7 pour un moteur à 14 aimants, cas le plus courant sur un 5 pouces) : c’est pour cela que Betaflight demande le paramètre motor_poles.
Le codage GCR
La réponse n’est pas transmise comme la commande. L’ESC émet alors que le contrôleur de vol n’a aucune horloge commune avec lui ; le décodage se fait par suréchantillonnage (Betaflight échantillonne la ligne à environ trois fois le débit). Pour que le récepteur puisse se recaler, il faut garantir des fronts fréquents. Deux étapes s’en chargent :
- codage GCR 4b/5b : chaque quartet est remplacé par un code de 5 bits choisi pour ne jamais contenir plus de deux 0 consécutifs, même en enchaînant les codes ;
- codage en transitions (type NRZI) : un bit GCR à 1 provoque un changement de niveau de la ligne, un bit à 0 le laisse inchangé. Une trame commence par un bit de départ à 0, ce qui donne 1 + 4 × 5 = 21 bits.
| Quartet | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
| GCR | 11001 | 11011 | 10010 | 10011 | 11101 | 10101 | 10110 | 10111 |
| Quartet | 8 | 9 | A | B | C | D | E | F |
|---|---|---|---|---|---|---|---|---|
| GCR | 11010 | 01001 | 01010 | 01011 | 11110 | 01101 | 01110 | 01111 |
Exemple complet de réponse
Un moteur à 7 paires de pôles tourne à 18 000 tr/min, soit 126 000 eRPM et une période électrique d’environ 476 µs :
| Étape | Résultat |
|---|---|
| Période | 476 µs : tient sur 9 bits, donc e = 0, m = 476 |
| Valeur 12 bits | 0x1DC |
| CRC | 1 ⊕ D ⊕ C = 0, inversé : F → trame 0x1DCF |
| GCR des quartets 1, D, C, F | 11011 01101 11110 01111 |
| Transitions, bit de départ en tête | 0 10010 01001 01011 10101 |
| Décodage côté contrôleur de vol | 60 000 000 / 476 = 126 050 eRPM, soit 18 007 tr/min |
Le décodage suit le chemin inverse : XOR de la trame brute avec elle-même décalée d’un bit pour retrouver les bits GCR, table inverse pour les quartets, vérification du CRC, puis calcul de la période.
/* Table GCR inverse : code 5 bits -> quartet, -1 si code invalide */
static const int8_t gcr_inv[32] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, 9, 10, 11, -1, 13, 14, 15,
-1, -1, 2, 3, -1, 5, 6, 7, -1, 0, 8, 1, -1, 4, 12, -1
};
/* Décode une réponse eRPM de 21 bits (bit de départ en tête, poids fort d'abord).
Renvoie la vitesse en eRPM, 0 moteur arrêté, -1 si la trame est invalide. */
int32_t dshot_reponse_erpm(uint32_t brut21)
{
uint32_t gcr = (brut21 ^ (brut21 >> 1)) & 0xFFFFF; /* transitions -> 20 bits GCR */
uint32_t trame = 0;
for (int q = 3; q >= 0; q--) {
int8_t n = gcr_inv[(gcr >> (q * 5)) & 0x1F];
if (n < 0)
return -1;
trame = (trame << 4) | (uint32_t)n;
}
uint32_t x = trame ^ (trame >> 8);
x ^= x >> 4;
if ((x & 0x0F) != 0x0F) /* le XOR des 4 quartets doit valoir 0xF */
return -1;
uint32_t v = trame >> 4; /* 12 bits : eee mmmmmmmmm */
uint32_t periode_us = (v & 0x1FF) << (v >> 9);
if (periode_us == 0 || periode_us >= 65408u)
return 0; /* période maximale : moteur arrêté */
return (int32_t)(60000000u / periode_us);
}
Budget de temps
| Variante | Commande | Retournement | Réponse | Cycle complet | Boucle compatible |
|---|---|---|---|---|---|
| DShot300 | 53,3 µs | ≈ 30 µs | 56 µs | ≈ 140 µs | 4 kHz |
| DShot600 | 26,7 µs | ≈ 30 µs | 28 µs | ≈ 85 µs | 8 kHz |
| DShot1200 | 13,3 µs | ≈ 30 µs | 14 µs | ≈ 57 µs | 8 kHz et plus |
C’est pour cela que Betaflight associe généralement le DShot300 bidirectionnel à une boucle de 4 kHz et le DShot600 à une boucle de 8 kHz : un cycle de 140 µs ne tient pas dans les 125 µs d’une boucle à 8 kHz.
Télémétrie DShot étendue (EDT)
Une fois activée par la commande 13, la télémétrie étendue intercale d’autres mesures entre les trames de vitesse. Elle exploite un motif que les trames de période n’utilisent jamais : une période normalisée a toujours le bit de poids fort de la mantisse à 1 dès que l’exposant est non nul. Une trame dont les 4 premiers bits valent 0x2, 0x4, 0x6… porte donc une donnée de 8 bits d’un autre type :
| Type | Donnée | Unité |
|---|---|---|
| 0x02 | température | °C |
| 0x04 | tension | 0,25 V par pas |
| 0x06 | courant | A |
| 0x08, 0x0A, 0x0C | valeurs de débogage | selon le firmware |
| 0x0E | état / événements | selon le firmware |
Le support dépend du firmware de l’ESC : la tension et le courant sont surtout disponibles sur BLHeli_32 et AM32 ; Bluejay (firmware ouvert pour les ESC BLHeli_S) transmet notamment la température.
Calculateur de trames DShot
Le premier onglet construit une trame de commande et sa forme d’onde. Le second suit le chemin de la réponse bidirectionnelle : d’une vitesse de rotation jusqu’aux 21 bits émis par l’ESC, ou l’inverse à partir d’une trame relevée à l’analyseur logique.
Trame (16 bits)
–
CRC
–
Signification
–
Durée
–
Trame 16 bits
–
Période électrique
–
Vitesse
–
Sur la ligne
–
Mise en œuvre sur microcontrôleur
Générer des fronts à 1,67 µs près sur quatre moteurs ou plus ne se fait pas en logiciel pur. Côté émission, les contrôleurs de vol STM32 utilisent un timer en mode PWM dont le registre de comparaison est rechargé par DMA à chaque bit : le cœur prépare un tableau de 16 durées et le matériel fait le reste. Côté réception, deux approches coexistent :
- capture d’entrée par timer : le timer horodate chaque front et le DMA stocke les dates ;
- mode « bitbang » de Betaflight : le DMA recopie l’état d’un port GPIO complet à intervalle fixe, ce qui permet de lire plusieurs moteurs sur le même port avec un seul flux DMA. Ce mode s’accommode de presque toutes les affectations de broches, ce qui explique qu’il soit souvent choisi automatiquement.
Côté ESC, le protocole bidirectionnel demande un firmware compatible : BLHeli_32, AM32, et pour les ESC BLHeli_S les firmwares Bluejay ou JESC (le BLHeli_S d’origine ne le gère pas). Dans Betaflight, il s’active avec set dshot_bidir = ON ; l’onglet Moteurs affiche alors la vitesse de chaque moteur et le taux d’erreurs de réception, à vérifier avant de voler.
Mise en œuvre en LabVIEW FPGA
Sur une cible FPGA, le DShot se prête bien à une implémentation matérielle : chaque bit est une simple durée à l’état haut, et la réponse bidirectionnelle se lit en échantillonnant la ligne. Les diagrammes ci-dessous viennent d’une implémentation en LabVIEW FPGA ; ils suivent pas à pas les calculs décrits plus haut.
Construire la trame

La consigne de gaz arrive sur 0–2000 ; le booléen Setting distingue une commande spéciale d’une consigne de gaz, qui est alors décalée au-delà des 47 commandes. La valeur est ensuite décalée d’un bit, le bit de télémétrie ajouté, puis le sous-VI de CRC complète les 16 bits. La trame est enfin convertie en tableau de booléens dans l’ordre d’émission, poids fort en premier.

Le sous-VI de CRC est la traduction directe de (v ^ (v >> 4) ^ (v >> 8)) & 0xF ; la structure Condition inverse le résultat en mode bidirectionnel. Il renvoie à la fois le CRC seul et la trame complète (v << 4) + crc.
Générer les bits

Plutôt que de calculer les durées à la volée, une constante indexée par la variante donne, pour un bit à 1 et pour un bit à 0, trois durées exprimées en périodes d’une horloge à 200 MHz (5 ns) : le temps à l’état haut, la période du bit, et la période d’un bit de la réponse (5/4 du débit). On retrouve les valeurs du tableau de début d’article : en DShot600, 250 périodes (1,25 µs) pour un 1, 125 (0,625 µs) pour un 0, 334 (1,67 µs) par bit et 267 (1,33 µs) par bit de réponse. Les 16 descripteurs de bits sont poussés dans une FIFO, lue par la boucle cadencée qui pilote la broche : la génération des fronts reste ainsi parfaitement régulière, quelle que soit la charge du reste du code.
Décoder la réponse
La reconstitution des 21 bits à partir de la ligne échantillonnée (suréchantillonnage, puis décodage des transitions) se fait dans la boucle d’acquisition. Les 20 bits GCR obtenus passent ensuite par le décodeur ci-dessous.

La table inverse compte 32 entrées : chaque code GCR valide donne son quartet, les 16 autres valent 65535 et signalent une trame corrompue. La boucle traite quatre groupes de 5 bits, du poids fort au poids faible, et reconstruit la trame de 16 bits quartet par quartet. Le sous-VI Check CRC vérifie ensuite que le XOR des quatre quartets vaut 0xF, et la sortie Frame OK combine les deux contrôles.


Les 12 bits utiles donnent la période : la mantisse (masque 0x1FF) décalée vers la gauche de l’exposant (les 3 bits de poids fort). La conversion suit la convention de Betaflight, qui compte les eRPM par centaines : (600000 + période/2) / période, l’ajout de la demi-période assurant l’arrondi. La vitesse mécanique s’en déduit en multipliant par 200 et en divisant par le nombre d’aimants, soit 100 × eRPM / nombre de paires de pôles.
Les images sont des VI snippets : dans leur version d’origine, un fichier PNG glissé dans un diagramme LabVIEW recrée le code. Les versions publiées ici sont optimisées pour le web et ne servent qu’à la lecture.
Références
- DShot and Bidirectional DShot (brushlesswhoop.com) : durées, commandes, codage GCR et télémétrie étendue ;
- Pilotes DShot de Betaflight (dshot.c, dshot_bitbang_decode.c) : implémentation de référence du codage et du décodage ;
- Extended DShot Telemetry : spécification de l’EDT ;
- Calculateur de trames DShot de cet article, et le calculateur de CRC pour les vrais CRC (CRSF, Modbus…).

Laisser un commentaire
Vous devez vous connecter pour publier un commentaire.