单总线(OneWire)是一种串行通信协议,它允许多个设备通过一个单一的数据线进行通信。这个协议通常用于低速、短距离的数字通信,特别适用于嵌入式系统和传感器网络。
One Wire总线的通信过程分为三个阶段:
代码以状态机的方式展示,根据上图协议我们可以把状态机分成复位脉冲和在线应答脉冲的复位序列、写 0 时隙、写 1 时隙、读时隙等等。
module wb_onewire(
input wb_clk_i, // 时钟输入
input wb_rst_i, // 复位输入
input [15:0] wb_dat_i, // 16位宽的数据输入
output [15:0] wb_dat_o, // 16位宽的数据输出
output wb_ack_o, // 一拍有效的确认输出
input wb_we_i, // 一拍有效的写信号输入
input wb_cyc_i, // 一拍有效的周期信号输入
input wb_stb_i, // 一拍有效的稳定信号输入
output [7:0] onewire_o, // 8位宽的一线串行总线输出
output [7:0] onewire_oe_o, // 高表示总线为主机使用,低表示总线为从机使用
input [7:0] onewire_i // 8位宽的一线串行总线输入
);
parameter read_block_enable_opt = 1'b1; // 读块使能参数,默认为1
parameter push_1_opt = 1'b0; // push 1参数,默认为0
parameter wb_freq = 75000000; // 时钟频率参数,默认为75MHz
// 函数定义:计算微秒计数器值
function [15:0] usec_count;
input [9:0] usec;
begin
usec_count = (((wb_freq / 1000000) * usec) - 1) & 16'hffff;
end
endfunction
reg [2:0] lun, b; // 3位宽的lun和b寄存器
reg [3:0] read_bytes; // 4位宽的读取字节寄存器
reg [15:0] usec_counter; // 16位宽的微秒计数器
reg rst_bit, usec_counter_run; // 重置位和微秒计数器运行标志位
reg wb_ack, rxdone, onewire_i_q; // wb确认、接收完成、onewire输入队列标志位
reg usec_counter2_run; // 第二个微秒计数器运行标志位
reg [8:0] usec_counter2; // 9位宽的第二个微秒计数器
reg [7:0] dat, shiftreg, onewire, onewire_oe; // 数据、移位寄存器、onewire数据、onewire使能标志位
assign wb_ack_o = wb_ack; // wb确认输出信号
assign wb_dat_o = {lun, rst_bit, read_bytes, dat}; // wb数据输出信号
assign onewire_oe_o = onewire_oe; // 一线串行总线使能输出信号
assign onewire_o = onewire; // 一线串行总线输出信号
// 主逻辑块,在时钟上升沿或复位信号上升沿触发
always @(posedge wb_clk_i or posedge wb_rst_i) begin
if (wb_rst_i) begin
state <= 4'd0 ;
wb_ack <= 1'b0 ;
lun <= 3'd0 ;
read_bytes <= 4'd0 ;
usec_counter <= 16'd0;
usec_counter_run <= 1'b0 ;
usec_counter2 <= 9'd0 ;
usec_counter2_run<= 1'b0 ;
onewire <= 8'd0 ;
onewire_oe <= 8'd0 ;
rst_bit <= 1'b0 ;
dat <= 8'd0 ;
shiftreg <= 8'd0 ;
b <= 3'd0 ;
rxdone <= 1'b0 ;
push_done <= 1'b0 ;
onewire_i_q <= 1'b0 ;
end else begin
wb_ack <= 1'b0;
onewire_i_q <= onewire_i[lun];
if (usec_counter_run) begin
if (usec_counter == 16'd0)
usec_counter_run <= 1'b0;
usec_counter <= usec_counter - 1'b1;
end
if (usec_counter2_run) begin
if (usec_counter2 == 9'd0)
usec_counter2_run <= 1'b0;
usec_counter2 <= usec_counter2 - 1'b1;
end
if (wb_cyc_i && wb_stb_i && !wb_ack && !wb_we_i) begin
if (!read_block_enable_opt || (!rst_bit && (rxdone || read_bytes == 4'd0))) begin
wb_ack <= 1'b1;
rxdone <= 1'b0;
end
end
case (state) //代码核心,状态机部分
4'd0: //初始化,状态选择
if (!rxdone && read_bytes != 4'd0)
begin
rst_bit <= 1'b1;
state <= 4'd7;
if (read_bytes >= 4'he)
b <= read_bytes[0] ? 3'd6 : 3'd7;
end else if (wb_cyc_i && wb_stb_i && !wb_ack && wb_we_i) begin
wb_ack <= 1'b1;
lun <= wb_dat_i[15:13];
read_bytes <= wb_dat_i[11:8];
if (wb_dat_i[12] && wb_dat_i[7]) begin // reset
state <= 4'd1;
rst_bit <= 1'b1;
end else if (wb_dat_i[12] && wb_dat_i[6]) begin // write 1-bit
state <= 4'd5;
shiftreg <= wb_dat_i[7:0];
rst_bit <= 1'b1;
b <= 3'd7;
end else if (!wb_dat_i[12]) begin // write 8-bit
state <= 4'd5;
shiftreg <= wb_dat_i[7:0];
rst_bit <= 1'b1;
end
end
// Reset states
4'd1:
begin // 480us low pulse
onewire[lun] <= 1'b0;
onewire_oe[lun] <= 1'b1;
usec_counter <= usec_count(480);
usec_counter_run <= 1'b1;
state <= 4'd2;
end
4'd2:
if (usec_counter_run == 1'b0)
begin // 70us pull up
onewire_oe[lun] <= 1'b0;
usec_counter <= usec_count(70);
usec_counter_run <= 1'b1;
state <= 4'd3;
dat[1] <= 1'b1;
push_done <= 1'b0;
end
4'd3:
if (usec_counter_run == 1'b0)
begin // sample presence, 410us delay
if (onewire_i_q == 1'b0)
dat[0] <= 1'b1;
else dat[0] <= 1'b0;
usec_counter <= usec_count(410);
usec_counter_run <= 1'b1;
onewire_oe[lun] <= 1'b0;
onewire[lun] <= 1'b0;
state <= 4'd4;
end
else if (onewire_i_q && !push_1_opt)
dat[1] <= 1'b0;
else if (!push_done && onewire_i_q && push_1_opt)
begin
dat[1] <= 1'b0;
onewire_oe[lun] <= 1'b1;
onewire[lun] <= 1'b1;
usec_counter2 <= usec_count(2);
usec_counter2_run <= 1'b1;
push_done <= 1'b1;
end
else if (push_done && usec_counter2_run == 1'b0)
begin
onewire_oe[lun] <= 1'b0;
onewire[lun] <= 1'b0;
end
4'd4:
if (usec_counter_run == 1'b0)
begin
state <= 4'd0;
rst_bit <= 1'b0;
end
// Write state machine
4'd5:
if (usec_counter_run == 1'b0)
begin
// Write of 0/1 begins with 6us low (1) or 60us low (0)
onewire[lun] <= 1'b0;
onewire_oe[lun] <= 1'b1;
if (shiftreg[0])
usec_counter <= usec_count(6);
else usec_counter <= usec_count(60);
usec_counter_run <= 1'b1;
state <= 4'd6;
end
4'd6:
if (usec_counter_run == 1'b0)
begin
onewire[lun] <= 1'b1;
if (shiftreg[0])
usec_counter <= usec_count(64);
else
usec_counter <= usec_count(10);
usec_counter_run <= 1'b1;
shiftreg <= {onewire_i_q, shiftreg[7:1]}; // right shift
b <= b + 1'b1;
if (b == 3'd7)
state <= 4'd4;
else state <= 4'd5;
end
// Read state machine
4'd7:
begin
onewire[lun] <= 1'b0;
onewire_oe[lun] <= 1'b1;
usec_counter <= usec_count(6);
usec_counter_run <= 1'b1;
state <= 4'd8;
end
4'd8:
if (usec_counter_run == 1'b0)
begin
onewire_oe[lun] <= 1'b0;
usec_counter <= usec_count(9);
usec_counter_run <= 1'b1;
push_done <= 1'b0;
state <= 4'd9;
end
4'd9:
if (usec_counter_run == 1'b0)
begin
shiftreg <= {onewire_i_q, shiftreg[7:1]};
usec_counter <= usec_count(55);
usec_counter_run <= 1'b1;
state <= 4'd10;
onewire_oe[lun] <= 1'b0;
onewire[lun] <= 1'b0;
end
else if (!push_done && onewire_i_q && push_1_opt)
begin
onewire_oe[lun] <= 1'b1;
onewire[lun] <= 1'b1;
usec_counter2 <= usec_count(2);
usec_counter2_run <= 1'b1;
push_done <= 1'b1;
end
else if (push_done && usec_counter2_run == 1'b0)
begin
onewire_oe[lun] <= 1'b0;
onewire[lun] <= 1'b0;
end
4'd10:
if (usec_counter_run == 1'b0)
begin
b <= b + 1'b1;
if (b == 3'd7)
begin
dat[7:0] <= shiftreg;
if (read_bytes >= 4'he)
read_bytes <= 4'd0;
else
read_bytes <= read_bytes - 1'b1;
rxdone <= 1'b1;
state <= 4'd0;
rst_bit <= 1'b0;
end
else
state <= 4'd7;
end
endcase
end
end
endmodule
在One-Wire协议中,主机和从机通过DQ线进行通信。主机向DQ线发送时钟信号,从机根据时钟信号将数据写入DQ线。主机读取DQ线上的电压变化,从而获取从机发送的数据。由于DQ线上只有一条信号线,因此需要采用特殊的操作来区分数据位和应答位。