一.引言
????????單總線(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) beginif (wb_rst_i) beginstate <= 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 beginwb_ack <= 1'b0;onewire_i_q <= onewire_i[lun];if (usec_counter_run) beginif (usec_counter == 16'd0) usec_counter_run <= 1'b0;usec_counter <= usec_counter - 1'b1;endif (usec_counter2_run) beginif (usec_counter2 == 9'd0) usec_counter2_run <= 1'b0;usec_counter2 <= usec_counter2 - 1'b1;endif (wb_cyc_i && wb_stb_i && !wb_ack && !wb_we_i) beginif (!read_block_enable_opt || (!rst_bit && (rxdone || read_bytes == 4'd0))) beginwb_ack <= 1'b1;rxdone <= 1'b0;endendcase (state) //代碼核心,狀態機部分4'd0: //初始化,狀態選擇if (!rxdone && read_bytes != 4'd0) beginrst_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) beginwb_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;endend // 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;end4'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;end4'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) begindat[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) beginonewire_oe[lun] <= 1'b0;onewire[lun] <= 1'b0;end 4'd4: if (usec_counter_run == 1'b0) beginstate <= 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;end4'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: beginonewire[lun] <= 1'b0;onewire_oe[lun] <= 1'b1;usec_counter <= usec_count(6);usec_counter_run <= 1'b1;state <= 4'd8;end4'd8: if (usec_counter_run == 1'b0) beginonewire_oe[lun] <= 1'b0;usec_counter <= usec_count(9);usec_counter_run <= 1'b1;push_done <= 1'b0;state <= 4'd9;end4'd9: if (usec_counter_run == 1'b0) beginshiftreg <= {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) beginonewire_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) beginonewire_oe[lun] <= 1'b0;onewire[lun] <= 1'b0;end4'd10: if (usec_counter_run == 1'b0) beginb <= b + 1'b1;if (b == 3'd7) begindat[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;endendcaseend
end
endmodule
?六.總結
????????在One-Wire協議中,主機和從機通過DQ線進行通信。主機向DQ線發送時鐘信號,從機根據時鐘信號將數據寫入DQ線。主機讀取DQ線上的電壓變化,從而獲取從機發送的數據。由于DQ線上只有一條信號線,因此需要采用特殊的操作來區分數據位和應答位。?