FPGA课程设计论文
学生姓名周悦
学号20091321018
院系电子与信息工程学院
专业电子科学与技术
指导教师李敏
二O一二年5月28 日
基于FPGA的温度传感器系统设计
1引言
温度是一种最基本的环境参数,人们的生活与环境的温度息息相关,在工业生产过程中需要实时测量温度,在农业生产中也离不开温度的测量,因此研究温度的测量方法和装置具有重要的意义。测量温度的关键是温度传感器,温度传感器的发展经历了三个发展阶段:传统的分立式温度传感器;模拟集成温度传感器;智能集成温度传感器。目前,国际上新型温度传感器正从模拟式向数字式,从集成化向智能化、网络化的方向飞速发展。本文将介绍采用智能集成温度传感器DS18B20,并以FPGA为控制器的温度测量装置的硬件组成和软件设计,用液晶来实现温度显示。
2电路分析
系统框图如下:
第一部分:DS18B20温度传感器
美国 Dallas 半导体公司的数字化温度传感器 DS1820 是世界上第一片支持 "一线总线"接口的温度传感器,在其内部使用了在板(ON-B0ARD)专利技术。全部传感元件及转换电路集成在形如一只三极管的集成电路内。一线总线独特而且经济的特点,使用户可轻松地组建传感器网络,为测量系统的构建引入全新概念。现在,新一代的 DS18B20 体积更小、更经济、更灵活。使你可以充分发挥“一线总线”的优点。
DS18B20 的主要特性:(1)适应电压范围更宽,电压范围:3.0~5.5V,在寄生电源方式下可由数据线供电(2)独特的单线接口方式,DS18B20 在与微处理器连接时仅需要一条口线即可实现微处理器与DS18B20 的双向通讯(3)DS18B20 支持多点组网功能,多个DS18B20 可以并联在唯一的三线上,实现组网多点测(4)DS18B20 在使用中不需要任何外
围元件,全部传感元件及转换电路集成在
形如一只三极管的集成电路内(5)温范
围-55℃~+125℃,在-10~+85℃时精度
为±0.5℃(6)可编程的分辨率为 9~12
位,对应的可分辨温度分别为 0.5℃、
0.25℃、0.125℃和 0.0625℃,可实现高
精度测温(7)在 9 位分辨率时最多在
93.75ms 内把温度转换为数字,12 位分辨
率时最多在 750ms 内把温度值转换为数
字,速度更快(8)测量结果直接输
出数字温度信号,以"一线总线"串行传送
给 CPU,同时可传送 CRC 校验码,具有极
强的抗干扰纠错能力(9)负压特性:电源
极性接反时,芯片不会因发热而烧毁,但
不能正常工作。
DS18B20 引脚定义:
(1)DQ 为数字信号输入/输出端;
(2)GND 为电源地;
(3)VDD 为外接供电电源输入端
(在寄生电源接线方式时接地)。
DS18B20 中的温度传感器可完成
对温度的测量,以 12 位转化为例:
用 16 位符号扩展的二进制补码读数
形式提供,以 0.0625℃/LSB 形式表
达,其中 S 为符号位。
存储在 18B20 的两个 8 比特的
RAM 中,二进制中的前面 5 位是符号
位,如果测得的温度大于 0,这 5 位
为 0,只要将测到的数值乘于 0.0625 即可得到实际温度;如果温度小于 0,这 5 位为 1,测到的数值需要取反加 1 再乘于 0.0625 即可得到实际温度。
例如+125℃的数字输出为 07D0H,+25.0625℃的数字输出为 0191H,-25.0625℃的数字输出为 FF6FH,-55℃的数字输出为 FC90H。
第二部分 EPM240系列芯片
基本参数:
CPLD类型:闪存
宏单元数:192
输入/输出线数:80
传播延迟时间:5.9ns
整体时钟设定时间:2.7ns
频率:201.1MHz
电源电压范围:2.375V to 2.625V,
3V to 3.6V
工作温度范围:0°C to +85°C
针脚数:100
封装类型:TQFP
工作温度最低:0°C
工作温度最高:85°C
逻辑芯片功能:CPLD
逻辑芯片基本号:EPM240T
可编程逻辑类型:CPLD
输入/输出接口标准:LV TTL, LVCMOS, PCI
第三部分:LCD1602液晶显示屏
液晶显示器以其微功耗、体积小、显示内容丰富、超薄轻巧的诸多优点,在袖珍式仪表和低功耗应用系统中得到越来越广泛的应用。
1602 采用标准的 16 脚接口,其中:
第 1 脚:VSS 为地电源
第 2 脚:VDD 接 5V 正电源
第 3 脚:V0 为液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个 10K 的电位器调整对比度
第 4 脚:RS 为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。
第5脚:RW 为读写信号线,高电平时进行读操作,低电平时进行写操作。当 RS 和 RW 共同为低电平时可以写入指令或者显示地址,当 RS 为低电平 RW 为高电平时可以读忙信号,当 RS 为高电平 RW 为低电平时可以写入数据。
第 6 脚:E 端为使能端,当 E 端由高电平跳变成低电平时,液晶模块执行命令。
第 7~14 脚:D0~D7 为 8 位双向数据线。
它的读写操作、屏幕和光标的操作都是通过指令编程来实现的。
指令 1:清显示,指令码 01H,光标复位到地址 00H 位置
指令 2:光标复位,光标返回到地址 00H
指令 3:光标和显示模式设置 I/D:光标移动方向,高电平右移,低电平左移 S:屏幕上所有文字是否左移或者右移。高电平表示有效,低电平则无效
指令 4:显示开关控制。 D:控制整体显示的开与关,高电平表示开显示,低电平表示关显示 C:控制光标的开与关,高电平表示有光标,低电平表示无光标 B:控制光标是否闪烁,高电平闪烁,低电平不闪烁
指令 5:光标或显示移位 S/C:高电平时移动显示的文字,低电平时移动光标
指令 6:功能设置命令 DL:高电平时为 4 位总线,低电平时为 8 位总线N:低电平时为单行显示,高电平时双行显示 F: 低电平时显示 5x7 的点阵字符,高电平时显示 5x10 的点阵字符(有些模块是 DL:高电平时为 8 位总线,低电平时为 4 位总线)
指令 7:字符发生器 RAM 地址设置
指令 8:DDRAM 地址设置
指令 9:读忙信号和光标地址 BF:为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙。
指令 10:写数据
指令 11:读数据
电路连接如图所示~
2.2 软件设计
1、18b20驱动时序
程序采用C语言进行编写,主要完成LCD1602初始化、DS18B20的初始化、从DS18B20读取数据、向DS18B20写数据及温度转化等子程序的编写,此外在进行程序的编写时一定要严格保证读写时序的正确性,否则无法读取测温结果,其初始化时序和写时序的程序分别如下:
初始化时序包括一个主机发出的复位脉冲以及从机的应答脉冲这一过程,如图所示复位脉冲是一个480~960us的低电平然后释放总线,将总线拉至高电平,时间持续15~60us之后从机开始向总线发出一个应答脉冲,该脉冲是一个60~240us的低电平信号,表示从机已准备好,在初始化过程中主机接收脉冲的时间最少为480us。
DSl8820的访问步骤为:先进行初始化(Ini—tialization),然后发送ROM命令,最后发送Func—tion功能命令.对DSl8820访问必须通过上述步骤完成.初始化命令使主设备知道从设备是在总线上并可以进行访问.ROM命令可以使主设备知道有多少和什么类型的从设备连接在总线上并且判断是否超过温度报警值.共有5种ROM命令,分别是READROM(33H)、MATCH ROM(55H)、SEARCHROM(FOH)、SKIP ROM(CCH)、ALARM SEARCH命令(ECH).主设备可以通过功能命令来对DSl8820内部存储器进行读写.有6种功能命令,分别是CONVERT(44H)、WRITE SCRA TCHPAD(BEH)、READSCRA TCHPAD(4EH)、COPYSCRATCHPAD(48H)、RECALL E2(B8H)、读电源供电方式(B4H).DSl8820的单线通信功能是分时完成的,它有严格的时隙要求.
Library IEEE;
Use IEEE.Std_Logic_1164.All;
Entity wendu is
Port
(
led : out std_logic_vector(0 to 16);
wireout : inout std_logic;
clk : in std_logic;
rst : in std_logic );
End entity b;
Architecture init of wendu is
type init_states is (s00,s0,s1,s2,s3,s4,s5,s6,s7,w0,w1,w00,w01,read0,read1,read2,read3); signal state : init_states;
signal i : integer Range 0 to 1000000;
signal reset : std_logic;
begin
process (rst,wireout,clk)
--variable i : Integer Range 0 to 1000;
variable flag: Integer Range 0 to 200;
variable light: Integer Range 0 to 16;
begin
if rst = '0' then
state <= s00;
wireout <= 'Z';
flag:=0;
elsif rising_edge(clk) then
case state is
when s00=> flag:=0;
state <=s0;
when s0 => --初始化18b20
wireout <= '0';
--主机拉底总线
reset <='1';
state <=s1;
when s1 =>
reset <='0';
if (i = 500) then --延时500us
wireout <= 'Z'; --释放总线
reset <='1';
state <=s2;
end if;
when s2 => reset <='0';
if (i = 100) then --等待100us
reset <='1';
state <=s3;
end if;
when s3 => if (wireout = '0') then --若18b20拉低总线,初始化成功 state <=s4;
led(16)<='1'; --led16灯亮
elsif (wireout ='1') then --否则,初始化不成功,返回S0
state <= s0;
end if;
when s4 => reset<='0';
if (i = 400) then --再延时400us
reset<='1';
state <= s5;
end if;
when s5 => --写数据
if (flag = 0 ) then flag:=1;state <=w0; --cch
elsif (flag = 1 ) then flag:=2;state <=w0;
elsif (flag = 2 ) then flag:=3;state <=w01;wireout<='0';
elsif (flag = 3 ) then flag:=4;state <=w01;wireout<='0';
elsif (flag = 4 ) then flag:=5;state <=w0;
elsif (flag = 5 ) then flag:=6;state <=w0;
elsif (flag = 6 ) then flag:=7;state <=w01;wireout<='0';
elsif (flag = 7 ) then flag:=8;state <=w01;wireout<='0';
--第一次写完,750ms后,跳回s0
elsif (flag = 16 ) then flag:=20;state <=s6;wireout<='Z';
--再次置数cch和beh
elsif (flag = 20 ) then flag:=21;state <=w0; --cch
elsif (flag = 21 ) then flag:=22;state <=w0;
elsif (flag = 22 ) then flag:=23;state <=w01;wireout<='0';
elsif (flag = 23 ) then flag:=24;state <=w01;wireout<='0';
elsif (flag = 24 ) then flag:=25;state <=w0;
elsif (flag = 25 ) then flag:=26;state <=w0;
elsif (flag = 26 ) then flag:=27;state <=w01;wireout<='0';
elsif (flag = 27 ) then flag:=28;state <=w01;wireout<='0';
elsif (flag = 28 ) then flag:=29;state <=w0; --beh 10111110 elsif (flag = 29 ) then flag:=30;state <=w01;wireout<='0';
elsif (flag = 30 ) then flag:=31;state <=w01;wireout<='0';
elsif (flag = 31 ) then flag:=32;state <=w01;wireout<='0';
elsif (flag = 32 ) then flag:=33;state <=w01;wireout<='0';
elsif (flag = 33 ) then flag:=34;state <=w01;wireout<='0';
elsif (flag = 34 ) then flag:=35;state <=w0;
elsif (flag = 35 ) then flag:=36;state <=w01;wireout<='0';
--第二次写完,跳到s7,直接开始读数据
elsif (flag = 36 ) then flag:=40;state <=s7;
end if;
when s6 =>
reset<='0';
if (i = 750000 or wireout='1' ) then --延时750ms!!!!
state <= s0;
reset<='1'; --跳回s0,再次初始化
end if;
when s7 => --读数据
if (flag = 40 ) then flag:=41;state <=read0;light:=0; wireout<='0';
elsif (flag = 41 ) then flag:=42;state <=read0;light:=1; wireout<='0';
elsif (flag = 42 ) then flag:=43;state <=read0;light:=2; wireout<='0';
elsif (flag = 43 ) then flag:=44;state <=read0;light:=3; wireout<='0';
elsif (flag = 44 ) then flag:=45;state <=read0;light:=4; wireout<='0';
elsif (flag = 45 ) then flag:=46;state <=read0;light:=5; wireout<='0';
elsif (flag = 46 ) then flag:=47;state <=read0;light:=6; wireout<='0';
elsif (flag = 47 ) then flag:=48;state <=read0;light:=7; wireout<='0';
elsif (flag = 48 ) then flag:=49;state <=read0;light:=8; wireout<='0';
elsif (flag = 49 ) then flag:=50;state <=read0;light:=9; wireout<='0';
elsif (flag = 50 ) then flag:=51;state <=read0;light:=10;wireout<='0';
elsif (flag = 51 ) then flag:=52;state <=read0;light:=11;wireout<='0';
elsif (flag = 52 ) then flag:=53;state <=read0;light:=12;wireout<='0';
elsif (flag = 53 ) then flag:=54;state <=read0;light:=13;wireout<='0';
elsif (flag = 54 ) then flag:=55;state <=read0;light:=14;wireout<='0';
elsif (flag = 55 ) then flag:=60;state <=read0;light:=15;wireout<='0';
elsif (flag = 60 ) then flag:=0;state<=s0;
end if;
when w0 => wireout<='0'; --输出0
reset<='0';
if (i = 80) then --延时80us
wireout<='Z'; --释放总线,自动拉高
reset<='1';
state<=w00;
end if;
when w00 => state<=s5; --空状态
when w01=> state<=w1; --空状态
when w1 => wireout<='Z'; --输出1 释放总线,自动拉高 reset<='0';
if (i = 80) then --延时80us
reset<='1';
state<=s5;
end if;
when read0=> state <= read1; --空延时状态
when read1=> wireout <= 'Z'; --释放总线
reset<='0';
if (i = 10) then --再延时10us
reset<='1';
state <= read2;
end if;
when others =>
state <=s00;
end case;
end if;
end process;
process(clk,reset)
begin
if (reset='1')then
i<=0;
elsif rising_edge(clk) then i<=i+1;
end if;
end process;
end architecture init;
2、液晶显示程序
library ieee;
use IEEE.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity lcd1602 is
port(clk, reset: in std_logic;
LCD_Data: out std_logic_vector(8 downto 0);
en: out std_logic;
rw: out std_logic);
end lcd1602;
architecture gongneng of lcd1602 is
signal LCD_Clk: std_logic;
signal s : integer range 0 to 10000000;
begin
--50MHz to 5Hz
process(clk, reset)
begin
if reset = '0' then
LCD_Clk <= '0';
s <= 0;
elsif clk'event and clk = '1' then
if s = 10000000 then
s <= 0;
LCD_Clk <= not LCD_Clk;
else
s <= s + 1;
end if;
end if;
end process;
rw <='0';
en <= LCD_Clk;
process(LCD_Clk)
variable cnt: std_logic_vector(4 downto 0):="00000";
begin
if Reset='0'then
LCD_Data<="000000001"; -- Reset清屏
--LCD_Data[8..0], 其中LCD_Data[8]对应1602的RS,--LCD_Data[7..0]对应1602的八根数据线
cnt:="00000"; --计数器清零
elsif rising_edge(LCD_Clk) then
if cnt<"01111" then cnt:=cnt+1;
else cnt:="00000";
end if;
--设计计数器,每次计数间隔LCD_CLK定义的一个周期
case cnt is
when "00000"=>LCD_Data<="000111000";--/*设置8位格式,2行,5*7*/ ,顺序2,3的要求when "00001"=>LCD_Data<="000001100"; --/*整体显示,关光标,光标闪烁/ ,顺序4的要求
when "00010"=>LCD_Data<="000000001";--清屏,顺序5的要求
when "00011"=>LCD_Data<="000000110"; --/*显示移动格式,看最后两位,10表示光标右移,顺序6的要求
when "00100"=>LCD_Data<="010000000";--设定显示的位置在00H+80H,即显示屏第一行第一个位置,顺序7的要求
when "00101"=>LCD_Data<="101000011"; --顺序8的要求,上一步指定位置处显示字符C
when others =>LCD_Data<="101000100";
end case;
end if;
end process;
end gongneng;