引言
温度是工、农业生产的主要环境因素,它对工农业的生产有着重要的影响,所以对其进行准确快速的测量并进行处理是具有很特殊的重要意义。测量温度模块是否正确、快速、及时决定着工农业生产的产品的质量是否符合预期设计的要求,而在当今是世界中,质量是企业生存的基本,所以许多对温度要求很高的工、农业生产企业把温度测量控制系统看成是企业生存的基本。
在市场竞争日益激烈的压力面前,企业已经意识到要想提升自己企业的竞争能力,就必须采用新技术对企业产品进行质量等各方面的管理。而对温度要求严格的产品,测量产品生产环境的温度的技术决定着系统能否生存并继续发展,所以很多工业生产企业都非常重视温度控制系统。
对温度进行采集,并送到PC机实时处理,以前往往采用的是计算机的串行接口,但这一老式计算机接口存在着不少缺陷。比如接口规格不统一、非共享式接口、占用资源大等等这样那样的缺点。随着计算机的发展,USB(通用串行总线)接口是一种计算机应用领域的新型接口,USB接口的出现是计算机端接口的技术重大变革,其最早是由Compaq、Digital Equipment、IBM、Intel、Microsoft 、NEC和Northern Telecom 7家公司于1994年提出的,在2000年做出了改进,提出了USB2.0版本,随着这一版本的提出,USB越来越流行,目前它已经成为了一种标准接口,现在市场上的所有PC机都百分之百支持USB接口,本文将采用这一USB接口来对采集到的温度实时传输。
基于USB接口的温度控制系统就是通过温度传感器对现场温度进行测量,并利用微型处理器(单片机),通过USB接口实时的传给计算机,计算机利用软件根据已经设计好的要求对其进行报警等相应的处理。它是计算机技术、单片机技术、温度传感器技术的科技产物。适合于工业等需要对温度进行实时大范围的精确的处理。
1 背景知识
在USB产生之前,外设与PC机的通信主要是通过PC机主板上所提供的各种接口来
实现,如ISA接口、PCI接口、串行接口等,这些老式的接口存在着很多缺点:非共享式接口、体积大、接口规格不统一、采用传统的I/O模式等等。为了克服老式接口这些缺点,PC机制造商和用户迫切需要一种新型的外设连接方式。这时USB应运而生,它是一种快速、快速、双向、同步、廉价、并支持热插拨功能的串行接口。
早在1995年,就已经有PC机带有USB接口了,但由于缺乏软件及硬件设备的支持,这些PC机的USB接口都闲置未用。1998年后,随着微软在Windows 98中内置了对USB接口的支持模块,加上USB设备的日渐增多,USB接口才逐步走进了实用阶段。
这几年,随着大量支持USB的个人电脑的普及,USB逐步成为PC机的标准接口已经是大势所趋。在主机(host)端,最新推出的PC机几乎100%支持USB;而在外设(device)端,使用USB接口的设备也与日俱增,例如数码相机、扫描仪、游戏杆、磁带和软驱、图像设备、打印机、键盘、鼠标等等。正是USB具有热插拔、共享式接口、携带方便、标准统一、可以连接多个设备等这样的优点,才使USB得于快速的发展。
本文将利用这一新型计算机接口来设计温度控制系统。
2 系统概述
本章将对基于USB接口的温度控制系统在工业上的应用进行分析,并介绍系统的特点、功能以及使用到的开发工具。
2.1 系统的特性
由于该系统利用USB接口,所以具有USB的实时传送数据,与上位机进行信息交流,而上位机又可以连接在互联网上,所以远程的PC机也可以利用互联网对温度检测系统进行查看等各种操作。该系统利用先进的温度传感器,可以对温度快速的进行反应,把温度数据传到下位机进行初步处理数据,进而与上位机通信。总的来说,该系统有以下几点特点:
·工作人员可以远离生产环境通过计算机对其进行查看处理;
·多点温度测量;
·全天候检测温度,并可以在没有工作人员的参与下对生产环境进行简单处理;
·对生产环境的温度进行设计极限温度,一旦超过极限温度,系统将对起进行报警,并停止生产环境的工作;
·测量温度误差比较精确,在0.5℃内;
2.2 系统的功能
该系统主要有以下功能:
(1)对温度进行检测。利用该系统可以远离恶劣生产环境的情况下,对其进行温度测量;
(2)对现场温度进行实时采集;并在PC机上显示出来;
(3)在PC机上实时做出温度图像;工作人员在电脑上便可以直观的得到系统温度图像;
(4)简单的系统控制;通过计算机上的应用软件可以对温度设置,一旦超过极限温度,发出报警,进而通知工作人员快速的对生产现场进行各种相应操作,这样可以防止温度超出极限温度;
(5)在上位机端的应用软件上提供系统使用帮助。用户可以利用该功能帮助对系统进行操作。
2.3 系统开发平台
本次系统需要用到的开发工具为:keil C、 C++ builder、Protel、计算机、烧写器。
Keil C是目前世界上最好的MC-51单片机的汇编和C语言的开发工具。支持汇编、c语言以及混合编程。同时具备功能强大的软件仿真和硬件仿真。C++ builder 是计算机高级语言C++比较好用的编程工具,它是属于一种可视化的计算机语言。 Protel是世界上最好的硬件电路图制作的工具。
3 系统总体设计
3.1 系统整体方框图
根据前面的分析,知道系统要实现以上功能,必须由以下几部分组成:温度采集单元、下位机温度初步处理单元、USB设备接口、上位机应用程序。
系统的结构原理图如图3.1:

图3.1系统整体实现的结构原理图
温度传感器单元对生产温度环境进行测量,将测量温度传给下位机(单片机),单片机对采集到的温度进行初步处理后,将处理了的数据通过USB接口上传给PC机上位机,而上位机将实时的显示采集到的温度,如果要对现场环境进行处理,则上位机可以发送命令,经过USB接口传送到下位机,下位机根据接受到的数据并对其进行分析,进而做出处理,如报警等各种操作。
3.2 系统方案比较
对于本系统,方案的选择是根据温度传感器来选择。
目前市场上有两种传感器:模拟传感器和数字集成传感器,对于选择不同的传感器将会有不同的方案。下面给出两种不同的方案,并对其进行分析,最终选择其中一种方案。
3.2.1系统两种可行方案
方案1:选择模拟传感器
所谓模拟传感器,简单的说就是传感器对被测量的物质感应,并随着检测的不同做出不同的反应,但这一反应是有规律的,而且有规律的输出模拟信号。由于单片机是数字信号系统,只能识别数字信号,所以这种方案要想利用单片机对温度信号进行处理,必须将对模拟输出量数字化,也就是说要对其输出的模拟电压或电流转换成数字信号,这么一来就系统要加入模拟信号转换成数字信号的处理单元,通常,实现这一功能的是A/D转换器,市场对于这一A/D转换器有不少类型。所以选择这一方案也是可以有效而快速的设计出本系统。
图3.2给出使用模拟传感器这一方案的设计原理图。

图3.2 方案1的结构原理图
方案2:选择数字传感器
今天随着计算机的飞速发展以及单片机的日益普及,世界进入了数字时代,人们在处理被测信号时首先想到的是信息处理器(单片机或计算机)。具有输出数字信号便于电脑处理的传感器就是所谓的数字传感器。
数字传感器是近几年才出现的并得到广泛的应在在实践当中,所谓数字传感器,进一步的讲,就是将模拟传感器产生的信号经过放大、A/D转换、线性化及量纲处理后变成纯粹的数字信号,是在模拟传感器上加入数字处理单元,并将数字单元集成在一块芯片上,所以输出的是数字信号,便于数字处理机对其直接进行处理。
图3.3给出利用数字传感器设计的方案图:

