引脚 | 名称 | 描述 |
---|---|---|
GND | Ground | 接地 |
DQ | Data In/Out | 输入输出 |
VDD | Power Supply Voltage | 供电电源 |
NC | No Connect | 无连接【悬空】 |
ds18b20的主要特性:
DS18B20的另一个特性是能够在没有外部电源的情况下工作。当总线高时,通过DQ线针供电。高总线信号还为内部电容器(CPP)充电,当总线低时,CPP向设备供电。这种从1线总线中获得功率的方法被称为“寄生虫功率”。作为一种替代方案,DS18B20也可以由VDD上的外部电源供电。
ds18b20内部结构主要由四部分组成64位光刻ROM、温度传感器、非挥发的温度报警传感器TH和TL、配置寄存器。
每只ds18b20都有一个唯一的长达64位的只读存储器,存放在ROM中,低8位是ds18b20的单总线温度传感器,高8位是CRC循环冗余校验码,用以校正前56位是否正常,中间48位是一个唯一序列号,此64位只读寄存器常用于元器件的识别和匹配。
64位ROM和ROM操作控制区允许DS18B20作为单总线协议工作,只有建立了ROM操作协议,才能对DS18B20进行控制操作,单总线的所有ROM操作,都从一个初始化序列开始,单总线控制器提供了5个ROM操作命令和6个RAM操作命令。
ROM操作指令
RAM操作指令
温度寄存器的通电复位值为+85°C.
重要提示:
温度寄存格式:
0~3【温度小数位】;
4~10【温度整数位】
11~15【温度符号位】:0是正数;1是负数
负温需要取反加1
温度计算举例
温度为正数
①+10.125
二进制:0000 0000 1010 0010
符号位:蓝色 温度整数:红色 温度小数:粉红
0000 0000 10100010
符号位:0是正数+
2 3 + 2 1 = 10 2^3+2^1=10 23+21=10
2 1 = 2 ∗ 0.625 = 0.125 2^1=2*0.625=0.125 21=2∗0.625=0.125
温度为负数
①-10.125
二进制:1111 1111 0101 1110
符号位:蓝色 温度整数:红色 温度小数:粉红
1111 1111 01011110
符号位:1是负数—
取反:0000 0000 1010 0001
加1:0000 0000 1010 0010
2 3 + 2 1 = 10 2^3+2^1=10 23+21=10
2 1 = 2 ∗ 0.625 = 0.125 2^1=2*0.625=0.125 21=2∗0.625=0.125
符号位(S)表示该值是正数还是负数:对于正数S=0和负数S=1。
由于TH和TL是8位寄存器,因此在TH和TL比较中只使用温度寄存器的第11至第4位。如果测量的温度低于或等于TL或高于TH,则存在报警条件,并在DS18B20内设置报警标志。每次温度测量后更新,如果报警条件消失,则在下一次温度转换后关闭。
初始化由主机的复位脉冲和DS18B20的存在脉冲组成;总线拉低【大于480us】发送复位脉冲,然后主机释放总线进入接收模式,上拉电阻拉高总线;ds18b20检测到上升沿时等待【15~60us】; 拉低总线【 60-240us】发送存在脉冲
在写入时间槽期间将数据写入DS18B20,并在读取时间槽期间从DS18B20读取数据。
总线使用写入时隙“1”或“0”将逻辑1或0写入ds18b20,写入时间段持续时间至少为60us,恢复时间至少为1us。写入“1”由主线拉低而启动;拉低后需要在15us内释放总线,再由上拉电阻拉高总线;写入“0”拉低总线后需要保持总线拉低。
主机须在发送读取暂存寄存器命令【BEh】或读取电源【B4h】命令后生成读取时序,便于ds18b20可以提供所请求的数据。主机也可在发出转换【44h】或【B8h】命令后产生时序,用于查找ds18b20功能命令操作状态。
由于ds18b20是1wire总线协议,数据输入输出都由dq进行,故将其拆分为三态门。有关三态门可参考:
三态门介绍
基本概念
形式及真值表
【a】和【b】相同
a的真值表
【c】和【d】相同
c的真值表
从状态机
发送复位脉冲,释放总线、等待温度转换完成以及接收存在脉冲等时间参数
TIME_1US = 50, //基本时间1us
TIME_RST = 480, //复位脉冲 500us
TIME_REL = 20, //主机释放总线 20us
TIME_PRE = 200, //主机接收存在脉冲 200us
TIME_WAIT = 750000, //主机发完温度转换命令 等待750ms
TIME_LOW = 2, //主机拉低总线 2us
TIME_RW = 60, //主机读、写1bit 60us
TIME_REC = 2; //主机读写完1bit释放总线 3us
有关串口以及数码管,前面文章有详细介绍。这里主要介绍传感器驱动。
传感器驱动信号输出
//计数器 1us计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1us <= 0;
end
else if(add_cnt_1us)begin
if(end_cnt_1us)begin
cnt_1us <= 0;
end
else begin
cnt_1us <= cnt_1us + 1;
end
end
end
assign add_cnt_1us = add_flag;
assign end_cnt_1us = add_cnt_1us && cnt_1us == TIME_1US-1;
//add_flag 计数开始标志
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
add_flag <= 0;
end
else if(add_flag == 1'b0 & enable & m_state_c == M_IDLE)begin
add_flag <= 1'b1;
end
else if(add_flag & enable == 1'b0 & m_state_c == M_IDLE)begin
add_flag <= 1'b0;
end
end
//时间计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
end
assign add_cnt = end_cnt_1us;
assign end_cnt = add_cnt && cnt == X-1;
//时间参数
always @(*)begin
if(m_state_c == M_REST)begin
X = TIME_RST;
end
else if(m_state_c == M_RELE)begin
X = TIME_REL;
end
else if(m_state_c == M_RACK)begin
X = TIME_PRE;
end
else if(m_state_c == M_WAIT)begin
X = TIME_WAIT;
end
else begin
if(s_state_c == S_LOW)
X = TIME_LOW;
else if(s_state_c == S_SEND || s_state_c == S_SAMP)
X = TIME_RW;
else
X = TIME_REC;
end
end
//数据传输bit计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 0;
end
else begin
cnt_bit <= cnt_bit + 1;
end
end
end
assign add_cnt_bit = s_state_c == S_RELE && end_cnt;
assign end_cnt_bit = add_cnt_bit && cnt_bit == ((m_state_c == M_RTMP)?16-1:8-1);
//slave_ack 采样传感器的存在脉冲
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
slave_ack <= 1'b1;
end
else if(m_state_c == M_RACK && cnt == 60 && end_cnt_1us)begin
slave_ack <= dq_in;
end
end
//flag=0:发温度转换命令 flag=1:发温度读取命令
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag <= 0;
end
else if(m_wait2m_rest)begin//等待转换到复位
flag <= 1'b1;//转换完成,读取温度
end
else if(m_rtmp2m_idle)begin//读温度到初始
flag <= 1'b0;
end
end
//输出信号
//dq_out
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dq_out <= 0;
end
else if(m_idle2m_rest | s_idle2s_low | m_wait2m_rest | s_rele2s_low)begin
dq_out <= 1'b0;
end
else if(s_low2s_send)begin
dq_out <= wr_data[cnt_bit];
end
end
//dq_out_en
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dq_out_en <= 0;
end
else if(m_idle2m_rest | s_idle2s_low | s_rele2s_low | m_wait2m_rest)begin
dq_out_en <= 1'b1; //输出 dq_out
end
else if(m_rest2m_rele | s_send2s_rele | s_low2s_samp)begin
dq_out_en <= 1'b0; //不输出 dq_out
end
end
/* 注意:
在主机发完复位脉冲后要释放总线;
发完1bit数据后要释放总线;
在继续发下一bit的时候,仍然要先拉低总线;
在读数据时,拉低总线1us后要释放总线;
*/
//wr_data
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
wr_data <= 0;
end
else if(m_rack2m_roms)begin
wr_data <= CMD_ROMS;
end
else if(m_roms2m_cont)begin
wr_data <= CMD_CONT;
end
else if(m_roms2m_rcmd)begin
wr_data <= CMD_RTMP;
end
end
//orign_data 串并转换
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
orign_data <= 0;
end
else if(s_state_c == S_SAMP && cnt == 11 && end_cnt_1us)begin
orign_data[cnt_bit] <= dq_in;
end
end
//temp_data
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
temp_data <= 0;
end
else if(s_state_c == S_SAMP && cnt_bit == 15 && s_samp2s_rele)begin
if(orign_data[15])
temp_data <= ~orign_data[10:0] + 1'b1; //负温 则取反加1
else
temp_data <= orign_data[10:0]; //正温
end
end
/* 实际的温度值为 temp_data * 0.0625;
为了保留4位小数精度,将实际温度值放大了10000倍,
即 temp_data * 625;
*/
assign temp_data_w = temp_data * 625;
//temp_out
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
temp_out <= 0;
end
else if(m_state_c == M_RTMP && s_rele2s_done)begin
temp_out <= temp_data_w;
end
end
//temp_out_vld
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
temp_out_vld <= 0;
end
else begin
temp_out_vld <= m_state_c == M_RTMP && s_rele2s_done;
end
end
//temp_sign
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
temp_sign <= 0;
end
else if(s_state_c == S_SAMP && cnt_bit == 15 && s_samp2s_rele)begin
temp_sign <= orign_data[15];
end
end
信号输出
//dec_tmp_data
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dec_tmp_data <= 0;
dec_tmp_data_vld <= 1'b0;
end
else if(dec_tmp_din_vld)begin
dec_tmp_data <= dec_tmp_din;
dec_tmp_data_vld <= 1'b1;
end
else begin
dec_tmp_data <= dec_tmp_data;
dec_tmp_data_vld <= 1'b0;
end
end
//cnt
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
end
assign add_cnt = add_flag;
assign end_cnt = add_cnt && cnt == 11-1;
//add_flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
add_flag <= 0;
end
else if(uart_enable & ascii_data_vld)begin
add_flag <= 1'b1;
end
else if(end_cnt)begin
add_flag <= 1'b0;
end
end
assign bcd_en = (uart_enable | seg_enable) & dec_tmp_data_vld;
assign hex_data = bcd_tmp[23:0];
assign hex_data_vld = bcd_tmp_vld;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
ascii_data <= 0;
ascii_data_vld <= 1'b0;
end
else if(ascii_tmp_vld)begin
ascii_data <= {ascii_sign,ascii_tmp[47:32],ascii_dot,ascii_tmp[31:0],8'ha1,8'he6,8'h0a};
ascii_data_vld <= 1'b1;
end
else begin
ascii_data <= ascii_data;
ascii_data_vld <= 1'b0;
end
end
assign ascii_sign = dec_temp_sign?8'h2d:8'h2b; //符号的ascii码
assign ascii_dot = 8'h2e; //小数点的ascii码
//seg_data
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
seg_data <= 0;
seg_data_vld <= 1'b0;
end
else if(seg_enable & bcd_tmp_vld)begin
seg_data <= {bcd_sign,bcd_tmp[23:4]};
seg_data_vld <= 1'b1;
end
else begin
seg_data <= seg_data ;
seg_data_vld <= 1'b0;
end
end
assign disp_data = seg_data;
assign disp_data_vld = seg_data_vld;
assign bcd_sign = dec_temp_sign?4'ha:4'hb;
//tx_data
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_data <= 0;
tx_data_vld <= 1'b0;
end
else begin
tx_data <= rd_dout;
tx_data_vld <= rd_req;
end
end
assign ascii_dout = tx_data;
assign ascii_dout_vld = tx_data_vld;
//fifo
//fifo_rd_flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
fifo_rd_flag <= 0;
end
else begin
fifo_rd_flag <= empty == 1'b0;
end
end
//rd_req
always @(*)begin
if(fifo_rd_flag & uart_tx_busy == 1'b0)begin
rd_req <= 1'b1;
end
else begin
rd_req <= 1'b0;
end
end
控制模块例化
ascii2hex u_ascii2hex( //ascii码转十六进制
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input [7:0] */.ascii_din (ascii_din ),
/*input */.ascii_din_vld(ascii_din_vld ),
/*output [7:0] */.hex_dout (cmd_byte ),
/*output */.hex_dout_vld (cmd_byte_vld )
);
cmd_analy u_cmd_analy( //命令解析模块
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input [7:0] */.hex_din (cmd_byte ),
/*input */.hex_din_vld (cmd_byte_vld ),
/*output */.sensor_enable (sensor_enable ),
/*output */.seg_enable (seg_enable ),
/*output */.uart_enable (uart_enable )
);
binary2bcd#(.DIN_W(21),.DOUT_W(25)) u_binary2bcd( //将十进制温度值转换为BCD码格式的数据
/*input */.clk (clk ),//时钟
/*input */.rst_n (rst_n ),//复位
/*input */.en (bcd_en ),
/*input [DIN_W-1:0] */.binary_din (dec_tmp_data[20:0] ),//输入二进制数据
//输出信号定义
/*output reg [DOUT_W-1:0] */.bcd_dout (bcd_tmp ), //输出BCD码数据
/*output reg */.bcd_dout_vld(bcd_tmp_vld )
);
hex2ascii u_hex2ascii( //将BCD码格式的温度值转换为ASCII码,输出给uart_tx模块
/*input */.clk (clk ),//时钟
/*input */.rst_n (rst_n ),//复位
/*input [23:0] */.hex_din (hex_data ),//hex输入
/*input */.hex_din_vld (hex_data_vld ),
/*output [47:0] */.ascii_dout (ascii_tmp ),
/*output */.ascii_dout_vld (ascii_tmp_vld )
);
//fifo例化
fifo u_fifo (
.aclr (~rst_n ),
.clock (clk ),
.data (wr_din ),
.rdreq (rd_req ),
.wrreq (wr_req ),
.empty (empty ),
.full (full ),
.q (rd_dout )
);
顶层模块例化
uart_rx u_uart_rx(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input */.uart_rxd (uart_rxd ),
/*output [7:0] */.rx_data (rx_byte ),
/*output */.rx_data_vld (rx_byte_vld )
);
ds18b20_driver u_ds18b20_driver(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input */.enable (sensor_enable),//传感器开关信号
/*input */.dq_in (dq_in ),//传感器输入
/*output */.dq_out (dq_out ),//传感器输出
/*output */.dq_out_en (dq_out_en ),//传感器输出使能
/*output */.temp_sign (temp_sign ),//温度值符号位 0:正 1:负温
/*output [23:0] */.temp_out (dec_temp ),//十进制格式的温度值
/*output */.temp_out_vld (dec_temp_vld ) //温度值有效标志信号
);
controler u_controler(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input [7:0] */.ascii_din (rx_byte ),
/*input */.ascii_din_vld (rx_byte_vld ),
/*input */.dec_temp_sign (temp_sign ),
/*input [23:0] */.dec_tmp_din (dec_temp ),
/*input */.dec_tmp_din_vld(dec_temp_vld ),
/*input */.uart_tx_busy (tx_busy ),
/*output */.sensor_enable (sensor_enable ),//输出给ds18b20驱动模块
/*output */.disp_enable (disp_enable ),
/*output [23:0] */.disp_data (disp_data ),//输出给数码管驱动模块显示
/*output */.disp_data_vld (disp_data_vld ),
/*output [7:0] */.ascii_dout (ascii_dout ),//输出给串口发送模块发送
/*output */.ascii_dout_vld (ascii_dout_vld )
);
uart_tx u_uart_tx(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input */.tx_req (ascii_dout_vld),
/*input [7:0] */.tx_din (ascii_dout ),
/*output */.tx_busy (tx_busy ),//1:表示忙 发送数据 0:表示非忙
/*output */.tx_dout (uart_txd )
);
seg_driver u_seg_driver(
/*input */.clk (clk ),
/*input */.rst_n (rst_n ),
/*input */.seg_enable (disp_enable ),
/*input [23:0] */.din (disp_data ),
/*input */.din_vld (disp_data_vld ),
/*output [5:0] */.sel (sel ),//片选信号
/*output [7:0] */.dig (dig ) //段选信号
);
DS18B20温度采集报警系统,原理及汇编、C语言实现
DS18B20内部结构及功能
DS18B20百度百科
1-Wire总线数字温度传感器DS18B20原理及应用.pdf
三态门介绍