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

这篇具有很好参考价值的文章主要介绍了基于FPGA的ds18b20温度传感器使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一.传感器介绍

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的ds18b20温度传感器使用
ROM的作用是使每一个DS18B20都各不相同,这样就可以实现一根总线上挂接多个DS18B20的目的。
基于FPGA的ds18b20温度传感器使用

3.ds18b20管脚

  1. GND为电源地;
  2. DQ为数字信号输入/输出端;
  3. VDD为外接供电电源输入端(在寄生电源接线方式时接地)
    基于FPGA的ds18b20温度传感器使用

4.ds18b20内部高速暂存存储器

高速暂存存储器由9个字节组成,当温度转换命令发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第1个字节。单片机可通过单线接口读到该数据,读取时低位在前,高位在后,对应的温度计算:当符号位S=0时,直接将二进制位转换为十进制;当S=1时,先将补码变为原码,再计算十进制值。
 基于FPGA的ds18b20温度传感器使用
 说明:

字节0表示温度的低八位数据。
字节1表示温度的高八位数据。
字节2表示高温阀值。
字节3表示低温阀值。
字节4表示配置寄存器。
字节5、字节6、字节7是保留字节。

DS18B20中的温度传感器完成对温度的测量,用16位二进制形式提供,形式表达,其
中S为符号位。
基于FPGA的ds18b20温度传感器使用
 例如:
  +125℃的数字输出07D0H。(正温度直接把16进制数转成10进制即得到温度值 )
  -55℃的数字输出为 FC90H。(负温度把得到的16进制数取反后加1 再转成10进制数)
基于FPGA的ds18b20温度传感器使用

5.ds18b20工作时序

初始化时序:
 ①主机首先发出一个480-960微秒的低电平脉冲,然后释放总线变为高电平,并在随后的480微秒时间内对总线进行检测,如果有低电平出现说明总线上有器件已做出应答。若无低电平出现,一直都是高电平说明总线上无器件应答。
 ②做为从器件的DS18B20在一上电后就一直在检测总线上是否有480-960微秒的低电平出现,如果有,在总线转为高电平后等待15-60微秒后将总线电平拉低60-240微秒做出响应存在脉冲,告诉主机本器件已做好准备。若没有检测到就一直在检测等待。
基于FPGA的ds18b20温度传感器使用
写操作:
写周期最少为60微秒,最长不超过120微秒。写周期一开始做为主机先把总线拉低1微秒表示写周期开始。随后若主机想写0,则继续拉低电平最少60微秒直至写周期结束,然后释放总线为高电平。若主机想写1,在一开始拉低总线电平1微秒后就释放总线为高电平,一直到写周期结束。而做为从机的DS18B20则在检测到总线被拉底后等待15微秒然后从15us到45us开始对总线采样,在采样期内总线为高电平则为1,若采样期内总线为低电平则为0。
基于FPGA的ds18b20温度传感器使用
基于FPGA的ds18b20温度传感器使用

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

在该模块,主从状态机都是属于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状态。
基于FPGA的ds18b20温度传感器使用

基于FPGA的ds18b20温度传感器使用
主机状态机从REST–>RELE–>RACK:
复位脉冲要持续500us(在仿真文件里修改为200us)才会转移为释放状态,且释放状态持续20us后转移为接收存在脉冲状态。
基于FPGA的ds18b20温度传感器使用
基于FPGA的ds18b20温度传感器使用
主机状态机RACK–>ROM:
接收存在脉冲状态持续200us(仿真文件改为100us)且采样值为0后转移为ROM命令状态(由于只有一个温度传感器,这里使用的是跳过命令)。

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

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

从机状态机IDLE–>LOW–>SEND:
基于FPGA的ds18b20温度传感器使用
ROM命令结束,从机状态机从发送状态转移为释放状态恢复时隙,然后拉低总线,一个新的时隙的开始。
基于FPGA的ds18b20温度传感器使用
文章来源地址https://www.toymoban.com/news/detail-402474.html

