找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 6187|回复: 3
收起左侧

单片机+TLC2543湿敏HS1101 NTC热敏 温湿度计仿真程序设计

  [复制链接]
ID:367029 发表于 2019-9-20 17:23 | 显示全部楼层 |阅读模式
上个月刚做的智能仪器仪表设计。
首先感谢51黑,这次设计用到的很多程序都参考了51黑大神们的分享。
温湿度计本身也有很多大佬有发,我的设计也没有什么高明之处,还望见谅。
温度传感器为NTC热敏电阻,湿度传感器为HS1101湿敏电容,用的12位ADC。温湿度测量分辨率0.01,精度0.5%。
仿真图如下
0.png
51hei.gif

本文的主要目标是设计一款具有较高精度的低成本温度、湿度监测仪表。该温湿度监测仪表的主要功能及性能指标如下:

温度测量范围:0~99 ℃

湿度测量范围:0%~100% RH

温度分辨率:0.2 ℃

湿度分辨率:1% RH

温度值精度:±2 ℃

湿度值精度:±2% RH

为了降低成本,同时提高精度,本温湿度监测仪表大量采用模拟电路以代替价格高昂的集成芯片,产品预期效果图如图1-1所示。

图1-1 温湿度检测仪表预期成果图


二、整体设计方案
2.1 设计任务

本文的主要目标是设计一款具有较高精度的低成本温度、湿度监测仪表。设计动态性能更好的模拟电路以替代集成芯片。

该温湿度监测仪表需要满足以下性能指标:

温度测量范围:0~99 ℃

湿度测量范围:0%~100% RH

温度分辨率:0.2 ℃

湿度分辨率:1% RH

温度值精度:±2 ℃

湿度值精度:±2% RH

2.2 设计原则

要求嵌入式系统应具有高可靠性、易维护、高性价比等特点。设计原则有以下几个:

(1)可靠性高

可靠性是单片机系统应用的前提,在系统设计的每一个环节,都应该将可靠性作为首要的设计准则。提高系统的可靠性通常从以下几个方面考虑:使用可靠性高的元器件;设计电路板时布线和接地要合理;对供电电源采用抗干扰措施;进行软硬件滤波等。

(2)易维护

在系统的软硬件设计时,应从操作者的角度考虑操作和维护方便,尽量减少对操作人员专业知识的要求,以利于系统的推广。因此在设计时,要尽可能减少人机交互接口,多采用操作内置或简化的方法。

(3)性价比高

微处理器除体积小、功耗低等特点外,最大的优势在于高性能价格比。一个嵌入式系统能否被广泛应用,性价比是其中一个关键因素。因此,在设计时,除了保持高性能外,尽可能降低成本,在系统性能和速度允许的情况下尽可能用软件功能取代硬件功能等。

2.3 元件选型
2.3.1 嵌入式芯片

本文所设计的温湿度监测计要求在实现长时间稳定工作的基础上,进一步提高性能,降低成本。因此,核心运算部分选用久经市场考验的STC89C52RC芯片。它内部集成了通用8位中央处理器和8 kb Flash存储单元,功能上满足温湿度计的设计需要。同时,STC89C52芯片可以在0~75℃的环境下正常工作,具有较强的稳定性和抗干扰能力。

图2-1 STC89C52实物图

2.3.2 显示模块

液晶显示的原理是利用液晶的物理特性,通过电压对其显示区域进行控制,有电就有显示,这样即可以显示出图形。1602具有轻薄短小、低压微功耗、体积小、无辐射、平面直角显示及影像稳定不闪烁等优点,且可视面积大、效果好、分辨率高、抗干扰能力强,适合用于显示字母、数字、符号等信息,而且不需要扩展过多外围电路,可由单片机直接进行控制输出显示。


2.3.3 热敏元件

计量温度的单位有摄氏度和华氏度两种单位。摄氏度和华氏度。

摄氏度是目前世界上使用较为广泛的一种温标——摄氏温标的温度计量单位,用符号“℃”表示。指在1标准大气压下,纯净的冰水混合物的温度为0度,水的沸点为100度,其间平均分为100份,每一等份为1度,记作1℃。它最初是由瑞典天文学家安德斯·摄尔修斯(Anders Celsius,1701~1744)于1742年提出的,其后历经改进。摄氏温度现已纳入国际单位制(SI)。T(K)=t(℃)+273.15,T为绝对温度。

