FPGA|基于FPGA的ds18b20温度传感器使用


文章目录

    • 一.传感器介绍
      • 1.特点
      • 2.内部结构
      • 3.ds18b20管脚
      • 4.ds18b20内部高速暂存存储器
      • 5.ds18b20工作时序
      • 6.ds18b20单线通信
    • 二.FPGA代码实现
      • 1.状态图
      • 2.代码实现
        • 2.1DS18B20驱动模块
        • 2.2数据处理模块
        • 2.3数码管驱动模块
        • 2.4顶层模块
      • 3.仿真

一.传感器介绍 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的排的循环冗余校验码FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

ROM的作用是使每一个DS18B20都各不相同,这样就可以实现一根总线上挂接多个DS18B20的目的。
FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

3.ds18b20管脚
  1. GND为电源地;
  2. DQ为数字信号输入/输出端;
  3. VDD为外接供电电源输入端(在寄生电源接线方式时接地)
    FPGA|基于FPGA的ds18b20温度传感器使用
    文章图片
4.ds18b20内部高速暂存存储器
高速暂存存储器由9个字节组成,当温度转换命令发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第1个字节。单片机可通过单线接口读到该数据,读取时低位在前,高位在后,对应的温度计算:当符号位S=0时,直接将二进制位转换为十进制; 当S=1时,先将补码变为原码,再计算十进制值。
FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

说明:
字节0表示温度的低八位数据。
字节1表示温度的高八位数据。
字节2表示高温阀值。
字节3表示低温阀值。
字节4表示配置寄存器。
字节5、字节6、字节7是保留字节。
DS18B20中的温度传感器完成对温度的测量,用16位二进制形式提供,形式表达,其
中S为符号位。
FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

例如:
+125℃的数字输出07D0H。(正温度直接把16进制数转成10进制即得到温度值 )
-55℃的数字输出为 FC90H。(负温度把得到的16进制数取反后加1 再转成10进制数)
FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

5.ds18b20工作时序
初始化时序:
①主机首先发出一个480-960微秒的低电平脉冲,然后释放总线变为高电平,并在随后
的480微秒时间内对总线进行检测,如果有低电平出现说明总线上有器件已做出应答。若无低电平出现一直都是高电平说明总线上无器件应答。
②做为从器件的DS18B20在一上电后就一直在检测总线上是否有480-960微秒的低电平出
现,如果有,在总线转为高电平后等待15-60微秒后将总线电平拉低60-240微秒做出响应存在脉冲,告诉主机本器件已做好准备。若没有检测到就一直在检测等待。
FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

写操作:
写周期最少为60微秒,最长不超过120微秒。写周期一开始做为主机先把总线拉低1微秒表示写周期开始。随后若主机想写0,则继续拉低电平最少60微秒直至写周期结束,然后释放总线为高电平。若主机想写1,在一开始拉低总线电平1微秒后就释放总线为高电平,一直到写周期结束。而做为从机的DS18B20则在检测到总线被拉底后等待15微秒然后从15us到45us开始对总线采样,在采样期内总线为高电平则为1,若采样期内总线为低电平则为0。
FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

读操作:
对于读数据操作时序也分为读0时序和读1时序两个过程。读时隙是从主机把单总线拉低之后,在1微秒之后就得释放单总线为高电平,以让DS18B20把数据传输到单总线上。DS18B20在检测到总线被拉低1微秒后,便开始送出数据,若是要送出0就把总线拉为低电平直到读周期结束。若要送出1则释放总线为高电平。主机在一开始拉低总线1微秒后释放总线,然后在包括前面的拉低总线电平1微秒在内的15微秒时间内完成对总线进行采样检测,采样期内总线为低电平则确认为0。采样期内总线为高电平则确认为1。完成一个读时序过程,至少需要60us才能完成
FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

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指令-发温度转换指令-延时-温度读取指令-读取温度;
②从状态机负责发送数据或读取数据时序:拉低总线-发送数据/采样数据-释放总线;
主从机状态图:
FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

