FPGA开发——DS18B20读取温度并且在数码管上显示

一、简介

        在上一篇文章中我们对于DS18B20的相关理论进行了详细的解释,同时也对怎样使用DS18B20进行了一个简单的叙述。在这篇文章我们通过工程来实现DS18B20的温度读取并且实现在数码管伤显示。

1、基本实现思路

根据不同时刻的操作,我们可以使用一个状态机来实现温度的读取。

如图所示,通过 初始化——>ROM指令写入——>温度转换命令写入——>二次初始化——>二次ROM指令写入——>温度读取命令写入——>温度读取等一系列操作就可以实现DS18B20的温度读取。

2、状态转换框图

将实现思路划分成6个状态,从而减少代码量。

二、工程实现

1、设计文件的编写

(1)DS18B20

        新建一个ds18b20.v文件,如下:在这里需要用到的计数器非常多,如果对于每个状态都使用一个计数器的话,所占用的资源就会非常多,这里我们可以根据状态进行划分,实现同一个计数器在不同状态进行不同计数的方法,也就是分时复用的思路对于计数器进行带编写。并且在实现代码中,我们将跳过ROM指令和温度转换指令写在一起,将跳过ROM指令和温度读取指令写在一起,通过同一个位宽的bit计数器就可以进行命令的写入。