华氏度也是温度的一种度量单位,以其发明者德国人华伦海特(Gabriel D. Fahrenheit,1681—1736)命名的。由于种种历史局限性,华氏温标正逐渐退出历史舞台,因此,本系统默认显示摄氏温标。

测量温度的传感器有很多种,其中热敏电阻器是最早应用也是应用最广泛的一类测温元件。热敏电阻属于敏感元件,按照温度系数不同分为正温度系数热敏电阻器(PTC)和负温度系数热敏电阻器(NTC)。热敏电阻的典型特点是对温度敏感,不同的温度下会表现出不同的电阻值。PTC电阻在温度越高时电阻值越大,NTC电阻在温度越高时电阻值越低,根据这个特性可以制作出各种形式的温度传感器。

本系统的温度采集电路核心传感元件采用玻封热敏电阻NTC-MF58A104F3950FA,这款型号的热敏电阻为轴向引线,由于采用隔热玻璃封装的形式,所以其不易受轻微的热流扰动,大大提高了温度测量的灵敏度和精确度。本文所选热敏电阻的阻值为100千欧,B值为3950,阻值精度和B值精度均为1%。

热敏电阻价格低廉,小巧坚固,稳定性好,热感应快,被广泛应用于多种行业的温度检测系统。除了查询温度/阻值对照表以外,NTC热敏电阻还有其温度/阻值计算公式,本文所选型号热敏电阻在25℃下的B值为3950,因此其计算公式如下所示:

                 (1)

化简公式,并加上0.5的误差校正后,MF58热敏电阻的温度与所测阻值的关系式为:

                 (2)

通过上述温度/阻值计算公式,可以得出更高分辨率的温度值,在后面的温度测量中采用的正是通过公式法计算所测温度。

2.3.4 湿敏元件

空气的湿度有绝对湿度和相对湿度之分。

空气中水蒸气的压强 p 叫做空气的绝对湿度,可以用空气中所含水蒸汽的密度,即单位体积的空气中所含水蒸汽的质量来表示。由于直接测量空气中水蒸气的密度比较困难,而水蒸气的压强随水蒸气密度的增大而增大,所以通常用空气中水蒸气的压强来表示空气的湿度。

绝对湿度被广泛应用于天气预测上,而与我们生产生活密切相关的是相对湿度,即空气中实际水汽压与当时气温下的饱和水汽压之比的百分数。常见的湿度测量方法有伸缩式温度计、干湿球温度计、露点温度计、湿敏电阻和湿敏电容等。其中伸缩式温度计精度最高,但不能转换成电信号,干湿球温度计和露点温度计体积较大,且难以长时间稳定工作。湿敏电阻是利用湿敏材料吸收空气中的水分而导致本身电阻值发生变化这一原理而制成的,工业上流行的湿敏电阻主要有:氯化锂湿敏电阻,有机高分子膜湿敏电阻,但湿敏电阻只能用于交流电工作环境,直流电会导致高分子材料中的带电粒子偏向两极,从而导致其无法正常工作。

因此,本文选择法国Humirel公司设计生产的湿敏电容HS1101作为湿度监测敏感元件,它也可以称为MHS1101。它对湿度的监测精度高达±2%RH,典型值为180pF@55%RH,即在工作频率为10 KHz、环境温度为25℃、相对湿度为55%RH时测得电容为180 pF。在上述条件下,HS1101的电容变化范围大致为160pF~200pF, RF特性曲线如图所示,可以明显的看出它具有极好的线性输出。此外,依靠其独特的固态聚合物结构,HS1101还具有优异的长期稳定性及可靠性,漂移量仅0.5%RH/年。

图2-2 HS1101湿度电容特性曲线

2.3.5 模数转换芯片

信号调理电路将电阻和电容的变化量转换为电压的变化量,因此,数据采集电路需要使用模数转换芯片采集两路电压信号,基于低成本的原则,本文选择美国TI公司生产的一种12位多通道A/D转换芯片TLC2543作为模数转换芯片,图2-3为TLC2543的内部电路图。

图2-3 TLC2543的内部电路图

TLC2543是一种12位开关电容逐次逼近A/D 转换器,芯片共有11个模拟输入通道。芯片的三个控制端: 串行三态输出数据端(DATA OUTPUT)、输人数据端(DATA INPUT)、输人/出时钟(I/O CLOCK)能形成与微处理器之间数据传输较快和较为有效的串行外设接口——SPI。片内具有一个14 通道多路选择器用于在11个模拟输人通道和3 个内部自测试(SELF-TEST)电压中任选一个,可通过对其8 位内部控制寄存器进行编程完成通道的选择,并可对输出结果的位数、MSB/I.SB 导前和极性进行选择。