状态说明:
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驱动模块
module ds18b20_driver( inputclk,//时钟信号 inputrst_n,//复位信号 inputdq_in,outputregdq_out,//dq总线FPGA输出 outputregdq_out_en,//输出数据有效信号 outputregtemp_sign,//温度值符号位 0:正 1:负 outputreg[23:0]temp_out,//温度输出 outputregtemp_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释放总线 3uslocalparam 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计数器 wireadd_cnt_1us ; wireend_cnt_1us ; reg[19:0]cnt0; //复位脉冲、释放、存在脉冲、等待750ms wireadd_cnt0; wireend_cnt0; reg[19:0]X; reg[5:0]cnt1; //计数从机状态机每个状态持续时间 wireadd_cnt1; wireend_cnt1; reg[5:0]Y; reg[4:0]cnt_bit; wireadd_cnt_bit ; wireend_cnt_bit ; regslave_ack; //接收存在脉冲 regflag; //0:发送温度转换命令 1:发送温度读取命令 reg[7:0]wr_data; reg[15:0]orign_data; //采样温度值寄存器reg[10:0]temp_data; wire[23:0]temp_data_w ; //组合逻辑计算实际温度值 十进制wirem_idle2m_rest; wirem_rest2m_rele; wirem_rele2m_rack; wirem_rack2m_roms; wirem_roms2m_cont; wirem_roms2m_rcmd; wirem_cont2m_wait; wirem_wait2m_rest; wirem_rcmd2m_rtmp; wirem_rtmp2m_idle; wires_idle2s_low; wires_low2s_send; wires_low2s_samp; wires_send2s_rele; wires_samp2s_rele; wires_rele2s_low; wires_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 endassign 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 endassign 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); 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 endend 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 endend 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 endalways@(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 endend 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 endalways@(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 endend 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 endalways@(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 | 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 endendmodule

2.2数据处理模块
module ctrl( inputclk,//时钟信号 inputrst_n,//复位信号 inputdin_sign, input[23:0]din, inputdin_vld ,outputdout_sign, output[23:0]dout, outputregdout_vld ); //中间信号定义 reg[23:0]din_r; regdin_vld_r0; regdin_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 endassign 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 endassign 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 endassign 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 endassign dout_sign = din_sign; endmodule

2.3数码管驱动模块
module seg_driver( inputclk,//时钟信号 inputrst_n,//复位信号 inputdin_sign, input[23:0]din, inputdin_vld ,outputreg[5:0]sel, outputreg[7:0]dig ); //参数定义 parameterTIME_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; //扫描频率计数器 wireadd_cnt_1ms; wireend_cnt_1ms; reg[3 :0]disp_num; regdot; //数码管扫描频率计数 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 endend 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( inputclk,//时钟信号 inputrst_n,//复位信号inoutdq,//传感器总线output[5:0]sel, output[7:0]dig ); //信号定义 wiredq_in; wiredq_out; wiredq_out_en; wiretemp_sign; wire[23:0]temp_out; wiretemp_out_vld; wiredout_sign; wire[23:0]dout; wiredout_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/1psmodule ds18b20_driver_tb(); //参数定义 parameter CYCLE = 20; defparamu_ds18b20_driver.TIME_RST = 200, u_ds18b20_driver.TIME_PRE = 100, u_ds18b20_driver.TIME_WAIT = 750; //信号定义 regclk; regrst_n; regdq_in; wiredq_out; wiredq_out_en; wiretemp_sign; wire[23:0]temp_out; wiretemp_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状态。
FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

主机状态机从REST–>RELE–>RACK:
复位脉冲要持续500us(在仿真文件里修改为200us)才会转移为释放状态,且释放状态持续20us后转移为接收存在脉冲状态。
FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

主机状态机RACK–>ROM:
接收存在脉冲状态持续200us(仿真文件改为100us)且采样值为0后转移为ROM命令状态(由于只有一个温度传感器,这里使用的是跳过命令)。
FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

【FPGA|基于FPGA的ds18b20温度传感器使用】从机状态机IDLE–>LOW–>SEND:
FPGA|基于FPGA的ds18b20温度传感器使用
文章图片

ROM命令结束,从机状态机从发送状态转移为释放状态恢复时隙,然后拉低总线,一个新的时隙的开始。
FPGA|基于FPGA的ds18b20温度传感器使用
文章图片


    推荐阅读