传感器与数字电子系统设计温度度传感器DS18B20的温度采集

(1)设计要求

传感器检测技术与数字电子系统设计两门课程联合完成对温度传感器DS18B20的温度采集并在开发板的数码管上实时显示温度值。

(2)设计原理(介绍DS18B20的工作原理、工作时序);

温度传感器(temperature transducer)是指能感受温度并转换成可用输出信号的传感器,是各种传感器中最常用的一种。早期使用的是模拟温度传感器,如热敏电阻,随着环境温度的变化,其阻值也随之发生变化,用处理器采集电阻两端的电压,然后根据给定的公式就可计算出当前环境温度。随着科技的进步,现代的温度传感器已经走向数字化,外形小,接口简单,广泛应用在生产实践的各个领域,为我们的生活提供便利。随着现代仪器的发展,微型化、集成化、数字化正成为传感器发展的一个重要方向。美国DALLAS半导体公司推出的数字化温度传感器DS18B20采用单总线协议,即与FPGA接口仅需占用一个I/O端口,无须任何外部元件,直接将环境温度转化成数字信号,以数字码方式串行输出,从而大大简化了传感器与FPGA的接口设计。DS18B20测量温度范围为-55~+125℃,精度为±0.5℃。现场(实时) 温度直接以“单总线”的数字方式传输,大大提高了系统的抗干扰性。它能直接读出被测温度,并且可根据实际要求通过简单的编程实现9~l2位的数字值读数方式。它工作在3~5.5V的电压范围,采用多种封装形式,从而使系统设计灵活、方便,设定分辨率及用户设定的报警温度存储在EEPROM中,掉电后依然保存。其内部结构如图 所示:

 

       DS18B20中的温度传感器可以完成对温度的测量。其温度转换可由用户自定义(改变配置寄存器中R0与R1的值)为 9、 10、11、 12 位,精度分别为 0.5℃、 0.25℃、 0.125℃、 0.0625℃分辨率,若不设置则默认为 12位的转换精度。符号标志位(S)温度的正负极性:若 S=0,则为正数;若 S=1,则为负数。

 若要测量温度,主设备必须向 DS18B20 发送温度转换命令[44h]才能开始温度转换。温度转换后,转换的温度值将会保存在高速缓存器的温度寄存器中。只有通过读高速缓存器命令[BEh]才能将数据读出,数据通过 1-Wire 总线传输,传输顺序为低位到高位依次传输。如果 DS18B20 被定义为 12 位的转换精度,温度寄存器中所有位都将包含有效数据。若定义为 11 位转换精度,则 bit 0(最低位)为未定义的。若定义为 10 位转换精度,则 bit 0 和 bit 1 为未定义的。若定义 9 位转换精度,则 bit 0、 bit 1 和 bit 2 为未定义的。

 

 

待输出的数据是以二进制补码的形式存储在温度寄存器中的,所以要求温度值先得求原码再转化为十进制后乘以精度。正数的原码反码补码都是一样的,而负数的补码就是对反码加一。当温度为负数时,需先求得其原码(补码先减一再取反即为其原码)然后转换为十进制后乘以精度。例如数据输出为1111_1111_0101_1110,高六位全为1故判断此数为负数,将11_0101_1110减一后取反得到00_10100010,再将其转换为十进制得到162,乘以0.0625,填上负号最后算的温度为 -10.125摄氏度。 这里特别说明的是符号位只代表数据的正负,无论是取反还是求值它都是不算在里面的。另外上电复位时寄存器中的初始值为+85℃。

  DS18B20 采用严谨的 1-Wire 总线通信协议来保证数据的完整性。该协议定义多个信号形式:复位脉冲,存在脉冲,写 0,写 1,读 0,读 1 。除了存在脉冲由从设备发出,其他信号都由主设备控制。

step 1: 初始化时序

 

由上图可知,主机通过判断有无存在脉冲响应来判断有无传感器可以进行数据交互。

step 2: 写时序

主设备通过写时隙将命令写入 DS18B20 中,写时隙有两种:写“1”和写“0”时隙。主设备通过写 1 时隙来向 DS18B20 中写入逻辑 1,通过写 0 时隙来向 DS18B20 中写入逻辑 0。

 当主设备将总线从高电平拉至低电平时,启动写时隙,所有的写时隙持续时间最少为 60us,每个写时隙间的恢复时间最少为 1us。

