项目信息
名称:DS18B20温度采集与数码管显示 Verilog QuartusII
软件:QuartusII
语言:Verilog
代码功能
该工程实现DS18B20单总线温度采集与数码管显示:驱动模块按照初始化、发送跳过ROM命令、启动温度转换、等待转换完成、读取温度寄存器等步骤与传感器通信,得到原始温度数据并进行符号与数值处理;显示模块把温度数据组织为数码管动态扫描的段码与位选信号,并支持通过拨码开关切换显示内容。
开发板.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
182