名称:DS18B20温度采集与数码管显示的设计Verilog代码Quartus开发板
软件:QuartusII
语言:Verilog
代码功能
该工程实现DS18B20单总线温度采集与数码管显示:驱动模块按照初始化、发送跳过ROM命令、启动温度转换、等待转换完成、读取温度寄存器等步骤与传感器通信,得到原始温度数据并进行符号与数值处理;显示模块把温度数据组织为数码管动态扫描的段码与位选信号,并支持通过拨码开关切换显示内容。
FPGA代码Verilog/VHDL代码资源下载:www.hdlcode.com
本代码已在开发板验证,开发板如下,其他开发板可以修改管脚适配:
开发板.png
演示视频:
设计文档:
代码实现思路
核心难点是单总线严格的时序。工程通过对系统时钟分频得到1us节拍,并在该节拍下实现微秒计时器,从而精确生成复位脉冲、写时隙与读时隙。状态机按DS18B20的标准流程推进:初始化检测存在脉冲;写入命令字节时逐位控制dq拉低/释放;读取温度时按位采样并拼接成16位原始数据,再进行符号与量化处理输出。顶层把温度值交给数码管扫描模块,最终实现稳定、可观察的温度显示。
代码结构
顶层temp_disp只做模块集成:ds18b20_dri负责传感器通信与温度计算,display负责动态数码管扫描与显示组织。ds18b20_dri内部进一步拆分为:1us时钟与微秒计数;主状态机(初始化/写字节/等待/读字节);以及写位/读位的流程控制计数器。这样把“时序生成”和“流程控制”分开,既便于调试,也方便根据不同主频调整分频参数。
1、工程文件
2、程序文件
3、程序编译
4、RTL图
管脚分配
5、仿真图
整体仿真图
(使用force生成温度)
Testbench
仿真图
DS18B20模块仿真图
Testbench
仿真图
显示模块仿真图
Testbench
仿真图
文件/模块一览
temp_disp_bddisplay.v:display
temp_disp_bdtemp_disp_bddisplay.v:display
temp_disp_simdisplay.v:display
部分代码
temp_disp_simds18b20_dri.v
// Descriptions: DS18B20驱动模块 //****************************************************************************************// module ds18b20_dri( //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 localparamROM_SKIP_CMD = 8'hcc; // 跳过 ROM 命令 localparamCONVERT_CMD= 8'h44; // 温度转换命令 localparamREAD_TEMP = 8'hbe; // 读 DS1820 温度暂存器命令 //state define localparaminit = 3'd1 ; // 初始化状态 localparamrom_skip = 3'd2 ; // 加载跳过ROM命令 localparamwr_byte = 3'd3 ; // 写字节状态 localparamtemp_convert = 3'd4 ; // 加载温度转换命令 localparamdelay = 3'd5 ; // 延时等待温度转换结束 localparamrd_temp = 3'd6 ; // 加载读温度命令 localparamrd_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 ; // 对处理后的进行转换处理 //***************************************************** //** main code //***************************************************** 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; bit_width <= 5'd16; //指定读数据个数 org_data<= 16'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
112