图3.3 方案2的结构原理图
3.2.2方案最终选择
以上已经提出了两种方案,下面对这两重方案进行分析,并做最后方案选择:
分析近几年来传感器的发展,我们知道传感器在未来的发展中将会向以下的方向发展:(1)向高精度发展。随着自动化生产程度的不断提高,对传感器的要求也在不断提高,必须研制出具有灵敏度高、精确度高、响应速度快、互换性好的新型传感器以确保生产自动化的可靠性。目前能生产精度在万分之一以上的传感器的厂家为数很少,其产量也远远不能满足要求。(2)向高可靠性、宽温度范围发展。传感器的可靠性直接影响到电子设备的抗干扰等性能,研制高可靠性、宽温度范围的传感器将是永久性的方向。提高温度范围历来是大课题,大部分传感器其工作范围都在-20℃~70℃,在军用系统中要求工作温度在-40℃~85℃范围,而汽车锅炉等场合要求传感器工作在-20℃~120℃,在冶炼、焦化等方面对传感器的温度要求更高,因此发展新兴材料(如陶瓷)的传感器将很有前途。(3)向微型化发展。各种控制仪器设备的功能越来越大,要求各个部件体积能占位置越小越好,因而传感器本身体积也是越小越好,这就要求发展新的材料及加工技术,目前利用硅材料制作的传感器体积已经很小。如传统的加速度传感器是由重力块和弹簧等制成的,体积较大、稳定性差、寿命也短,而利用激光等各种微细加工技术制成的硅加速度传感器体积非常小、互换性可靠性都较好。(4)向微功耗及无源化发展。传感器一般都是非电量向电量的转化,工作时离不开电源,在野外现场或远离电网的地方,往往是用电池供电或用太阳能等供电,开发微功耗的传感器及无源传感器是必然的发展方向,这样既可以节省能源又可以提高系统寿命。目前,低功耗损的芯片发展很快,如TI2702运算放大器,静态功耗只有1.5mA,而工作电压只需2~5V。(5)向智能化数字化发展。传感器一般都是非电量向电量的转化,工作时离不开电源,在野外现场或远离电网的地方,往往是用电池供电或用太阳能等供电,开发微功耗的传感器及无源传感器是必然的发展方向,这样既可以节省能源又可以提高系统寿命。目前,低功耗损的芯片发展很快,如TI2702运算放大器,静态功耗只有1.5mA,而工作电压只需2~5V。
由于模拟传感器,没有把模拟转化为数字处理单元集成在一块芯片上,这样必带来传感检测单元的体积大等各个方面的影响,再者,模拟转化成数字单元也需要单片机对其进行处理,这样会浪费时间、资源。所以这样一来我们知道数字传感器在未来必将取代模拟传感器。而且随着数字传感器的发展,现在市场上数字传感也不是那么贵,本次系统对温度精度的要求不是很高,所以选择方案二。
4 系统硬件设计
4.1 中央处理器----AT89C51
AT89C51由美国Atmel 公司生产的,是一种带4K字节闪烁可编程可擦除只读存储器(FPEROM—Falsh Programmable and Erasable Read Only Memory)的低电压,高性能CMOS8位单片机,该器件采用ATMEL高密度非易失存储器制造技术制造,与工业标准的MCS-51指令集和输出管脚相兼容。下文将对AT89C51单片机做简单介绍。
(1) AT89C51的特点
AT89C51具有以下几个特点:
①中央处理器CPU;
②AT89C51与MCS-51系列的单片机在指令系统和引脚上完全兼容;
③片内有4k字节在线可重复编程快擦写程序存储器;
④全静态工作,工作范围:0Hz~24MHz;
⑤三级程序存储器加密;
⑥128×8位内部RAM;
⑦32位双向输入输出线;
⑧两个十六位定时器/计数器
⑨五个中断源,两级中断优先级;
⑩一个全双工的异步串行口;
(2)AT89C51的结构图如图4.1
图4.1 89C51的结构图
由上图,知:AT89C51主要由CPU、存储器、I/O端口等几部分组成。
(3)AT89C51的功能描述
AT89C51是一种低损耗、高性能、CMOS八位微处理器,片内有4k字节的在线可重复编程、快速擦除快速写入程序的存储器,能重复写入/擦除1000次,数据保存时间为十年。它与MCA-51系列单片机在指令系统和引脚上完全兼容,不仅可完全代替MCS-51系列单片机,而且能使系统具有许多MCS-51系列产品没有的功能。
AT89C51可构成真正的单片机最小应用系统,缩小系统体积,增加系统的可靠性,降低系统的成本。只要程序长度小于4K,四个I/O口全部提供给用户。可用5V电压编程,而且擦写时间仅需10毫秒,仅为8751/87C51的擦除时间的百分之一,与8751/87C51的12V电压擦写相比,不易损坏器件,没有两种电源的要求,改写时不拔下芯片,适合许多嵌入式控制领域。工作电压范围宽(2.7V~6V),全静态工作,工作频率宽在0Hz~24MHz之间,比8751/87C51等51系列的6MHz~12MHz更具有灵活性,系统能快也能慢。
AT89C51芯片提供三级程序存储器加密,提供了方便灵活而可靠的硬加密手段,能完全保证程序或系统不被仿制。P0口是三态双向口,通称数据总线口,因为只有该口能直接用于对外部存储器的读/写操作。
(4)AT89C51引脚功能及说明
图4.2是AT89C51的引脚

图4.2
①口线:P0、P1、P2、P3口。
P0口是三态双向口,通称数据总线口,为一个8位漏级开路双向I/O口,每脚可吸收8TTL门电流。当P1口的管脚第一次写1时,被定义为高阻输入。P0能够用于外部程序数据存储器,它可以被定义为数据/地址的第八位。在FIASH编程时,P0 口作为原码输入口,当FIASH进行校验时,P0输出原码,此时P0外部必须被拉高。因为只有该口能直接用于对外部存储器的读/写操作。P0口也用以输出外部存储器的低8位地址。由于是分时输出,故应在外部加锁存器将此地址数据锁存,地址锁存信号用ALE。
P1口是专门供用户使用的I/O口,是准双向口。P1口是一个内部提供上拉电阻的8位双向I/O口,P1口缓冲器能接收输出4TTL门电流。
P2口是从系统扩展时作高8位地址线用。不扩展外部存储器时,P2口也可以作为用户I/O口线使用,P2口也是准双向口。
P3口是双功能口,该口的每一位均可独立地定义为第一I/O功能或第二功能。作为第一功能使用时操作同P1口。P3口的第二功能如表4.1所示。
②控制口线:PSEN(片外选取控制)、ALE(地址锁存控制)、EA(片外存储器选择)、RESET(复位控制);
③电源及时钟:VCC、VSS、XTAL1、XTAL2 操作方法。
表4.1 P3口的第二功能

④操作方法
程序存储器加密。AT89C51芯片程序存储器有三级硬件加密,能够有效地保证系统不被仿制和软件不被复制,加密等级设置见附录2。
工作模式。AT89C51有间歇和掉电两种工作模式。间歇模式是由软件来设置的,当外围器件仍然处于工作状态时,CPU可根据工作情况适时地进入睡眠状态,内部RAM和所有特殊的寄存器值将保持不变。这种状态可被任何一个中断所终止或通过硬件复位。
掉电模式是VCC电压低于电源下限,振荡器停止振动,CPU停止执行指令。该芯片内RAM和特殊功能寄存器值保持不变,直到掉电模式被终止。只有VCC电压恢复到正常工作范围而且在振荡器稳定振荡后,通过硬件复位掉电模式可被终止。
4.2 温度传感器DS18B20
温度传感器是该系统的测量器件,温度传感器的好坏直接影响到测量结果,所以本文将对温度传感器的选择详细介绍。根据本次设计论文的要求,包括精度要求等,经过分析,本文决定选择数字温度传感器DSB8B20。下面将给予介绍。
(1) DS18B20的概述
DS18B20是DALLAS公司生产的一线式数字温度传感器,具有3引脚TO-92小体积封装形式;温度测量范围为-55℃~+125℃,可编程为9位~12位A/D转换精度,测温分辨率可达0.0625℃,被测环境的温度用符号扩展的16位数字量方式串行输出;其工作电源既可在远端引入,也可采用寄生电源方式产生;多个DS18B20可以并联到3根或2根线上,CPU只需一根端口线就能与诸多DS18B20通信,占用微处理器的端口较少,可节省大量的引线和逻辑电路。以上特点使DS18B20非常适用于远距离多点温度检测系统。
(2)DS18B20的内部结构
图4.3是DS18B20的内部结构图

图4.3 DS18B20的内部结构
由图可知,DS18B20主要由4部分组成:64位ROM、温度传感器、非挥发的温
警触发器TH和TL、配置寄存器。DS18B20的管脚排列如图4.4所示,

