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,且带有循环冗余校验位,很爽。

速度对比(全油门情况下的对比)

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/

//	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

简易测试文件

`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