module ds18b20_driver( input				clk		        ,input				rst_n	        ,//ds182b20 portinout               dq              ,//单总线(双向口)//user portoutput  reg         sign            ,//符号位,正数为0,负数为1output  reg [19:0]  dout       ,output  reg         dout_vld   
);								 
//-------------<参数定义>---------------------------------------------------------
//状态机参数定义localparam  INIT        = 6'b0000_01,//主机初始化状态:主机发送复位脉冲-->主机释放总线-->主机接收存在脉冲(ds18b20接收到存在脉冲后拉低总线)WRCMD       = 6'b0000_10,//主机发送跳过ROM命令状态WAIT        = 6'b0001_00,//等待温度转换完成状态INIT_AGAIN  = 6'b0010_00,//二次初始化RDCMD       = 6'b0100_00,//主机发送读取温度命令状态RDTEM       = 6'b1000_00;//主机读取温度(2字节)状态//时间参数定义parameter   TIME_1US = 50;//1usparameter   TM_INIT = 720   ,//初始化时间750us:主机发送复位脉冲至少480us(取500us)-->主机释放总线15-60us(取20us)-->主机接收存在脉冲60-240us(取200us)TM_LOW  = 2     ,//读写时隙,拉低总线至少1us(取2us)TM_60US = 60    ,//读写时隙至少60usTM_3US  = 3     ,//连续两个读或写时隙至少间隔1us的恢复时间TM_WAIT = 750_000 ;//温度转换最大750ms(精度12bits)//命令参数定义localparam  CMD_SKROM = 8'hCC,//跳过ROM命令CMD_CONVE = 8'h44,//温度转换命令CMD_RDCMD = 8'hBE;//读取温度命令//-------------<内部信号定义>-----------------------------------------------------wire                dq_in       ;//双向端口输入reg                 dq_out      ;//双向端口输出reg                 dq_out_en   ;//双向端口输出使能reg     [5:0]       state_c     ;//状态机现态reg     [5:0]       state_n     ;//状态机次态reg		[7:0]	    cnt_1us	    ;//1us基准计数器wire				add_cnt_1us ;wire				end_cnt_1us ;reg     [19:0]      xx          ;reg		[19:0]	    cnt_xx 	    ;//复用计数器wire				add_cnt_xx  ;wire				end_cnt_xx  ;reg		[4:0]	    cnt_bit	    ;//比特计数器wire				add_cnt_bit ;wire				end_cnt_bit ;reg                 presen_pulse;//存在脉冲reg                 flag        ;//标志信号     发起温度转换,为0;读取温度,为1reg     [15:0]       wr_data     ;//写入数据(命令)reg     [15:0]      rd_data     ;//读出的温度数据wire                init2wrcmd      ;wire                wrcmd2wait      ;wire                wait2initagain   ;wire                initagain2rdcmd  ;wire                rdcmd2rdtem     ;wire                rdtem2init      ;assign  dq = dq_out_en ? dq_out : 1'bz;//输出使能为1时输出;为0时高阻态assign  dq_in = dq                    ;//高阻态时,将双向总线的数据赋值给输入//--state_c(三段式状态机)always @(posedge clk or negedge rst_n)begin if(!rst_n)beginstate_c <= INIT;end else begin state_c <= state_n;end endalways @(*) begincase(state_c)INIT : beginif(init2wrcmd)begin state_n = WRCMD;endelse begin state_n = state_c;endendWRCMD : beginif(wrcmd2wait)begin state_n = WAIT;endelse begin state_n = state_c;endendWAIT : beginif(wait2initagain)begin state_n = INIT_AGAIN;endelse begin state_n = state_c;endendINIT_AGAIN : beginif(initagain2rdcmd)begin state_n = RDCMD;endelse begin state_n = state_c;endendRDCMD : beginif(rdcmd2rdtem)begin state_n = RDTEM;endelse begin state_n = state_c;endendRDTEM : beginif(rdtem2init)begin state_n = INIT;endelse begin state_n = state_c;endenddefault : state_n = INIT;endcaseendassign init2wrcmd      = (state_c == INIT      ) && end_cnt_xx && !presen_pulse;assign wrcmd2wait      = (state_c == WRCMD     ) && end_cnt_bit ;assign wait2initagain  = (state_c == WAIT      ) && end_cnt_xx;assign initagain2rdcmd = (state_c == INIT_AGAIN) && end_cnt_xx && !presen_pulse;assign rdcmd2rdtem     = (state_c == RDCMD     ) && end_cnt_bit;assign rdtem2init      = (state_c == RDTEM     ) && end_cnt_bit;//--cnt_1usalways @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_1us <= 'd0;end else if(add_cnt_1us)begin if(end_cnt_1us)begin cnt_1us <= 'd0;endelse begin cnt_1us <= cnt_1us + 1'b1;end endelse cnt_1us <= cnt_1us;end assign add_cnt_1us = 1'b1;assign end_cnt_1us = add_cnt_1us && cnt_1us == TIME_1US - 1;//--cnt_xxalways @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_xx <= 'd0;end else if(add_cnt_xx)begin if(end_cnt_xx)begin cnt_xx <= 'd0;endelse begin cnt_xx <= cnt_xx + 1'b1;end endend assign add_cnt_xx = end_cnt_1us;assign end_cnt_xx = add_cnt_xx && cnt_xx == xx - 1;//xx  always @(posedge clk or negedge rst_n)begin if(!rst_n)beginxx <= 'd0;end else if((state_c == INIT) ||(state_c == INIT_AGAIN))begin xx <= TM_INIT;end else if(state_c == WAIT)beginxx <= TM_WAIT;endelse begin                      //除了INIT和WAIT两个状态,其它几个状态都在读或写(IDLE状态未考虑,计数器在IDLE状态下不工作)xx <= TM_LOW + TM_60US + TM_3US;//读写1bit数据需要的时间end end//--cnt_bitalways @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bit <= 'd0;end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0;endelse begin cnt_bit <= cnt_bit + 1'b1;end endend assign add_cnt_bit = end_cnt_xx && (state_c == WRCMD || state_c == RDCMD || state_c == RDTEM);assign end_cnt_bit = add_cnt_bit && cnt_bit == 16-1;//--presen_pulsealways @(posedge clk or negedge rst_n)begin if(!rst_n)beginpresen_pulse <= 'd0;end else if(((state_c == INIT) ||(state_c == INIT_AGAIN))&& (cnt_xx == 550) && (end_cnt_1us))begin presen_pulse <= dq_in;end end
//--wr_data always @(posedge clk or negedge rst_n)begin if(!rst_n)beginwr_data <= 'd0;end else if(state_c == WRCMD)begin wr_data <= 16'h44cc;end else if(state_c == RDCMD)begin wr_data <= 16'hbecc;endelse begin wr_data <= 'd0;end end
//--dq_out、dq_out_en always @(posedge clk or negedge rst_n)begin if(!rst_n)begindq_out_en <= 'd0;dq_out <= 'd0;end else if((state_c == INIT ||state_c == INIT_AGAIN) && (cnt_xx < 20'd500))begin dq_out_en <= 1'b1;dq_out <= 1'b0;end else if(state_c == WRCMD || state_c == RDCMD)begin if(cnt_xx <TM_LOW)begin dq_out_en <= 1'b1;dq_out <= 1'b0;endelse if(cnt_xx >= TM_LOW && cnt_xx < (TM_LOW+TM_60US))begin if(wr_data[cnt_bit] == 0)begin  //写0时隙dq_out_en <= 1'b1;dq_out <= 1'b0;endelse if(wr_data[cnt_bit] == 1)begin //写1时隙dq_out_en <= 1'b0;dq_out <= 1'b0;endendelse begin              //记住不能省略dq_out_en <= 1'b0;dq_out <= 1'b0;endendelse if(state_c == RDTEM)begin if(cnt_xx < TM_LOW)begin dq_out_en <= 1'b1;dq_out <= 1'b0;endelse begin dq_out_en <= 1'b0;dq_out <= 1'b0;endendelse begin dq_out_en <= 1'b0;dq_out <= 1'd0;endend//--rd_dataalways @(posedge clk or negedge rst_n)begin if(!rst_n)beginrd_data <= 'd0;end else if(state_c == RDTEM && cnt_xx == 20'd12 )begin rd_data[cnt_bit] <= dq_in;end end//--dout、dout_vld、signalways @(posedge clk or negedge rst_n)begin if(!rst_n)begindout <= 'd0;sign <= 'd0;end else if(state_c == RDTEM && end_cnt_bit)begin if(rd_data[15] == 1'b0)begin //读出的温度数据机器码符号位为0,温度为正dout <= rd_data[10:0]*20'd625;sign <= 1'b0;endelse begin                  //读出的温度数据机器码符号位为1,温度为负 dout <= (~rd_data[10:0] + 1'b1)*20'd625;  //考虑位宽溢出的问题sign <= 1'b1;endend endalways @(posedge clk or negedge rst_n)begin if(!rst_n)begindout_vld <= 1'b0;end else dout_vld <= rdtem2init;endendmodule