图4.4 DS18B20的管脚排列
DS18B20的3个管脚说明如下:
DQ为数字信号输入/输出端。是漏极开路一线接口。也在寄生电源接线方式时,给设备提供电源。
GND为电源地。
VDD为外接供电电源输入端(在寄生电源接线方式时接地)。
DS18B20的64位ROM保存了设备的唯一序列码,是DS18B20的地址序列码,每一个DS18B20的地址序列码是不同的,这样就可以实现一根总线上挂接多个DS18B20。高速闪存(scratchpad)包括2个字节的温度寄存器。保存了温度传感器的数字输出。该闪存还提供了对上限(TH)和下限(TL)的超标报警寄存器、配置寄存器(一个字节)的访问。TH、TL和配置寄存器是EEPROM,所以系统掉电时可以保存数据。
DS18B20利用DALLAS的单总线控制协议,实现了利用单线控制信号在总线上进行通信。由于所有的设备通过漏极开路端(DQ脚)连在总线上,控制线需要一个大约5K上拉电阻。在这一总线控制系统中,微控制器通过唯一的64位地址序列码识别和访问总线上的器件。由于地址序列码不同,所以连接在总线上的DS18B20可以说是无限的 。
(3)DS18B20的寄存器
DS18B20存储器组织结构如表4.2所示:
表4.2 存储器的组织图
高速闪存(上电状态)
字节0 | 温度底字节 |
字节1 | 温度高字节 |
字节2 | 高温报警用户字节“1” |
字节3 | 低温报警用户字节“2” |
字节4 | 配置寄存器 |
字节5 | 保留(FFH) |
字节6 | 保留(0CH) |
字节7 | 保留(10H) |
字节8 | CRC校验 |
字节0和字节1分别包含温度寄存器的LSB和MSB,这些字节是只读的,字节2和字节3提供对TH(上限报警触发寄存器)和TL(下限报警触发寄存器)的访问,字节4包配置寄存数据,字节5、6和7保留做器件内部使用,不能被改写,当读时,这些字节返回全1值,字节8是只读的,含有字节0到字节的CRC校验。
高速闪存的第四个字节包含配置寄存器,其组织结构如下表4.3所示:
表4.3 配置寄存器
Bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
表4.4 测温分辨率配置
R1 | R1 | 分辨率 | 最大转换时间 |
0 | 0 | 9bit | 93.75ms |
0 | 1 | 10bit | 187.5ms |
1 | 0 | 11bit | 375ms |
1 | 1 | 12bit | 750ms |
(4)DS18B20的读写时序
访问DS18B20的顺序如下:
----初始化;
----ROM命令(接着是任何需要的数据交换);
----DS18B20的函数命令(接着是任何需要的数据交换);
每一次访问DS18B20时必须要按照这一顺序,如果其中的任何一个步骤缺少或打乱,DS18B20将不会响应。
①初始化时序
初始化时序如图4.5:

图4.5 DS18B20初始化时序
所有与DS18B20的通信都要首先初始化,从而才能进行下一部的工作:控制器发出复位脉冲,DS18B20以存在脉冲响应。图4.5给出了描述。当DS18B20发出存在脉冲对复位响应时,它指示控制器该DS18B20已经在总线上并准备好操作。
②读/写时序
控制器在写时序到数据到DS18B20,在读时序从DS18B20中读数据,每一个总线时序传送一个数据位。
读/写时序见下图4.6

(a)写时序

(b)读时序
图4.6 DS18B20的工作时序图
③写时序
有两种类型的写时序:写1时序和写0时序。控制器用写“1”时序写逻辑“1” 到DS18B20,用写“0”时序写逻辑“0”到DS18B20。所有写时序必须持续60μs,每一个写时序之间必须要至少有1μs的恢复时间。两种类型的写时序都从控制器把总线拉低开始。
为产生写“1”的时序,在将总线拉低之后,总线控制器必须在15μs内释放总线。总线释放后,5K的上拉电阻将总线电平抬高。为产生写“0”时序,在总线拉低后,控制器在整个时序内必须持续控制总线为低电平(至少60μs)。
DS18B20在控制器发出写时序后的15-60μs的时间内采样总线。如果在采样窗口期间总线为高,“1”就被写到DS18B20;如果在采样窗口期间为低电平,则“0”就被写入DS18B20。
④读时序
当总线发出读时序时,DS18B20可以发送数据到控制器。所有读时序必须持续最少60μs,每一个读时序之间必须有至少1μs的恢复时间。读时序从控制设备将总线拉低至少1μs后释放总线开始。控制器启动读时序后,DS18B20开始在总线上传送“1”或者“ 0”。DS18B20通过保持总线为高发送“1”,将总线拉低发送“0”。发送“0”时,DS18B20在60μs时释放总线; 发送“1” 时,总线被上拉电阻高电平空闲状态。从DS18B20输出的数据在启动时序的下降沿后15μs有效。因此,控制器必须在时序开始的15μs内释放总线,然后采样总线状态。
通过读/写时序,控制器可以发出控制命令,对DS18B20进行读写操作。
(5)DS18B20的常用命令
①SKIP ROM [CCH]
控制器可以用这一命令同时访问总线上的所有设备而不需要发送ROM序列码信息。控制器可以使总线上的所有DS18B20同时进行温度转换。
②SEARCH ROM [F0]
当系统开始上电时,控制器必须识别总线上所有从机的ROM序列码,以确定从机的数目和它们的类型。控制器需要执行 search ROM循环足够多次才能识别所有的从设备。如果只有一个从属设备在总线上,可使用简单的Read ROM命令期待Search ROM。每一个Search ROM命令之后必须返回到事务序列的步骤(初始化)。
③READ ROM[33]
这一命令只有在总线上只有一个设备的时候使用,它使得控制器可以不用Search ROM命令就可以读出从机的64位ROM序列码。当多于一个从机设备在总线上时,如果还使用该命令,由于所有的设备企图响应该设备,这样将产生数据冲突。
④CONVRTT [44]
这一命令开始一次温度转换。变换之后,数据保存在暂存器的2个字节温度寄存器中,DS18B20回到低功耗空闲状态。如果设备工作在寄存电压模式,则这一命令发送后10μs之内,整个变换期间控制器必须在总线上能够有较强的上拉。如果DS18B20由外部电源供电,那么Convert T命令后控制器可以发出读时序。如果温度变换正在进行,那么返回“0”;如果已经完毕,则返回“1”。
⑤WRITE SCTATCHPAD [4EH]
这一命令使得控制器可以写3个字节数据到DS18B20的寄存器中。第一字节数据
到TH寄存器中,第二字节写到TL中,第3字节写到配置寄存器中。数据以最低有效位先发送。所有3字节必须在控制器发出复位或者数据丢失之前写完。
⑥READ SCRTCHPAD[BEH]
这一命令使得控制器可以读寄存器的内容。数据传送开始于字节0的最低位,直到寄存器的第9字节被读出,任何时候,如果只需部分寄存器数据,控制器可以结束读操作。
上面已经介绍了数字温度传感器的原理,下面将利用DS18B20设计温度采集模块电路。
4.3 温度采集模块电路设计
以上已经介绍了AT89C51和数字温度传感器DS18B20基本知识,下面将利用它们来设计本系统的温度采集电路。DS18B20连接到单片机的方法很简单,它有两种方法连接到电路上,既外接电源方式和寄生电源方式,这里使用的是系统提供的外接电源方式,而不采用寄生电源,只要VCC、DQ、GND连接到单片机的电源正极、一个I/O端口、电源地就可以了。但是要注意的是在DQ数据线中要加一个4.7K的上拉电阻,这一个是必须要加的,无论它是接在P1口还是P0口,这点特别注意,特别提醒。之外在电源两端之间加个0.01U的电容,这样的作用主要是滤波。