2.4 方案设计思路

温湿度监测系统的设计以嵌入式微处理器STC89C52为核心,热敏元件NTC-MF58和湿敏元件HS1101作为物理量敏感元件,通过硬件电路将敏感元件变化的物理量转换成模数转换芯片可以获取识别的电压模拟量,再由模数转换芯片将电压模拟量转换为数字量,通过串行通信接口传给微处理器。最后经过软件算法的处理,得出精确的温度、湿度数值,并将数据通过LCD液晶屏呈现给用户。

因此,整个方案设计应包含四个部分,即:信号调理部分、模数转换部分、嵌入式系统部分、人机交互部分。设计思路如图2-4所示,信号调理部分包含了传感器和多个不同类型的转换电路。

图2-4 整体设计方案思路


三、理论分析

仪器仪表最重要的性能指标之一为精度,在设计系统硬件和软件之前,需要对设计方案做系统而完备的理论分析。以便于在软、硬件设计时对不合理之处进行修改补偿。

温敏元件和湿敏元件的物理量分别为电阻和电容,通过信号调理电路将电阻转换为电压;将电容依次转换为频率、电压。由于微处理采集的是8位分辨率的电压信号,因此需要多个计算公式将电压值转换为频率、电容、电阻,最后根据敏感元件的特性转换为对应的温度、湿度值。其中需要的算法及步骤如下所示:

整理可得:

其中VolOut分别为微处理器采集的两路电压数字量,R、C分别为温敏元件的电阻和湿敏元件的电容。多次转换会带来信号上的失真,且微处理器难以高精度地执行数据处理算法。本课题组使用Matlab工具软件,通过模拟微处理器的运算方式计算出系统的理论误差。其中温度测量的最大误差为1.8℃,湿度测量的最大误差为2.0%RH,因此,需要在软件设计中通过合适的算法补偿误差,使显示结果接近真值。

四、系统硬件设计

整体硬件由STC89C52RC嵌入式运算芯片、LCD1602液晶显示器、湿敏电容HS1101、温敏电阻MF58、模数转换芯片TLC2543、555计时器和LM331构成。基于上述硬件模块的外围电路分别为单片机最小系统电路、LCD1602驱动电路、TLC2543驱动电路、电阻电压转换电路、电容频率转换电路、电压频率转换电路等。

本节以硬件模块为中心分别介绍各个硬件及其外围电路。

4.1 STC89C52RC

本设计选择STC89C52RC作为主控芯片,其最小系统由单片机芯片、时钟电路及复位电路四部分组成。

(1)时钟电路

本设计采用外部时钟方式,时钟电路图如图4-1所示。AT89C52中有高增益的反相放大器,它是构成内部振荡器的主要单元,XTAL1和XTAL2两个引脚分别是该放大器的输出端和输入端。晶振和放大器共同构成了自激振荡器,两个旁路电容与外接晶振接在具有反馈功能的放大器中,构成了并联反馈振荡电路。

图4-1 时钟电路

C1、C2 为旁路电容,对频率有微调作用,电容值根据晶振要求来选择,对振荡器的稳定性、幅值有轻微的影响。但如果C1、C2 值相差太大,容易引发谐振不平衡,所以此处选择电容大小为100pF,以确保振荡器的稳定性。


(2)复位电路

无论是在单片机刚开始接上电源时,还是运行过程中发生故障都需要复位。故复位电路是单片机小系统中重要的一环,如图4-2所示。

复位电路用于将单片机内部各电路的状态恢复到一个确定的初始值,并从这个状态开始工作。单片机的复位条件:必须使其RST引脚上持续出现两个(或以上)机器周期的高电平。

图4-2 复位电路

单片机有两种复位形式:上电复位和按键复位。

上电复位电路中,利用电容充电来实现复位。在电源接通瞬间,RST引脚上的电位是高电平(VCC),电源接通后对电容进行快速充电,随着充电的进行,RST引脚上的电位也会逐渐下降为低电平。只要保证RST引脚上高电平出现的时间大于两个机器周期,便可以实现正常复位。

按键复位电路中,当按键没有按下时,电路同上电复位电路。如在单片机运行过程中,按下S1键,已经充好电的电容会快速通过电阻的回路放电,从而使得RST引脚上的电位快速变为高电平,此高电平会维持到按键释放,从而满足单片机复位的条件实现按键复位。


4.2 温度采集电路

