一.传感器介绍
ds18b20是常用的数字温度传感器,具有体积小,硬件开销低,抗干扰能力强,精度高的特点。
1.特点
ds18b20单线数字温度传感器具有独特的优点:
( 1 )采用单总线的接口方式,与微处理器连接时仅需要一条线即可实现微处理器与DS18B20的双向通讯。单总线具有经济性好,抗干扰能力强,适合于恶劣环境的现场温度测量,使用方便等优点,使用户可轻松地组建传感器网络,为测量系统的构建引入全新概念。
( 2 )测量温度范围宽。测量精度高 DS18B20的测量范围为 -55 ℃ ~+ 125 ℃ ; 在 -10~+ 85°C范围内,精度为 ± 0.5°C 。
( 3 )在使用中不需要任何外围元件。
( 4 )支持多点组网功能。多个 DS18B20 可以并联在唯一的单线上,实现多点测温。
( 5 )供电方式灵活。DS18B20 可以通过内部寄生电路从数据线上获取电源。因此,当数据线上的时序满足一定的要求时,可以不接外部电源,从而使系统结构更趋简单,可靠性更高。
( 6 )测量参数可配置。DS18B20的测量分辨率可通过程序设定 9~12 位。
( 7 ) 负压特性电源极性接反时,温度计不会因发热而烧毁,但不能正常工作。
( 8 )掉电保护功能。DS18B20 内部含有 EEPROM ,在系统掉电以后,它仍可保存分辨率及报警温度的设定值。
2.内部结构
ds18b20内部主要由四部分组成:64位ROM,温度传感器,非挥发的温度报警触发器TH和TL,配置寄存器。
64位ROM
ROM中的64位序列号是出厂前被光刻好的,它可以看作是该DS18B20的地址序列码,每个DS18B20的64位序列号均不相同。
64位ROM的循环冗余校验码:
ROM的作用是使每一个DS18B20都各不相同,这样就可以实现一根总线上挂接多个DS18B20的目的。
3.ds18b20管脚
- GND为电源地;
- DQ为数字信号输入/输出端;
- VDD为外接供电电源输入端(在寄生电源接线方式时接地)
4.ds18b20内部高速暂存存储器
高速暂存存储器由9个字节组成,当温度转换命令发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第1个字节。单片机可通过单线接口读到该数据,读取时低位在前,高位在后,对应的温度计算:当符号位S=0时,直接将二进制位转换为十进制;当S=1时,先将补码变为原码,再计算十进制值。
说明:
字节0表示温度的低八位数据。
字节1表示温度的高八位数据。
字节2表示高温阀值。
字节3表示低温阀值。
字节4表示配置寄存器。
字节5、字节6、字节7是保留字节。
DS18B20中的温度传感器完成对温度的测量,用16位二进制形式提供,形式表达,其
中S为符号位。
例如:
+125℃的数字输出07D0H。(正温度直接把16进制数转成10进制即得到温度值 )
-55℃的数字输出为 FC90H。(负温度把得到的16进制数取反后加1 再转成10进制数)
5.ds18b20工作时序
初始化时序:
①主机首先发出一个480-960微秒的低电平脉冲,然后释放总线变为高电平,并在随后的480微秒时间内对总线进行检测,如果有低电平出现说明总线上有器件已做出应答。若无低电平出现,一直都是高电平说明总线上无器件应答。
②做为从器件的DS18B20在一上电后就一直在检测总线上是否有480-960微秒的低电平出现,如果有,在总线转为高电平后等待15-60微秒后将总线电平拉低60-240微秒做出响应存在脉冲,告诉主机本器件已做好准备。若没有检测到就一直在检测等待。
写操作:
写周期最少为60微秒,最长不超过120微秒。写周期一开始做为主机先把总线拉低1微秒表示写周期开始。随后若主机想写0,则继续拉低电平最少60微秒直至写周期结束,然后释放总线为高电平。若主机想写1,在一开始拉低总线电平1微秒后就释放总线为高电平,一直到写周期结束。而做为从机的DS18B20则在检测到总线被拉底后等待15微秒然后从15us到45us开始对总线采样,在采样期内总线为高电平则为1,若采样期内总线为低电平则为0。
读操作:
对于读数据操作时序也分为读0时序和读1时序两个过程。读时隙是从主机把单总线拉低之后,在1微秒之后就得释放单总线为高电平,以让DS18B20把数据传输到单总线上。DS18B20在检测到总线被拉低1微秒后,便开始送出数据,若是要送出0就把总线拉为低电平直到读周期结束。若要送出1则释放总线为高电平。主机在一开始拉低总线1微秒后释放总线,然后在包括前面的拉低总线电平1微秒在内的15微秒时间内完成对总线进行采样检测,采样期内总线为低电平则确认为0。采样期内总线为高电平则确认为1。完成一个读时序过程,至少需要60us才能完成。
6.ds18b20单线通信
DS18B20 单线通信功能是分时完成的,他有严格的时隙概念,如果出现序列混乱,1-WIRE 器件将不响应主机,因此读写时序很重要。系统对 DS18B20 的各种操作必须按协议进行。根据 DS18B20 的协议规定,微控制器控制 DS18B20 完成温度的转换必须经过以下 3个步骤 :
(1)每次读写前对 DS18B20 进行复位初始化。复位要求主 CPU 将数据线下拉500us ,然后释放, DS18B20 收到信号后等待 16us-60us 左右,然后发出60us-240us 的存在低脉冲,主 CPU 收到此信号后表示复位成功。
(2)发送一条 ROM 指令
指令名称 | 指令代码 | 指令功能 |
---|---|---|
读ROM | 33H | 读DS18B20ROM中的编码(即读64位地址) |
ROM匹配(符合ROM) | 55H | 发出此命令之后,接着发出64位 ROM编码,访问单总线上与编码相对应DS18820使之作出响应,为下一步对该 DS18B20的读写作准备 |
搜索ROM | 0F0H | 用于确定挂接在同一总线上 DS18B20的个数和识别64位ROM地址,为操作各器件作好准备 |
跳过ROM | 0CCH | 忽略64位ROM地址,直接向DS18B20发温度变换命令,适用于单片机工作 |
警报搜索 | 0ECH | 该指令执行后,只有温度超过设定值上限或下限的片子才做出响应 |
(3)发送存储器指令
指令名称 | 指令代码 | 指令功能 |
---|---|---|
温度变换 | 44H | 启动 DS18B20进行温度转换,转换时间最长为500ms(典型为200ms ),结果存入内部9字节 RAM中 |
读暂存器 | 0BEH | 读内部 RAM中9宇节的内容 |
写暂存器 | 4EH | 发出向内部RAM的第3, 4字节写上,下限温度数据命令,紧跟该命令之后,是传送两字节的数据 |
复制暂存器 | 48H | 将RAM中第3,4字节的内容复制到EEPROM中 |
重调EEPROM | 0B8H | EEPROM中的内容恢复到 RAM中的第3, 4字节 |
读供电方式 | 0B4H | 读DS18B20的供电模式,寄生供电时 DS18B20发送“0”,外接电源供电DS18B20发送“1” |
二.FPGA代码实现
1.状态图
FPGA作为主机,DS18B20作为从机。
①主状态机实现发复位脉冲-接收存在脉冲-发ROM指令-发温度转换指令-延时-温度读取指令-读取温度;
②从状态机负责发送数据或读取数据时序:拉低总线-发送数据/采样数据-释放总线;
主从机状态图:
状态说明:
M_IDLE:空闲状态,等待开始通信;
M_RST:发送复位脉冲;
M_RELE:释放总线;
M_RACK:接收存在脉冲;
M_RSKP:发送跳过ROM指令;
M_SCON:发送温度转换命令;
M_WAIT:等待750ms;
M_SRTM:发送温度读取指令;
M_RTMP:读取温度值;
S_IDLE:空闲状态,等待传输请求;
S_LOW:发数据前先拉低1us;
S_SEND:发送1bit数据;
S_SAMP:接收1bit数据;
S_RELE:释放总线;
S_DONE:发送/接收一次数据完成;
2.代码实现
2.1DS18B20驱动模块
在该模块,主从状态机都是属于FPGA主机部分,主状态机控制整个流程,而从状态机在主状态机的控制下实现相关主机状态下的操作。
module ds18b20_driver(
input clk ,//时钟信号
input rst_n ,//复位信号
input dq_in ,
output reg dq_out ,//dq总线FPGA输出
output reg dq_out_en ,//输出数据有效信号
output reg temp_sign ,//温度值符号位 0:正 1:负
output reg [23:0] temp_out ,//温度输出
output reg temp_out_vld //温度输出有效信号
);
//状态机参数
localparam
//主机状态参数
M_IDLE = 9'b0_0000_0001 ,//空闲状态
M_REST = 9'b0_0000_0010 ,//复位
M_RELE = 9'b0_0000_0100 ,//释放总线 -- ds18b20等待
M_RACK = 9'b0_0000_1000 ,//接收应答 -- 主机接收存在脉冲
M_ROMS = 9'b0_0001_0000 ,//ROM命令 -- 跳过ROM命令
M_CONT = 9'b0_0010_0000 ,//转化
M_WAIT = 9'b0_0100_0000 ,//等待 -- 12bit分辨率下的温度转化时间
M_RCMD = 9'b0_1000_0000 ,//读命令 -- 读暂存器命令
M_RTMP = 9'b1_0000_0000 ,//读温度 -- 产生读时隙 -- 接收2字节带符号位的补码温度
//从机状态参数
S_IDLE = 6'b00_0001 ,//空闲状态
S_LOW = 6'b00_0010 ,//拉低总线 -- 时隙的开始
S_SEND = 6'b00_0100 ,//发送 -- 15us内
S_SAMP = 6'b00_1000 ,//采样 -- 在15us内
S_RELE = 6'b01_0000 ,//释放 -- 时隙的恢复时间
S_DONE = 6'b10_0000 ;//
parameter
TIME_1US = 50 ,//1us
TIME_RST = 500 ,//复位脉冲 500us
TIME_REL = 20 ,//主机释放总线 20us
TIME_PRE = 200 ,//主机接收存在脉冲 200us
TIME_WAIT= 750000 ,//主机发完温度转换命令 等待750ms
TIME_LOW = 2 ,//主机拉低总线 2us
TIME_RW = 60 ,//主机读、写1bit 60us
TIME_REC = 3 ;//主机读、写完1bit释放总线 3us
localparam
CMD_ROMS = 8'hCC ,//跳过ROM指令
CMD_CONT = 8'h44 ,//温度转化指令
CDM_RTMP = 8'hBE ;//读暂存器指令
//信号定义
reg [8:0] m_state_c ;//主机现态
reg [8:0] m_state_n ;//主机次态
reg [5:0] s_state_c ;//从机现态
reg [5:0] s_state_n ;//从机次态
reg [5:0] cnt_1us ;//1us计数器
wire add_cnt_1us ;
wire end_cnt_1us ;
reg [19:0] cnt0 ;//复位脉冲、释放、存在脉冲、等待750ms
wire add_cnt0 ;
wire end_cnt0 ;
reg [19:0] X ;
reg [5:0] cnt1 ;//计数从机状态机每个状态持续时间
wire add_cnt1 ;
wire end_cnt1 ;
reg [5:0] Y ;
reg [4:0] cnt_bit ;
wire add_cnt_bit ;
wire end_cnt_bit ;
reg slave_ack ;//接收存在脉冲
reg flag ;//0:发送温度转换命令 1:发送温度读取命令
reg [7:0] wr_data ;
reg [15:0] orign_data ;//采样温度值寄存器
reg [10:0] temp_data ;
wire [23:0] temp_data_w ;//组合逻辑计算实际温度值 十进制
wire m_idle2m_rest ;
wire m_rest2m_rele ;
wire m_rele2m_rack ;
wire m_rack2m_roms ;
wire m_roms2m_cont ;
wire m_roms2m_rcmd ;
wire m_cont2m_wait ;
wire m_wait2m_rest ;
wire m_rcmd2m_rtmp ;
wire m_rtmp2m_idle ;
wire s_idle2s_low ;
wire s_low2s_send ;
wire s_low2s_samp ;
wire s_send2s_rele ;
wire s_samp2s_rele ;
wire s_rele2s_low ;
wire s_rele2s_done ;
//主机状态机设计 描述状态转移
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
m_state_c <= M_IDLE;
end
else begin
m_state_c <= m_state_n;
end
end
//主机状态转移条件
always @(*) begin
case(m_state_c)
M_IDLE:begin
if(m_idle2m_rest)
m_state_n = M_REST;
else
m_state_n = m_state_c;
end
M_REST:begin
if(m_rest2m_rele)
m_state_n = M_RELE;
else
m_state_n = m_state_c;
end
M_RELE:begin
if(m_rele2m_rack)
m_state_n = M_RACK;
else
m_state_n = m_state_c;
end
M_RACK:begin
if(m_rack2m_roms)
m_state_n = M_ROMS;
else
m_state_n = m_state_c;
end
M_ROMS:begin
if(m_roms2m_cont)
m_state_n = M_CONT;
else if(m_roms2m_rcmd)
m_state_n = M_RCMD;
else
m_state_n = m_state_c;
end
M_CONT:begin
if(m_cont2m_wait)
m_state_n = M_WAIT;
else
m_state_n = m_state_c;
end
M_WAIT:begin
if(m_wait2m_rest)
m_state_n = M_REST;
else
m_state_n = m_state_c;
end
M_RCMD:begin
if(m_rcmd2m_rtmp)
m_state_n = M_RTMP;
else
m_state_n = m_state_c;
end
M_RTMP:begin
if(m_rtmp2m_idle)
m_state_n = M_IDLE;
else
m_state_n = m_state_c;
end
default:m_state_n = M_IDLE;
endcase
end
assign m_idle2m_rest = m_state_c == M_IDLE && (1'b1) ;//主机IDLE状态直接转换成复位状态 一次采集的开始 重复采集
assign m_rest2m_rele = m_state_c == M_REST && (end_cnt0);//500us复位脉冲
assign m_rele2m_rack = m_state_c == M_RELE && (end_cnt0);//20us释放总线
assign m_rack2m_roms = m_state_c == M_RACK && (end_cnt0 && slave_ack == 1'b0);//200us,主机接收存在脉冲
assign m_roms2m_cont = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b0);//主机发送完8bit跳过ROM命令 0:温度转化命令
assign m_roms2m_rcmd = m_state_c == M_ROMS && (s_state_c == S_DONE && flag == 1'b1);//主机发送完8bit跳过ROM命令 1:读温度命令
assign m_cont2m_wait = m_state_c == M_CONT && (s_state_c == S_DONE);//主机发送8bit温度转化命令
assign m_wait2m_rest = m_state_c == M_WAIT && (end_cnt0);//等待750ms转换完成
assign m_rcmd2m_rtmp = m_state_c == M_RCMD && (s_state_c == S_DONE);//主机发送8bit读命令 --等待从机接收数据完成
assign m_rtmp2m_idle = m_state_c == M_RTMP && (s_state_c == S_DONE);//主机读温度 --等待从机发送数据完成
//从机状态机设计 描述状态转移
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
s_state_c <= S_IDLE;
end
else begin
s_state_c <= s_state_n;
end
end
//从机状态转移条件
always @(*) begin
case(s_state_c)
S_IDLE:begin
if(s_idle2s_low)
s_state_n = S_LOW;
else
s_state_n = s_state_c;
end
S_LOW :begin
if(s_low2s_send)
s_state_n = S_SEND;
else if(s_low2s_samp)
s_state_n = S_SAMP;
else
s_state_n = s_state_c;
end
S_SEND:begin
if(s_send2s_rele)
s_state_n = S_RELE;
else
s_state_n = s_state_c;
end
S_SAMP:begin
if(s_samp2s_rele)
s_state_n = S_RELE;
else
s_state_n = s_state_c;
end
S_RELE:begin
if(s_rele2s_done)
s_state_n = S_DONE;
else if(s_rele2s_low)
s_state_n = S_LOW;
else
s_state_n = s_state_c;
end
S_DONE:begin
s_state_n = S_IDLE;
end
default:s_state_n = S_IDLE;
endcase
end
assign s_idle2s_low = s_state_c == S_IDLE && (m_state_c == M_ROMS ||
m_state_c == M_CONT || m_state_c == M_RCMD || m_state_c == M_RTMP);//主状态机要发送ROM命令 温度转化命令 读温度命令 读温度过程 每1bit数据 按照协议要求主机拉低
assign s_low2s_send = s_state_c == S_LOW && (m_state_c == M_ROMS ||
m_state_c == M_CONT || m_state_c == M_RCMD) && end_cnt1;//主机拉低2us后 从状态机开始发送数据(命令)
assign s_low2s_samp = s_state_c == S_LOW && (m_state_c == M_RTMP && end_cnt1);//主机拉低2us后 从状态机采样接收温度数据
assign s_send2s_rele = s_state_c == S_SEND && (end_cnt1);//主机读1bit数据 (60us内完成)
assign s_samp2s_rele = s_state_c == S_SAMP && (end_cnt1);//主机写1bit数据 (60us内完成)
assign s_rele2s_low = s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b0);//主机读写完1bit (3us) 继续下一bit
assign s_rele2s_done = s_state_c == S_RELE && (end_cnt1 && end_cnt_bit == 1'b1);//主机读写完1bit (3us) bit数读写完
//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 = m_state_c != M_IDLE;//非IDLE状态持续计数
assign end_cnt_1us = add_cnt_1us && cnt_1us == TIME_1US - 1;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0) begin
if(end_cnt0)begin
cnt0 <= 0;
end
else begin
cnt0 <= cnt0 + 1;
end
end
end
assign add_cnt0 = (m_state_c == M_REST || m_state_c == M_RELE || m_state_c == M_RACK || m_state_c == M_WAIT) && end_cnt_1us;
assign end_cnt0 = add_cnt0 && cnt0 == X - 1;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
X <= 0;
end
else if(m_state_c == M_REST)begin//复位:500us (480us)
X <= TIME_RST;
end
else if(m_state_c == M_RELE)begin//释放总线:20us (15-60us内)
X <= TIME_REL;
end
else if(m_state_c == M_RACK)begin//接收应答:200us (60-240us)
X <= TIME_PRE;
end
else if(m_state_c == M_WAIT) begin//等待:750ms (等待转换完成)
X <= TIME_WAIT;
end
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1) begin
if(end_cnt1)begin
cnt1 <= 0;
end
else begin
cnt1 <= cnt1 + 1;
end
end
end
assign add_cnt1 = (s_state_c == S_LOW || s_state_c == S_SEND ||
s_state_c == S_SAMP || s_state_c == S_RELE) && end_cnt_1us;
assign end_cnt1 = add_cnt1 && cnt1 == Y - 1;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
Y <= 0;
end
else if(s_state_c == S_LOW)begin
Y <= TIME_LOW;//主机拉低总线 2us (大于1us)
end
else if(s_state_c == S_SEND || s_state_c == S_SAMP) begin
Y <= TIME_RW;//主机读写1bit 60us (至少60us)
end
else begin
Y <= TIME_REC;//主机读写完1bit释放总线 3us (至少1us)
end
end
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_cnt1;
assign end_cnt_bit = add_cnt_bit && cnt_bit == ((m_state_c == M_RTMP)?16-1:8-1);//读温度状态有16bit数据,其余状态8bit数据
//slave_ack 采样传感器的存在脉冲
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
slave_ack <= 1'b1;
end//接收应答状态 计数器计到60us 进行采样
else if(m_state_c == M_RACK && cnt0 == 60 && end_cnt_1us)begin
slave_ack <= dq_in;
end
end
always@(posedge clk or negedge rst_n)begin//命令发送标志 (区分温度转换和温度读取命令)
if(!rst_n)begin
flag <= 0;
end
else if(m_wait2m_rest)begin//从等待状态转移到复位状态 flag置一读温度
flag <= 1'b1;
end
else if(m_rtmp2m_idle) begin//从读温度状态转移到复位状态 flag置零读温度命令
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 | s_rele2s_low | m_wait2m_rest)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
//wr_data 命令
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
wr_data <= 0;
end
else if(m_rack2m_roms)begin///ROM跳过命令
wr_data <= CMD_ROMS;
end
else if(m_roms2m_cont) begin//温度转换命令
wr_data <= CMD_CONT;
end
else if(m_roms2m_rcmd)begin//读暂存器温度命令
wr_data <= CDM_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 && cnt1 == 12 && 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;//负数,取反加一
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
endmodule
2.2数据处理模块
module ctrl(
input clk ,//时钟信号
input rst_n ,//复位信号
input din_sign,
input [23:0] din ,
input din_vld ,
output dout_sign,
output [23:0] dout ,
output reg dout_vld
);
//中间信号定义
reg [23:0] din_r ;
reg din_vld_r0 ;
reg din_vld_r1 ;
wire [7:0] tmp_int_w ;//整数部分
reg [7:0] tmp_int_r ;
wire [3:0] tmp_int_w2 ;
wire [3:0] tmp_int_w1 ;
wire [3:0] tmp_int_w0 ;
reg [3:0] tmp_int_r2 ;
reg [3:0] tmp_int_r1 ;
reg [3:0] tmp_int_r0 ;
wire [15:0] tmp_dot_w ;//小数部分
reg [15:0] tmp_dot_r ;
wire [3:0] tmp_dot_w3 ;
wire [3:0] tmp_dot_w2 ;
wire [3:0] tmp_dot_w1 ;
wire [3:0] tmp_dot_w0 ;
reg [3:0] tmp_dot_r3 ;
reg [3:0] tmp_dot_r2 ;
reg [3:0] tmp_dot_r1 ;
reg [3:0] tmp_dot_r0 ;
//din_r
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
din_r <= 0;
end
else if(din_vld)begin
din_r <= din;
end
end
//din_vld_r0 din_vld_r1 打拍
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
din_vld_r0 <= 0;
din_vld_r1 <= 0;
end
else begin
din_vld_r0 <= din_vld;
din_vld_r1 <= din_vld_r0;
end
end
assign tmp_int_w = din_r/10000;
assign tmp_dot_w = din_r%10000;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tmp_int_r <= 0;
end
else if(din_vld_r0)begin
tmp_int_r <= tmp_int_w;
tmp_dot_r <= tmp_dot_w;
end
end
assign tmp_int_w2 = tmp_int_r/100;//百位
assign tmp_int_w1 = tmp_int_r/10%10;//十位
assign tmp_int_w0 = tmp_int_r%10;//个位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tmp_int_r2 <= 0;
tmp_int_r1 <= 0;
tmp_int_r0 <= 0;
end
else if(din_vld_r1)begin
tmp_int_r2 <= tmp_int_w2;
tmp_int_r1 <= tmp_int_w1;
tmp_int_r0 <= tmp_int_w0;
end
end
assign tmp_dot_w0 = tmp_dot_r/1000;
assign tmp_dot_w1 = tmp_dot_r/100%10;
assign tmp_dot_w2 = tmp_dot_r/10%10;
assign tmp_dot_w3 = tmp_dot_r%10;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tmp_dot_r0 <= 0;
tmp_dot_r1 <= 0;
tmp_dot_r2 <= 0;
tmp_dot_r3 <= 0;
end
else if(din_vld_r1)begin
tmp_dot_r0 <= tmp_dot_w0;
tmp_dot_r1 <= tmp_dot_w1;
tmp_dot_r2 <= tmp_dot_w2;
tmp_dot_r3 <= tmp_dot_w3;
end
end
assign dout = {tmp_int_r1,tmp_int_r0,tmp_dot_r0,tmp_dot_r1,tmp_dot_r2,tmp_dot_r3};
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout_vld <= 1'b0;
end
else begin
dout_vld <= din_vld_r1;
end
end
assign dout_sign = din_sign;
endmodule
2.3数码管驱动模块
module seg_driver(
input clk ,//时钟信号
input rst_n ,//复位信号
input din_sign,
input [23:0] din ,
input din_vld ,
output reg [5:0] sel ,
output reg [7:0] dig
);
//参数定义
parameter TIME_1MS = 25_000,
ZERO = 7'b100_0000,
ONE = 7'b111_1001,
TWO = 7'b010_0100,
THREE = 7'b011_0000,
FOUR = 7'b001_1001,
FIVE = 7'b001_0010,
SIX = 7'b000_0010,
SEVEN = 7'b111_1000,
EIGHT = 7'b000_0000,
NINE = 7'b001_0000,
P = 7'b000_1111,
N = 7'b011_1111;
//信号定义
reg [19:0] cnt_1ms;//扫描频率计数器
wire add_cnt_1ms;
wire end_cnt_1ms;
reg [3 :0] disp_num;
reg dot;
//数码管扫描频率计数
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1ms <= 0;
end
else if(add_cnt_1ms) begin
if(end_cnt_1ms)begin
cnt_1ms <= 0;
end
else begin
cnt_1ms <= cnt_1ms + 1;
end
end
end
assign add_cnt_1ms = 1'b1;
assign end_cnt_1ms = add_cnt_1ms && cnt_1ms == TIME_1MS - 1;
//seg_sel 数码管片选信号
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sel <= 6'b111110;
end
else if(end_cnt_1ms) begin
sel <= {sel[4:0],sel[5]};
end
end
//译码
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
disp_num <= 0;
end
else begin
case(sel)
6'b011111:begin disp_num <= din_sign?4'ha:4'hb;dot <= 1; end
6'b101111:begin disp_num <= din[23:20];dot <= 1;end
6'b110111:begin disp_num <= din[19:16];dot <= 0;end
6'b111011:begin disp_num <= din[15:12];dot <= 1;end
6'b111101:begin disp_num <= din[11:8] ;dot <= 1;end
6'b111110:begin disp_num <= din[7 :4] ;dot <= 1;end
default :begin disp_num <= 4'hF ;end
endcase
end
end
//segment 段选译码
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dig <= 8'hff;
end
else begin//显示小数点
case(disp_num)
4'h0:dig <= {dot,ZERO } ;
4'h1:dig <= {dot,ONE } ;
4'h2:dig <= {dot,TWO } ;
4'h3:dig <= {dot,THREE} ;
4'h4:dig <= {dot,FOUR } ;
4'h5:dig <= {dot,FIVE } ;
4'h6:dig <= {dot,SIX } ;
4'h7:dig <= {dot,SEVEN} ;
4'h8:dig <= {dot,EIGHT} ;
4'h9:dig <= {dot,NINE } ;
4'ha:dig <= {dot,N } ;
4'hb:dig <= {dot,P } ;
default:dig <= 8'hff ;
endcase
end
end
endmodule
2.4顶层模块
module ds18b20_top(
input clk ,//时钟信号
input rst_n ,//复位信号
inout dq ,//传感器总线
output [5:0] sel ,
output [7:0] dig
);
//信号定义
wire dq_in ;
wire dq_out ;
wire dq_out_en ;
wire temp_sign ;
wire [23:0] temp_out ;
wire temp_out_vld;
wire dout_sign ;
wire [23:0] dout ;
wire dout_vld ;
assign dq = dq_out_en?dq_out:1'bz;
assign dq_in = dq;
//模块例化
ds18b20_driver u_ds18b20_driver(
.clk (clk ),
.rst_n (rst_n ),
.dq_in (dq_in ),//dq总线DS18B20输出
.dq_out (dq_out ),//dq总线FPGA输出,DS18B20输入
.dq_out_en (dq_out_en ),//dq总线输出使能控制信号
.temp_sign (temp_sign ),//温度的正负
.temp_out (temp_out ),//输出十进制温度
.temp_out_vld (temp_out_vld ) //温度采集数据有效
);
ctrl u_ctrl(
.clk (clk ),
.rst_n (rst_n ),
.din_sign (temp_sign ),
.din (temp_out ),
.din_vld (temp_out_vld ),
.dout_sign (dout_sign ),
.dout (dout ),//输出温度值数码管对应位置的bcd码
.dout_vld (dout_vld )
);
seg_driver seg_driver(
.clk (clk ),
.rst_n (rst_n ),
.din_sign (temp_sign ),
.din (dout ),
.din_vld (dout_vld ),
.sel (sel ),
.dig (dig )
);
endmodule
3.仿真
ds18b20模块测试文件
`timescale 1ns/1ps
module ds18b20_driver_tb();
//参数定义
parameter CYCLE = 20;
defparam u_ds18b20_driver.TIME_RST = 200,
u_ds18b20_driver.TIME_PRE = 100,
u_ds18b20_driver.TIME_WAIT = 750;
//信号定义
reg clk ;
reg rst_n ;
reg dq_in ;
wire dq_out ;
wire dq_out_en ;
wire temp_sign ;
wire [23:0] temp_out ;
wire temp_out_vld;
//模块例化
ds18b20_driver u_ds18b20_driver(
.clk (clk ),//时钟信号
.rst_n (rst_n ),//复位信号
.dq_in (dq_in ),
.dq_out (dq_out ),//dq总线FPGA输出
.dq_out_en (dq_out_en ),//输出数据有效信号
.temp_sign (temp_sign ),//温度值符号位 0:正 1:负
.temp_out (temp_out ),//温度输出
.temp_out_vld (temp_out_vld) //温度输出有效信号
);
always #(CYCLE/2) clk = ~clk;
integer i=0;
initial begin
clk = 1'b1;
rst_n = 1'b0;
dq_in = 0;
#(CYCLE*20);
rst_n = 1'b1;
repeat(5)begin
for(i=0;i<500000;i=i+1)begin
dq_in = {$random};
#(CYCLE*20);
end
#(CYCLE*20);
end
$stop;
end
endmodule
主机状态机从IDLE–>REST:
在IDLE状态直接转换为REST状态。
主机状态机从REST–>RELE–>RACK:
复位脉冲要持续500us(在仿真文件里修改为200us)才会转移为释放状态,且释放状态持续20us后转移为接收存在脉冲状态。
主机状态机RACK–>ROM:
接收存在脉冲状态持续200us(仿真文件改为100us)且采样值为0后转移为ROM命令状态(由于只有一个温度传感器,这里使用的是跳过命令)。
文章来源:https://www.toymoban.com/news/detail-402474.html
从机状态机IDLE–>LOW–>SEND:
ROM命令结束,从机状态机从发送状态转移为释放状态恢复时隙,然后拉低总线,一个新的时隙的开始。
略文章来源地址https://www.toymoban.com/news/detail-402474.html
到了这里,关于基于FPGA的ds18b20温度传感器使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!