(2)数码管

新建一个seg_driver.v文件,如下:

module seg_driver(input clk,input rst_n,input [23:0] din,input        sign,output reg [5:0] sel,output reg [7:0] dig);
//参数定义
parameter TIME_1MS  =50_000;
localparam    ZERO  =7'b100_0000,ONE   =7'b111_1001,TWO   =7'b010_0100,TEREE =7'b011_0000,FOUR  =7'b001_1001,FIVE  =7'b001_0010,SIX   =7'b000_0010,SEVEN =7'b111_1000,EIGHT =7'b000_0000,NINE  =7'b001_0000,P     =7'b000_1100,N     =7'b100_1000,NULL  =7'b111_1111;//内部信号定义
reg  [15:0]   cnt_1ms;
wire          add_cnt_1ms;
wire          end_cnt_1ms;reg    [5:0]  seg_r;
reg           flag;//小数点标志位
reg    [23:0] dis_data;
always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_1ms <= 'd0;end else if(add_cnt_1ms)begin if(end_cnt_1ms)begin cnt_1ms <= 'd0;endelse begin cnt_1ms <= cnt_1ms + 1'b1;end end
end assign add_cnt_1ms =1'b1 ;
assign end_cnt_1ms = add_cnt_1ms && cnt_1ms == TIME_1MS-1;//数码管位选
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginsel <= 6'b111_110;end else if(end_cnt_1ms)begin sel <={sel[4:0],sel[5]};end 
end
//小数点位置 0为亮,1为灭
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginflag <= 'd1;end else begin case (sel)6'b110_111:flag <= 1'b0; //第三个数码管位置default: flag<= 'd1;endcaseend 
end//数码管显示数据分布
always @(posedge clk or negedge rst_n)begin if(!rst_n)begindis_data <= 'd0;end else begin case (sel)6'b111_110:dis_data <= din[7:4];6'b111_101:dis_data <= din[11:8];6'b111_011:dis_data <= din[15:12];6'b110_111:dis_data <= din[19:16];6'b101_111:dis_data <= din[23:20];6'b011_111:dis_data <=(sign ?4'ha:4'hb) ; default: ;endcaseend 
end//数据显示
always @(posedge clk or negedge rst_n)begin if(!rst_n)begindig <= NULL;end else begin case (dis_data)0   :dig<={flag,ZERO };1   :dig<={flag,ONE  };2   :dig<={flag,TWO  };3   :dig<={flag,TEREE};4   :dig<={flag,FOUR };5   :dig<={flag,FIVE };6   :dig<={flag,SIX  };7   :dig<={flag,SEVEN};8   :dig<={flag,EIGHT};9   :dig<={flag,NINE };4'ha:dig<={flag,N    };4'hb:dig<={flag,P    };default:dig<= NULL;endcaseend 
end
endmodule

