单总线协议(One-Wire)是Dallas Semiconductor(现为Maxim Integrated)推出的一种独特通信协议。与常见的I2C、SPI等多线通信方式不同,它仅用一根数据线就能实现双向通信,这在I/O资源紧张的嵌入式系统中显得尤为珍贵。我第一次接触这个协议是在一个农业物联网项目中,当时需要低成本监测大棚温度,DS18B20以其单总线特性和高精度成为理想选择。
DS18B20温度传感器的核心优势在于:
实际项目中常见的问题往往始于硬件连接。记得有次调试时温度读数始终异常,最后发现是忘了接4.7kΩ上拉电阻。单总线设备在空闲时需要上拉电阻将总线保持在高电平状态,这个细节对通信稳定性至关重要。
初始化是单总线通信的起点,包含两个关键阶段:
用Verilog实现时,我习惯用状态机控制时序:
verilog复制parameter RESET_LOW = 3'd0;
parameter RESET_HIGH = 3'd1;
parameter WAIT_PRESENCE = 3'd2;
always @(posedge clk) begin
case(state)
RESET_LOW:
if(cnt < 480_000/20 - 1) begin // 假设20ns时钟周期
cnt <= cnt + 1;
one_wire <= 1'b0;
end else begin
cnt <= 0;
state <= RESET_HIGH;
end
RESET_HIGH:
if(cnt < 70) begin // 释放总线后等待70个周期
cnt <= cnt + 1;
one_wire <= 1'bz; // 高阻态
end else begin
state <= WAIT_PRESENCE;
end
// 其他状态...
endcase
end
单总线的每位传输都遵循严格的时序规范:
写时序对比表:
| 操作 | 主机拉低时间 | 采样窗口 | 典型持续时间 |
|---|---|---|---|
| 写1 | 1-15μs | 15-60μs | 60μs |
| 写0 | 60-120μs | 15-60μs | 60μs |
读时序的关键点:
实测中发现,在FPGA中实现时,时钟分频精度直接影响通信成功率。我曾遇到因时钟偏差导致采样位置偏移的问题,最终通过校准计数器值解决。
根据DS18B20的操作流程,主状态机应包含以下状态:
verilog复制parameter [3:0]
S_INIT = 4'd0,
S_SKIP_ROM = 4'd1,
S_CONVERT = 4'd2,
S_DELAY = 4'd3,
S_READ_CMD = 4'd4,
S_READ_LOW = 4'd5,
S_READ_HIGH = 4'd6;
每个字节传输需要精确控制8个位周期,我的实现方案是:
verilog复制// 位传输控制示例
always @(posedge clk) begin
if(bit_cnt == 7 && us_cnt == US_MAX) begin
bit_cnt <= 0;
byte_cnt <= byte_cnt + 1;
end else if(us_cnt == US_MAX) begin
bit_cnt <= bit_cnt + 1;
end
if(us_cnt < US_MAX)
us_cnt <= us_cnt + 1;
else
us_cnt <= 0;
end
初始化阶段需要特别注意时间参数:
verilog复制// 初始化检测代码片段
wire presence_detected;
assign presence_detected = (presence_state == 2'b01);
always @(posedge clk) begin
case(presence_state)
2'b00: if(!one_wire) presence_state <= 2'b01; // 检测下降沿
2'b01: if(one_wire) presence_state <= 2'b10; // 检测上升沿
default: presence_state <= 2'b00;
endcase
end
DS18B20的温度数据格式特殊:
温度转换算法示例:
verilog复制// 温度值转换
reg [15:0] temp_raw;
wire [11:0] temp_abs = temp_raw[15] ? ~temp_raw[11:0] + 1 : temp_raw[11:0];
wire [7:0] temp_int = temp_abs[11:4];
wire [3:0] temp_frac = temp_abs[3:0];
// 输出温度值(放大10倍避免浮点)
assign temperature = {temp_raw[15], temp_int} * 10 + temp_frac * 625 / 1000;
根据我的项目经验,这些问题最常出现:
通过实测发现几个关键优化点:
verilog复制// 时序优化示例
always @(posedge clk_1mhz) begin // 专门分频得到的1MHz时钟
if(bit_done) begin
one_wire <= 1'bz;
#10; // 10us恢复时间
end
end
在最终实现的温室监测系统中,这套驱动方案成功实现了0.1℃精度的温度监测,并且单FPGA可以同时驱动8路DS18B20传感器。对于更复杂的应用,可以考虑加入CRC校验和自动重试机制来提升通信可靠性。