温敏元件为NTC热敏电阻,随着温度的变化,其电阻阻值会发生相应的变化。因此,本温度采集电路的功能是将电阻变化量转化为电压变化量,嵌入式微处理器会将采集到的电压量通过算法转换成温度量。

图4-3 电阻转电压电路

温度采集电路如图4-3所示,为一种基于欧姆定律的电阻转电压电路,也称为电阻分压电路。分压电路输出电压的计算公式为

其中RT1为热敏电阻。输入电压VCC和电阻R10固定不变时,如果热敏电阻的阻值发生变化,输出电压会随之发生变化。

当嵌入式微处理器采集到电压数据时,可以通过分压电路输出电压公式反向推导出对应的电阻值,具体计算公式如下所示


4.3 湿度采集电路

MHS1101的电容随着湿度的变化而变化,因此,获取准确的电容值即可通过电容-湿度表或换算公式得到湿度值。由于单片机无法直接采集电容物理量,因此需要通过设计测量电路将电容值转换为单片机可采集的模拟量或数字量。

湿度采集电路分为两部分,首先将MHS1101置于555振荡电路(C/F)中,将电容值转换成方波脉冲信号,由于转换后的频率范围在6kHz~7kHz之间,属于中高频,核心芯片难以有效采集,因此,本文设计了频率转电压(F/V)电路,将555振荡电路输出的频率信号进一步线性转换成电压信号,便于处理器的高效采集与处理。

1. C/F电路

C/F电路如图4-4所示。555芯片外接电阻R1,R2与MHS1101,构成对MHS1101的充电回路。7端通过芯片内部的晶体管对地短路实现对MHS1101的放电回路,并将引脚2,6端相连引入到片内比较器,构成一个多谐波振荡器,其中,R1相对于R2必须非常的小,但决不能低于一个最小值。R4是防止短路的保护电阻。

图4-4 电容转频率电路

MHS1101作为一个变化的电容器,连接2和6引脚。引脚作为R1的短路引脚。MHS1101的等效电容通过R1和R2充电达到上限电压(近似于0.67VCC,时间记为T1),这时555的引脚3由高电平变为低电平,然后通过R2开始放电,由于R1被7引脚内部短路接地,所以只放电到触发界线(近似于0.33VCC,时间记为T2),这时555芯片的引脚3变为高电平。通过不同的两个电阻R1,R2进行传感器的不停充放电,产生方波输出。

充电、放电时间分别为:

输出波形的频率和占空比的计算公式如下:

由此可以看出,空气相对湿度与555定时器输出频率存在一定线性关系。表1给出了典型频率湿度关系(参考点:25℃,相对湿度:55%,输出频率:6.208kHz)。在对精度要求不高的场合,可以通过微处理器采集555定时器输出的频率,然后查表即可得出相对湿度值。

表1 部分典型频率/湿度对照表

RH(%)

0

10

20

30

40

50

60

70

80

Frequency

6852

6734

6618

6503

6388

6271

6152

6029

5901


2. F/V电路

频率/电压转换电路就是把输入的脉冲信号转换为电压信号输出的一种电路。输出的电压与输入的脉冲频率成线性关系,并可通过测量其输出端的电压值来间接测量输入的脉冲频率。目前市面上已有成熟的基于ΣΔ技术的频率/电压转换芯片,但是芯片的成本较高,构成的电路结构比较复杂,功耗较大。本文选择了基于LM331频率转换芯片设计的频率/电压转换电路,其由专用的频率/电压转换芯片LM331及少量的电阻电容组成。

图4-5 频率转电压电路

LM331外接电路简单,只需接入几个外部元件就可方便构成V/F或F/V等变换电路,并且转换精度较高。LM331构成的频率/电压转换电路如图4-5所示,经C/F电路输出的频率FreOut经过R3、C1组成的微分电路加到LM331的6脚。当FreOut的下降沿到来时经过微分电路将在6脚产生负向尖峰脉冲,当负向尖峰脉冲大于VCC/3时LM331的内部触发器将置位,其内部的电流源对电容C3充电,同时电源VCC通过R7对电容C2充电。当C3上的电压大于2VCC/3时, LM331内部的触发器复位,C3通过R8放电,同时定时电容C2迅速放电,完成一次充放电过程。此后,每经过一次充放电过程电路重复上面的工作过程,这样就实现了频率/电压的转换。LM331输出的电压Vol2Out与输入信号频率FreOut的关系可表示为:

              此关系式具有较好的线性度,且转换速度快,便于微处理器进行采集处理。

4.4 模数转换电路