2、数据处理文件的编写

当我们进行温度读取时,需要对原始数据进行处理之后采用将其显示在数码管中,所以这里还需要一个处理数据文件进行数据转换,新建一个data.v文件,如下:

module data(input  clk,input rst_n,input [19:0] din,input        din_vld,output   [23:0] dout
);
wire  [7:0]  data_r_int;
wire  [15:0]  data_r_float;
reg  [3:0]   data_r1;
reg  [3:0]   data_r2;
reg  [3:0]   data_r3;
reg  [3:0]   data_r4;
reg  [3:0]   data_g ;
reg  [3:0]   data_s ;
reg          din_vld_r;
assign  data_r_int=din/10000;
assign  data_r_float=din%10000;always @(posedge clk or negedge rst_n)begin if(!rst_n)begindin_vld_r   <= 'd0;end else din_vld_r <= din_vld;
end
always @(posedge clk or negedge rst_n)beginif(!rst_n)begindata_r1<='d0;data_r2<='d0;data_r3<='d0;data_r4<='d0;data_g <='d0;data_s <='d0;endelse if(din_vld_r)begindata_s <=data_r_int/10;data_g <=data_r_int%10;data_r1<=data_r_float/1000;data_r2<=data_r_float/100%10;data_r3<=data_r_float/10%10;data_r4<=data_r_float%10;endend
assign dout={data_s,data_g,data_r1,data_r2,data_r3,data_r4};
endmodule

3、顶层文件的编写

通过编写一个顶层文件将三个.v文件整合在一起,新建一个top.v文件,如下:

module top (input       clk     ,input       rst_n   ,inout       dq      ,output      tx ,output   [5:0] sel,output   [7:0] dig          
);
wire  [23:0] dis_data;//温度数值
wire   sign;   //符号位
wire   sign_out;
wire  [19:0]   dout    ;
wire     dout_vld;seg_driver seg_driver_inst(/*input            */ .clk   (clk)          ,/*input            */ .rst_n (rst_n)        ,/*input [23:0]     */ .din   (dis_data )    ,/*input            */ .sign  (sign)         ,/*output reg [5:0] */ .sel   (sel)          ,/*output reg [7:0] */ .dig   (dig));data data_inst(/*input          */ .clk    (clk),/*input          */ .rst_n  (rst_n),/*input [19:0]   */ .din    (dout),/*input          */ .din_vld(dout_vld),/*output   [23:0]*/ .dout   (dis_data)
);ds18b20_driver  ds18b20_driver_inst(/* input              */. clk      (clk     )  ,/* input              */. rst_n    (rst_n   )  ,/* input              */. dq       (dq   )  ,//输入信号/* output  reg  [19:0]*/. dout     (dout    )  ,//串行发送出去的数据/* output             */. dout_vld (dout_vld)  ,/* output             */. sign     (sign    )  //发送一字节完成信号
);
endmodule

