FPGA驱动DS18B20温度传感器

FPGA驱动DS18B20温度传感器
先介绍一下DS18B20

DS18B20是一款广泛应用的高精度数字温度传感器。测温范围为-55℃到+125℃,采用1-Wire通信即仅采用一根数据线与微控制器进行通信。

温度存储在ROM中,占据2byte数据,其中第11位用于判断温度正负,其他各位的权重如图所示。正数即为本身,负数为补码加一

温度转换如图,温度值的分辨率是0.0625,将对应的十进制数*0.0625即为实际的摄氏度值

7D0h的十进制是 2000,对应的温度值是 2000 * 0.0625 = 125°C

FE6F的补码 + 1 对应的十进制数是 -401,对应的温度是是 -401 * 0.0625 = -25.0625°C

几个重要的指令,本次实验使用到这几个寄存器,如果有多个设备,建议阅读DS18B20规格书

44H : 温度转换

BEH : 读取温度寄存器

4EH : 写温度寄存器

CCH : 跳过设备选址,只适用于总线上只有一个设备

单总线时序

复位时序

读写时序

实验介绍

本次实验包含三个部分:

PLL锁相环:用于实现1us时钟的分频

DS18B20驱动模块:用于控制DS18B20的温度采集

数码管显示模块

PLL锁相环(此处也可以使用分频器实现)

生成1Mhz时钟,一个时钟周期是1us

DS18B20驱动模块

输入 : 1us时钟,单总线,复位

输出 : 数据位,符号位

驱动过程

开始 → 初始化 → 发送转换命令 → 等待转换 → 重新初始化 → 发送读取命令 → 读取温度数据 → 数据处理 → 结束

1.初始化 : 拉低500us,松开总线,等待70us,检测总线状态(如果总线有设备,总线会被拉低),总线低电平,输出存在从机脉冲flag_pulse,跳转到发送状态

2.发送状态 : 包含跳过ROM(CCh)命令和温度转换命令(44h),(低位在前,即44CC)一共需要发送16个bit。

添加bit计数器bit_cnt,写0/1都是65us,

写0,拉低63us,释放总线2us

写1,拉低15us,释放总线50us

先拉低1us,检测bit位为0/1,0的话一直拉低,1的话释放总线,等待2us,

当bit_cnt == 15,并且cnt_1us计数器等于64,进入等待状态

3.等待状态 : 等待750ms,即计数器记到750_000,进入重新初始化状态

4.重新初始化 : 完进入接收状态

5.接收状态 : 包含跳过ROM(CCH)和温度读取命令(BE),低位在前,即BECC,

之后进入读取温度状态

6.读取温度状态 : 读数据,拉低1us总线,13us处采样电平值,每次将总线的电平值给data_temp的最高位,当读完16bit数据,判断最高位是0/1,即正负,正数不操作,复数取反码加一,符号位同样是读最高位。

7.读完之后回到初始状态

代码如下:

module ds18b20_ctrl ( input wire clk_1us, //1us的时钟 input wire sys_rst_n,//复位 inout wire dq, //单总线,用于和ds18b20交换数据, 双向 output reg sign, // 输出数据的符号 output wire [19:0] data_out //输出的温度数据 ); parameter ERROR = 3'd0; parameter INIT = 3'd1; parameter WRITE = 3'd2; parameter WAIT = 3'd3; parameter INIT_AGAIN= 3'd4; parameter READ = 3'd5; parameter GET_TEMP = 3'd6; parameter MAX_WAIT_TIME = 20'd749_999; //等待时间 parameter TIME_ONE_WRITE_READ = 7'd64; //一次读写的时间,64us parameter WRITE_DATA = 16'h44CC; parameter READ_DATA = 16'hBECC; reg [2:0] State; reg [19:0] cnt_1us; //计数器,每个时钟计数器增加一 reg flag_pulse; //用于判断初始化时有没有收到从机回应 reg [3:0] bit_cnt; // 计算读写的bit数 reg dq_en; //总线使能控制位 reg [15:0] data_temp; //直接读取的寄存器的数据 reg [19:0] data; //判断正负后的数据 reg dq_out; assign dq = (dq_en == 1'b1) ? dq_out : 1'dz; //只有总线使能位为1时,总线才受FGPA控制 assign data_out = data * 10'd625 / 4'd10; //数码管只有六位,只显示小数点后三位,点加在第四个数码管上面 //计数器 cnt_1us //清零情况 1.两种初始化状态结束 2.读写完一次 3.等待时间结束 always @(posedge clk_1us or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) cnt_1us <= 1'b0; else if(((State == INIT || State == INIT_AGAIN) && (cnt_1us == 999)) || ((State == WRITE || State == READ || State == GET_TEMP) && (cnt_1us == TIME_ONE_WRITE_READ)) || (State == WAIT && cnt_1us == MAX_WAIT_TIME)) cnt_1us <= 1'b0; else cnt_1us <= cnt_1us + 1'b1; end //flag_pulse 用于判断初始化时有没有收到从机回应,即dq == 0 //flag_pulse是在1000us时清零 always @(posedge clk_1us or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) flag_pulse <= 1'b0; else if((State == INIT || State == INIT_AGAIN) && cnt_1us == 20'd570 && dq == 1'b0) flag_pulse <= 1'b1; else if(cnt_1us == 999) flag_pulse <= 1'b0; else flag_pulse <= flag_pulse; end //bit_cnt 计算收发的bit位数 always @(posedge clk_1us or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) bit_cnt <= 4'd0; else if((State == WRITE || State == READ || State == GET_TEMP) && cnt_1us == TIME_ONE_WRITE_READ && bit_cnt == 4'd15) //读写完一次清零 bit_cnt <= 4'd0; else if((State == WRITE || State == READ || State == GET_TEMP) && cnt_1us == TIME_ONE_WRITE_READ) bit_cnt <= bit_cnt + 1'b1; else bit_cnt <= bit_cnt; end //表示状态变化 always @(posedge clk_1us or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) State <= INIT; else case (State) INIT: //570us检测到从机存在(flag_pulse == 1)切换到WRITE begin if(cnt_1us == 999 && flag_pulse == 1'b1) State <= WRITE; else State <= INIT; end WRITE: //发送写指令,一共需要发送16bit数据,当全部发完进入等待状态,1bit数据用时65us begin if(bit_cnt == 4'd15 && cnt_1us == TIME_ONE_WRITE_READ) State <= WAIT; else State <= WRITE; end WAIT: begin if(cnt_1us == MAX_WAIT_TIME) State <= INIT_AGAIN; else State <= WAIT; end INIT_AGAIN: begin if(cnt_1us == 999 && flag_pulse == 1'b1) State <= READ; else State <= INIT_AGAIN; end READ: //发送读命令,一共需要发送16bit数据,当全部发完进入读取温度状态,1bit数据用时65us begin if(bit_cnt == 4'd15 && cnt_1us == TIME_ONE_WRITE_READ) State <= GET_TEMP; else State <= READ; end GET_TEMP: //读取温度,读取总线上的温度值,一共读取16bit,当全部发完进入初始化状态,1bit数据用时65us begin if(bit_cnt == 4'd15 && cnt_1us == TIME_ONE_WRITE_READ) State <= INIT; else State <= GET_TEMP; end default: State <= ERROR; endcase end //控制总线 dq,dq_en,data_temp always @(posedge clk_1us or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) begin dq_en <= 1'b0; dq_out <= 1'b0; end else case (State) INIT: begin if(cnt_1us <= 20'd500) //初始化先拉低500us begin dq_en <= 1'b1; dq_out <= 1'b0; end else begin dq_en <= 1'b0; dq_out <= 1'b0; end end WRITE: begin if(cnt_1us <= 20'd1) //写总线需要先拉低1us begin dq_en <= 1'b1; dq_out <= 1'b0; end else if(cnt_1us >= 20'd62) //释放总线,恢复时间,至少1us,此处是2us begin dq_en <= 1'b0; dq_out <= 1'b0; end else if(WRITE_DATA[bit_cnt] == 1'b0) // 写0 begin dq_en <= 1'b1; dq_out <= 1'b0; end else if(WRITE_DATA[bit_cnt] == 1'b1) // 写1 begin dq_en <= 1'b0; dq_out <= 1'b0; end end WAIT: //为适应寄生电容供电 begin dq_en <= 1'b1; dq_out <= 1'b1; end INIT_AGAIN: begin if(cnt_1us <= 20'd500) //初始化先拉低500us begin dq_en <= 1'b1; dq_out <= 1'b0; end else begin dq_en <= 1'b0; dq_out <= 1'b0; end end READ: begin if(cnt_1us <= 20'd1) //写总线需要先拉低1us begin dq_en <= 1'b1; dq_out <= 1'b0; end else if(cnt_1us >= 20'd62) //释放总线,恢复时间,至少1us,此处是2us begin dq_en <= 1'b0; dq_out <= 1'b0; end else if(READ_DATA[bit_cnt] == 1'b0) // 写0 begin dq_en <= 1'b1; dq_out <= 1'b0; end else if(READ_DATA[bit_cnt] == 1'b1) // 写1 begin dq_en <= 1'b0; dq_out <= 1'b0; end end GET_TEMP: begin if(cnt_1us <= 20'd1) //写总线需要先拉低1us begin dq_en <= 1'b1; dq_out <= 1'b0; end else //1us之后就释放总线,在第13us处采样总线值 begin dq_en <= 1'b0; dq_out <= 1'b0; end end default: begin dq_en <= 1'b0; dq_out <= 1'b0; end endcase end //data_temp,data,sign 读取的数据,判断正负的数据,符号位 always @(posedge clk_1us or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) begin data_temp <= 16'd0; end else if(State == GET_TEMP && cnt_1us == 20'd13) data_temp <= {dq,data_temp[15:1]}; else if(State == GET_TEMP && bit_cnt == 4'd15 && cnt_1us == TIME_ONE_WRITE_READ) begin if(data_temp[12] == 1'b1) //负数 begin data <= ~data_temp[10:0] + 1'b1; sign <= 1'b1; end else if(data_temp[12] == 1'b0) begin data <= data_temp[10:0]; sign <= 1'b0; end end end endmodule

数码管显示模块此处不介绍

绑定引脚:

实现现象如图(图中为野火征途开发板) :

Read more

【实战教程】MATLAB GUI实现多算法雷达CFAR检测:从原理到可视化分析

1. 什么是雷达CFAR检测? 雷达恒虚警检测(CFAR)是雷达信号处理中的一项核心技术,简单来说就是在复杂多变的噪声环境中,始终保持稳定的目标检测能力。想象一下你在一个嘈杂的派对上试图听清朋友的谈话,CFAR就像是你的大脑自动调节"听力阈值"的过程——当环境噪音变大时,你会不自觉地提高注意力阈值;当环境安静时,又能降低阈值捕捉细微声音。 在雷达系统中,CFAR技术通过动态调整检测门限来实现这个功能。传统固定门限检测在噪声变化时要么漏检目标(门限过高),要么产生大量误报(门限过低)。而CFAR算法能够根据周围环境的噪声水平,实时计算出最合适的检测门限值。 MATLAB GUI实现的最大优势在于可视化交互。通过图形界面,我们可以直观地看到: * 原始噪声信号的波形特征 * 不同CFAR算法计算出的动态门限曲线 * 目标检测结果的标记位置 * 算法在不同信噪比下的表现差异 2. CFAR核心算法原理解析 2.1 均值类CFAR算法 均值类算法是CFAR家族中最基础的成员,其核心思想可以用"邻里比较"来理解。就像通过比较周围房屋的价格来评估某处房产价值一样,这些算法通

By Ne0inhk
【AirSim 教程指南】Part 4:无人机物理引擎与动力学模拟(碰撞、风场、传感器噪声、飞行动力学)

【AirSim 教程指南】Part 4:无人机物理引擎与动力学模拟(碰撞、风场、传感器噪声、飞行动力学)

AirSim 教程指南——Part 4:无人机物理引擎与动力学模拟(碰撞、风场、传感器噪声、飞行动力学) 🚁 面向准备进行 真实无人机仿真、控制算法验证(MPC / PID / RL) 的开发者 🌪 全面解析 AirSim 如何模拟无人机的动力学、环境扰动与噪声模型 🎯 让你的仿真结果“更像真机”,避免算法过拟合虚拟环境 📌 目录 1. AirSim 中的无人机动力学:从六自由度到推进模型 2. 碰撞系统:检测、事件回调与恢复策略 3. 风场模型:风向、风速、阵风、湍流 4. 噪声系统:传感器噪声、动力噪声、IMU 噪声 5. 真机一致性:如何校准仿真让它接近你的无人机 1. AirSim 中的无人机动力学:

By Ne0inhk
【Microi吾码】 发现Microi吾码:低代码世界的超级英雄 ‍

【Microi吾码】 发现Microi吾码:低代码世界的超级英雄 ‍

🚀 发现Microi吾码:低代码世界的超级英雄 🦸‍♂️ 目录 🚀 发现Microi吾码:低代码世界的超级英雄 🦸‍♂️ 🌟 无拘无束的创作空间 🌈 跨平台跨数据库的无缝体验 代码示例:跨数据库连接 🚀 分布式架构的轻松部署 代码示例:Docker部署 🎨 界面自定义与SaaS引擎的完美结合 代码示例:自定义界面 ⚙️ 表单和接口引擎的高效协同 代码示例:接口引擎使用V8脚本 🔒 工作流和权限控制的精细管理 代码示例:工作流引擎配置 🔐 单点登录与移动端开发的便捷性 代码示例:单点登录集成 🏁 结语 作为一名对技术充满热情的业务分析师,我一直在寻找一个能够快速实现创意、满足我们多样化业务需求的平台。🔍 在这个快速变化的数字世界中,我找到了Microi吾码——一个开源的低代码平台,它以其卓越的性能和灵活性,成为了我日常工作中的得力助手。👩‍💻💼 🌟 无拘无束的创作空间 在我使用Microi吾码之前,我常常受限于平台的各种使用限制,比如用户数、表单数等。Microi吾码的无限制使用政策让我彻底摆脱了这些束缚。💥

By Ne0inhk

NotoSansSC-Regular.otf介绍与下载

总体概述 NotoSansSC-Regular.otf 是 “思源黑体” 家族中用于简体中文的常规字重(Regular)的 OpenType 字体文件。它是由 Adobe 与 Google 合作领导开发的一款开源字体,旨在作为一款“全能型”字体,满足各种场景下的中文显示需求。 核心特点详解 1. 名称含义 * Noto: 名称源于“No Tofu”(没有豆腐)。其目标是消除在计算机上因缺少对应字体而显示的空白方块(俗称“豆腐块”☐),实现“无豆腐”的全球文字支持。 * SansSC: “Sans” 表示无衬线体,“SC” 代表“简体中文”。所以 NotoSansSC 就是“用于简体中文的无衬线字体”。 * Regular: 指字体的字重为“常规”或“正常”,不是细体(Light)

By Ne0inhk