|
基于FPGA的DS18B20温度测量以及数码管显示
FPGA与各器件的连接如图所示:
dq为DS18B20的单总线
dtube_cs_n为数码管的4位
dtube_data为数码管的8段
ext_clk_25m为时钟输入
ext_rst_n为复位输入
顶层文件:
module DS18B20(
            input ext_clk_25m,        //外部输入25MHz时钟信号
            input ext_rst_n,        //外部输入复位信号,低电平有效
            inout dq,                //DS18B20的单总线接口    
            output[3:0] dtube_cs_n,    //7段数码管位选信号
            output[7:0] dtube_data    //7段数码管段选信号(包括小数点为8段)                
        );
wire CLK_1MHz;
wire sys_rst_n;
pll_controller    pll_controller_inst                 (
                                                .areset ( !ext_rst_n ),
                                                .inclk0 ( ext_clk_25m),
                                                .c0 (CLK_1MHz),
                                                .locked ( sys_rst_n )
                                                    );
wire[15:0] num_temp;
ds18b20_demo  uut_ds18b20_demo                        (
                                                .clk(CLK_1MHz),
                                                .rst_n(sys_rst_n),
                                                .pin_ds18b20(dq),
                                                .temp(num_temp)
                                                    );
wire[15:0] temperature;                               
temperature_calculation  uut_temperature_calculation(
                                                .clk(ext_clk_25m),
                                                .rst_n(sys_rst_n),
                                                .two_byte_temp(num_temp),
                                                .temperature(temperature)
                                                    );
digital_tube_display  uut_digital_tube_display(
                                                .clk(ext_clk_25m),    
                                                .rst_n(sys_rst_n),    
                                                .display_num(temperature),    
                                                .dtube_cs_n(dtube_cs_n),    
                                                .dtube_data(dtube_data)    
                                               );
                            
endmodule                
其次是DS18B20的控制代码:
module ds18b20_demo(
                           input clk,
                           input rst_n,
                           inout pin_ds18b20,
                           output [15:0] temp
                          );
//*************************************************************                          
//DS18B20状态机各个阶段                          
parameter STATE_1 = 4'd1,//等待initen_cnt计数到10(即10us)才使能初始化    
          STATE_2 = 4'd2,//主机把总线电平拉低500us,等待DS18B20回应
          STATE_3 = 4'd3,//DS18B20作出回应阶段
          STATE_4 = 4'd4,//向DS18B20写0X44CCH跳过ROM操作和进行温度转换
          STATE_5 = 4'd5,//等待2ms,等DS18B20把温度转换完成
          STATE_6 = 4'd6,//回到STATE_2~STATE_3,再来一遍初始化
          STATE_7 = 4'd7,//向DS18B20写0XBECCH跳过ROM操作和读取温度数据
          STATE_8 = 4'd8,//读取DS18B20的两个字节温度数据
          STATE_9 = 4'd9;//等待1S,重新开始上述过程