4、在线调试

因为这里需过用modelsim进行仿真的话需要编写大量的测试文件代码才能进行仿真,所以我们直接采取Signal Tap进行在线调试的方式进行波形抓取。

通过在线抓取,经过调试之后,我们可以直接看到温度的值,通过下板验证可以看到数码管显示的值和抓取过程的值是一样的。在数码管中因为我们要显示一个符号位,所以值保留3位小数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/407453.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TCP与UDP传输的学习

void *memset(void *s, int c, size_t n); 功能&#xff1a;将一块内存空间的每个字节都设置为指定的值&#xff1b;这个函数通常用于初始化一个内存空间&#xff0c;或者清空一个空间&#xff1b; 参数&#xff1a;viod * s 空类型指针&#xff0c;指向要填充内存块&#xf…

代码随想录训练营 Day37打卡 动态规划 part05 完全背包理论基础 518. 零钱兑换II 377. 组合总和 Ⅳ 卡码70. 爬楼梯(进阶版)

代码随想录训练营 Day37打卡 动态规划 part05 一、完全背包理论基础 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品都有无限个&#xff08;也就是可以放入背包多次&#xff09;&#xff0c;求解将哪些物品装…

Arco Design,字节跳动出品的UI库

Arco Design是字节跳动出品的UI库&#xff0c;支持Vue和React。还是比较美观的。并且Arco Design还提供了中后台模版。但是通过提供的arco-cli连接了github&#xff0c;正常情况下无法构建。但效果还是挺好的&#xff0c;下面是效果图&#xff1a; 更新&#xff1a; 传送门可…

【C++ Primer Plus习题】2.3

问题: 解答: #include <iostream> using namespace std;void print01() {cout << "三只眼瞎的老鼠" << endl; }void print02() {cout << "看他们怎么跑" << endl; }int main() {print01();print01();print02();print02();r…

Linux:Linux多线程

目录 线程概念 什么是线程 二级页表 线程的优点 线程的缺点 线程异常 线程用途 Linux进程VS线程 进程和线程 进程的多个线程共享 进程和线程的关系 Linux线程控制 POSIX线程库 线程创建 线程等待 线程终止 分离线程 线程ID及进程地址空间布局 线程概念 什么…

VS Code 远程连接SSH服务

随着技术的不断迭代更新&#xff0c;在 Linux 系统中使用 Vim、nano 等基于 Shell 终端的编辑器&#xff08;我曾经也是个 vimer&#xff0c;但是 VS Code 实在太香了&#xff09;&#xff0c;已经很难适应当下的开发效率。因此大多数开发者开始使用 VS Code 远程连接 Linux 系…

博物馆地图导览:利用GIS与蓝牙定位技术,融合语音解说功能

引言 亲爱的技术员、开发者朋友们&#xff0c;随着科技的不断进步&#xff0c;博物馆等文化场所的导览方式也在不断创新。今天&#xff0c;我将为大家介绍我们的新产品——博物馆地图导览系统&#xff0c;该系统集成了GIS&#xff08;地理信息系统&#xff09;、蓝牙定位技术以…

xss靶场详解

目录 1.第一题 2.第二题 3.第三题 4.第四题 5.第五题 6.第六题 7.第七题 8.第八题 1.第一题 在源码script标签里边&#xff0c;innerhtml是用于访问或修改 HTML 元素内的 HTML 内容的&#xff0c;这里是访问spaghet这个元素的&#xff0c;并通过括号里面的东西搜索当前…

【图机器学习系列】(二)从传统机器学习角度理解图(一)

微信公众号&#xff1a;leetcode_algos_life&#xff0c;代码随想随记 小红书&#xff1a;412408155 CSDN&#xff1a;https://blog.csdn.net/woai8339?typeblog &#xff0c;代码随想随记 GitHub: https://github.com/riverind 抖音【暂未开始&#xff0c;计划开始】&#xf…

