一.引言
单总线(OneWire)是一种串行通信协议,它允许多个设备通过一个单一的数据线进行通信。这个协议通常用于低速、短距离的数字通信,特别适用于嵌入式系统和传感器网络。
二.one wire通信优点缺点
优点:
- 单一数据线: 单总线仅需要一根数据线,这极大地简化了硬件连接。设备可以在同一总线上连接,并且通过地址来区分彼此。
- 低成本: 单总线协议不需要复杂的硬件,这降低了成本。这使其成为连接多个设备的经济实惠选择。
- 数据传输速率: 单总线通常以较低的数据传输速率工作,适用于一些低功耗和简单的应用。
- 异步通信: 数据在单总线上传输是异步的,不需要共享时钟信号。这使得它适用于各种设备和微控制器。
- 支持供电: 单总线通常支持从总线上获得电源,这对于一些小型设备非常有用。
缺点:
- 传输距离有限:由于采用单线传输数据,因此传输距离有限,通常在几米以内。
- 抗干扰能力较弱:由于采用单线传输数据,因此容易受到外界干扰的影响,导致数据传输错误。
- 扩展性较差:由于采用单线传输数据,因此无法实现多从机的通信,扩展性较差。
三.one wire工作原理
- 物理层连接: 单总线通信通常包括一个总线上的主设备和一个或多个从设备。这些设备通过一根物理数据线连接。总线上还可能有一个电源线用于为从设备提供电源。
- 数据帧: 通信基于数据帧的传输。一个数据帧通常包括起始位(Start Bit)、数据位、可选的校验位,以及停止位(Stop Bit)。
- 数据传输: 数据传输是异步的,没有共享时钟信号。数据通过时间间隔来表示逻辑 0 和逻辑 1。逻辑 0 和逻辑 1通常是通过时间长短来区分的,即短脉冲表示逻辑 0,长脉冲表示逻辑 1。
- 设备地址: 每个从设备都有一个唯一的地址,主设备通过发送从设备的地址来选择与之通信的特定设备。
- 总线控制: 主设备负责控制总线上的通信。它生成起始条件(Start Condition)和停止条件(Stop Condition)来开始和结束通信。
- 时序要求: 单总线通信非常依赖时序。每个位都必须在特定的时间内传输和采样,以确保数据的正确性。
- 供电: 一些单总线设备可以从总线上获得电源,这减少了对额外电源线的需求。
- 错误处理: 单总线通信通常包括错误检测和纠正机制,以确保数据的完整性。
四.协议简介
One Wire总线的通信过程分为三个阶段:
- 初始化阶段:主机发送一个复位信号,将总线上的所有设备复位。
- 数据传输阶段:主机发送一个时钟信号,从机根据主机的时钟信号逐位发送数据。主机可以接收从机发送的数据,也可以向从机发送数据。
- 结束阶段:主机发送一个停止信号,结束通信过程。
复位和应答
写协议
读协议
五.verilog代码
代码以状态机的方式展示,根据上图协议我们可以把状态机分成复位脉冲和在线应答脉冲的复位序列、写 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线上只有一条信号线,因此需要采用特殊的操作来区分数据位和应答位。