DSHOT600电调协议
Dshot,一种全新的电调协议。穿越机,真的是航模发展的奇迹之一。 Cleanflight,Raceflight,Betaflight,Kiss,四大开发团队,发展目标不太一样。大家知道,在遥控接收机上面,有PWM,PPM,这些都是模拟信号;而SBUS和IBUS等这些xxxBus协议就是数字协议,走的是单片机的串行接收端口。那么由Futaba和Frsky等遥控器厂家大力推行串行数字接收协议,大家也看到他们的牛逼之处。但是我们现在用的电调都是模拟PWM方波信号,于是,由Flyduino(Kiss飞控的公司)联合Betaflight开发团队共同研发了Dshot数字电调协议。
数字电调协议的组成
16位 = 11位油门信号 + 1位电调信息回传 + 4位循环冗余校验
11位 - 2048步骤的分辨率油门值
1位 -遥测要求
4位 - CRC校验(检查信号的正确性)
如何代表0和1呢?
拿Dshot600来说,一位信号就大概是1.67微秒,那么通过对时间的占空比代表0或者1.
如果表示0:高电平占据625纳秒,
如果表示1:高电平占据1250纳秒
Dshot数字电调协议的优点
1.不再需要校准电调油门行程
2.精准的电调信号,数字信号的最大优点,由于PWM是模拟信号,容易出现传输过程中出现的波形变形问题
3.相比目前“1000-2000”的值,Dshot的行程由“0-2048”扩展(从00000000000到11111111111),毕竟有11位二进制数,2的11次方就是2048
4.速度上面,比Oneshot电调协议快太多
5.安全性,Dshot自带四位循环冗余校验(CRC)
与Oneshot和Mutishot的对比
Dshot完全是数字信号,Oneshot和Mutishot是模拟信号,如果在使用上,电调硬件或者软件多多少少需要进行滤波设置,去除由于传输过程中带来的噪声,大家如果想用bls电调升级到dshot,估计就得手动把电调是的电容去除掉,而这颗电容或电阻就是用于硬件滤波的,而Dshot除了省去校准电调行程外,在传输过程中根本不需要进行滤波!毕竟0就是0,1就是1,且带有循环冗余校验位,很爽。
速度对比(全油门情况下的对比)
1
2
3
4
5
6
|
Oneshot125 – 250 uS
DShot150 – 106.7 uS
Oneshot42 – 84 uS
DShot300 – 53.3 uS
DShot600 – 26.7 uS
Multishot – 25 uS
|
以上转载至:https://www.pianshen.com/article/7004689118/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
// Module Name: dshot600
// Describe:
// DSHOT600协议,0-10位为油门数据,11位为遥
// 测信号规定,此处默认为1,12-15位为循环冗余
// 校验码。
//
module dshot600(
input i_sys_clk,
input i_sys_rst_n,
output reg o_out,
input [10:0] i_value
);
localparam CNT_1_67US = 83;//1.67us
localparam CNT_LOW = 31;//表示0时,高电平时间625ns
localparam CNT_HIGH = 62;//表示1时,高电平时间1250ns
reg[7:0] cnt_cycle;
reg[4:0] cnt_bit;
reg[10:0] r_value;
reg[7:0] cnt_high_temp;
reg[3:0] crc;
reg r_i_crc_valid;
wire[3:0] w_crc_in;
wire[3:0] w_crc_out;
wire[10:0] w_value;
assign w_value = r_value;
assign w_crc_in = crc;
wire w_i_crc_valid;
wire w_o_crc_valid;
assign w_i_crc_valid = r_i_crc_valid;
CRC4_D11 CRC4_D11_inst(
.i_sys_clk (i_sys_clk),
.i_sys_rst_n (i_sys_rst_n),
.i_data (w_value),
.i_crc (w_crc_in),
.o_crc_new (w_crc_out),
.i_valid (w_i_crc_valid),
.o_valid (w_o_crc_valid)
);
always @(posedge i_sys_clk or negedge i_sys_rst_n)begin
if(~i_sys_rst_n)begin
cnt_bit <= 5'd0;
r_value <= 11'd0;
end
else case(cnt_bit)
5'd0:begin//采集一次输入的油门值
cnt_bit <= cnt_bit + 1'b1;
r_value <= i_value;
end
default:begin//第1位
if(cnt_cycle==CNT_1_67US&&cnt_bit==5'd16)
cnt_bit <= 5'd0;
else if(cnt_cycle==CNT_1_67US)
cnt_bit <= cnt_bit + 1'b1;
end
endcase
end
always @(posedge i_sys_clk or negedge i_sys_rst_n)begin
if(~i_sys_rst_n)begin
cnt_cycle <= 8'd0;
end
else case(cnt_bit)
5'd0:begin
cnt_cycle <= 8'd0;
end
default:begin//第1位
if(cnt_cycle==CNT_1_67US)
cnt_cycle <= 8'd0;
else
cnt_cycle <= cnt_cycle + 1'b1;
end
endcase
end
always @(posedge i_sys_clk or negedge i_sys_rst_n)begin
if(~i_sys_rst_n)
cnt_high_temp <= 8'd0;
else case(cnt_bit)
0:cnt_high_temp <= CNT_LOW;
1,2,3,4,5,6,7,8,9,10,11:begin
if(r_value[cnt_bit-1]==1'b1)
cnt_high_temp <= CNT_HIGH;
else if(r_value[cnt_bit-1]==1'b0)
cnt_high_temp <= CNT_LOW;
end
12:cnt_high_temp <= CNT_HIGH;
13,14,15,16:begin
if(crc[cnt_bit-13]==1'b1)
cnt_high_temp <= CNT_HIGH;
else if(crc[cnt_bit-13]==1'b0)
cnt_high_temp <= CNT_LOW;
end
endcase
end
always @(*)begin
if(cnt_bit==5'd0)
o_out = 1'b0;
else if(cnt_bit<=5'd16&&cnt_cycle<=cnt_high_temp)
o_out = 1'b1;
else if(cnt_bit<=5'd16&&cnt_cycle>cnt_high_temp)
o_out = 1'b0;
end
/*
crc获取行为
*/
always @(posedge i_sys_clk or negedge i_sys_rst_n)begin
if(~i_sys_rst_n)begin
r_i_crc_valid <= 1'b0;
crc <= 4'd0;
end
else begin
case(cnt_bit)
5'd1:r_i_crc_valid <= 1'b1;
5'd2:r_i_crc_valid <= 1'b0;
endcase
if(w_o_crc_valid==1'b1)//获取一帧CRC
crc <= w_crc_out;
end
end
endmodule
|
简易测试文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
`timescale 1ns/1ns //时间精度
`define clock_period 20 //时钟周期
module tb_dshot600; //实体名称
//=====================<系统端口>=============================
reg i_sys_clk ;
reg i_sys_rst_n ;
wire o_out ;
reg [10:0] i_value ;
dshot600 dshot600_inst(
.i_sys_clk (i_sys_clk),
.i_sys_rst_n (i_sys_rst_n),
.o_out (o_out),
.i_value (i_value)
);
//=====================<时钟信号>=============================
initial begin
i_sys_clk = 1;
forever
#(`clock_period/2) i_sys_clk = ~i_sys_clk;
end
//=====================<复位信号>=============================
initial begin
i_sys_rst_n = 0;#(`clock_period*20+1);
i_sys_rst_n = 1;
end
//=====================<激励信号>=============================
initial begin
i_value = 11'd0;
#(`clock_period*20+1);//初始化
i_value = 11'd386;
end
endmodule
|