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