TLC2543具有20个引脚,TLC2543与单片机的接口应为5条数据线,分别是CS、CLK、SDO、SDI和EOC。

图4-6 模数转换电路

              模数转换电路的驱动电路如图4-6所示,其中AIN0、AIN1端口分别连接温度采集电路和湿度采集电路的电压输出端,CS、CLK、SDO、SDI和EOC则与微处理器通用I/O口相连接。

4.5 液晶屏驱动电路

              LCD1602液晶屏采用标准的16脚接口,驱动电路如图4-7所示。其中1号引脚和2号引脚分别为地电源和正电源。3号引脚为显示器对比度调整端,电压越高,对比度越低,接地时对比度最高。LCD驱动电路中使用可调电阻,方便使用者调整对比度。

4号引脚RS端为寄存器选择端,高电平1时选择数据寄存器、低电平0时选择指令寄存器。RW为读写信号线,高电平时进行读操作,低电平时进行写操作。EN端为使能端,高电平时读取信息,负跳变时执行指令。D0~D7为8位双向数据端。第15引脚为背光正极,第16引脚背光负极,加入隔离电容C9可以防止显示器烧坏。

图4-7 LCD1602驱动电路


五、系统软件设计
5.1 软件设计思路

              本课题设计的温湿度检测仪表具有较多的功能,且各个功能模块对实时性的要求较高,因此本系统引入了多任务实时操作系统,所有功能子程序均运行在实时系统之上。整体设计思路如图5-1所示

图5-1 系统软件设计思路

              job 0执行各个模块的初始化程序,初始化完毕后,会创建job 1、job 2、job 3三个功能任务,最后删除job 0的任务,因此,job 0任务在微处理器运行过程中只执行一次。

              job 1、job 2、job 3三个功能任务为同级轮转任务,每个任务分得的时间均为10 ms,各自的运行时间结束后,会按顺序跳转执行下一个任务。在一个标准时间内,每个软件功能模块都会得到运行,不会出现由于大规模的数据处理导致其他功能停滞的现象。


5.2 实时系统的设计(程序暂未实现)

RTX-51 TINY是一个由美国Keil公司设计开发的用于8051系列处理器多任务实时操作系统。RTX-51采用时间片轮转任务切换和使用信号进行任务切换,所有的任务平等运行,占用资源极低。

RTX-51 TINY的程序用标准的C语言构造,在传统的C语言程序入口都是从main函数作为入口,而基于RTX-51 TINY构建的嵌入式程序却没有main函数,而是从任务0(job 0)开始执行的。在job 0中执行各个模块的初始化程序,程序执行后,关闭job 0,开始轮流执行其他job函数。RTX-51 TINY使用无优先级时间片轮询法,每个任务使用相同大小的时间片,时间片可以在相关参数配置中进行修改设置。

RTX51 Tiny的配置参数中有INT_CLOCK和TIMESHARING两个参数。这两个参数决定了每个任务使用时间片的大小:INT_CLOCK是时钟中断使用的周期数,也就是基本时间片;TIMESHARING是每个任务一次使用的时间片数目。两者决定了一个任务一次使用的最大时间片。本系统设置INT_CLOCK为10000,即TIMESHARING=1,一个任务使用的最大时间片是10ms。

实时系统相关代码如下图5-2所示:

图5-2 实时系统部分代码(暂未实现 调试中)


5.3 功能子程序的设计
1.模拟量采集模块

TLC2543与单片机的接口应为5条数据线,分别是CS、CLK、SDO、SDI和EOC。模拟量采集模块开始工作后,片选CS必须从高到低,才能开始一次工作周期,此时EOC为高,输入数据寄存器被置为0,输出数据寄存器的内容是随机的。

开始采集时,CS片选为高,I/O CLOCK、DATA INPUT被禁止,DATA OUT 呈高阻状,EOC为高。接下来使CS变低,I/OCLOCK、DATAINPUT使能,DATAOUT脱离高阻状态。12个时钟信号从I/OCLOCK端依次加入,随着时钟信号的加入,控制字从DATAINPUT一位一位地在时钟信号的上升沿时被送入TLC2543(高位先送入),同时上一周期转换的A/D数据,即输出数据寄存器中的数据从DATAOUT一位一位地移出(下降沿),在CS=0时输出第一位,其他的在下降沿输出。部分代码如图所示5-3所示,为TLC2543读取低8位转换结果。

图5-3 读取低8位转换结果