如果要产生写 1 时隙,必须先将总线拉至逻辑低电平然后释放总线,允许总线在写时隙开始后 15us 内上拉至高电平。若要产生写 0 时隙,必须将总线拉至逻辑低电平并保持不变最少 60us。当总线(DQ)拉低后, DS18B20 在 15us 至 60us 之间对总线进行采样,如果采的 DQ为高电平则发生写 1,如果为低电平则发生写 0,如下图所示。

 

step 3: 读时序

 当我们发送完读取供电模式[B4h]或读高速缓存器[BEh]命令时,必须及时地生成读时隙,只有在读时隙 DS18B20 才能向主设备传送数据。

       每个读时隙最小必须有 60us 的持续时间以及每个读时隙间至少要有 1us 的恢复时间。当主设备将总线从高电平拉至低电平超过 1us,启动读时隙

       当启动读时隙后, DS18B20 将会向主设备发送“0”或者“1”。 DS18B20 通过将 DQ 引脚上的上拉电阻将总线在开始的15us内拉高发送“1”,不拉高发送“0”。主设备在读时隙开始后的 15us 内必须释放总线,并且对总线进行采样。时序图如下。

 

(3)状态图

 跳过 ROM 命令和温度跳转命令都是写 0,写 1 的时序,过程是一样的所以我们让其在同一个状态进行写入。同理可以让跳过 ROM 和读温度命令也在同一个状态写入,相关的状态描述,如下表所示。

 

(4)RTL图;

 

5)仿真分析(数码管模块的时序仿真图);

 

6)引脚锁定图;

 

7)验证和调试;

本实验工程文件包括ds18b20.v,ds18b20-ctrl.v,led.v和led.-tb.v,四个文件

ds18b20.v 是 ds18b20 的顶层模块;ds18b20_ctrl.v 是 ds18b20 的控制

模块;led.v 是数码管动态显示模块;led_tb.v 是 led_tb.v的仿真测试模块。

实验结果如下

室内温度

 

手的温度

 

附页

顶层文件、底层文件和TB 文件的Verilog程序代码放在最后的附录里。

TOP文件

module ds18b20(

    input            sys_clk     ,    //输入的系统时钟

    input            sys_rst_n   ,    //输入的复位信号

    inout            dq          ,    //ds18b20温度传感器单总线

    output  [2:0]    sel         ,    //输出数码管位选信号

    output  [7:0]    seg_led          //输出数码管段选信号

);

//parameter define

parameter POINT = 6'b000100;          // 数码管小数点的位置



//wire define

wire    [19:0]  temp_data;            // 温度数值

wire            sign;                 // 符号位



//*****************************************************

//**                    main code

//*****************************************************

//例化动态数码管驱动模块