面试题详解

前言&#xff1a;这一期我们专门来巩固所学知识&#xff0c;同时见识一些面试题。对知识做出一个总结。 1 不创建临时变量交换两个整数 . 第一种方法 #include<stdio.h> int main() {int a 0;int b 0;scanf("%d %d", &a, &b);printf("交换前…

深度学习 --- VGG16卷积核的可视化(JupyterNotebook实战)

VGG16卷积核的可视化 在前一篇文章中&#xff0c;我对VGG16输入了一张图像&#xff0c;并实现了VGG16各层feature map的可视化。深度学习 --- VGG16各层feature map可视化(JupyterNotebook实战)-CSDN博客文章浏览阅读615次&#xff0c;点赞13次&#xff0c;收藏15次。在VGG16模…

Linux三剑客-sedawk

一、三剑客-sed命令 1、格式 sed 找谁干啥 文件 找谁:条件&#xff0c;匹配哪一行&#xff0c;哪些行. 干啥:动作&#xff0c;增删改查. #显示文件的第3行 sed -n 3p /etc/passwd选项说明-n取消默认输出-p查找-rsed支持扩展正则-i修改文件内容&#xff0c;这个选项放在最后…

【C++ Primer Plus习题】3.5

问题: 解答: #include <iostream> using namespace std;int main() {long long populationWorld 0;long long populationChina 0;cout << "请输入全球的人口数:";cin >> populationWorld;cout << "请输入中国的人口数:";cin &g…

XSS- - - DOM 破坏案例与靶场

目录 链接靶场&#xff1a; 第一关 Ma Spaghet 第二关 Jefff 第三关 Ugandan Knuckles 第四关 Ricardo Milos 第五关 Ah Thats Hawt 第六关 Ligma 第七关 Mafia 第八关 Ok, Boomer 链接靶场&#xff1a; XS…

GenAI 的产品:快速行动,但失败

2022 年秋季&#xff0c;我正在做一个很酷的项目。是的&#xff0c;你猜对了——使用公司特定的数据对预先训练的 LLM&#xff08;Bert&#xff09;进行微调。 然而&#xff0c;很快 ChatGPT 就发布了&#xff0c;并席卷了全世界。既然已经有一门非常强大的 LLM 了&#xff0c…

ARM——驱动——inmod加载内核模块

在上一篇文章的代码上添加出错处理 #include <linux/init.h> // 包含初始化宏和函数 #include <linux/kernel.h> // 包含内核函数和变量 #include <linux/fs.h> // 包含文件操作的结构和函数 #include <linux/kdev_t.h> /…

PyTorch——transforms

接着上一篇&#xff0c;我们这一篇讲transforms 1、什么是transform 首先transform是来自PyTorch的一个扩展库——【torchvision】&#xff0c;【torchvision】这个库提供了许多计算机视觉相关的工具和功能&#xff0c;能够在神经网络中&#xff0c;将图像、数据集、预处理模型…

【系统架构设计】软件架构设计(1)

【系统架构设计】软件架构设计&#xff08;1&#xff09; 软件架构概述架构需求与软件质量属性软件架构风格数据流风格批处理序列管道-过滤器2者风格比较 仓库风格--黑板系统 层次系统架构风格二层及三层C/S架构风格MVCMVP 面向服务的架构 软件架构概述 基于架构的软件开发模型…

网络通信tcp

一、udp案例 二、基于tcp: tcp //c/s tcp 客户端: 1.建立连接 socket bind connect 2.通信过程 read write close tcp服务器: 1.建立连接 socket bind listen accept 2.通信过程 read write close connect函数 int connect(int sockfd, con…

postgresql 集群文档

https://www.cnblogs.com/Alicebat/p/14148933.html [命令] Pacemaker 命令 pcs cluster &#xff08;管理节点&#xff09; – Eternal Center PostgreSQL实战之物理复制和逻辑复制&#xff08;五&#xff09;_postgresql 流复制和物理复制-CSDN博客 https://jingyan.baidu…