//*************************************************************
//1S计数器
reg[19:0] one_second_cnt;
always @ (posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            one_second_cnt <= 20'd0;
        else if(DS18b20_state == STATE_9)
            one_second_cnt <= one_second_cnt + 1'b1;
        else
            one_second_cnt <= 20'd0;
    end      
//*************************************************************
//初始化计数器,计数到10(即10us)才使能初始化过程
reg[9:0] initen_cnt;
always @ (posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            initen_cnt <= 10'd0;
        else if(DS18b20_state == STATE_1)//只在第一阶段自递增
            initen_cnt <= initen_cnt + 1'b1;
        else
            initen_cnt <= 10'd0;
    end    
//*************************************************************    
//用来控制主机发出的初始化信号长度
reg[9:0] master_cnt;
always @ (posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            master_cnt <= 10'd0;
        else if(DS18b20_state == STATE_2)//在第二阶段自递增
            master_cnt <= master_cnt + 1'd1;
        else
            master_cnt <= 10'd0;
    end
//*************************************************************    
//用来控制何时获得从机的应答信号        
reg[9:0] slave_cnt;
always @ (posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            slave_cnt <= 10'd0;
        else if(DS18b20_state == STATE_3)//只在STATE_3自递增,即主机把总线拉低500us之后
            slave_cnt <= slave_cnt + 1'd1;
        else    
            slave_cnt <= 10'd0;
    end
//*************************************************************
//写一个比特周期计数器,每个写周期62us,60us写数据+2us高电平间隔
reg[6:0] write_cnt;
always @ (posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            write_cnt <= 7'd0;
        else if((DS18b20_state == STATE_4) || (DS18b20_state == STATE_7))
            write_cnt <= write_cnt + 1'd1;
        else if(write_cnt > 7'd62)
            write_cnt <= 7'd0;
        else
            write_cnt <= 7'd0;
    end    
//*************************************************************
//读一个比特周期计数器,每个读周期62us,60us读数据+2us高电平间隔
reg[5:0] read_cnt;
always @ (posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            read_cnt <= 6'b0;
        else if(DS18b20_state == STATE_8)
            read_cnt <= read_cnt + 1'b1;
        else if(read_cnt > 6'd62)
            read_cnt <= 6'b0;
        else
            read_cnt <= 6'b0;
    end    
//*************************************************************    
reg [23:0] wait_cnt;//等待温度转换完成
always @ (posedge clk or negedge rst_n)
    begin    
        if(!rst_n)
            wait_cnt <= 24'b0;
        else if(DS18b20_state == STATE_5)
            wait_cnt <= wait_cnt + 1'b1;
        else
            wait_cnt <= 24'b0;
    end    
//*************************************************************    
//DS18b20_state,DS18b20_next_state赋值,后面的初始化状态机的状态判断要用到
reg [3:0] DS18b20_state,DS18b20_next_state;
always @ (posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            DS18b20_state <= STATE_1;
        else
            DS18b20_state <= DS18b20_next_state;
    end
//*************************************************************    
//两个字节的数据一起写进DS18B20
reg[15:0] hex_write;
always @ (posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            hex_write <= 16'h0000;
        else if(DS18b20_state == STATE_4)
            hex_write <= 16'h44cc;
        else if(DS18b20_state == STATE_7)
            hex_write <= 16'hbecc;
        else
            hex_write <= 16'h0000;
    end
//*************************************************************
//选择写入字节中的第几位
reg[4:0] write_bit_num;//对当前写的位进行计数,一个字节8位
reg write_over;//一个字节写完标志位
always @ (posedge clk or negedge rst_n)
    begin
    
        if(!rst_n)
            begin
                write_bit_num <= 5'd0;
                write_over <= 1'b0;
            end
        else if(write_cnt == 7'd60)
            write_bit_num <= write_bit_num + 1'd1;
        else if(write_bit_num == 5'd16)
            begin
                write_bit_num <= 5'd0;
                write_over <= 1'b1;
            end
        else if(DS18b20_state != STATE_4 || DS18b20_state != STATE_7)
            write_over <= 1'b0;
        else;
    end    
//*************************************************************
reg bit_write;   //当前需要写入的一个比特数据
always @ (posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            bit_write <= 1'b0;
        else if(write_cnt <= 7'd2)
            bit_write <= 1'b0;//每次写1bit拉低总线>1us
        else if(write_cnt > 7'd3 && write_cnt < 7'd60)//传输当前bit
            begin
                     if(write_bit_num == 5'd0)   bit_write <= hex_write[0];
                else if(write_bit_num == 5'd1)   bit_write <= hex_write[1];
                else if(write_bit_num == 5'd2)   bit_write <= hex_write[2];
                else if(write_bit_num == 5'd3)   bit_write <= hex_write[3];
                else if(write_bit_num == 5'd4)   bit_write <= hex_write[4];
                else if(write_bit_num == 5'd5)   bit_write <= hex_write[5];
                else if(write_bit_num == 5'd6)   bit_write <= hex_write[6];
                else if(write_bit_num == 5'd7)   bit_write <= hex_write[7];
                else if(write_bit_num == 5'd8)   bit_write <= hex_write[8];
                else if(write_bit_num == 5'd9)   bit_write <= hex_write[9];
                else if(write_bit_num == 5'd10)  bit_write <= hex_write[10];
                else if(write_bit_num == 5'd11)  bit_write <= hex_write[11];
                else if(write_bit_num == 5'd12)  bit_write <= hex_write[12];
                else if(write_bit_num == 5'd13)  bit_write <= hex_write[13];
                else if(write_bit_num == 5'd14)  bit_write <= hex_write[14];
                else if(write_bit_num == 5'd15)  bit_write <= hex_write[15];
            end
        else if(write_cnt >= 7'd60 && write_cnt < 7'd62)
            bit_write <= 1'b1;//写1bit末都需要把总线拉高>1us
        else;    
    end
//*************************************************************
//对当前读的位进行计数,一个字节8位,两个一起读16位
reg[4:0] read_bit_num;
always @ (posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            read_bit_num <= 5'd0;
        else if(read_cnt == 6'd60 && DS18b20_state == STATE_8)
            read_bit_num <= read_bit_num + 5'd1;
        else if(DS18b20_state != STATE_8)
            read_bit_num <= 5'd0;
        else;
    end    
//*************************************************************
//读DS18B20的两个字节数据,一起读
reg[15:0] read_two_byte;
always @ (posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            read_two_byte <= 16'd0;
        else if(read_cnt == 6'd12 && read_bit_num <= 5'd15)
            begin
                read_two_byte <= {1'b0, read_two_byte[15:1]};            
                read_two_byte[15] <= pin_ds18b20;
            end
        else;
    end    
assign temp[15:0] = read_two_byte[15:0];
//*************************************************************
//读两个字节完成标志
reg read_over;//两个字节读完标志位
always @ (posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            read_over <= 1'b0;
        else if(read_bit_num == 5'd16)
            begin
                read_over <= 1'b1;
            end
        else
            read_over <= 1'b0;
    end    
//*************************************************************    
//DS18B20初始化状态机,完成整个初始化过程的调度
reg rece_dat;
reg re_init_en;
//reg[15:0] test;//测试用,与之相关的代码行可以忽略
always @ (DS18b20_state or DS18b20_next_state or master_cnt or slave_cnt or initen_cnt or wait_cnt or write_over or read_over or one_second_cnt or rece_dat or re_init_en or pin_ds18b20)
    begin
        case(DS18b20_state)
            STATE_1:
                        if(initen_cnt == 10'd100)
                          DS18b20_next_state <= STATE_2;
                        else
                          DS18b20_next_state <= STATE_1;
            STATE_2:    
                        if(master_cnt == 10'd702)//主机把总线电平拉低480~960us,这里是700us
                            DS18b20_next_state <= STATE_3;
                        else
                            DS18b20_next_state <= STATE_2;
            STATE_3:
                        if(slave_cnt == 10'd100)//接收DS18B20的低电平应答
                            rece_dat <= pin_ds18b20;
                        else if(slave_cnt == 10'd480 && rece_dat == 1'b1)
                            begin
                                DS18b20_next_state <= STATE_1;
                                //test <= 16'hFAFA;
                            end
                        else if(slave_cnt == 10'd480 && rece_dat == 1'b0 && re_init_en == 1'b0)
                            begin
                                DS18b20_next_state <= STATE_4;
                                //test <= 16'hD0D0;
                            end
                        else if(slave_cnt == 10'd480 && rece_dat == 1'b0 && re_init_en == 1'b1)
                                DS18b20_next_state <= STATE_7;
                        else
                           DS18b20_next_state <= STATE_3;
            STATE_4:
                        if(write_over == 1'b1)
                            DS18b20_next_state <= STATE_5;
                        else
                            DS18b20_next_state <= STATE_4;
            STATE_5:
                        if(wait_cnt == 24'd3000)
                            DS18b20_next_state <= STATE_6;
                        else
                            DS18b20_next_state <= STATE_5;
            STATE_6:
                        begin
                              re_init_en <= 1'b1;
                              DS18b20_next_state <= STATE_1;
                        end
            STATE_7:
                        if(write_over == 1'b1)
                            DS18b20_next_state <= STATE_8;
                        else
                            begin
                                DS18b20_next_state <= STATE_7;
                                re_init_en <= 1'b0;
                            end
            STATE_8:
                        if(read_over == 1'b1)
                            DS18b20_next_state <= STATE_9;
                        else
                            DS18b20_next_state <= STATE_8;
            STATE_9:
                        if(one_second_cnt == 20'd30_000)
                            DS18b20_next_state <= STATE_1;
                        else
                            DS18b20_next_state <= STATE_9;
            default:
                        DS18b20_next_state <= STATE_1;
        endcase
    end
//assign temp[15:0] = test[15:0];
//*************************************************************    
//DS18B20的单总线信号方向以及读写控制
reg dq;
always @ (posedge clk or negedge rst_n)
    begin
        if(!rst_n)
            dq <= 1'b1;
        else if(master_cnt >= 10'd2 && master_cnt <= 10'd702)//把总线拉低700us,初始化DS18B20
            dq <= 1'b0;
        else if(slave_cnt >= 10'd15 && slave_cnt <= 10'd320)//接收DS18B20时设为高阻态
            dq <= 1'bz;
        else if (DS18b20_state == STATE_4 || DS18b20_state == STATE_7)//向DS18B20写数据
            dq <= bit_write;
        else if(DS18b20_state == STATE_8 && read_cnt <= 2)//读周期一开始做为主机先把总线拉低1微秒表示读周期开始
            dq <= 1'b0;
        else if(read_cnt > 2 && read_cnt <= 60)//60us的窗口内保持高阻态
            dq <= 1'bz;
        else
            dq <= 1'b1;
    end
assign pin_ds18b20 = dq;
endmodule
 
数据处理代码:
module temperature_calculation(
                                input clk,
                                input rst_n,
                                input[15:0]  two_byte_temp,
                                output[15:0] temperature
                              );
wire[13:0]    result_sig;                         
mul    mul_inst (
    .clock ( clk ),
    .dataa ( two_byte_temp[3:0]),
    .datab ( 10'd625 ),
    .result ( result_sig )
    );
wire [13:0] one_decimal,remain_sig;
div_14bit    div_14bit_inst_one (
    .clock ( clk ),
    .denom ( 14'd1000 ),
    .numer ( result_sig ),
    .quotient ( one_decimal ),
    .remain ( remain_sig )
    );
wire [13:0] two_decimal;
div_14bit    div_14bit_inst_two (
    .clock ( clk ),
    .denom ( 14'd100 ),
    .numer ( remain_sig ),
    .quotient ( two_decimal ),
    .remain ( remain_sig_two )
    );
wire [7:0] ten_quotint,ten_fractional;
div_8bit    div_8bit_inst (
    .clock ( clk ),
    .denom ( 8'd10),
    .numer ({1'b0,two_byte_temp[10:4]}),
    .quotient ( ten_quotint ),
    .remain ( ten_fractional )
    );
assign temperature = {ten_quotint[3:0],ten_fractional[3:0],one_decimal[3:0],two_decimal[3:0]};
endmodule
    
然后是温度的数码管显示代码:
module digital_tube_display(
            input clk,
            input rst_n,
            input [15:0] display_num,
            output reg[3:0] dtube_cs_n,//7段数码管位选信号
            output reg[7:0] dtube_data//7段数码管段选信号
           );
//-------------------------------
//参数定义
//数码管显示0~F对应段选输出
parameter    NUM0 = 8'h3f,
            NUM1 = 8'h06,
            NUM2 = 8'h5b,
            NUM3 = 8'h4f,
            NUM4 = 8'h66,
            NUM5 = 8'h6d,
            NUM6 = 8'h7d,
            NUM7 = 8'h07,
            NUM8 = 8'h7f,
            NUM9 = 8'h6f,
             OINT_NUM0 = 8'hbf,
             OINT_NUM1 = 8'h86,
             OINT_NUM2 = 8'hdb,
             OINT_NUM3 = 8'hcf,
             OINT_NUM4 = 8'he6,
             OINT_NUM5 = 8'hed,
             OINT_NUM6 = 8'hfd,
             OINT_NUM7 = 8'h87,
             OINT_NUM8 = 8'hff,
             OINT_NUM9 = 8'hef;
//数码管位选0~3输出
parameter    CSN = 4'B1111,
            CS0 = 4'b1110,
            CS1 = 4'b1101,
            CS2 = 4'b1011,
            CS3 = 4'b0111;
//-----------------------------------
//分时显示数据控制单元
reg[3:0] current_display_num;//当前显示数据
reg[7:0] div_cnt;//分时计数器
always @ (posedge clk or negedge rst_n)
        if(!rst_n)
            div_cnt <= 8'd0;
        else 
            div_cnt <= div_cnt + 1'b1;
        //显示数据
always @ (posedge clk or negedge rst_n)
        if(!rst_n)
            current_display_num <= 4'b0;
        else
            begin
                case(div_cnt)
                    8'hff : current_display_num <= display_num[3:0];
                    8'h3f : current_display_num <= display_num[7:4];
                    8'h7f : current_display_num <= display_num[11:8];
                    8'hbf : current_display_num <= display_num[15:12];
                    default : ;
                endcase
            end
//段选数据译码
always @ (posedge clk or negedge rst_n)
        if(!rst_n)
            dtube_data <= NUM0;
        else if(dtube_cs_n == CS2)
            begin 
                case(current_display_num)   //小数点显示位
                    4'h0 : dtube_data <= POINT_NUM0;
                    4'h1 : dtube_data <= POINT_NUM1;
                    4'h2 : dtube_data <= POINT_NUM2;
                    4'h3 : dtube_data <= POINT_NUM3;
                    4'h4 : dtube_data <= POINT_NUM4;
                    4'h5 : dtube_data <= POINT_NUM5;
                    4'h6 : dtube_data <= POINT_NUM6;
                    4'h7 : dtube_data <= POINT_NUM7;
                    4'h8 : dtube_data <= POINT_NUM8;
                    4'h9 : dtube_data <= POINT_NUM9;
                    default : ;
                endcase
            end
            else if(dtube_cs_n != CS2)
            begin 
                case(current_display_num)
                    4'h0 : dtube_data <= NUM0;
                    4'h1 : dtube_data <= NUM1;
                    4'h2 : dtube_data <= NUM2;
                    4'h3 : dtube_data <= NUM3;
                    4'h4 : dtube_data <= NUM4;
                    4'h5 : dtube_data <= NUM5;
                    4'h6 : dtube_data <= NUM6;
                    4'h7 : dtube_data <= NUM7;
                    4'h8 : dtube_data <= NUM8;
                    4'h9 : dtube_data <= NUM9;
                    default : ;
                endcase
            end
//位选译码
always @ (posedge clk or negedge rst_n)
        if(!rst_n)
            dtube_cs_n <= CSN;
        else
            begin
                case(div_cnt[7:6])
                    2'b00 : dtube_cs_n <= CS0;
                    2'b01 : dtube_cs_n <= CS1;
                    2'b10 : dtube_cs_n <= CS2;
                    2'b11 : dtube_cs_n <= CS3;
                    default : dtube_cs_n <= CSN;
                endcase
            end
endmodule
---------------------
作者:墨晕纸
来源:CSDN
原文:https://blog.csdn.net/wangyiyunyinyue/article/details/88055724
版权声明:本文为博主原创文章,转载请附上博文链接! |
|