基础实验十三,DS18B20温度传感器
//==========================================================================
// Author : ChanRa1n
// Description: Training for Intel FPGA
// Web : www.myfpga.cn
// CoryRight :本项目是在stark-lin的开源文章下二次开发,遵循CC 4.0 BY-SA协议开源
//==========================================================================
module FUNCTION_SEG(
input sys_clk ,//时钟,50MHz
input sys_rst,//复位信号,下降沿有效
input [15:0] data_out,//DS18B20_data
output reg [5:0] io_seg_sel ,//位选信号,六个数码管
output reg [7:0] io_seg_led //段选信号,八段
);
wire [15:0] sig_data;
reg time_flag;
reg [5:0] temp_data;
reg [9:0] time_divider;
reg [4:0] value;//暂存真值
reg [2:0] current_state ;//当前状态
reg [2:0] next_state ;//下一个状态
always@(posedge sys_clk or negedge sys_rst)
begin
if(!sys_rst)
begin//下降沿有效
time_divider <= 0;//当前状态置0
end
else
begin
if(time_divider>=1000)
begin
time_divider<=0;
time_flag<=1;
end
else
begin
time_flag<=0;
time_divider <= time_divider+1;//状态跳转
end
end
end
//状态跳转
always@(posedge sys_clk or negedge sys_rst)
begin
if(!sys_rst)
begin//下降沿有效
current_state <= 3'd0;//当前状态置0
end
else
begin
current_state <= next_state;//状态跳转
end
end
//下一个状态判断(组合逻辑)
always@(*)
begin
case(current_state)
3'd0:
if(time_flag)
next_state = 3'd1;
else
next_state = 3'd0;
3'd1:
if(time_flag)
next_state = 3'd2;
else
next_state = 3'd1;
3'd2:
if(time_flag)
next_state = 3'd3;
else
next_state = 3'd2;
3'd3:
if(time_flag)
next_state = 3'd4;
else
next_state = 3'd3;
3'd4:
if(time_flag)
next_state = 3'd5;
else
next_state = 3'd4;
3'd5:
if(time_flag)
next_state = 3'd0;
else
next_state = 3'd5;
default:
next_state= 3'd1;
endcase
end
//各个状态下的动作(可以组合也可以时序)
always@(posedge sys_clk or negedge sys_rst)
begin
if(!sys_rst)
begin
value = 3'd0;
temp_data<=0;
end
else
begin
case(current_state)
3'd0:
begin
io_seg_sel = 6'b111_110;//选择最右边数码管。 io_seg_sel低有效
value<=20;//C
end
3'd1:
begin
io_seg_sel = 6'b111_101;
temp_data <= data_out[3:0] * 11'd625;
value<=temp_data/10;
end
3'd2:
begin
io_seg_sel = 6'b111_011;
value<=temp_data%10;
end
3'd3:
begin
io_seg_sel = 6'b110_111;
value = data_out[10:4]%10+10;//加小数点
end
3'd4:
begin
io_seg_sel = 6'b101_111;
value = data_out[10:4]/10;
end
3'd5:
begin
io_seg_sel = 6'b011_111;
value = 22;//NULL
end
default:;
endcase
end
end
//数码管io_seg_led的输出
always@(*)
begin
if(!sys_rst)
begin
io_seg_led = 8'b11111111;
end
else
begin
case(value)
5'd0:
io_seg_led = 8'b11000000;
5'd1:
io_seg_led = 8'b11111001;
5'd2:
io_seg_led = 8'b10100100;
5'd3:
io_seg_led = 8'b10110000;
5'd4:
io_seg_led = 8'b10011001;
5'd5:
io_seg_led = 8'b10010010;
5'd6:
io_seg_led = 8'b10000010;
5'd7:
io_seg_led = 8'b11111000;
5'd8:
io_seg_led = 8'b10000000;
5'd9:
io_seg_led = 8'b10010000;
//以下是带小数点的数
5'd10:
io_seg_led = 8'b01000000;
5'd11:
io_seg_led = 8'b01111001;
5'd12:
io_seg_led = 8'b00100100;
5'd13:
io_seg_led = 8'b00110000;
5'd14:
io_seg_led = 8'b00011001;
5'd15:
io_seg_led = 8'b00010010;
5'd16:
io_seg_led = 8'b00000010;
5'd17:
io_seg_led = 8'b01111000;
5'd18:
io_seg_led = 8'b00000000;
5'd19:
io_seg_led = 8'b00010000;
//下面还可以写其他的,例如字母等
5'd20:
io_seg_led = 8'b10100111;//C
default :
io_seg_led = 8'b11111111;
endcase
end
end
endmodule
//模块来自电子森林
module DS18B20Z
(
input clk_in, //系统时钟
input rst_n_in, //系统复位,低有效
inout one_wire, //DS18B20Z传感器单总线,双向管脚
output reg [15:0] data_out //DS18B20Z有效温度数据输出
);
/*
本设计通过驱动DS18B20Z芯片获取温度数据,
需要了解inout类型的接口如何实现双向通信,
中间涉及各种不同的延时和寄存器指令操作,注释部分以作简要说明,更多详情需参考数据手册
*/
localparam IDLE = 3'd0;
localparam MAIN = 3'd1;
localparam INIT = 3'd2;
localparam WRITE = 3'd3;
localparam READ = 3'd4;
localparam DELAY = 3'd5;
//计数器分频产生1MHz的时钟信号
reg clk_1mhz;
reg [5:0] cnt_1mhz;
always@(posedge clk_in or negedge rst_n_in) begin
if(!rst_n_in) begin
cnt_1mhz <= 6'd0;
clk_1mhz <= 1'b0;
end else if(cnt_1mhz >= 6'd25) begin
cnt_1mhz <= 6'd0;
clk_1mhz <= ~clk_1mhz; //产生1MHz分频
end else begin
cnt_1mhz <= cnt_1mhz + 1'b1;
end
end
reg [2:0] cnt;
reg one_wire_buffer;
reg [3:0] cnt_main;
reg [7:0] data_wr;
reg [7:0] data_wr_buffer;
reg [2:0] cnt_init;
reg [19:0] cnt_delay;
reg [19:0] num_delay;
reg [3:0] cnt_write;
reg [2:0] cnt_read;
reg [15:0] temperature;
reg [7:0] temperature_buffer;
reg [2:0] state = IDLE;
reg [2:0] state_back = IDLE;
//使用1MHz时钟信号做触发完成下面状态机的功能
always@(posedge clk_1mhz or negedge rst_n_in) begin
if(!rst_n_in) begin
state <= IDLE;
state_back <= IDLE;
cnt <= 1'b0;
cnt_main <= 1'b0;
cnt_init <= 1'b0;
cnt_write <= 1'b0;
cnt_read <= 1'b0;
cnt_delay <= 1'b0;
one_wire_buffer <= 1'bz;
temperature <= 16'h0;
end else begin
case(state)
IDLE:begin //IDLE状态,程序设计的软复位功能,各状态异常都会跳转到此状态
state <= MAIN; //软复位完成,跳转之MAIN状态重新工作
state_back <= MAIN;
cnt <= 1'b0;
cnt_main <= 1'b0;
cnt_init <= 1'b0;
cnt_write <= 1'b0;
cnt_read <= 1'b0;
cnt_delay <= 1'b0;
one_wire_buffer <= 1'bz;
end
MAIN:begin //MAIN状态控制状态机在不同状态间跳转,实现完整的温度数据采集
if(cnt_main >= 4'd11) cnt_main <= 1'b0;
else cnt_main <= cnt_main + 1'b1;
case(cnt_main)
4'd0: begin state <= INIT; end //跳转至INIT状态进行芯片的复位及验证
4'd1: begin data_wr <= 8'hcc;state <= WRITE; end //主设备发出跳转ROM指令
4'd2: begin data_wr <= 8'h44;state <= WRITE; end //主设备发出温度转换指令
4'd3: begin num_delay <= 20'd750000;state <= DELAY;state_back <= MAIN; end //延时750ms等待转换完成
4'd4: begin state <= INIT; end //跳转至INIT状态进行芯片的复位及验证
4'd5: begin data_wr <= 8'hcc;state <= WRITE; end //主设备发出跳转ROM指令
4'd6: begin data_wr <= 8'hbe;state <= WRITE; end //主设备发出读取温度指令
4'd7: begin state <= READ; end //跳转至READ状态进行单总线数据读取
4'd8: begin temperature[7:0] <= temperature_buffer; end //先读取的为低8位数据
4'd9: begin state <= READ; end //跳转至READ状态进行单总线数据读取
4'd10: begin temperature[15:8] <= temperature_buffer; end //后读取的为高8为数据
4'd11: begin state <= IDLE;data_out <= temperature; end //将完整的温度数据输出并重复以上所有操作
default: state <= IDLE;
endcase
end
INIT:begin //INIT状态完成DS18B20Z芯片的复位及验证功能
if(cnt_init >= 3'd6) cnt_init <= 1'b0;
else cnt_init <= cnt_init + 1'b1;
case(cnt_init)
3'd0: begin one_wire_buffer <= 1'b0; end //单总线复位脉冲拉低
3'd1: begin num_delay <= 20'd500;state <= DELAY;state_back <= INIT; end //复位脉冲保持拉低500us时间
3'd2: begin one_wire_buffer <= 1'bz; end //单总线复位脉冲释放,自动上拉
3'd3: begin num_delay <= 20'd100;state <= DELAY;state_back <= INIT; end //复位脉冲保持释放100us时间
3'd4: begin if(one_wire) state <= IDLE; else state <= INIT; end //根据单总线的存在检测结果判定是否继续
3'd5: begin num_delay <= 20'd400;state <= DELAY;state_back <= INIT; end //如果检测正常继续保持释放400us时间
3'd6: begin state <= MAIN; end //INIT状态操作完成,返回MAIN状态
default: state <= IDLE;
endcase
end
WRITE:begin //按照DS18B20Z芯片单总线时序进行写操作
if(cnt <= 3'd6) begin //共需要发送8bit的数据,这里控制循环的次数
if(cnt_write >= 4'd6) begin cnt_write <= 1'b1; cnt <= cnt + 1'b1; end
else begin cnt_write <= cnt_write + 1'b1; cnt <= cnt; end
end else begin
if(cnt_write >= 4'd8) begin cnt_write <= 1'b0; cnt <= 1'b0; end //两个变量都恢复初值
else begin cnt_write <= cnt_write + 1'b1; cnt <= cnt; end
end
//对于WRITE状态中cnt_write来讲,执行过程为:0;[1~6]*8;7;8;
case(cnt_write)
//lock data_wr
4'd0: begin data_wr_buffer <= data_wr; end //将需要写出的数据缓存
//发送 1bit 数据的用时在60~120us之间,参考数据手册
4'd1: begin one_wire_buffer <= 1'b0; end //总线拉低
4'd2: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end //延时2us时间,保证15us以内
4'd3: begin one_wire_buffer <= data_wr_buffer[cnt]; end //先发送数据最低位
4'd4: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end //延时80us时间
4'd5: begin one_wire_buffer <= 1'bz; end //总线释放
4'd6: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end //延时2us时间
//back to main
4'd7: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end //延时80us时间
4'd8: begin state <= MAIN; end //返回MAIN状态
default: state <= IDLE;
endcase
end
READ:begin //按照DS18B20Z芯片单总线时序进行读操作
if(cnt <= 3'd6) begin //共需要接收8bit的数据,这里控制循环的次数
if(cnt_read >= 3'd5) begin cnt_read <= 1'b0; cnt <= cnt + 1'b1; end
else begin cnt_read <= cnt_read + 1'b1; cnt <= cnt; end
end else begin
if(cnt_read >= 3'd6) begin cnt_read <= 1'b0; cnt <= 1'b0; end //两个变量都恢复初值
else begin cnt_read <= cnt_read + 1'b1; cnt <= cnt; end
end
case(cnt_read)
//读取 1bit 数据的用时在60~120us之间,总线拉低后15us时间内读取数据,参考数据手册
3'd0: begin one_wire_buffer <= 1'b0; end //总线拉低
3'd1: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end //延时2us时间
3'd2: begin one_wire_buffer <= 1'bz; end //总线释放
3'd3: begin num_delay <= 20'd5;state <= DELAY;state_back <= READ; end //延时5us时间
3'd4: begin temperature_buffer[cnt] <= one_wire; end //读取DS18B20Z返回的总线数据,先收最低位
3'd5: begin num_delay <= 20'd60;state <= DELAY;state_back <= READ; end //延时60us时间
//back to main
3'd6: begin state <= MAIN; end //返回MAIN状态
default: state <= IDLE;
endcase
end
DELAY:begin //延时控制
if(cnt_delay >= num_delay) begin //延时控制,延时时间由num_delay指定
cnt_delay <= 1'b0;
state <= state_back; //很多状态都需要延时,延时后返回哪个状态由state_back指定
end else cnt_delay <= cnt_delay + 1'b1;
end
endcase
end
end
assign one_wire = one_wire_buffer;
endmodule