2. LCD显示模块

              LCD1602的驱动程序一共有3个功能子程序,分别为初始化函数、写命令函数和写数据函数。具体函数如图5-4所示。

图5-4 LCD1602驱动程序


5.4 数据处理算法

              在理论分析中已经说明,嵌入式微处理并不能负担大规模的实时数据处理运算。但是可以通过化简、等价代换等方法将多个公式组合成线性表达式,如下所示:

              代入各个电阻、电容的数值,化简可得:


              由于理论计算与实际典型值有偏差,因此需要对计算公式进行补偿,其中补偿后温度处理算法如图5-5所示,补偿后的湿度处理算法如图5-6所示,对不同大小的计算电容值做出对应的补偿,可以有效的使最终结果逼近真值。

图5-5 温度处理算法

图5-6 湿度处理算法


六、系统调试和实验结果分析
6.1 仿真测试

在完成硬件设计和软件编程后,启动Proteus软件进行仿真,仿真测试结果如图6-1所示。启动仿真后,LCD屏幕首先会显示开机界面,如图6-1所示。

图6-1 开机界面

启动后1秒钟以内,LCD屏幕即能够显示当前时刻的温度值、湿度值和敏感元件的物理量,且串口也会同步发送对应数据。

图6-2 Proteus仿真示意图

如图6-2所示,在NCT热敏电阻设定的虚拟温度值为28. 0℃时,本温湿度检测仪表显示温度值为27.85℃。HS1101湿敏电容设定电容值为180pF,仪表显示测得的湿敏电容的电容为179.74pF。根据HS1101特性曲线可知,180pF电容值下对应的环境湿度为49.15%RH,本温湿度检测仪表显示湿度值为48.35%RH。

              本课题组针对此温湿度检测仪表进行了多组测试,测试结果如下表所示。

表1 对比测试表


1

2

3

4

5

实际温度

5.3

25.3

45.3

65.3

85.3

测量温度

5.33

25.16

45.08

64.99

84.94

实际湿度

10

30

50

70

90

测量湿度

9.97

29.82

49.56

69.85

89.60

              可以看出,本文所设计的温湿度检测仪表具有较好的快速性和准确性,温度测量精度高达0.5%,湿度测量精度高达1%。满足性能指标。


6.2 PCB效果图

              本文所设计的温湿度检测仪表的控制系统如图6-3所示,3D效果图如图6-4所示。

图6-3 PCB设计图


图6-4 PCB 3D效果图