图4.7 单片机和DS18B20接口图
由图4.7,知DS18B20工作在外部电源供电方式。单片机采用采用P1.1口与DS18B20通信。下面根据单片机的初始化时序和读写时序,写出DS18B20和单片机之间的读写操作,这里只给了温度的读取,下位机部分程序在附录中给出。
这里特别提醒的是DS18B20对时序要求很高,精度要求很高,所以程序的延时对是否能读起数据起到非常关键的作用。
DS18B20读写数据程序如下:
void delaym(int time) //延时为(time*2+2)us
{ int s;
for(s=0;s<time; s++)
}
void write_bite(unit8 bite) //写一位数据位
{ DQ=0;
if(bite==1)
DQ=1; //如果写"1",DQ=1;
delaym(29);//延时60us提供DS18B20采样
DQ=1; //释放DQ
}
unit8 read_bite(void) //读一位数据位
{ DQ=0; //将总线DQ拉低开始读时序
delaym(0); //延时2us
DQ=1; //释放DQ;
delaym(1); //延时4us后再读数据
return(DQ);
}
void write_byte(unit8 dat) //写一字节数据
{ unit8 i;
unit8 temp;
for(i=0;i<8;i++)
{ temp=dat>>1; //右移一位
temp&=0x01;
write_bite(temp);
}
}
unit8 read_byte(void)
{ unit8 i,value=0;
for(i=0;i<8;i++)
{ if(read_bite())
value|=0x01<<i;//读一字节数据,一个读时序读一位,并做移位
delaym(29) ; //延时60us有以完成读一位,之后再读下一位
}
return(value);
}
unit8 DS18B20_RESET(void)
{ unit8 data;
DQ=0;
delaym(239); //保持DQ低480us
DQ=1;
delaym(35);
da=DQ;
delaym(211);
return (data);//有芯片应答data=0,无则data=1
}
4.4 系统接口模块电路设计
本系统的采集模块采集到数据后,必须要经过CH372传到上位机应用软件才能实现控制下位机的各种操作。而CH372是USB接口芯片,下位机和上位机通信的要通过CH372接口芯片来完成,其和计算机的连接很简单,所以这一部分主要的硬件实现是CH372和单片机AT89C51的连接问题。
4.4.1 USB简介
USB(Universal Serial Bus)是外围设备与计算机进行连接的新型接口,既一种新型的通用串型总线接口,USB具有即插即用、热插拨、接口体积小、节省系统资源、传输可靠、提供电源、良好的兼容性、共享试通信等优点。
在USB产生之前,外部设备和计算机的通信主要是通过计算机主板所提供的各种接口,比如ISA接口、PCI接口、PS/2接口、串行接口,并行接口等,这些接口,存在这样那样的缺陷,比如接口规格不统一、不共享等为了克服上述外围设备的缺陷,P制造商和用户迫切需要一种新型的外设接口,USB正是在这样的环境下产生的,它是一种快速、双向、同步、廉价、并支持热插拨功能的串行接口。
USB是一种新型的接口,那么它必定有它的通信标准,也就是我们所说的协议,下面简单介绍USB的通信协议。
一般的,对终端用户来看,USB系统是USB设备连接到主机的简单连接,但对开放人员来说,这中连接可分为三个层次:功能层、USB设备层、USB总线接口层,且每一层都由主机和USB设备的不同功能模块组成。可以用下面的图型来形容。下图4.8是这种分层通信机制的简化。

图4.8 USB通信层次模型
由图,一个USB设备由三个功能模块组成:USB总线接口、USB逻辑设备、功能单元。USB总线接口是USB设备中的串行引擎(SIE);USB逻辑单元被看作是一个端点的集合;功能单元客户软件被看作接口的集合。
USB传输类型包括批量传输、同步传输、中断传输和控制传输,每种传输类型的传输速度、可靠性以及应用范围都不同。控制传输可靠性是最高的,但速度最慢;同步传输速度快,满足实时性,但可靠性低。在具体应用中,端点传输类型可根据传输速度和可靠性选择。
在USB通信协议中,主机取得绝对主动权利,设备只能是“听命令行事”,通过一定的命令格式(设备请求)完成通信。USB设备请求包括标准请求、厂商请求和设备类请求。设备的枚举是标准请求命令完成的;厂商请求是用户定义的请求;设备类请求是特定的USB设备类发出的请求,例如海量储存类、打印机类和HID(人机接口)类。固件编程中设备请求必须遵循一定的格式,包括请求类型、设备请求、值、索引和长度。
4.4.2 USB芯片选择
USB的传输速度可分为低速(1.5Mbps)、全速(12Mbps)和高速(480Mb/s),按传输速度来分,供选择的USB芯片类型主要有:低速(1.5Mbps)和全速(12Mbps),可选择Philips公司的PDIUSBD12和Cypress公司的EZ-USB2100系列以及国产的CH372芯片; 高速(480Mbps)可选Philips公司的ISP1581和Cypress公司的USB接口芯片CY7C68013。
本次系统要传输的速率比较少,可以所以选择全速的USB接口芯片,由于国产的芯片已经有所好转,再加上资料比较齐全,这次系统设计所选择的USB接口芯片是国产芯片CH372。
芯片介绍:
CH372是南京沁恒电子有限公司生产的新型USB接口芯片,具有8位数据总线和读、写、片选控制线以及中断输出,可以方便地挂接到单片机/DSP/MCU/MPU等控制器的系统总线上;它屏蔽了USB通信协议,用户如果没有涉及到底层开放,那么只要了解芯片的普通用法就可以快速的设计USB设备。利用CH372进行USB设备的通信设计可以如下的方框图4.9
图4.9 CH372和单片机框架连接
下面给出CH372的引脚图(图4.10)及部分命令:
图4.10 CH372的管脚
CH372芯片占用两个地址位,当A0 引脚为高电平时选择命令端口,可以写入命令;当A0引脚为低电平时选择数据端口,可以读写数据。单片机通过8 位并行口对CH372 芯片进行读写,所有操作都是由一个命令码、若干个输入数据和若干个输出数据组成,部分命令不需要输入数据,部分命令没有输出数据。命令操作步骤如下:
①在A0=1 时向命令端口写入命令代码;
②如果该命令具有输入数据,则在A0=0 时依次写入输入数据,每次一个字节;
③如果该命令具有输出数据,则在A0=0 时依次读取输出数据,每次一个字节;
④命令完成,可以暂停或者转到①继续执行下一个命令。
CH372的命令如表4.5:
表 4.5 CH372命令
代码 | 命令名称 | 输入数据 | 输出数据 | 命令用途 |
05H | RESET_ALL | | (等40mS) | 执行硬件复位 |
06H | CHECK_EXIST | 任意数据 | 按位取反 | 测试工作状态 |
12H | SET_USB_ID | VID 字节 PID 字节 | | 设置USB的厂商VID和PID |
15H | SET_USB_MODE | 模式代码 | (等20uS) 操作状态 | 设置USB工作模式 |
22H | GET_STATUS | | 中断状态 | 获取中断状态并取消请求 |
28H | RD_USB_DATA | | | 从当前USB中断的端点缓冲区读取数据块并释放当前缓冲区 |
2AH | WR_USB_DATA5 | | | 向USB端点1的上传缓冲区写入数据块 |
2BH | WR_USB_DATA7 | | | 向USB端点2的上传缓冲区写入数据块 |
4.4.3 USB设备端的接口设计
CH372芯片在本地端提供了通用的被动并行接口,包括:8位双向数据总线D7~D0、读选通输入引脚RD#、写选通输入引脚WR#、片选输入引脚CS#、中断输出引脚INT#以及地址输入引脚A0。通过被动并行接口,CH372 芯片可以很方便地挂接到各种8位单片机、DSP、MCU 的系统总线上,并且可以与多个外围器件共存。CH372芯片的RD#和WR#可以分别连接到单片机的读选通输出引脚和写选通输出引脚。CS#由地址译码电路驱动,用于当单片机具有多个外围器件时进行设备选择。INT#输出的中断请求是低电平有效,可以连接到单片机的中断输入引脚或者普通I/O 引脚,单片机可以使用中断方式或者查询方式获知中断请求。当WR#为高电平并且CS#和RD#及A0 都为低电平时,CH372 中的数据通过D7~D0输出;当RD#为高电平并且CS#和WR#及A0 都为低电平时,D7~D0上的数据被写入CH372 芯片中;当RD#为高电平并且CS#和WR#都为低电平而A1 为高电平时,D7~D0 上的数据被作为命令码写入CH372芯片中。CH372 芯片的VD+和VD-引脚应该直接连接到USB 总线上。如果为了芯片安全而串接保险电阻或者电感,那么交直流等效串联电阻应该在5Ω之内。
USB定义了四种传输类型:控制传输、快传输、中断传输和同步传输。南京沁恒公司的USB接口芯片CH372支持控制传输、批量传输、中断传输。CH372主要有两种数据流传输方式:单向数据流方式和请求应答方式。其中请求应答方式步骤如下:
① 计算机应用层按事先约定的格式将数据请求发送给CH372芯片;
② CH372 芯片以中断方式通知单片机;
③ 单片机进入中断服务程序,获取CH372的中断状态并分析;
④ 如果是上传,则释放当前USB 缓冲区,然后退出中断程序;
⑤ 如果是下传,则从数据下传缓冲区中读取数据块;
⑥ 分析接收到的数据块,准备应答数据,也可以先退出中断程序再处理;
⑦ 单片机将应答数据写入批量端点的上传缓冲区中,然后退出中断程序;
⑧ CH372 芯片将应答数据返回给计算机;
⑨ 计算机应用层接收到应答数据。
这种传输方式是必需要有计算机断发出响应才可以进行的。在本次系统设计中,可以通过向计算机发出命令,下位机根据命令来进行各种操作,包括设计报警温度,采集温度等。
由于系统不需要那么多I/O口,所以本次系统设计的单片机端不需要扩展,直接用端片机的I/O端口,值得注意的是MCU不扩展,那么P0只能口只能作为I/O口使用,而不能作为数据/地址端口复用。这一点必须注意,而P0口做普通I/O使用必需要加上拉电阻1K到10K左右。否则系统将无法运行,这点特别提醒要注意的地方。
最终CH372与单片机AT89C51之间的连接图,如下图4.11

