DS18B20 温度传感器
一、DS18B20概述
DS18B20是一种数字温度传感器,应用非常广泛。它输出的是数字信号,同时具有体积小,硬件资源耗费少,抗干扰能力强,精度高等特点。
DS18B20温度传感器特点
1、采用单总线的接口方式 与微处理器连接时仅需要一条口线即可实现微处理器与 DS18B20 的双向通讯。单总线具有经济性好,抗干扰能力强,适合于恶劣环境的现场温度测量。
2、测温范围: DS18B20温度传感器的测温范围可达-55℃~+125℃,在-10℃到+85℃范围内误差为±0.4°。
3、支持多点组网功能:多个DS18B20温度传感器可以并联在一条数据线上,最多可以并联8个,实现多点测温。
4、工作电源: 3.0~5.5V/DC ,DS18B20温度传感器可以采用外部独立电源供电,也可以用数据线寄生电源供电,负压特性电源极性接反时,温度计不会因发热而烧毁,但不能正常工作。。
5、DS18B20温度传感器在应用过程中不需要任何外围元件。
6、DS18B20温度传感器测量温度的结果以9~12位数字量方式串行传送。
7、掉电保护功能, DS18B20温度传感器内部含有 EEPROM ,通过配置寄存器可以设定数字转换精度和报警温度。在DS18B20温度传感器掉电以后仍可保存分辨率及报警温度的设定值。
8、DS18B20温度传感器返回16位二进制数代表此刻探测的温度值,其高五位代表正负。如果高五位全部为1,则代表返回的温度值为负值。如果高五位全部为0,则代表返回的温度值为正值。后面的11位数据代表温度的绝对值,将其转换为十进制数值之后,再乘以0.0625即可获得此时的温度值。
二、DS18B20手册提炼
2.1 引脚说明
只有三个引脚,说明ds18b20是单总线,使用三态门的方式去实现单总线,原理图如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/e56fb611f05142f39fbef2eadaebd5dc.png
assign dq = dq_oe ? dq_out : 1'bz;
assign dq_in = dq;
dq_in 就是ds18b20传感器发来的数据
dq_out就是主机发给ds18b20的数据
dq_oe就是主机发送使能,1能发,0不能发
2.2 数据说明
数据[10:0]是温度数据
数据[15:11]是温度的正负,1代表负,0代表正
2.3 DS18B20暂存器数据
DS18B20的每个暂存器都有8bit存储空间,用来存储相应数据,
byte0和byte1分别为温度数据的低位和高位,用来储存测量到的温度值,且这两个字节都是只读的;
byte2和byte3为TH、TL告警触发值的拷贝,可以在从片内的电可擦可编程只读存储器EEPROM中读出,也可以通过总线控制器发出的[48H]指令将暂存器中TH、TL的值写入到EEPROM,掉电后EEPROM中的数据不会丢失;
byte4的配置寄存器用来配置温度转换的精确度(最大为12位精度);
byte5、6、7为保留位,禁止写入;
byte8亦为只读存储器,用来存储以上8字节的CRC校验码。
2.4 DS18B20需要的命令
2.5 初始化时序
主机控制总线给从机发送复位脉冲480us,主机释放总线从机等待15-60us,从机控制总线给主机发送存在脉冲60-240us
写时隙(主机向从机传数据)
无论是写0还是写1,一开始都是主机控制总线拉低总线15us,如果是写0,需要在15us-60us内主机控制总线保持低电平,如果是写1,需要在15us-60us内主机释放总线,上拉电阻将总线拉高,
2.6 关键时间参数
三、DS18B20模块划分
DS18B20 的设计分为系统框架图如下:
四、ds18b20_drive模块
4.1 主从状态机设计
-
M_IDLE :主状态机空闲状态,当enable信号有效时,复位脉冲持续时间M_REST 状态;**
-
M_REST:发送复位脉冲,复位脉冲发送完成后,跳转至M_RELS 状态,复位脉冲持续时间为48us;
-
M_RELS :释放总线15~60us后,跳转至M_RACK 状态;
-
M_RACK :判断是否有存在脉冲,未收到存在脉冲跳转至M_IDLE状态,收到存在脉冲则跳转至M_ROMS状态;
-
M_ROMS :发送ROM命令,跳过ROM,状态跳转至M_CONT发送温度转换命令,状态跳转至M_RCMD则发送读暂存器命令
-
M_CONT :发送温度转换命令,温度转换命令发送完成后跳转至M_WAIT
-
M_WAIT = :等待温度转化完成后,(等待时间为750ms),跳转至M_REST;
-
M_RCMD =:发送读暂存器命令,读暂存器命令发送完成后跳转至M_RTMP
-
M_RTMP = :接收温度数据,16bit温度数据读取完成后跳回M_IDLE。
assign m_idle2m_rest = m_state == M_IDLE && enable; //温度采集一直工作 assign m_rest2m_rels = m_state == M_REST && end_cnt; assign m_rels2m_rack = m_state == M_RELS && end_cnt; assign m_rack2m_idle = m_state == M_RACK && end_cnt && (rx_ack == 1); //未收到存在脉冲,存在脉冲持续时间240us已到 assign m_rack2m_roms = m_state == M_RACK && end_cnt && (rx_ack == 0); //收到了存在脉冲 assign m_roms2m_cont = m_state == M_ROMS && s_done2s_idle && (m_flag == 0); //命令发送完成了 assign m_roms2m_rcmd = m_state == M_ROMS && s_done2s_idle && (m_flag == 1); assign m_cont2m_wait = m_state == M_CONT && s_done2s_idle; assign m_wait2m_rest = m_state == M_WAIT && end_cnt; //750ms计数完成 assign m_rcmd2m_rtmp = m_state == M_RCMD && s_done2s_idle; assign m_rtmp2m_idle = m_state == M_RTMP && s_done2s_idle;
状态机主从互动:
/**************************************************************
从状态机flag
**************************************************************/
//辅助从状态机状态跳转
always@(posedge clk or negedge rst_n)
if(!rst_n)
s_flag = 0;
else if(s_done2s_idle)
s_flag = 0;
else if(end_bit_cnt)
s_flag = 1;
- S_IDLE :从状态机空闲状态,接收到cmd_vld信号时,从状态机相应;
- S_LOW :读写时隙拉低,此时主状态机在M_RTMP状态的前提下,1us的拉低时间后跳转至S_SAMP ,否则1us计时结束后 跳转至S_SEND;
- S_SEND :写时隙持续59us,一次写时隙最少60us,计时结束后跳转至S_RELS ;
- S_SAMP :读时隙,持续59us后跳转至S_RELS ;
- S_RELS :时隙之间的间隔时间,1us释放时间达到,并且数据已经传输完成,跳转至S_DONE 状态,则1us计时结束后跳转至S_LOW ;
- S_DONE :一次命令或者数据执行完成,无条件跳转回到S_IDLE 。
assign s_idle2s_low = s_state == S_IDLE && cmd_vld;
assign s_low2s_samp = s_state == S_LOW && end_cnt && m_state == M_RTMP; //1us的拉低时间已到
assign s_low2s_send = s_state == S_LOW && end_cnt; //1us
assign s_send2s_rels = s_state == S_SEND && end_cnt; //59us
assign s_samp2s_rels = s_state == S_SAMP && end_cnt; //59us
assign s_rels2s_done = s_state == S_RELS && end_cnt && s_flag; //1us释放时间达到,并且数据已经传输完成
assign s_rels2s_low = s_state == S_RELS && end_cnt; //1us
assign s_done2s_idle = s_state == S_DONE && 1;
4.2 三态门设计
/**************************************************************
三态门控制
**************************************************************/
assign dq = dq_oe? dq_out : 1'bz;
assign dq_in = dq;
always@(posedge clk or negedge rst_n)
if(!rst_n)
dq_oe <= 0;
else if(m_idle2m_rest || m_rack2m_roms || m_roms2m_cont || m_wait2m_rest || m_roms2m_rcmd || s_idle2s_low || s_rels2s_low)
dq_oe <= 1;
else if(m_rest2m_rels || m_cont2m_wait || s_low2s_samp || s_send2s_rels)
dq_oe <= 0;
always@(posedge clk or negedge rst_n)
if(!rst_n)
dq_out <= 0;
else if(m_idle2m_rest || m_wait2m_rest || s_idle2s_low || s_rels2s_low)
dq_out <= 0;
else if(s_low2s_send)
dq_out <= cmd[cnt_bit];
4.3 时间参数
parameter T_CYC = 20 , //工作时钟周期
RST_T = 480_000 , //复位时间
RELS_T = 15_000 , //复位时间释放时间
RACK_T = 240_000 , //存在脉冲持续时间
WAIT_T = 750_000_000 , //温度转换等待时间
LOW_T = 1_000 , //时隙拉低1us,时隙间隔释放时间
SLOT_T = 59_000 , //读写时隙存在时间
RX_RACK_T = 60_000 , //判断存在脉冲是否生效的时刻
RX_DATA_T = 12_000 ; //读时隙接收数据点
4.4 计数器复用
/**************************************************************
计数器
**************************************************************/
always@(posedge clk or negedge rst_n)
if(!rst_n)
cnt <= 'd0;
else if(add_cnt) begin
if(end_cnt)
cnt <= 'd0;
else
cnt <= cnt + 1'b1;
end
assign add_cnt = m_state != M_IDLE;
assign end_cnt = add_cnt && cnt == cnt_max - 1;
always@(*)
case(m_state)
M_REST : cnt_max = RST_T / T_CYC; //复位脉冲持续时间
M_RELS : cnt_max = RELS_T / T_CYC; //复位脉冲释放时间
M_RACK : cnt_max = RACK_T / T_CYC; //存在脉冲持续时间
M_WAIT : cnt_max = WAIT_T / T_CYC; //温度转换时间
//实现命令/读取数据(从状态机工作)
M_ROMS,M_CONT,M_RCMD,M_RTMP :
case(s_state)
S_LOW,S_RELS : cnt_max = LOW_T / T_CYC; //1us拉低时间,或者时隙间隔时间
S_SEND,S_SAMP : cnt_max = SLOT_T / T_CYC; //59us时隙持续时间
default : cnt_max = 1;
endcase
default : cnt_max = 1;
endcase
4.5 接收存在脉冲
/**************************************************************
接收存在脉冲
**************************************************************/
always@(posedge clk or negedge rst_n)
if(!rst_n)
rx_ack <= 1;
else if(m_state == M_REST) //发送复位脉冲时,复位该信号
rx_ack <= 1;
else if(m_state == M_RACK && rx_ack == 1) //在接收存在脉冲的状态下,在采样时刻点接收dq端的数据
rx_ack <= dq_in;
4.6 发送命令控制
/**************************************************************
发送命令控制
**************************************************************/
localparam SKIP = 8'hCC , //跳过ROM命令
CONV = 8'h44 , //温度转换命令
RSCR = 8'hBE ; //读暂存器命令
always@(posedge clk or negedge rst_n)
if(!rst_n)
cmd <= 8'h00;
else case(m_state)
M_ROMS : cmd <= SKIP;
M_CONT : cmd <= CONV;
M_RCMD : cmd <= RSCR;
default : cmd <= 8'h00;
endcase
always@(posedge clk or negedge rst_n)
if(!rst_n)
cmd_vld <= 0;
else if(m_rack2m_roms || m_roms2m_cont || m_roms2m_rcmd || m_rcmd2m_rtmp)
cmd_vld <= 1;
else
cmd_vld <= 0;
4.7 接收温度数据
/**************************************************************
接收数据
**************************************************************/
always@(posedge clk or negedge rst_n)
if(!rst_n)
rx_data <= 0;
else if(s_state == S_SAMP && cnt == RX_DATA_T/T_CYC)
rx_data <= {dq_in,rx_data[15:1]};
else if(m_state == M_IDLE)
rx_data <= 0;
assign rx_data_vld = m_state == M_RTMP && s_done2s_idle;
//原始数据处理,补码转原码
always@(posedge clk or negedge rst_n)
if(!rst_n)
true_data <= 0;
else if(rx_data_vld) begin
if(rx_data[15]) //负数 补码转原码
true_data <= ~rx_data[10:0] + 1;
else
true_data <= rx_data[10:0];
end
//扩大数据1000倍,根据温度精度,计算实际的温度值 *10000*0.0625
assign temp_data = true_data * 625;
always@(posedge clk or negedge rst_n)
if(!rst_n)
temp_data_vld <= 0;
else
temp_data_vld <= rx_data_vld;
五、uart_tx_ctrl模块及hex2ascii模块
/**************************************功能介绍***********************************
Date :
Author : linxiaoxiao.
Version :
Description: 温度数据处理后传输至串口进行显示
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module uart_tx_ctrl(
input clk ,
input rst_n ,
input enable ,
input uart_tx_ready ,
input [23:0] bcd_data ,
input bcd_data_vld ,
output reg [7:0] uart_tx_data ,
output uart_tx_data_vld
);
//---------<参数定义>---------------------------------------------------------
//状态机参数定义
localparam IDLE = 'b01,//
DATA = 'b10;//
parameter NUM_MAX = 14;
//---------<内部信号定义>-----------------------------------------------------
reg [4:0] cnt_num ;
wire add_cnt_num,end_cnt_num ;
wire ascii_data_vld;
wire [47:0] ascii_data ;
reg [1:0] state ;//现态
/****************************************************************
例化
****************************************************************/
hex2ascii hex2ascii_inst(
/* input */.clk ( clk ) ,
/* input */.rst_n ( rst_n ) ,
/* input [23:0] */.hex_din ( bcd_data ) ,
/* input */.hex_din_vld ( bcd_data_vld ) ,
/* output [47:0] */.ascii_dout ( ascii_data ) ,
/* output */.ascii_dout_vld ( ascii_data_vld )
);
/****************************************************************
状态机
****************************************************************/
// 时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state <= IDLE;
end
else case(state)
IDLE : if(ascii_data_vld && enable)
state <= DATA;
DATA : if(end_cnt_num)
state <= IDLE;
default : state <= IDLE;
endcase
end
/****************************************************************
发送数据计数
****************************************************************/
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_num <= 'd0;
end
else if(add_cnt_num)begin
if(end_cnt_num)begin
cnt_num <= 'd0;
end
else begin
cnt_num <= cnt_num + 1'b1;
end
end
end
assign add_cnt_num = state == DATA && uart_tx_ready;
assign end_cnt_num = add_cnt_num && cnt_num == NUM_MAX - 1;
always @(*)begin
case (cnt_num)
0 : uart_tx_data = 8'h00;
1 : uart_tx_data = 8'hce; //温
2 : uart_tx_data = 8'hc2;
3 : uart_tx_data = 8'hb6;
4 : uart_tx_data = 8'hc8;
5 : uart_tx_data = 8'h3a;
6 : uart_tx_data = ascii_data[47-:8];
7 : uart_tx_data = ascii_data[39-:8];
8 : uart_tx_data = 8'h2e;
9 : uart_tx_data = ascii_data[31-:8];
10 : uart_tx_data = ascii_data[23-:8];
11 : uart_tx_data = ascii_data[15-:8];
12 : uart_tx_data = ascii_data[7-:8];
13 : uart_tx_data = 8'h0d;
default: uart_tx_data = 0;
endcase
end
assign uart_tx_data_vld = state == DATA;
endmodule
/**************************************功能介绍***********************************
Date :
Author : linxiaoxiao.
Version :
Description: 实现16进制转换成ASCII码
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module hex2ascii(
input clk ,
input rst_n ,
input [23:0] hex_din ,
input hex_din_vld ,
output [47:0] ascii_dout ,
output ascii_dout_vld
);
//---------<内部信号定义>-----------------------------------------------------
wire [47:0] ascii_data ;
reg ascii_data_vld ;
assign ascii_data[7:0] = hex_din[3:0] + 8'd48;
assign ascii_data[15:8] = hex_din[7:4] + 8'd48;
assign ascii_data[23:16] = hex_din[11:8] + 8'd48;
assign ascii_data[31:24] = hex_din[15:12] + 8'd48;
assign ascii_data[39:32] = hex_din[19:16] + 8'd48;
assign ascii_data[47:40] = hex_din[23:20] + 8'd48;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
ascii_data_vld <= 'd0;
end
else begin
ascii_data_vld <= hex_din_vld;
end
end
assign ascii_dout = ascii_data ;
assign ascii_dout_vld = ascii_data_vld;
六、顶层设计
/**************************************功能介绍***********************************
Date :
Author : linxiaoxiao.
Version :
Description: 顶层文件
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module top(
input clk ,
input rst_n ,
input rx ,
output tx ,
inout dq
);
//---------<内部信号定义>-----------------------------------------------------
wire [20:0] temp_data ;
wire [23:0] bcd_data ;
wire temp_data_vld ;
reg [5:0] reg_seg_mask ;
wire [5:0] seg_mask ;
wire [7:0] data ;
reg [1:0] enable ;
reg [23:0] reg_bcd_data ;
wire bcd_data_vld ;
wire tx_ready ;
wire [7:0] uart_tx_data ;
wire uart_tx_data_vld ;
/****************************************************************
模块例化
****************************************************************/
ds18b20_drive ds18b20_drive_inst (
/* input */.clk ( clk ),
/* input */.rst_n ( rst_n ),
/* input */.enable ( 1 ),
/* output [20:0] */.temp_data ( temp_data ),
/* output reg */.temp_data_vld ( temp_data_vld ),
/* inout */.dq ( dq )
);
control control_inst(
/* input */.clk ( clk ),
/* input */.rst_n ( rst_n ),
/* input [23:0] */.temp_out ( temp_data ),
/* input */.temp_out_vld ( temp_data_vld ),
/* output [23:0] */.bcd_data ( bcd_data ),
/* output */.bcd_data_vld ( bcd_data_vld )
);
tx_fsm_uart #(
.CHECK_BIT ("None" ), //校验方法,“None”无校验,“Odd”奇校验,“Even”偶校验
.BPS (115200 ),
.CLK (50_000_000)
)tx_fsm_uart_inst(
/* input */.clk ( clk ),
/* input */.rst_n ( rst_n ),
/* input */.tx_data_vld ( uart_tx_data_vld ),
/* input [7:0] */.tx_data ( uart_tx_data ),
/* output */.ready ( tx_ready ),
/* output reg */.tx ( tx )
);
uart_tx_ctrl uart_tx_ctrl_inst(
/* input */.clk ( clk ),
/* input */.rst_n ( rst_n ),
/* input [23:0] */.bcd_data ( bcd_data ),
/* input */.bcd_data_vld ( bcd_data_vld ),
/* input */.enable ( 1 ),
/* output reg [7:0] */.uart_tx_data ( uart_tx_data ),
/* output */.uart_tx_data_vld ( uart_tx_data_vld ),
/* input */.uart_tx_ready ( tx_ready )
);
endmodule