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