4.11 CH372和89C51的连接图
图中P2作为CH372芯片8位双向数据总线通信端口来使用。P1和P0端口作为普通的I/O口,USB 总线包括一对5V 电源线和一对(D+、D-)数据信号线。通常,+5V 电源线是红色,接地线是黑色,D+信号线是绿色,D-信号线是白色。USB 总线提供的电源电流最大可以达到500mA,一般情况下,低功耗的USB 产品可以直接使用USB 总线提供的5V 电源。如果USB 产品通过其它供电方式提供常备电源,那么CH372 应该与单片机一起使用该常备电源并且断开USB总线的电源;如果需要同时使用USB 总线的电源,那么可以通过阻值约为1Ω的电阻R1 连接USB 总线的5V 电源线与USB 产品的5V 常备电源,并且两者的接地线直接相连接。
CH372 的CS#固定为低电平,一直处于片选状态,在单片机程序中,可以控制各个I/O引脚模拟并口时序与CH372 进行数据交换。比如控制P05、P06、P07来读、写命令。
图4.11中,可选电阻R2 用于在电源断电后将电解电容C5 中的电能及时释放掉,使VCC 及时下降到0V,确保在下次通电时CH372 能够可靠地上电复位。电容C3 用于CH372 内部电源节点退耦,C3 是容量为0.01μF 的独石或高频瓷片电容,如果对EMI 没有要求那么可以省掉C3。电容C4和C5用于外部电源退耦,C4 是容量为0.1μF的独石或高频瓷片电容。晶体X1、电容C1 和C2 用于CH372的时钟振荡电路。X1的频率是12MHz,C1和C2是容量为15pF-30pF 的独石或高频瓷片电容。
本次系统设计的USB接口硬件设计已经完成。而且应用部分的硬件设计也已经完成,下节将给出整体硬件设计的电路图。
4.5 系统硬件整体实现
下位机的应用部分是DS18B20,DS18B20采集到温度信号,并转换成数字信号然后输出到单片机AT89C51,单片机对数字信号进行处理,然后将处理的数据通过USB接口芯片传到上位机的USB设备驱动程序,上位机的主USB控制驱动程序接受来自USB设备驱动程序的数据,在计算机端的应用软件进行各种操作。反之,计算机的控制命令通过USB总线接口,把控制命令通过USB芯片传到单片机,由于本系统已经对计算机程序的命令编码过,所以单片机接受来自上位机命令,对其进行判断,从而做出不同的响应。
硬件电路整体实现电路图在附件中给出。
5 系统软件设计
本系统进行软件设计包括下位机软件设计和上位机软件设计,下位机软件设计可以使用汇编语言和单片机C语言,上位机设计可以采用的很多计算机高级语言,比如VC++、Delphi、C++ builder等,在这里选择C++ builder来编写上位机程序。至于下位机程序,主要有汇编语言、PL/M语言和C语言。汇编语言有执行效率高、速度快、与硬件结合紧密等特点,尤其在I/O端口管理时,使用汇编语言有快捷、直观的优点。但是使用汇编语言相对于高级语言,比如单片机C语言来讲,难度要大很多,而且汇编语言的呈现可读性低、开放性差。所以下位机的程序设计选择单片机C语言。下面简单介绍本次软件设计的编程工具。
5.1 软件设计开发工具
Keil C51是单片机的编写软件,是美国Keil Software公司出品的51系列兼容单片机C语言软件开发系统,与汇编相比,C语言在功能上、结构性、可读性、可维护性上有明显的优势,因而易学易用。用过汇编语言后再使用C来开发,体会更加深刻。 Keil C51软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows界面。另外重要的一点,只要看一下编译后生成的汇编代码,就能体会到Keil C51生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。在开发大型软件时更能体现高级语言的优势。
C++ builder是Inprise公司1998 年推出的,面向对象的32位Windows程序设计开放工具,C++ builder不仅继承了Delphi使用简便、功能强大、效率高等特点,而且它还结合了C++语言的所有优点。 C++ builder可以说是至今为止功能最强、最简单易学的Windows开发工具之一。C++ builder 6具有非常友好的集成开发环境,提供一百多个VCL组件,使开发人员不需要太多的时间编码,就能实现很多复杂的功能。它的编译器 能够自动列出VCL组件的属性和方法供程序员选择,而不必手工输入复杂的代码。正式因为这些优点,所以本次系统设计选择 C++ builder作为上位机软件设计的开发工具。
5.2 系统软件设计
系统要对温度进行采集,首先必须利用上位机对下位机(单片机)发出命令,单片机根据接收到的命令,根据预先的规则对不同的命令做出不同的处理。然后把处理结果发回上位机,上位机接收到温度数据后,根据温度数据画出数据采集图像。这就是系统要实现功能,根据这一要求,设计出系统的软件。
系统整体模块流程图如图5.1
图5.1 系统整体模块图
下面分别设计下位机和上位机的程序流程图。
5.2.1下位机的软件设计
单片机端的应用程序主要实现的功能就是对DS18B20进行采集温度,把采集到的温度通过USB接口芯片上传到计算机端的应用程序。
下位机的程序设计流程图5.2


图5.2 下位机的程序设计流程图
下位机的程序流程图中包括单片机和DS18B20和单片机的通信流程图,以及单片机和USB设备接口芯片的通信流程图。
其中DS18B20和单片机的通信流程图如下图5.3
这里是单片机如何根据DS18B20的特性来控制它,进行温度转换,并读取温度。把温度数据保存在单片机端。

图5.3 DS18B20的程序流程图
单片机先初始化DS18B20温度传感器,对其复位,然后按DS18B20的时序来读写命令,DS18B20根据命令进行操作,单片机和DS18B20的通信过程主要为:单片机对DS18B20复位,跳过读写系列号操作(送0xCC命令到DS18B20),启动温度转换,再次复位,送读温度命令0xBE,读出温度。这就是单片机和DS18B20的通信过程,将得到的温度单片机在进行处理,把温度数据传到上位机。上位机实时显示温度值。为了便于说明DS18B20和单片机间的通信过程,下面给出读出DS18B20温度的程序:
unsigned int readtemperature(void) //读温度
{ unit8 a=0,b=0;
unit8 c,d;
unsigned int t=0;
float tt=0;
c=DS18B20_RESET();
write_byte(0xCC); // 跳过读序号列号的操作
write_byte(0x44); // 启动温度转换
d=DS18B20_RESET();
write_byte(0xCC); //跳过读序号列号的操作
write_byte(0xBE); //读取温度寄存器等前两个就是温度
a=read_byte();
b=read_byte();
t=b;
t<<=8;
t=t|a;
return(t); }
单片机读出温度后,接下来的工作就是单片机和CH372芯片的通信。CH372已经屏蔽了USB通讯协议,所以只要知道CH372单片机之间的通信过程,就很方便的写出它们的通信流程图。图5.4是CH372和89C51的程序流程图。
(1)单片机向CH372上传数据 (2) 下载数据到单片机
图5.4 单片机和CH372的通信流程图
上传数据是通过查询DS18B2是否转换完,转换完,则上传,否则等待;上位机的应用软件向下位机发送数据,CH372的端点接受器接受来着来自计算机的数据,通过中断来通知单片机,单片机开始接受收据,并进行处理。
5.2.2 上位机软件设计
上位机接受来自下位机的数据,根据数据进行操作,实现各种功能。有一点必须要注意的是,要利用动态连接库的知识,才能打开USB设备,从而进行通信,所以在编写应用软件前,一定要了解C++ builder 动态连接库的使用。由于CH372驱动程序提供的动态连接库是用VC编写的,C++ builder要建立起PC机和CH372的通信,必须要使用VC编写的动态连接库,但C++ builder不能直接使用VC编写的动态连接库。关于这点,在调试部分将做更多说明,这里只介绍上位机软件整体设计方面的内容。
下面给出上位机的程序流程图5.5:

