做了一个LED测试仪器的项目,半年过去了,都没有时间写博客,现在闲下来写写!
项目中用到了DS18B20温度传感器,在网上找到了一个驱动代码,经过我改造一下做成定制IP和大家分享!
一、DS18B20定制IP Verilog HDL代码
//**********************************************************************************//
module avalon_slave_DS18B20(
address,
clk,
reset_n,
readdata,
TEMP
);
output [ 15: 0] readdata;
input [ 1: 0] address;
input clk;
input reset_n;
inout TEMP;
wire clk_en;
wire [ 15: 0] data_in;
wire [ 15: 0] read_mux_out;
reg [ 15: 0] readdata;
assign clk_en = 1;
//s1, which is an e_avalon_slave
assign read_mux_out = {16 {(address == 0)}} & data_in;
always @(posedge clk or negedge reset_n)
begin
if (reset_n == 0)
readdata <= 0;
else if (clk_en)
readdata <= read_mux_out;
end
assign data_in = temperature_buf; //Ds18b20用12位存贮温值度最高位为符号位;
//++++++++++++++++++++++++++++++++++++++
// 分频器50MHz->1MHz 开始
//++++++++++++++++++++++++++++++++++++++
reg [5:0] cnt; // 计数子
always@(posedge clk or negedge reset_n)
begin
if (!reset_n) cnt <= 1'b0;
else if (cnt == 6'd49) cnt <= 1'b0;
else cnt <= cnt + 1'b1;
end
reg clk_1us; // 1MHz 时钟
always@(posedge clk or negedge reset_n)
begin
if (!reset_n) clk_1us <= 1'b0;
else if (cnt <= 6'd24) clk_1us <= 1'b0;
else clk_1us <=1'b1;
end
//--------------------------------------
// 分频器50MHz->1MHz 结束
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
// 延时模块 开始
//++++++++++++++++++++++++++++++++++++++
reg [19:0] cnt_1us; // 1us延时计数子
reg cnt_1us_clear; // 请1us延时计数子
always@(posedge clk_1us)
begin
if(cnt_1us_clear) cnt_1us <= 1'b0;
else cnt_1us <= cnt_1us + 1'b1;
end
//--------------------------------------
// 延时模块 结束
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
// DS18B20状态机 开始
//++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++
// 格雷码
parameter S00 = 5'h00;
parameter S0 = 5'h01;
parameter S1 = 5'h03;
parameter S2 = 5'h02;
parameter S3 = 5'h06;
parameter S4 = 5'h07;
parameter S5 = 5'h05;
parameter S6 = 5'h04;
parameter S7 = 5'h0C;
parameter WRITE0 = 5'h0D;
parameter WRITE1 = 5'h0F;
parameter WRITE00 = 5'h0E;
parameter WRITE01 = 5'h0A;
parameter READ0 = 5'h0B;
parameter READ1 = 5'h09;
parameter READ2 = 5'h08;
parameter READ3 = 5'h18;
reg [4:0] state; // 状态寄存器
//-------------------------------------
reg TEMP_buf; // One-Wire总线 缓存寄存器
reg [15:0] temperature_buf; // 采集到的温度值缓存器(未处理)
reg [5:0] step; // 子状态寄存器 0~50
reg [3:0] bit_valid; // 有效位
always@(posedge clk_1us or negedge reset_n)
begin
if (!reset_n)
begin
TEMP_buf <= 1'bZ;
step <= 6'd0;
state <= S00;
end
else
begin
case (state)
S00 : begin
temperature_buf <= 16'h001F;
state <= S0;
end
S0 : begin // 初始化
cnt_1us_clear <= 1'b1;
TEMP_buf <= 1'b0;
state <= S1;
end
S1 : begin
cnt_1us_clear <= 1'b0;
if (cnt_1us == 20'd500) // 延时500us
begin
cnt_1us_clear <= 1'b1;
TEMP_buf <= 1'bZ; // 释放总线
state <= S2;
end
end
S2 : begin
cnt_1us_clear <= 1'b0;
if (cnt_1us == 20'd100) // 等待100us
begin
cnt_1us_clear <= 1'b1;
state <= S3;
end
end
S3 : if (~TEMP) // 若18b20拉低总线,初始化成功
state <= S4;
else if (TEMP) // 否则,初始化不成功,返回S0
state <= S0;
S4 : begin
cnt_1us_clear <= 1'b0;
if (cnt_1us == 20'd400) // 再延时400us
begin
cnt_1us_clear <= 1'b1;
state <= S5;
end
end
S5 : begin // 写数据
if (step == 6'd0) // 0xCC
begin
step <= step + 1'b1;
state <= WRITE0;
end
else if (step == 6'd1)
begin
step <= step + 1'b1;
state <= WRITE0;
end
else if (step == 6'd2)
begin
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= WRITE01;
end
else if (step == 6'd3)
begin
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= WRITE01;
end
else if (step == 6'd4)
begin
step <= step + 1'b1;
state <= WRITE0;
end
else if (step == 6'd5)
begin
step <= step + 1'b1;
state <= WRITE0;
end
else if (step == 6'd6)
begin
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= WRITE01;
end
else if (step == 6'd7)
begin
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= WRITE01;
end
else if (step == 6'd8) // 0x44
begin
step <= step + 1'b1;
state <= WRITE0;
end
else if (step == 6'd9)
begin
step <= step + 1'b1;
state <= WRITE0;
end
else if (step == 6'd10)
begin
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= WRITE01;
end
else if (step == 6'd11)
begin
step <= step + 1'b1;
state <= WRITE0;
end
else if (step == 6'd12)
begin
step <= step + 1'b1;
state <= WRITE0;
end
else if (step == 6'd13)
begin
step <= step + 1'b1;
state <= WRITE0;
end
else if (step == 6'd14)
begin
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= WRITE01;
end
else if (step == 6'd15)
begin
step <= step + 1'b1;
state <= WRITE0;
end
// 第一次写完,750ms后,跳回S0
else if (step == 6'd16)
begin
TEMP_buf <= 1'bZ;
step <= step + 1'b1;
state <= S6;
end
// 再次置数0xCC和0xBE
else if (step == 6'd17) // 0xCC
begin
step <= step + 1'b1;
state <= WRITE0;
end
else if (step == 6'd18)
begin
step <= step + 1'b1;
state <= WRITE0;
end
else if (step == 6'd19)
begin
TEMP_buf <= 0;
step <= step + 1'b1;
state <= WRITE01;
end
else if (step == 6'd20)
begin
step <= step + 1'b1;
state <= WRITE01;
TEMP_buf <= 1'b0;
end
else if (step == 6'd21)
begin
step <= step + 1'b1;
state <= WRITE0;
end
else if (step == 6'd22)
begin
step <= step + 1'b1;
state <= WRITE0;
end
else if (step == 6'd23)
begin
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= WRITE01;
end
else if (step == 6'd24)
begin
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= WRITE01;
end
else if (step == 6'd25) // 0xBE
begin
step <= step + 1'b1;
state <= WRITE0;
end
else if (step == 6'd26)
begin
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= WRITE01;
end
else if (step == 6'd27)
begin
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= WRITE01;
end
else if (step == 6'd28)
begin
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= WRITE01;
end
else if (step == 6'd29)
begin
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= WRITE01;
end
else if (step == 6'd30)
begin
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= WRITE01;
end
else if (step == 6'd31)
begin
step <= step + 1'b1;
state <= WRITE0;
end
else if (step == 6'd32)
begin
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= WRITE01;
end
// 第二次写完,跳到S7,直接开始读数据
else if (step == 6'd33)
begin
step <= step + 1'b1;
state <= S7;
end
end
S6 : begin
cnt_1us_clear <= 1'b0;
if (cnt_1us == 20'd750000 | TEMP) // 延时750ms!!!!
begin
cnt_1us_clear <= 1'b1;
state <= S0; // 跳回S0,再次初始化
end
end
S7 : begin // 读数据
if (step == 6'd34)
begin
bit_valid <= 1'b0;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd35)
begin
bit_valid <= bit_valid + 1'b1;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd36)
begin
bit_valid <= bit_valid + 1'b1;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd37)
begin
bit_valid <= bit_valid + 1'b1;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd38)
begin
bit_valid <= bit_valid + 1'b1;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd39)
begin
bit_valid <= bit_valid + 1'b1;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd40)
begin
bit_valid <= bit_valid + 1'b1;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd41)
begin
bit_valid <= bit_valid + 1'b1;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd42)
begin
bit_valid <= bit_valid + 1'b1;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd43)
begin
bit_valid <= bit_valid + 1'b1;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd44)
begin
bit_valid <= bit_valid + 1'b1;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd45)
begin
bit_valid <= bit_valid + 1'b1;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd46)
begin
bit_valid <= bit_valid + 1'b1;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd47)
begin
bit_valid <= bit_valid + 1'b1;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd48)
begin
bit_valid <= bit_valid + 1'b1;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd49)
begin
bit_valid <= bit_valid + 1'b1;
TEMP_buf <= 1'b0;
step <= step + 1'b1;
state <= READ0;
end
else if (step == 6'd50)
begin
step <= 1'b0;
state <= S0;
end
end
//++++++++++++++++++++++++++++++++
// 写状态机
//++++++++++++++++++++++++++++++++
WRITE0 :
begin
cnt_1us_clear <= 1'b0;
TEMP_buf <= 1'b0; // 输出0
if (cnt_1us == 20'd80) // 延时80us
begin
cnt_1us_clear <= 1'b1;
TEMP_buf <= 1'bZ; // 释放总线,自动拉高
state <= WRITE00;
end
end
WRITE00 : // 空状态
state <= S5;
WRITE01 : // 空状态
state <= WRITE1;
WRITE1 :
begin
cnt_1us_clear <= 1'b0;
TEMP_buf <= 1'bZ; // 输出1 释放总线,自动拉高
if (cnt_1us == 20'd80) // 延时80us
begin
cnt_1us_clear <= 1'b1;
state <= S5;
end
end
//--------------------------------
// 写状态机
//--------------------------------
//++++++++++++++++++++++++++++++++
// 读状态机
//++++++++++++++++++++++++++++++++
READ0 : state <= READ1; // 空延时状态
READ1 :
begin
cnt_1us_clear <= 1'b0;
TEMP_buf <= 1'bZ; // 释放总线
if (cnt_1us == 20'd10) // 再延时10us
begin
cnt_1us_clear <= 1'b1;
state <= READ2;
end
end
READ2 : // 读取数据
begin
temperature_buf[bit_valid] <= TEMP;
state <= READ3;
end
READ3 :
begin
cnt_1us_clear <= 1'b0;
if (cnt_1us == 20'd55) // 再延时55us
begin
cnt_1us_clear <= 1'b1;
state <= S7;
end
end
//--------------------------------
// 读状态机
//--------------------------------
default : state <= S00;
endcase
end
end
assign TEMP = TEMP_buf; // 注意双向口的使用
//--------------------------------------
// DS18B20状态机 结束
//--------------------------------------
endmodule
//**********************************************************************************//
二、定制IP在SOPC Builder中的设定
在Quartus II软件安装目录下建立自己的IP文件夹,<Quartus II软件安装目录>\altera\90\ip里面建立一个MyIP的文件夹,
在文件夹在建立一个DS18B20文件夹,我们把上面代码COPY下来做成Verilog代码的文件avalon_slave_DS18B20.v,
在Quartus II建立工程,打开SOPC Builder,点击File菜单下的New Component,选择HDL File的标签,点击Add
,<Quartus II软件安装目录>\altera\90\ip\MyIP\DS18B20\avalon_slave_DS18B20.v文件,在Signal标签里面的Temp
选择如下图:

Interface标签如下图:

Component Wizard标签:

所有Edit的沟都去掉,点击Finish即可!
三、SOPC Builder配置
不用多说,看看图片:

但会报错:

把Interface标签参数类型全都改为integer型就可
四、Quartus 工程文件

五、NIOS II C 代码
//**************************************************************************//
#include"stdio.h"
#include"system.h"
int main(void)
{
float temp;
volatile short *data = ( short * ) DS18B20_BASE;
while(1)
{
temp = (float) (*data / 16.0); //DS18B20 采用12位补码形式,读出数据直接除以16.0,不是16,不然就是小数部分了
printf("-DS18B20 SENSOR-\n**TEMP:%6.2f**\n",temp);
}
return 0;
}
//*************************************************************************//
浙公网安备 33010602011771号