led u_led(

    //module clock

    .clk           (sys_clk  ),       // 时钟信号

    .rst_n         (sys_rst_n),       // 复位信号

    //seg_led interface

    .seg_sel       (sel      ),       // 位选

    .seg_led       (seg_led  ),       // 段选

    //user interface

    .data          (temp_data),       // 显示的数值

    .point         (POINT    ),       // 小数点具体显示的位置,从高到低,高电平有效

    .en            (1'b1     ),       // 数码管使能信号

    .sign          (sign     )        // 符号位(高电平显示“-”号)

);



//例化DS18B20驱动模块

ds18b20_ctrl u1_ds18b20_ctrl(

    //module clock

    .clk          (sys_clk  ),        // 时钟信号(50MHz)

    .rst_n        (sys_rst_n),        // 复位信号

    //user interface

    .dq           (dq       ),        // DS18B20的DQ引脚数据

    .temp_data    (temp_data),        // 转换后得到的温度值

    .sign         (sign     )         // 符号位

);



endmodule





module ds18b20_ctrl(

    //module clock

    input              clk        ,         // 时钟信号(50MHz)

    input              rst_n      ,         // 复位信号

    //user interface

    inout              dq         ,         // DS18B20的DQ引脚数据

    output reg [19:0]  temp_data  ,         // 转换后得到的温度值

    output reg         sign                 // 符号位

);

//parameter define

localparam  ROM_SKIP_CMD = 8'hcc;           // 跳过 ROM 命令

localparam  CONVERT_CMD  = 8'h44;           // 温度转换命令

localparam  READ_TEMP    = 8'hbe;           // 读 DS1820 温度暂存器命令

//state define

localparam  init         = 3'd1 ;           // 初始化状态

localparam  rom_skip     = 3'd2 ;           // 加载跳过ROM命令

localparam  wr_byte      = 3'd3 ;           // 写字节状态

localparam  temp_convert = 3'd4 ;           // 加载温度转换命令

localparam  delay        = 3'd5 ;           // 延时等待温度转换结束

localparam  rd_temp      = 3'd6 ;           // 加载读温度命令

localparam  rd_byte      = 3'd7 ;           // 读字节状态

//reg define

reg     [ 4:0]         cnt         ;        // 分频计数器

reg                    clk_1us     ;        // 1MHz时钟

reg     [19:0]         cnt_1us     ;        // 微秒计数

reg     [ 2:0]         cur_state   ;        // 当前状态

reg     [ 2:0]         next_state  ;        // 下一状态

reg     [ 3:0]         flow_cnt    ;        // 流转计数

reg     [ 3:0]         wr_cnt      ;        // 写计数

reg     [ 4:0]         rd_cnt      ;        // 读计数

reg     [ 7:0]         wr_data     ;        // 写入DS18B20的数据

reg     [ 4:0]         bit_width   ;        // 读取的数据的位宽

reg     [15:0]         rd_data     ;        // 采集到的数据

reg     [15:0]         org_data    ;        // 读取到的原始温度数据

reg     [10:0]         data1       ;        // 对原理温度进行符号处理

reg     [ 3:0]         cmd_cnt     ;        // 发送命令计数

reg                    init_done   ;        // 初始化完成信号

reg                    st_done     ;        // 完成信号

reg                    cnt_1us_en  ;        // 使能计时

reg                    dq_out      ;        // DS18B20的dq输出

//wire define

wire    [19:0]         data2       ;        // 对处理后的进行转换处理

assign dq = dq_out;

//分频生成1MHz的时钟信号

always @ (posedge clk or negedge rst_n) begin

    if (!rst_n) begin

        cnt     <= 5'b0;

        clk_1us <= 1'b0;

    end

    else if(cnt < 5'd24) begin

        cnt     <= cnt + 1'b1;

        clk_1us <= clk_1us;

    end

    else begin

        cnt     <= 5'b0;

        clk_1us <= ~clk_1us;

    end

end

//微秒计时

always @ (posedge clk_1us or negedge rst_n) begin

    if (!rst_n)

        cnt_1us <= 20'b0;

    else if (cnt_1us_en)

        cnt_1us <= cnt_1us + 1'b1;

    else

        cnt_1us <= 20'b0;

end

//状态跳转

always @ (posedge clk_1us or negedge rst_n) begin

    if(!rst_n)

        cur_state <= init;

    else

        cur_state <= next_state;

end

//组合逻辑状态判断转换条件

always @( * ) begin

    case(cur_state)

        init: begin                             // 初始化状态

            if (init_done)

                next_state = rom_skip;

            else

                next_state = init;

        end

        rom_skip: begin                         // 加载跳过ROM命令

            if(st_done)

                next_state = wr_byte;

            else

                next_state = rom_skip;

        end

        wr_byte: begin                          // 发送命令

            if(st_done)

                case(cmd_cnt)                   // 根据命令序号,判断下个状态

                    4'b1: next_state = temp_convert;

                    4'd2: next_state = delay;

                    4'd3: next_state = rd_temp;

                    4'd4: next_state = rd_byte;

                    default:

                                         next_state = temp_convert;

                endcase

            else

                next_state = wr_byte;

        end

        temp_convert: begin                     // 加载温度转换命令

            if(st_done)

                next_state = wr_byte;

            else

                next_state = temp_convert;

        end

        delay: begin                            // 延时等待温度转换结束

            if(st_done)

                next_state = init;

            else

                next_state = delay;

        end

        rd_temp: begin                          // 加载读温度命令

            if(st_done)

                next_state = wr_byte;

            else

                next_state = rd_temp;

        end

        rd_byte: begin                          // 读数据线上的数据

            if(st_done)

                next_state = init;

            else

                next_state = rd_byte;

        end

        default: next_state = init;

    endcase

end



//整个操作步骤为初始化、发送跳过ROM操作命令、发送温度转换指令、

//再初始化、再发送跳过ROM操作指令、发送读数据指令。

always @ (posedge clk_1us or negedge rst_n) begin

    if(!rst_n) begin

        flow_cnt     <=  4'b0;

        init_done    <=  1'b0;

        cnt_1us_en   <=  1'b1;

        dq_out       <=  1'bZ;

        st_done      <=  1'b0;

        rd_data      <= 16'b0;

        rd_cnt       <=  5'd0;

        wr_cnt       <=  4'd0;

        cmd_cnt      <=  3'd0;

    end

    else begin

        st_done <= 1'b0;

        case (next_state)

            init:begin                              //初始化

                init_done <= 1'b0;

                case(flow_cnt)

                    4'd0:

                                          flow_cnt <= flow_cnt + 1'b1;

                                          4'd1: begin                               //发出500us复位脉冲

                        cnt_1us_en <= 1'b1;        

                        if(cnt_1us < 20'd500)

                            dq_out <= 1'b0;        

                        else begin

                            cnt_1us_en <= 1'b0;

                            dq_out <= 1'bz;

                            flow_cnt <= flow_cnt + 1'b1;

                        end

                    end

                    4'd2:begin                                         //释放总线,等待30us

                        cnt_1us_en <= 1'b1;

                        if(cnt_1us < 20'd30)

                            dq_out <= 1'bz;

                        else

                            flow_cnt <= flow_cnt + 1'b1;

                    end

                    4'd3: begin                                        //检测响应信号

                        if(!dq)

                            flow_cnt <= flow_cnt + 1'b1;

                        else

                            flow_cnt <= flow_cnt;

                    end

                    4'd4: begin                                        //等待初始化结束

                        if(cnt_1us == 20'd500) begin

                            cnt_1us_en <= 1'b0;

                            init_done  <= 1'b1;         //初始化完成

                            flow_cnt   <= 4'd0;

                        end

                        else

                            flow_cnt <= flow_cnt;

                    end

                    default: flow_cnt <= 4'd0;

                endcase

            end

            rom_skip: begin                         //加载跳过ROM操令

                wr_data  <= ROM_SKIP_CMD;

                flow_cnt <= 4'd0;

                st_done  <= 1'b1;

            end

            wr_byte: begin                          //写字节状态(发送令)

                if(wr_cnt <= 4'd7) begin

                    case (flow_cnt)

                        4'd0: begin

                            dq_out <= 1'b0;                //拉低数据线,开始写作

                            cnt_1us_en <= 1'b1;         //启动计时器

                            flow_cnt <= flow_cnt + 1'b1;

                        end

                        4'd1: begin                                //数据线拉低1us

                            flow_cnt <= flow_cnt + 1'b1;

                        end

                        4'd2: begin

                            if(cnt_1us < 20'd60)  //发送数据

                                dq_out <= wr_data[wr_cnt];

                            else if(cnt_1us < 20'd63)

                                dq_out <= 1'bz;         //释放总线(发送间隔)

                            else

                                flow_cnt <= flow_cnt + 1'b1;

                        end

                        4'd3: begin                                //发送1位数据完成

                            flow_cnt <= 0;

                            cnt_1us_en <= 1'b0;

                            wr_cnt <= wr_cnt + 1'b1;//写计数器加1

                        end

                        default : flow_cnt <= 0;

                    endcase

                end

                else begin                                           //发送指令(1Byte)结束

                    st_done <= 1'b1;

                    wr_cnt <= 4'b0;

                    cmd_cnt <= (cmd_cnt == 3'd4) ?  //标记当前发送的指令序号

                                              3'd1 : (cmd_cnt+ 1'b1);

                end

            end

            temp_convert: begin                     //加载温度转换命令

                wr_data <= CONVERT_CMD;

                st_done <= 1'b1;

            end

            delay: begin                            //延时500ms等待温度转换结束

                cnt_1us_en <= 1'b1;

                if(cnt_1us == 20'd500000) begin

                    st_done <= 1'b1;

                    cnt_1us_en <= 1'b0;

                end

            end

            rd_temp: begin                          //加载读温度命令

                wr_data <= READ_TEMP;

                bit_width <= 5'd16;                                  //指定读数据个数

                st_done <= 1'b1;

            end

            rd_byte: begin                          //接收16位温度数据

                if(rd_cnt < bit_width) begin

                    case(flow_cnt)

                        4'd0: begin

                            cnt_1us_en <= 1'b1;

                            dq_out <= 1'b0;                //拉低数据线,开始读操作

                            flow_cnt <= flow_cnt + 1'b1;

                        end

                        4'd1: begin

                            dq_out <= 1'bz;                 //释放总线并在15us内接收数据

                            if(cnt_1us == 20'd14) begin

                                rd_data <= {dq,rd_data[15:1]};

                                flow_cnt <= flow_cnt + 1'b1 ;

                            end

                        end

                        4'd2: begin

                            if (cnt_1us <= 20'd64)      //读1位数据结束

                                dq_out <= 1'bz;

                            else begin

                                flow_cnt <= 4'd0;    

                                rd_cnt <= rd_cnt + 1'b1;//读计数器加1

                                cnt_1us_en <= 1'b0;

                            end

                        end

                        default : flow_cnt <= 4'd0;

                    endcase

                end

                else begin

                    st_done <= 1'b1;

                    org_data  <= rd_data;

                    rd_cnt <= 5'b0;       

                end

            end

            default: ;

        endcase

    end

end

//判断符号位

always @(posedge clk_1us or negedge rst_n) begin

    if(!rst_n) begin

        sign  <=  1'b0;

        data1 <= 11'b0;

    end

    else if(org_data[15] == 1'b0) begin

        sign  <= 1'b0;

        data1 <= org_data[10:0];

    end

    else if(org_data[15] == 1'b1) begin

        sign  <= 1'b1;

        data1 <= ~org_data[10:0] + 1'b1;

    end

end

//对采集到的温度进行转换

assign data2 = (data1 * 11'd625)/ 7'd100;

//温度输出

always @(posedge clk_1us or negedge rst_n) begin

    if(!rst_n)

        temp_data <= 20'b0;

    else

        temp_data <= data2;

end

endmodule





led模块



module led(

    input                   clk    ,        // 时钟信号

    input                   rst_n  ,        // 复位信号

    input         [19:0]    data   ,        // 6位数码管要显示的数值

    input         [5:0]     point  ,        // 小数点具体显示的位置,从高到低,高电平有效

    input                   en     ,        // 数码管使能信号

    input                   sign   ,        // 符号位(高电平显示“-”号)

    output   reg  [2:0]     seg_sel,        // 数码管位选,最左侧数码管为最高位

    output   reg  [7:0]     seg_led         // 数码管段选

    );

//parameter define

localparam  CLK_DIVIDE = 4'd10     ;        // 时钟分频系数

localparam  MAX_NUM    = 13'd5000  ;        // 对数码管驱动时钟(5MHz)计数1ms所需的计数值

//reg define

reg    [ 3:0]             clk_cnt  ;        // 时钟分频计数器

reg                       dri_clk  ;        // 数码管的驱动时钟,5MHz

reg    [23:0]             num      ;        // 24位bcd码寄存器

reg    [12:0]             cnt0     ;        // 数码管驱动时钟计数器

reg                       flag     ;        // 标志信号(标志着cnt0计数达1ms)

reg    [2:0]              cnt_sel  ;        // 数码管位选计数器

reg    [3:0]              num_disp ;        // 当前数码管显示的数据

reg                       dot_disp ;        // 当前数码管显示的小数点

//wire define

wire   [3:0]              data0    ;        // 个位数

wire   [3:0]              data1    ;        // 十位数

wire   [3:0]              data2    ;        // 百位数

wire   [3:0]              data3    ;        // 千位数

wire   [3:0]              data4    ;        // 万位数

wire   [3:0]              data5    ;        // 十万位数

//提取显示数值所对应的十进制数的各个位

assign  data0 = data % 4'd10;               // 个位数

assign  data1 = data / 4'd10 % 4'd10   ;    // 十位数

assign  data2 = data / 7'd100 % 4'd10  ;    // 百位数

assign  data3 = data / 10'd1000 % 4'd10 ;   // 千位数

assign  data4 = data / 14'd10000 % 4'd10;   // 万位数

assign  data5 = data / 17'd100000;          // 十万位数

//对系统时钟10分频,得到的频率为5MHz的数码管驱动时钟dri_clk

always @(posedge clk or negedge rst_n) begin

   if(!rst_n) begin

       clk_cnt <= 4'd0;

       dri_clk <= 1'b1;

   end

   else if(clk_cnt == CLK_DIVIDE/2 - 1'd1) begin

       clk_cnt <= 4'd0;

       dri_clk <= ~dri_clk;

   end

   else begin

       clk_cnt <= clk_cnt + 1'b1;

       dri_clk <= dri_clk;

   end

end

//将20位2进制数转换为8421bcd码(即使用4位二进制数表示1位十进制数)

always @ (posedge dri_clk or negedge rst_n) begin

    if (!rst_n)

        num <= 24'b0;

    else begin

        if (data5 || point[5]) begin     //如果显示数据为6位十进制数,

            num[23:20] <= data5;         //则依次给6位数码管赋值

            num[19:16] <= data4;

            num[15:12] <= data3;

            num[11:8]  <= data2;

            num[ 7:4]  <= data1;

            num[ 3:0]  <= data0;

        end

        else begin                        

            if (data4 || point[4]) begin //如果显示数据为5位十进制数,则给低5位数码管赋值

                num[19:0] <= {data4,data3,data2,data1,data0};

                if(sign)                   

                    num[23:20] <= 4'd11; //如果需要显示负号,则最高位(第6位)为符号位

                else

                    num[23:20] <= 4'd10; //不需要显示负号时,则第6位不显示任何字符

            end

            else begin                   //如果显示数据为4位十进制数,则给低4位数码管赋值

                if (data3 || point[3]) begin

                    num[15: 0] <= {data3,data2,data1,data0};

                    num[23:20] <= 4'd10; //第6位不显示任何字符

                    if(sign)             //如果需要显示负号,则最高位(第5位)为符号位

                        num[19:16] <= 4'd11;

                    else                 //不需要显示负号时,则第5位不显示任何字符

                        num[19:16] <= 4'd10;

                end

                else begin               //如果显示数据为3位十进制数,则给低3位数码管赋值

                    if (data2 || point[2]) begin

                        num[11: 0] <= {data2,data1,data0};

                                         //第6、5位不显示任何字符

                        num[23:16] <= {2{4'd10}};

                        if(sign)         //如果需要显示负号,则最高位(第4位)为符号位

                            num[15:12] <= 4'd11;

                        else             //不需要显示负号时,则第4位不显示任何字符

                            num[15:12] <= 4'd10;

                    end

                    else begin           //如果显示数据为2位十进制数,则给低2位数码管赋值

              if (data1 || point[1]) begin

                   num[ 7: 0] <= {data1,data0};

                                         //第6、5、4位不显示任何字符

                    num[23:12] <= {3{4'd10}};

              if(sign)    //如果需要显示负号,则最高位(第3位)为符号位

                    num[11:8]  <= 4'd11;

              else         //不需要显示负号时,则第3位不显示任何字符

                    num[11:8] <=  4'd10;

              end

              else begin       //如果显示数据为1位十进制数,则给最低位数码管赋值

                      num[3:0] <= data0;

                                         //第6、5位不显示任何字符

                      num[23:8] <= {4{4'd10}};

                if(sign)  //如果需要显示负号,则最高位(第2位)为符号位

 

                                num[7:4] <= 4'd11;

               else         //不需要显示负号时,则第2位不显示任何字符

                    num[7:4] <= 4'd10;

                        end

                    end

                end

            end

        end

    end

end

//每当计数器对数码管驱动时钟计数时间达1ms,输出一个时钟周期的脉冲信号

always @ (posedge dri_clk or negedge rst_n) begin

    if (rst_n == 1'b0) begin

        cnt0 <= 13'b0;

        flag <= 1'b0;

     end

    else if (cnt0 < MAX_NUM - 1'b1) begin

        cnt0 <= cnt0 + 1'b1;

        flag <= 1'b0;

     end

    else begin

        cnt0 <= 13'b0;

        flag <= 1'b1;

     end

end

//cnt_sel从0计数到5,用于选择当前处于显示状态的数码管

always @ (posedge dri_clk or negedge rst_n) begin

    if (rst_n == 1'b0)

        cnt_sel <= 3'b0;

    else if(flag) begin

        if(cnt_sel < 3'd5)

            cnt_sel <= cnt_sel + 1'b1;

        else

            cnt_sel <= 3'b0;

    end

    else

        cnt_sel <= cnt_sel;

end

//控制数码管位选信号,使6位数码管轮流显示

always @ (posedge dri_clk or negedge rst_n) begin

    if(!rst_n) begin

        seg_sel  <= 3'd7;              //位选信号低电平有效

        num_disp <= 4'b0;          

        dot_disp <= 1'b0;                   //共阳极数码管,低电平导通

    end

    else begin

        if(en) begin

            case (cnt_sel)

                3'd0 :begin

                    seg_sel  <= 3'd6;  //显示数码管最低位

                    num_disp <= num[3:0] ;  //显示的数据

                    dot_disp <= point[0];  //显示的小数点

                end

                3'd1 :begin

                    seg_sel  <= 3'd5;  //显示数码管第1位

                    num_disp <= num[7:4] ;

                    dot_disp <= point[2];

                end

                3'd2 :begin

                    seg_sel  <= 3'd4;  //显示数码管第2位

                    num_disp <= num[11:8];

                    dot_disp <= point[1];

                end

                3'd3 :begin

                    seg_sel  <= 3'd3;  //显示数码管第3位

                    num_disp <= num[15:12];

                    dot_disp <= point[3];

                end

                3'd4 :begin

                    seg_sel  <= 3'd2;  //显示数码管第4位

                    num_disp <= num[19:16];

                    dot_disp <= point[4];

                end

                3'd5 :begin

                    seg_sel  <= 3'd1;  //显示数码管最高位

                    num_disp <= num[23:20];

                    dot_disp <= point[5];

                end

                default :begin

                    seg_sel  <= 3'd0;

                    num_disp <= 4'b0;

                    dot_disp <= 1'b0;

                end

            endcase

        end

        else begin

            seg_sel  <= 3'd0;       //使能信号为0时,所有数码管均不显示

            num_disp <= 4'b0;

            dot_disp <= 1'b0;

        end

    end

end

//控制数码管段选信号,显示字符

always @ (posedge dri_clk or negedge rst_n) begin

    if (!rst_n)

        seg_led <= 8'hc0;

    else begin

        case (num_disp)

         4'd0 : seg_led <= {dot_disp,7'b1000000}; //显示数字 0

            4'd1 : seg_led <= {dot_disp,7'b1111001}; //显示数字 1

            4'd2 : seg_led <= {dot_disp,7'b0100100}; //显示数字 2

            4'd3 : seg_led <= {dot_disp,7'b0110000}; //显示数字 3

            4'd4 : seg_led <= {dot_disp,7'b0011001}; //显示数字 4

            4'd5 : seg_led <= {dot_disp,7'b0010010}; //显示数字 5

            4'd6 : seg_led <= {dot_disp,7'b0000010}; //显示数字 6

            4'd7 : seg_led <= {dot_disp,7'b1111000}; //显示数字 7

            4'd8 : seg_led <= {dot_disp,7'b0000000}; //显示数字 8

            4'd9 : seg_led <= {dot_disp,7'b0010000}; //显示数字 9

            4'd10: seg_led <= 8'b11111111;           //不显示任何字符

            4'd11: seg_led <= 8'b10111111;           //显示负号(-)

            default:

                   seg_led <= {dot_disp,7'b1000000};

        endcase

    end

end

endmodule







TB文件

`timescale 1ns / 1ns

module seg_595_dynamic_tb;

    // 输入

    reg clk;

    reg rst_n;

    reg [19:0] data;

    reg [5:0] point;

    reg en;

    reg sign; 

    // 输出

    wire [5:0] seg_sel;

    wire [7:0] seg_led;

    // 实例化

    seg_595_dynamic dut (

        .clk(clk),

        .rst_n(rst_n),

        .data(data),

        .point(point),

        .en(en),

        .sign(sign),

        .seg_sel(seg_sel),

        .seg_led(seg_led)

    );  

    // 生成时钟

    always begin

        #5 clk = ~clk;

    end

    // 初始化输入

    initial begin

        clk = 0;

        rst_n = 0;

        data = 20'd0;

        point = 6'b000000;

        en = 0;

        sign = 0;      

        #10 rst_n = 1;

        #10 en = 1;       

        // 测试实例1

        data = 20'd12345;

        point = 6'b001000;

        sign = 0;

        #100;  

        // 测试实例2

        data = 20'd9876;

        point = 6'b000010;

        sign = 1;

        #100;

        #100;

        $finish;

    end

endmodule

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值