图5.5 上位机流程图
这就是上位机软件的设计思想,具体实现在程序中有将进一步说明。
6 系统调试
前面几章节,已经对整个系统进行了设计,可是对于硬件来说,这只是完成一部分的工作 ,硬件系统真正的挑战在于调试,很多情况下,电路图是在理论是正确的,可以实现的,可是真正的到了实际中,会出现很多情况,但不管怎么样,重要的一点是结合设计理论知识来分析系统,逐步调试系统。
6.1 硬件调试
系统硬件电路并不是很复杂,但调试中必需要注意以下几点,否则将会出现系统工作不稳定情况,严重的话,将CH372芯片烧坏,这是电路特别需要的地方。下面给出硬件调式中碰到的问题,并提出如何解决。
(1)CH372的WR、RD、A0信号线是由单片机89C51的P0口提供的,P0口作为I/O口使用,必须要加上拉电阻,很多资料写着加1K-10K的上拉电阻,但这对于本系统是不正确的。
实际上在本系统中P0口的上拉F范围是30K-125K,最好选择100K的上拉电阻。因为如果只是10K,那么流入CH372的芯片的电流为500uA,远远超过了芯片规定的最高电流160uA,这样肯定会烧坏芯片。所以这个上拉电阻的范围是一定要根据芯片的参数来设计,而不能从其他资料上说的选择1K-10K。这点要特别注意。
(2)在CH372的滤波电容0.1uF中,并联一个5K-10K的电阻。这样的作用是系统在断电时,把电容中的电能及时的释放掉,使VCC及时的下降到0V,确保电路下使用通电时CH372能够可上电复位。
如果不加这个电阻,不少情况下,一旦下位机通电,芯片很久才显示出来,上位机需要较久时间来确认接口芯片,所以应该加5K-10K的电阻,这是系统的一个改进。
(3)共用晶振问题。USB接口芯片CH372和单片机都要需要晶振才能工作,但CH372的晶振必须是12M。单片机可以和CH372共用同一个晶振,但必需是12M。这样的优点是可以节约材料,但是不便于PCB排版,还有如果有芯片坏了,也不容易觉察出是不是CH372坏,还是单片机坏了,所以这里考虑单片机和CH372不能共用晶振。方便硬件调试。
(4)DS18B20的接法。DS18B20连接到单片机的接法很简单,这里提出需要注意的一点是,DS18B20用在P11做通信端口,但必须接4.7K的上拉电阻。P1口是I/O口用驱动外部电路时,一般都不用接上拉电阻的,但这里需要上拉电阻。这一点也就要注意。否则DS18B20工作不稳定,有可能有干扰。
本次系统调试硬件主要出现的问题就是上面所提到的,只要根据理论来分析,综合实际应用,便可以做出正确的硬件,这样为后期软件调试减少很多麻烦,否则,后期软件调试的时候,出现问题有可能是软件问题,或者是硬件问题,混合起来处理会很不方便,需花很多时间。
6.2 软件调试
本系统的软件调试主要有下位机端的软件调试和上位机端的软件调试。
6.2.1 下位机的软件调试
下位机的软件调试主要分两部分:单片机和CH372Z时间的通信、单片机与温度传感器DS18B20的通信。
(1)单片机和CH372之间的通信
这一部分是必须首先要调试出来的, 因为要依靠这一部分通过计算机才能显示出单片机和温度传感器之间的通信。
硬件部分利用具有上拉电阻的P0口作为CH372的WR、RD、A0线,所以通过软件控制P0口信号来达到控制CH372的读写命令和读写数据。只要注意CH372的读写时间要求就可以很快实现。单片机的P2口是作为CH372的数据口,利用它来发送和接受CH372的数据。
这里特别强调的一点是CH372的初始化程序,如果初始化不成功,电脑无法识别硬件,后期将无法进行。所以初始化程序必须注意的一点。下面给出CH372的初始化程序。
#define VID 0x8888 //厂商ID
#difine PID 0x9999 //产品ID
CH372_Init() //初始化CH372
{ int i;
CH372_WR_CMD_PORT(CMD_SET_USB_ID); //设置USB设备VID和PID
CH372_WR_DATA_PORT(VID&0xff); //写入厂商ID的低字节
CH372_WR_DATA_PORT(VID>>8); //写入厂商ID的高字节
CH372_WR_DATA_PORT(PID&0xff); //写入产品ID的低字节
CH372_WR_DATA_PORT(PID>>8); //写入产品ID的高字节
CH372_WR_CMD_PORT(CMD_SET_USB_MODE); //设置CH372工作模式
CH372_WR_DATA_PORT(2);//工作模式2
for( i=200;i>0;i--) //20us时间复位
if (CH372_RD_DATA_PORT==CMD_RET_SUCCESS) //复位成功
break;
}
另外,CH372最大可以传送64个字节,每一个读写函数每一次操作的只能一个字节,要读写多个字节,这里使用的是循环的方法。
CH372初始化成功后,下位机连接到PC机上,安装了CH372驱动程序的PC机就可以识别硬件。完成这部分工作之后先进入上为机和下位机联合的调试,然后再调试DS18B20和DS18B20之间的通信。
(2)单片机和DS18B20之间的通信
单片机要根据DS18B20的时序要求和读写要求来读取温度数据。由于DS18B20是一线式数字温度传感器,对时序要求比较高,延时程序误差大,则不能读出数据。还有要按照访问DS18B20的顺序来操作。这里再次说明访问DS18B20的顺序如下:①初始化;②ROM命令;③DS18B20的函数命令。
总之,这部分编程主要注意的就是延时程序的准确性。并按照DS18B20的操作顺序便可以把温度数字传到单片机。
6.2.2 上位机的软件调试
上位机的编程工具是C++ builder,主要是VCL控件的使用。主要有两个模块组成:通信模块和图像处理模块。其中通信模块负责处理上位机和下位机之间的通信,图像处理模块负责温度采集图像。
对于通信模块,主要是调用CH372的动态连接库,但由于厂家的CH372动态连接库是使用Visual C++制作的,C++ builder 不能直接运用,否则将会有出错信息。所以要经过一定的处理。
处理这一问题主要有两种方法:显式连接法和使用C++Builder中提供的导入库生成工具。由于显示连接对于在系统中多次调用动态连接库的多个函数很不方便,这里选择使用C++Builder中提供的导入库生成工具。步骤如下:
(1)用C++Builder提供的implib.exe工具重新生成该动态库(xxx.dll)的导入库(xxx.lib)。命令如下:
implib ch372.lib ch372.dll。
ch372.dll为已有动态库,ch372.lib为要生成的导入库。由此生成的导入库ch372.lib格式与C++Builder开发平台是相容的;
(2)在动态库的头文件ch372.h中,对其输出函数重新说明,语句如下:
extern _stdcall HANDLE WINAPI CH375OpenDevice(
ULON GiIndex );// 指定CH372
设备序号,0对应第一个设备
(3)然后采用隐式链接法,将重新生成的导入库(ch372.lib)和重新说明的头文件(ch372.h)加入到C++Builder应用程序的工程项目中,进行编译和连接。
对于图像处理模块,应用软件根据接受到的温度,利用C++ builder在窗体上画出动态连接图,主要采用窗体Canvas属性来实现。
6.2.3 上位机和下位机联机调试
下位机(单片机)对CH372初始化成功之后,上位机就能够识别下位机设备(USB设备),上位机调试部分也初步完成,那就进入系统整体调试。
整体调试主要分三步:
(1)测试单片机和PC机能否正常通信;
这里利用的方法如下:在下位机的程序部分设计一个往上位机发送的字符数组,比如“1214”,然后在PC机上进行操作,看是否能成功接受数组,如果能,则说明单片机可以往上位机发生数据,不行则修改上位机和下位机相关部分的程序。接下来PC往下位机发送数据,如果下位机能够成功返回相同的数据到PC机上,则说明上为机和下位之间的通信已经成功。
(2)测试单片机能否对DS18B20正常的读取温度;
完成上面一步才能进行这一步调试,前面已经提到,DS18B20的对时序要求很高,一定要准确,并且按DS18B20的顺序来进行操作,在硬件电路原理没有错的情况下,如果温度读起不正确,或者无法读取温度,只能是出现两种错误,一是时序问题,没有按照DS18B20的时序精度来对其进行操作,二是单片机访问DS18B20的顺序问题,单片机没有严格按照访问DS18B20的顺序对起发送命令,单片机访问DS18B20的顺序这里再次声明:初始化、ROM命令、DS18B20的函数命令。
这一部分出现的两个问题,大部分情况下出现的是时序问题,所以特别注意单片机对于DS18B20的精确延时。
(3)采集温度;
各部分通信正常后,便可以采集数据并处理。因为系统采集过程是上位机每发送一条采集命令,下位机就上传一次温度数据,所以这一步主要调试的是上位机要间隔多长时间定时向下位机发送采集命令,使系统能快速采集温度并上传,并防止发生读写等冲突。
6.3 系统性能指标
系统性能指标主要是:
(1)测量温度误差小于或者等于0.5℃;
(2)温度显示分辨率为0.0625℃;
(3)测量温度范围在0℃~70℃;
(4)具有控制报警功能。
7 系统的简单操作说明
若用户使用本系统,将可以对其进行以下的简单操作:
(1)首先打开USB设备;在上位机软件上打开USB设备,建立起上位机和下位机的通信机制。使用本系统都首先要打开USB设备。
(2)对计算机端的应用软件进行温度读取操作,将立即得到该环境温度的实时温度;
(3)可以在上位机上输入报警极限温度,上位机把极限温度发送到下位机,并且保留在上位机,一旦超过,则报警;
(4)上位机应用软件提供实时使用帮助功能。如果用户不知道怎么样使用软件,则可以通过帮助,便能快速使用本系统软件。
8 总结
在此次毕业设计的开发过程中,感觉C 语言(单片机C语言和C++)的熟练程度是项目开发的关键。许多问题其实原理上都是非常成熟的东西,从一些资料上都可以找到。在自己的题目中,要做的也就是将原理表述的温度采集后传到单片机。因此,开发工作本身没有多少创造性,没有多少高深的知识,要具备的仅是对C 语言的熟练,以及学会解决遇到的问题。
做完这次毕业设计的另一个体会就是对于硬件的设计不能硬搬书上或者是相关材料上的。往往由于系统硬件体系不同,所以需要一定的变通,书和材料只能起到提示参考的作用。比如本系统单片机的上拉电阻,书上都说一般用的是1K-10K,可是实际在这个系统中是不适用的,如果用1K-10K,由于进入CH372的电流段过大,这个冲击电流会烧坏芯片。而关于芯片的参数很多情况下,设计人员并不在乎,所以往往只凭书上的知识来设计电路,忽略了细节,这就是本次系统设计中存在的问题。
本系统设计采用的传感器DS18B20对延时要求很高,往往在调试的过程中,温度无法显示,检查硬件电路和软件问题都没有发现出问题,当时首先要考虑的是温度传感器DS18B20芯片已经坏了,经过多次分析,觉得芯片才第一次使用,坏的可能性很小,而一线温度传感器对时序要求很高,所以温度无法读起,首先考虑到单片机无法正确按照DS18B20的时序读取温度。最后确实读到了温度数据。单片机的延时程序一般对系统来说,要求不是很精确,可是本次设计却要求非常严格,这点必须注意。
之外,上位机应用软件的编写工具是C++ builder , 国内很多资料都是介绍的都是基本控件的使用,涉及到画图的资料比较少,特别是画动态图象,本系统只能是另做窗体来画出系统的温度采集图像。而且C ++的动态连接问题并不是书上介绍的那样,真正操作起来碰到了不少问题,连接错误等等。
谢 辞
首先,我要感谢我的导师龙老师,在这次毕业设计的整个过程中,她给与我极大的帮助。
经过这次毕业设计,使我真正的运用了大学里学到的很多理论知识,应用到实际系统的设计中,这次毕业设计可以说是一个实战,一个理论与实践的有机结合,这次毕业设计历程3个月,通过3个月的时间了,我不断的学习新知识,掌握新东西,并且即学即用,龙老师在这里毕业设计过程中就我碰到的问题给以正确有效的引导,使我能够顺利的做完毕业时间,她治学严谨、知识渊博、态度热情认真,给我留下了深刻的印象,所以我在这里非常的感谢我的导师老老师,也感谢学校给以我这样的一次锻炼的机会,感谢本次毕业设计中给以我帮助的所有的老师、同学!
参考文献
[1] 张培仁.基于C语言编程--MS-51单片机用力和应用[M] .清华大学出版社 .2003.1
[2] 席卫文.C++ builder 6程序设计与实例[M] .冶金工业出版社.2003.6
[3] 余明兴.Borland C++ Builder 5 实例精讲[M] . 清华大学出版社.2001.6
[4] 雷晓平.单片机及其应用[M].电子可见大学出版社. 2003.3
[5] 陈启美. 计算机USB接口技术[M] 南京大学出版社. 2003.1
[6] 徐科 杨朝霖.C++ builder实用技术与经典案例[M].清华大学出版社. 2002.11
[7] 王成儒 李英伟.USB2.0原理与工程开发[M].国防工业出版社.2002.3
[8] 沙占友 葛家怡.集成化智能传感器原理与应用[M].电子工业出版社.2004.1
[9] 刘明业.集成电路/计算机硬件描述语言VHDL高等教程[M].清华大学出版社.2003.6
[10] Universal Serial Bus Specification Revision 1.1. Compaq Computer Corporation,Intel
Corporation,Microsoft Corporation,NEC Corporation: September 23, 1998.
附 录
1.protel原理图