到了这里,关于基于FPGA的ds18b20温度传感器使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • DS18B20温度传感器——测试环境温度及代码

    醒醒!,还在睡呢,开始干代码了! 单片机通过OneWire协议与DS18B20通信,最终测出环境温度 OneWire 总线的硬件接口很简单,只需要把 DS18B20 的数据引脚和单片机的一个 IO 口接上      说明:GND接地,DQ单引线用于数据的输入,VDD接电源正极(注意正负极不能接反) 通过编程,

    2024年02月09日
    浏览(69)
  • 51单片机温度传感器DS18B20

    实现功能 插上DS18B20温度传感器,数码管显示检测的温度值 单片机型号:STC89C52 DS18B20介绍 1、DS18B20简介 DS18B20 是由 DALLAS 半导体公司推出的一种的“一线总线(单总线)”接口的温度传感器。与传统的热敏电阻等测温元件相比,它是一种新型的体积小、 适用电压宽、与微处理

    2024年02月01日
    浏览(52)
  • 【单片机】17-温度传感器DS18B20

    (1)测温度的方式:物理(汞柱,气压),电子(金属电性能随温度变化) (2)早期:热敏电阻(模拟接口---》 A/D转换 ) (3)现代:专用sensor(数字接口,如I2C,DS18B20单总线接口等) DS18B20 可编程分辨率单总线 温度传感器 (1)内置集成ADC,外部数字接口 (2) 单总线

    2024年02月04日
    浏览(52)
  • DS18B20温度传感器原理及使用教程

           DS18B20数字温度传感器提供9-Bit到12-Bit的摄氏温度测量精度和一个用户可编程的非易失性且具有过温和低温触发报警的报警功能。DS18B20采用的1-Wire通信即仅采用一个数据线(以及地)与微控制器进行通信。该传感器的温度检测范围为-55℃至+125℃,并且在温度范围超过

    2024年02月11日
    浏览(38)
  • 温度传感器DS18B20【单总线时序结构】

    DS18B20是一种常见的 数字温度传感器 ,其控制命令和数据都是以数字信号的方式输入输出,相比较于模拟温度传感器,具有功能强大、硬件简单、易扩展、抗干扰性强等特点 测温范围:-55°C 到 +125°C 通信接口: 1-Wire(单总线) 其它特征:可形成 总线结构 、内置温度报警功

    2024年02月04日
    浏览(51)
  • 51单片机读取DS18B20温度传感器

    1 .首先我们知道DS18B20是 单总线协议 ,只有一根数据线。所以Data数据线即使发送端又是接收端,同时DS18B20内部接了弱上拉电阻(如图一所示),数据线默认为高电平。有了这些概念,我们就能进行下一步。                                                图一 (截取

    2024年02月08日
    浏览(40)
  • 51单片机(十三)DS18B20温度传感器

    ❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋

    2024年02月11日
    浏览(44)
  • 单片机第二季:温度传感器DS18B20

    目录 1,DS18B20介绍 2,DS18B20数据手册  2.1,初始化时序  2.2,读写时序  3,DS18B20工作流程 4,代码   DS18B20的基本特征: (1)内置集成ADC,外部数字接口,也就是可以直接与单片机的数字接口连接,DS18B20 在使用中不需要任何外围元件,全部传感元件及转换电路集成在形如一只

    2024年02月10日
    浏览(54)
  • ArduinoUNO实战-第十四章-LM35温度传感器和DS18B20温度传感器

    在Arduino中使用DS18B20温度传感器(基于OneWire和DallasTemperature库) 通过LM35测量温度值,通过串口发送给电脑 LM35使用非常普遍,他使用内部补偿机制,输出可以从0°C开始。封装在T0992,工作电压4-30V。而且在上述电压范围内,芯片的工作电流不超过60ua。根据产品使用手册,得知

    2024年02月03日
    浏览(49)
  • 毕业设计常用温度测量模块之DS18B20温度传感器介绍

    DS18B20是一种单总线数字温度传感器,测试温度范围-55℃-125℃,具有体积小,硬件开销低,抗干扰能力强,精度高的特点。 单总线通信 ,意味着没有时钟线,只有一根通信线。单总线读写数据是靠控制起始时间和采样时间来完成,所以时序要求很严格,这也是DS18B20驱动编程

    2024年02月08日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包