单片机源程序如下:
  1. #include <reg52.h>
  2. #include <intrins.h>
  3. #include <lcd1602.h>
  4. #include <math.h>
  5. #include "stdio.h"
  6. #include "2543.h"
  7. //#include "rtx51tny.h"


  8. /************************************************
  9.       变量/常量声明
  10. ***************************************************/
  11. unsigned char Num[6]={0x00,0x00,0x00,0x00,0x00,0x00}; //数值显示缓冲区
  12. unsigned int date0=0,date1=0; //AD值(0-255)
  13. unsigned int MID;        //中间变量(勿删)
  14. double Temp,Hemi;        //温度值,湿度值。
  15. double RES,CAP;        //温度值,湿度值。
  16. unsigned char pencil;
  17. unsigned char rubber;

  18. /************************************************
  19.       子函数声明
  20. ***************************************************/
  21. void convert(unsigned char a,unsigned char b);        //ADC寄存器转数组
  22. void display(void);        //显示电压数值
  23. void process(void);        //数据处理(电压转温度,湿度)
  24. void ShowNum(void);        //显示数据(显示温度值,湿度)
  25. void ShowPhy(void);


  26. //////
  27. //串口函数
  28. uchar flag = 0;
  29. uchar index = 0;
  30. //uchar data c[2];
  31. void Delay_ms(uint n)
  32. {
  33.                 uchar i;
  34.                 while(n--)
  35.                                 for(i = 0;i < 120;i++);
  36. }
  37. /*********************************************
  38. * 函数名         :UsartInit()
  39. * 函数功能                 :设置串口并初始化*/
  40. void UsartInit()
  41. {
  42.                 SCON=0x50;        //串口模式1,允许接收
  43.                 TMOD=0x20;        //T1工作模式2
  44.                 TH1=0xFD;        //波特率9600
  45.                 TL1=TH1;
  46.                 PCON=0x00;        //波特率倍增
  47.                 EA = 1;                //开总中断
  48.                 ES = 1;                //开串口中断
  49.                 TR1=1;                //开定时器1中断
  50. }
  51. /**********************************************
  52. * 函数名         :Send_string(uchar *c)
  53. * 函数功能                 :通过串口发送数据*/
  54. void Send_string(uchar c)
  55. {
  56. //                while(*c != '!')
  57. //                {        
  58. //                                SBUF=*c;
  59. //                                c++;
  60. //                                while(TI==0);
  61. //                                                TI=0;
  62. //                }
  63.                 SBUF=c;
  64.                 while(TI==0);
  65.                 TI=0;
  66. }


  67. /*******************************************************************************

  68. * 主函数         

  69. *******************************************************************************/
  70. void main()
  71. {
  72.                 UsartInit();        //串口初始化
  73.                 init_lcd1602();//液晶1602显示初始化
  74.                 Welcome();
  75.                 pencil=0;
  76.                 Delay_ms(200);
  77.                 Delay_ms(200);

  78.                 write_cmd(0x01);  //清屏
  79.                 while(1)
  80.                 {
  81.                                 date0=A_D(0);
  82.                                 date1=A_D(1);
  83.                                 
  84.                                 process();
  85.                                 if(pencil == 0) pencil=1;
  86.                                 else
  87.                                 {
  88.                                                 ShowPhy();        //显示电阻、电容
  89.                                                 ShowNum();        //显示温度、湿度
  90.                                 }
  91.                 }
  92. }

  93. /***********************************************
  94. 数据处理程序
  95. 功能:将电压值(0-5V)转换成温度值(-40.0-250.0)和湿度值(0.0-100.0)
  96. ***********************************************/
  97. void process(void)
  98. {            
  99.                 RES = 409500.0/date0-100;
  100.         
  101.                 CAP = 635071.5/date1;                                                                //775.4230771397/V   =   635071.50017744/D
  102.                 rubber = ((uchar)CAP%100)/10;
  103.                 if(rubber==6) CAP=0.994*CAP;
  104.                 else if(rubber==7) CAP=0.993*CAP;
  105.                 else if(rubber==8) CAP=0.992*CAP;
  106.                 else if(rubber==9) CAP=0.991*CAP;
  107.                 else ;
  108.                 Hemi= (CAP-163.3)/0.34;

  109.                 Temp= 1.0/((log(RES/100)/3950)+0.003354)-273.15;        //1/(273.15+25)=0.0033540164
  110.                

  111. }





  112. /*****************************************
  113. 显示传感数值函数
  114. *****************************************/
  115. void ShowPhy(void)
  116. {
  117.                 unsigned char i;        //显示器指针用

  118.                 MID = (unsigned int)(RES * 100);
  119.                 Num[0]=MID/10000;
  120.                 Num[1]=(MID%10000)/1000;
  121.                 Num[2]=(MID%1000)/100;
  122.                 Num[4]=(MID%100)/10;
  123.                 Num[5]=MID%10;
  124.                 write_cmd(0x80);        //指针设置到第二行
  125.                 for(i=0;i<6;i++)
  126.                 {
  127.                                 if(i==3)
  128.                                 {
  129.                                                 write_date(0x2e);
  130.                                                 Send_string('.');
  131.                                 }
  132.                                 else
  133.                                 {
  134.                                                 write_date(0x30+Num[i]);        //0x30是48。
  135.                                                 Send_string(0x30+Num[i]);
  136.                                 }
  137.                                 delay(5);
  138.                 }
  139.                 write_date('K');
  140.                 Send_string('K');
  141.                 Send_string(0x20);

  142.                 //设置第二个数值
  143.                 MID = (unsigned int)(CAP * 100);
  144.                 Num[0]=MID/10000;
  145.                 Num[1]=(MID%10000)/1000;
  146.                 Num[2]=(MID%1000)/100;
  147.                 Num[4]=(MID%100)/10;
  148.                 Num[5]=MID%10;
  149.                 write_cmd(0x80+8);        //指针设置到第二行中间
  150.                 for(i=0;i<6;i++)
  151.                 {
  152.                                 if(i==3)
  153.                                 {
  154.                                                 write_date(0x2e);
  155.                                                 Send_string('.');
  156.                                 }
  157.                                 else
  158.                                 {
  159.                                                 write_date(0x30+Num[i]);        //0x30是48。
  160.                                                 Send_string(0x30+Num[i]);
  161.                                 }
  162.                                 delay(5);
  163.                 }
  164.                 write_date('p');
  165.                 write_date('F');
  166.                 Send_string('p');
  167.                 Send_string('F');
  168.                 Send_string(0x20);
  169. }

  170. /*****************************************
  171. 显示处理数值函数
  172. *****************************************/
  173. void ShowNum(void)
  174. {
  175.                 unsigned char i;        //显示器指针用
  176.                
  177.                 MID = (unsigned int)(Temp * 100);
  178.                 Num[0]=MID/10000;
  179.                 Num[1]=(MID%10000)/1000;
  180.                 Num[2]=(MID%1000)/100;
  181.                 Num[4]=(MID%100)/10;
  182.                 Num[5]=MID%10;
  183.                 write_cmd(0x80+0x40);        //指针设置到第二行
  184.                 for(i=0;i<6;i++)
  185.                 {
  186.                                 if(i==3)
  187.                                 {
  188.                                                 write_date(0x2e);
  189.                                                 Send_string('.');
  190.                                 }
  191.                                 else
  192.                                 {
  193.                                                 write_date(0x30+Num[i]);        //0x30是48。
  194.                                                 Send_string(0x30+Num[i]);
  195.                                 }
  196.                                 delay(5);
  197.                 }
  198.                 write_date('C');  //最后一位后显示字符'C'
  199.                 Send_string('C');
  200.                 Send_string(0x20);
  201.                
  202.                 //设置第二个数值
  203.                 MID = (unsigned int)(Hemi * 100);
  204.                 Num[0]=MID/10000;
  205.                 Num[1]=(MID%10000)/1000;
  206.                 Num[2]=(MID%1000)/100;
  207.                 Num[4]=(MID%100)/10;
  208.                 Num[5]=MID%10;
  209.                 write_cmd(0x80+0x40+8);        //指针设置到第二行中间
  210.                 for(i=0;i<6;i++)
  211.                 {
  212.                                 if(i==3)
  213.                                 {
  214.                                                 write_date(0x2e);
  215.                                                 Send_string('.');
  216.                                 }
  217.                                 else
  218.                                 {
  219.                                                 write_date(0x30+Num[i]);        //0x30是48。
  220.                                                 Send_string(0x30+Num[i]);
  221.                                 }
  222.                                 delay(5);
  223.                 }
  224.                 write_date('R');  //最后一位后显示字符'Rh'
  225.                 write_date('h');  //
  226.                 Send_string('R');
  227.                 Send_string('h');
  228.                 Send_string(0x20);
  229.                 Send_string(0x20);
  230. }


  231. //串口接收中断函数
  232. //void Serial_INT() interrupt 4
  233. //{
  234. // if(RI==0) return;
  235. // ES=0;        //关闭串口中断
  236. // RI=0;        //清接收中断标志
  237. // c[index++]=SBUF;
  238. //    if(SBUF == '!')
  239. //   flag = 1;
  240. // ES=1;
  241. //}


  242. ///************************************************
  243. //      job0函数
  244. //***************************************************/
  245. //void job0(void) _task_ 0
  246. //{
  247. //                UsartInit();        //串口初始化
  248. //                init_lcd1602();//液晶1602显示初始化
  249. //                os_create_task(1);
  250. //                os_create_task(2);
  251. //                os_create_task(3);
  252. //                os_delete_task(0);
  253. //}
  254. //void job1(void) _task_ 1
  255. //{
  256. //                while(1)
  257. //                {
  258. //                                date0=A_D(0);
  259. //                                date1=A_D(1);
  260. //                }
  261. //}
  262. //void job2(void) _task_ 2
  263. //{
  264. //                while(1)
  265. //                {
  266. //                                process();
  267. //                }
  268. //        
  269. //}

  270. ///************************************************
  271. //      job3函数  Show
  272. //***************************************************/
  273. //void job3(void) _task_ 3
  274. //{
  275. ////                while(1)
  276. ////                {
  277. //                                ShowNum();        //显示温度、湿度
  278. //                                ShowPhy();        //显示电阻、电容
  279. ////                }
  280. //        
  281. //}

  282. ……………………

  283. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码

0.png

仿真原理图和Keil程序下载:
温湿度计.zip (1.62 MB, 下载次数: 150)

评分

参与人数 1黑币 +100 收起 理由
admin + 100 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:42615 发表于 2022-2-17 10:40 | 显示全部楼层
好文章,
回复

使用道具 举报

ID:793344 发表于 2022-2-28 10:18 | 显示全部楼层
谁能用RTX51 OS重写一下代码吗?,楼主的代码是常规的顺序执行
回复

使用道具 举报

ID:730009 发表于 2022-3-26 18:11 | 显示全部楼层
他把rtx51tny.h注释掉了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表