1PCB图

2本系统部分程序
(1)下位机部分程序图
#include <reg51.h>
#include <string.h>
#include "ch372.h"
#define uchar unsigned char
#define uint unsigned int
#define VID 0x8888
#define PID 0x9999
#define USBCMD_WR 100 //上传数据
#define USBCMD_RD 101 //设置报警温度
#define DS18B20 102 //检测是否有温度传感器
#define CH375PORT P2
sbit CH375_WR=P0^5; //控制CH372的写数据信号
sbit CH375_RD=P0^6; //控制CH372的读数据信号
sbit CH375_A0=P0^7; //控制CH372的写命令信号
data uchar buffer[64];
data uchar USBCMD;
bit UsbRecvOk;
sbit LED0=P1^1;
sbit LED1=P1^2;
sbit DQ=P1^0;
static float bwendu;
//DS18B20的程序
void delaym(uchar time) //延时为(time*2+3)us
{
for(;time>0;time--);
}
uchar read_byte(void)
{uchar i,value=0;
for(i=0;i<8;i++)
{value>>=1;
DQ=0; //将总线DQ拉低开始读时序
DQ=1; //释放DQ;
delaym(1);
if(DQ) value|=0x80;
delaym(6); //读取时间间隙,要大于1us
}
return (value);
}
void write_byte(uchar value)
{ uchar i;
for(i=0;i<8;i++)
{DQ=0;
DQ=value&0x01; //每次写1位,通过val右移得到
delaym(5); //延时34us(15~60us采样时间)
DQ=1;
value>>=1;
}
delaym(5); //2次写的时间间隙要大于1us
}
uchar DS18B20_RESET(void)
{uchar da;
DQ=0;
delaym(29); //保持DQ低480us
DQ=1;
delaym(3); //等待15~60us
da=DQ;
delaym(25);
return (da);//有芯片应答da=0,无则da=1
}
unsigned int readtemperature(void) //读温度
{ uchar a=0,b=0; // c为温度的小数部分?
unsigned int t=0;
DS18B20_RESET();
write_byte(0xCC); // 跳过读序号列号的操作
write_byte(0x44); // 启动温度转换
DS18B20_RESET();
write_byte(0xCC); //跳过读序号列号的操作
write_byte(0xBE); //读取温度寄存器等(共可读9个寄存器) 前两个就是温度
a=read_byte();
b=read_byte();
t=b;
t<<=8;
t=t|a;
return(t);
}
void Delayms(uint m)
{
uint i;
while(m-->0)
for (i=0; i<500; i++);
}
void CH375_CMD(uchar x) //向CH372写命令
{
CH375_A0=1;
CH375PORT=x;
CH375_WR=0;
CH375_WR=1;
}
void CH375_DAT_WR(uchar x) //向CH372写数据
{
CH375_A0=0;
CH375PORT=x;
CH375_WR=0;
CH375_WR=1;
}
uchar CH375_DAT_RD() //从CH372读数据
{
uchar x;
CH375_A0=0;
CH375PORT=0xff;
CH375_RD=0;
x=CH375PORT;
CH375_RD=1;
return x;
}
void CH375_Init( ) //CH372初始化
{
CH375_CMD(CMD_SET_USB_ID); //写 VID和PID
CH375_DAT_WR(VID&0xff);
CH375_DAT_WR(VID>>8);
CH375_DAT_WR(PID&0xff);
CH375_DAT_WR(PID>>8);
CH375_CMD(CMD_SET_USB_MODE);
CH375_DAT_WR(2);
}
void LoadUpData( uchar data *Buf, uchar Len ) //上传数据
{
uchar i;
CH375_CMD(CMD_WR_USB_DATA7);
CH375_DAT_WR(Len);
for ( i=0; i<Len; i++ )
CH375_DAT_WR(Buf[i]); /* 加载数据 */
}
void CH375Interrupt( ) interrupt 0 using 1
{
unsigned char Status;
unsigned char length, i;
EX0 = 0;
CH375_CMD(CMD_GET_STATUS);
Status = CH375_DAT_RD();
switch(Status)
{
case USB_INT_EP2_OUT: /* 批量端点下传成功 */
CH375_CMD(CMD_RD_USB_DATA);
length = CH375_DAT_RD();
if(length>64) length=64;
for(i=0; i<length; i++)
buffer[i] = CH375_DAT_RD();
LED1=0;
UsbRecvOk=1;
USBCMD=buffer[0];
break;
case USB_INT_EP2_IN:
LED1=0;
CH375_CMD(CMD_UNLOCK_USB);
break;
case USB_INT_EP1_IN:
CH375_CMD(CMD_UNLOCK_USB);
break;
}
EX0 = 1;
}
void main( )
{ uint wendu;
float tt;
uchar a,b;
uchar reset; //DS18B20复位成功
uint k;
EA=1; EX0 = 1;
CH375_CMD(CMD_RESET_ALL);
Delayms(50);
CH375_Init();
UsbRecvOk=0;
LED0=1;
LED1=0;
while(1)
{ k++;
if(UsbRecvOk)
{
UsbRecvOk=0;
switch(USBCMD)
{
case USBCMD_RD:
wendu=readtemperature(); //读温度
b=wendu&0x0f;
a=(wendu>>4)&0xff;
buffer[0]=a; //温度整数部分
buffer[1]=b; //温度小数部分
LoadUpData(buffer,3); //上传温度
tt=a+b*0.0625;
break;
case USBCMD_WR: //设置报警温度
bwendu=buffer[1]+buffer[2]*0.1;
break;
case DS18B20 : //检测有无温度传感器
reset=DS18B20_RESET();
buffer[0]=reset;
LoadUpData(buffer,3);
}
}
if(tt>=bwendu)
LED0=0;
else
LED0=1;
}
}
(2)上位机部分程序:
//----------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//----------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
//----------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *Button1;
TButton *Button2;
TButton *Button3;
TEdit *Edit1;
TEdit *Edit2;
TButton *Button4;
TLabel *Label1;
TLabel *Label2;
TShape *Shape1;
TShape *Shape2;
TShape *Shape3;
TButton *Button5;
TTimer *Timer1;
TTimer *Timer2;
TShape *Shape4;
TLabel *Label3;
TLabel *Label4;
TLabel *Label5;
TLabel *Label6;
TLabel *Label7;
TLabel *Label8;
TLabel *Label9;
TLabel *Label10;
void __fastcall Button4Click(TObject *Sender);
void __fastcall Button1Click(TObject *Sender);
void __fastcall Button3Click(TObject *Sender);
void __fastcall Button2Click(TObject *Sender);
void __fastcall Button5Click(TObject *Sender);
void __fastcall Timer1Timer(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
ULONG index ;
float baojing; //存放设置的报警温度
unsigned char sent[64],jieshou[64];
unsigned long length;
unsigned int image[64];
unsigned int x1,y1; //坐标
};
//----------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//----------------------------------------------------------------------
#endif
//----------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "CH375DLL.H"
//----------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//----------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{ Form1->Timer1->Enabled=false; //关定时器
Form1->Timer2->Enabled=false;
baojing=50;
}
//----------------------------------------------------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{
Form1->Close(); //关闭窗口
CH375CloseDevice(0);//关闭CH372
}
//----------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{ length=64;
if(LoadLibrary("CH375DLL.DLL")==NULL)
ShowMessage("无法加载DLL文件");
if ( CH375OpenDevice(0) == INVALID_HANDLE_VALUE )
ShowMessage("无法打开CH372") ;
else
ShowMessage("成功打开CH372");
sent[0]=102;
CH375WriteData(0,&sent,&length); //发送检测温度传感器的命令
CH375ReadData(0,&jieshou,&length); //接受收据
if(jieshou[0])
ShowMessage("无法检测到温度传感器");
Form1->Timer1->Enabled=false;
Form1->Label9->Canvas->Pen->Width=6; //画系统坐标轴
Form1->Label9->Canvas->MoveTo(0,0); Form1->Label9->Canvas->LineTo(0,270);
Form1->Label9->Canvas->MoveTo(0,270); Form1->Label9->Canvas->LineTo(350,270);
Form1->Label9->Canvas->Pen->Width=1;
Form1->Label9->Canvas->Pen->Style=psDashDotDot;
Form1->Label9->Canvas->MoveTo(0,220); Form1->Label9->Canvas->LineTo(350,220);
Form1->Label9->Canvas->MoveTo(0,170); Form1->Label9->Canvas->LineTo(350,170);
Form1->Label9->Canvas->MoveTo(0,120); Form1->Label9->Canvas->LineTo(350,120);
Form1->Label9->Canvas->MoveTo(0,70);
Form1->Label9->Canvas->LineTo(350,70);
Form1->Label9->Canvas->MoveTo(0,20); Form1->Label9->Canvas->LineTo(350,20);
x1=0;
y1=30;
}
//----------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
Form1->Timer1->Enabled=true;
}
//----------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{ float j;
unsigned int t1,t2;
unsigned long length=64;
unsigned char value,i;
unsigned char n,m;
i=StrToInt(Edit1->GetTextLen());
if(i==0)
ShowMessage("请输入数据");
else
j=StrToFloat(Edit1->Text);
if(j>50)
value=Application->MessageBox(" 你设置的报警温度已经超过上限温度:50", "information", 5);
switch(value)
{ case 4: Edit1->Text=InputBox("温度","请输入报警温度","");
break;
case 2: break;
}
j=StrToFloat(Edit1->Text);
baojing=j;
t1=(int)(j);
t2=(int)(j*10);
m=(char) t1; //整数部分
n=(char)(t2%10); //小数部分
sent[0]=101;
sent[1]=m;
sent[2]=n;
if(CH375WriteData(0,&sent,&length))
Form1->Shape1->Brush->Color=clTeal; //下传成功
else ShowMessage("无法设置报警温度");
}
//----------------------------------------------------------------------
void __fastcall TForm1::Button5Click(TObject *Sender)
{
Form1->Timer1->Enabled=false; //打开定时器
}
//----------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
unsigned char a,b,c,i;// a为温度整数部分,b为温度小数部分
unsigned long len=64;
float wendu;
sent[0]=101;
CH375WriteData(0,&sent,&len) ;
if(CH375ReadData(0,&jieshou,&len))
{ a=jieshou[0]; //a温度的整数部分
b=jieshou[1]; //b为小数部分
wendu=(float)(a+b*0.0625);
Edit2->Text=FloatToStr(wendu);
Form1->Shape2->Brush->Color=clRed;
if(wendu>=baojing)
Form1->Shape4->Brush->Color=clRed;
else
Form1->Shape4->Brush->Color=clWhite;
if(c>0.5)
a=a+1; //四舍五入;
Form1->Label9->Canvas->Pen->Width=2; //画动态图象
Form1->Label9->Canvas->Pen->Color=clRed;
Form1->Label9->Canvas->MoveTo(x1,270-y1*5);
Form1->Label9->Canvas->LineTo(x1+10,270-a*5);
y1=a;
x1=x1+10;
if(x1>350)
{ Form1->Label9->Refresh();
x1=0,y1=30;
Form1->Label9->Canvas->Pen->Color=clBlack; //重新画坐标
Form1->Label9->Canvas->Pen->Width=6;
Form1->Label9->Canvas->MoveTo(0,0); Form1->Label9->Canvas->LineTo(0,270);
Form1->Label9->Canvas->MoveTo(0,270); Form1->Label9->Canvas->LineTo(350,270);
Form1->Label9->Canvas->Pen->Width=1;
Form1->Label9->Canvas->Pen->Style=psDashDotDot;
Form1->Label9->Canvas->MoveTo(0,220); Form1->Label9->Canvas->LineTo(350,220);
Form1->Label9->Canvas->MoveTo(0,170); Form1->Label9->Canvas->LineTo(350,170);
Form1->Label9->Canvas->MoveTo(0,120); Form1->Label9->Canvas->LineTo(350,120);
Form1->Label9->Canvas->MoveTo(0,70); Form1->Label9->Canvas->LineTo(350,70);
Form1->Label9->Canvas->MoveTo(0,20); Form1->Label9->Canvas->LineTo(350,20);}
}
else
{Form1->Timer1->Enabled=false;
ShowMessage("接收不成功"); }
}
//----------------------------------------------------------------------
3上位机应用软件
