通信控制篇——DS18B20温度传感器
1.简介
DS18B20温度传感器温度读取——基于FPGA。
2.原理
DS18B20简介:
The DS18B20 Digital Thermometer provides 9 to 12–bit centigrade temperature measurements and has an alarm function with nonvolatile user-programmable upper and lower trigger points. The DS18B20 communicates over a 1-Wire bus that by definition requires only one data line (and ground) for communication with a central microprocessor. In addition, the DS18B20 can derive power directly from the data line (“parasite power”), eliminating the need for an external power supply.
引脚分布
name | description |
---|---|
Vdd | Power Supply Voltage |
GND | Ground |
DQ | Data In/Out |
初始化时序
初始化操作:
Master拉低总线,持续至少480us后,释放总线,等待15-60us后,在60-120us内读取总线,若为0,表示接收到响应,初始化成功,释放总线,等待一段时间,初始化完成。
读写时序
写时序:
写0:Master拉低总线,持续60-120us,释放总线,等待至少1us;
写1:Master拉低总线,持续至少1us,但要在15us内释放总线,然后等待;
注:写0和写1尽量控制周期一致,可设置为90us
读时序:
读0:Master拉低总线,持续至少1us,然后释放总线,在15us内读取总线(Master拉低总线持续时间不宜过长,否则会影响数据读取),数据持续时间大约45us,所以需要等待超过45us;
读1:Master拉低总线,持续至少1us,然后释放总线,在15us内读取总线(Master拉低总线持续时间不宜过长,否则会影响数据读取);
注:读0和读1尽量控制周期一致,可设置为90us
温度读取时序
初始化→Skip ROM(发送命令0XCC,LSB First)→启动温度转换(发送命令0X44)→初始化→Skip ROM(发送命令0XCC,LSB First)→启动数据接收(发送命令0XBE)→数据接收→复位(即初始化)
Master Mode | Data(LSB First) | Description |
---|---|---|
TX(RX) | Reset | Master reset, DS18B20 respond |
TX | 0XCC | Skip ROM command |
TX | 0X44 | Convert command |
TX(RX) | Reset | Master reset, DS18B20 respond |
TX | 0XCC | Skip ROM command |
TX | 0XBE | Read data command |
RX | data | Temperature data |
TX(RX) | Reset | Master reset, DS18B20 respond |
寄存器数据格式
当我们读取完所需要的数据之后,可以立刻进行复位(初始化),停止数据读取。
模块接口
name | description | direction | length |
---|---|---|---|
clk | 系统时钟 | input | 1 |
rst | 复位信号 | input | 1 |
DQ | 通信总线 | inout | 1 |
data | 温度数据 | output | <=16 |
3.程序实现
RTL视图
clk_div模块
`timescale 1ns/1ps
///
//Module Name : clk_div
//Description : get DS18B20 communication clk 204.8KHz ---- 5us
//Editor : yongxiang
//Time : 2019-12-16
///
module clk_div
(
input wire clk_in, //16.384M
input wire rst_n,
output reg clk_out
);
reg[5:0] cnt;
//80分频,得到204.8KHz时钟,周期约5us
always @(posedge clk_in)
begin
if(!rst_n)begin
clk_out <= 1'b0;
cnt <= 6'd0;
end
else begin
if(cnt == 6'd39)begin
clk_out <= !clk_out;
cnt <= 6'd0;
end
else begin
cnt <= cnt + 6'd1;
end
end
end
endmodule
temp_rd模块
`timescale 1ns/1ps
///
//Module Name : temp_rd
//Description : get temperature
//Editor : yongxiang
//Time : 2019-12-16
///
module temp_rd
(
input wire clk_in, //1.024M
input wire rst_n,
inout wire dq, //单总线
output reg[7:0] temp //温度值,单位℃
);
reg dq_is_out;
reg dq_reg;
reg respond;
reg[5:0] state;
reg[6:0] cnt;
assign dq = dq_is_out ? dq_reg : 1'bz; //输入输出方向控制
always @(posedge clk_in)
begin
if(!rst_n)begin
state <= 6'd0;
cnt <= 7'd0;
dq_reg <= 1'b1;
dq_is_out <= 1'b1;
temp <= 8'd0;
end
else begin
case(state)
//复位,初始化
6'd0:begin //置0,等待500us,置1
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd100)begin
cnt <= 7'd0;
dq_reg <= 1'b1;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
6'd1:begin //等待70us,接收应答0,建立通信
dq_is_out <= 1'b0; //输入
if(cnt == 7'd14)begin
respond <= dq; //接收0
cnt <= cnt + 7'd1;
end
else if(cnt == 7'd15)begin
if(respond == 1'b0)begin
cnt <= cnt + 7'd1;
end
else begin //初始化失败
state <= 6'd0;
cnt <= 7'd0;
end
end
else if(cnt == 7'd100)begin //初始化结束
state <= state + 6'd1;
cnt <= 7'd0;
end
else begin
cnt <= cnt + 7'd1;
end
end
//Skip ROM 发送0XCC----1100_1100
6'd2,6'd3,6'd6,6'd7:begin //发送0
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd16)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
6'd4,6'd5,6'd8,6'd9:begin //发送1
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd2)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
//启动convert,发送0X44----0100_0100
6'd10,6'd11,6'd13,6'd14,6'd15,6'd17:begin //发送0
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd16)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
6'd12,6'd16:begin //发送1
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd2)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
//复位,初始化
6'd18:begin //置0,等待500us,置1
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd100)begin
cnt <= 7'd0;
dq_reg <= 1'b1;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
6'd19:begin //等待70us,接收应答0,建立通信
dq_is_out <= 1'b0; //输入
if(cnt == 7'd14)begin
respond <= dq; //接收0
cnt <= cnt + 7'd1;
end
else if(cnt == 7'd15)begin
if(respond == 1'b0)begin
cnt <= cnt + 7'd1;
end
else begin //初始化失败
state <= 6'd0;
cnt <= 7'd0;
end
end
else if(cnt == 7'd100)begin //初始化结束
state <= state + 6'd1;
cnt <= 7'd0;
end
else begin
cnt <= cnt + 7'd1;
end
end
//Skip ROM 发送0XCC----1100_1100
6'd20,6'd21,6'd24,6'd25:begin //发送0
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd16)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
6'd22,6'd23,6'd26,6'd27:begin //发送1
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd2)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
//发送读取数据命令0XBE----1011_1110
6'd28,6'd34:begin //发送0
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd16)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
6'd29,6'd30,6'd31,6'd32,6'd33,6'd35:begin //发送1
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd2)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
//读取温度数据
6'd36,6'd37,6'd38,6'd39:begin
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd1)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
dq_is_out <= 1'b0; //输入
end
else if(cnt == 7'd3)begin
cnt <= cnt + 7'd1;
//小数数据不读取
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
6'd40:begin
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd1)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
dq_is_out <= 1'b0; //输入
end
else if(cnt == 7'd3)begin
cnt <= cnt + 7'd1;
temp[0] <= dq;//读取7位温度值
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
6'd41:begin
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd1)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
dq_is_out <= 1'b0; //输入
end
else if(cnt == 7'd3)begin
cnt <= cnt + 7'd1;
temp[1] <= dq;//读取7位温度值
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
6'd42:begin
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd1)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
dq_is_out <= 1'b0; //输入
end
else if(cnt == 7'd3)begin
cnt <= cnt + 7'd1;
temp[2] <= dq;//读取7位温度值
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
6'd43:begin
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd1)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
dq_is_out <= 1'b0; //输入
end
else if(cnt == 7'd3)begin
cnt <= cnt + 7'd1;
temp[3] <= dq;//读取7位温度值
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
6'd44:begin
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd1)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
dq_is_out <= 1'b0; //输入
end
else if(cnt == 7'd3)begin
cnt <= cnt + 7'd1;
temp[4] <= dq;//读取7位温度值
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
6'd45:begin
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd1)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
dq_is_out <= 1'b0; //输入
end
else if(cnt == 7'd3)begin
cnt <= cnt + 7'd1;
temp[5] <= dq;//读取7位温度值
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
6'd46:begin
if(cnt == 7'd0)begin
dq_is_out <= 1'b1; //输出
cnt <= cnt + 7'd1;
dq_reg <= 1'b0; //reset
end
else if(cnt == 7'd1)begin
cnt <= cnt + 7'd1;
dq_reg <= 1'b1;
dq_is_out <= 1'b0; //输入
end
else if(cnt == 7'd3)begin
cnt <= cnt + 7'd1;
temp[6] <= dq;//读取7位温度值
end
else if(cnt == 7'd18)begin
cnt <= 7'd0;
state <= state + 6'd1;
end
else begin
cnt <= cnt + 7'd1;
end
end
//读取结束,复位
6'd47:begin
state <= 6'd0;
end
endcase
end
end
endmodule
顶层模块
`timescale 1ns/1ps
///
//Module Name : temp_demo
//Description : DS18B20 communication
//Editor : yongxiang
//Time : 2019-12-16
///
module temp_demo
(
input wire clk_in,
input wire rst_n,
inout wire dq,
output wire[7:0] temp
);
wire clk;
//clk_div
clk_div clk_div_inst
(
.clk_in(clk_in),
.rst_n(rst_n),
.clk_out(clk)
);
//temp_rd
temp_rd temp_rd_inst
(
.clk_in(clk), //1.024M
.rst_n(rst_n),
.dq(dq), //单总线
.temp(temp) //温度值,单位℃
);
endmodule