本系统由前级采集电路和运算放大电路等信号处理模块,A/D转换模块和数据显示模块三部分组成。单片机模块以STC15F60S2为核心,利用PH传感器检测液体PH值,超声波传感器检测水位。本系统主要实现PH值测量,液位情况测量和电压测量三个功能,经过最终级联、调试,结果表明,PH值测量误差低于0.3,液位测量误差低于1mm,因此达到很高的准确度,各项指标均优于题目要求。
一系统方案1.1 PH传感器选择【方案一】采用无温度检测的PH传感器 该方案电路简单,易于实现,但是在温度变化大的情况下,易受温度影响,测量值波动较大,误差较大不易修正。 【方案二】采用自带温度监测的PH传感器 该方案克服了环境温度与液体温度相差较小时,PH检测误差较小,易于检测,稳定性好,误差易于修正。 【方案三】采用液体温度检测与PH传感器相结合 该方案电路相对来说比较复杂,可实现性不大,稳定性不高。 考虑到电路的稳定性及可实现性,我们选择方案二。 1.2 液位测量选择【方案一】光电监测 该方法对于定位测量效果好、精度高。但不适合对水位高度的测量,无法实时检测液面高度。 【方案二】超声波监测 此方法测量时间短,对实时测量液面精度高。测量范围取决于高频脉冲的频率及声波的大小,对于近距离测量精确都高,价格实惠。 【方案三】平行导线线迹测量 此方法测量精度高,但测量范围小、易沾被测物,不适合强酸性或强碱性液体测量。 考虑到测量精度和系统的可行性,我们选择方案二。 二系统理论分析与计算
2.1 水情信号处理的分析2.1.1 PH值处理的分析 本系统利用PH传感器检测液体酸碱度。PH检测电路由前置120dB缓冲放大部分与后级高增益部分构成。本模块的PH检测采取TLC4502进行放大。最终系统将输出信号与标准信号源的输出值进行比较,得出PH值,并计算出相对精度。 2.1.2 液位处理的分析 采用超声波传感器对水和白醋、纯净水进行液位检测,运用TI公司的高速比较器TL047对前级放大电路输出信号进行处理。通过超声波发射装置发出超声波,根据接收器接到超声波时的时间差就可以知道距离。超声波发射器向某一方向发射超声波,在发射时刻的同时开始计时,超声波在空气中传播,途中碰到障碍物就立即返回来,超声波接收器收到反射波就立即停止计时。返回信号通过IO口ECHO输出一个高电平,高电平持续的时间就是超声波发射到返回的时间,测试距离=(高电平持续时间*声速(340M/S))/2,最后将测量值与系统所得值进行对比,并计算得到相对误差。 2.2 电压检测方法的分析利用STC15F60S2单片机采用A/D转换电路将输入电压转换成时间(脉冲宽度信号)或频率(脉冲频率),然后由定时器/计数器获得数字值。与万用表所测结果进行对比,并计算精度。
三电路与程序设计
3.1 总体方案设计该水情检测系统由PH传感器和超声波传感器组成。PH传感器用于液体酸碱度测量,超声波传感器用于液体高度测量。信号经过前级的放大整形后进入A/D转换,A/D转换后将数据传输给单片机,单片机对数据进行整理和计算,并将结果显示出来。系统总体实现框图如图1.1所示。
3.2 电路设计
3.2.1PH检测电路设计 PH检测电路由前置120dB缓冲放大部分与后级高增益部分构成。本模块的PH检测采取TLC4502进行放大,可有效抑制噪声。本模块采用的温度传感器为热敏电阻,其阻值随温度变化而变化。采用惠斯登电桥保证后续的差分放大时,运算放大器的正反向输入电压值为正值,则在此极限温度下,电桥处于平衡状态,差分放大输入电压差为0。如图3.2所示。
3.2.2液位检测电路运用TI公司的高速比较器TL047对前级放大电路输出信号进行处理。同时采用了EM78小型微处理器进行信号转换使其更加准确,并且很好的抑制了噪声干扰。如图3.3所示。
3.3 软件设计STC15F60S2单片机在该系统主要完成数据计算,功能切换和显示功能。主程序流程图如图3.4所示。
四测试方案与测试结果
4.1 测试方法与仪器4.1.1测试方法 1)PH值的测量方法:通过对水和白醋、纯净水进行多次测量,采取TLC4502进行放大,最终将系统的输出信号与标准信号源的输出值进行比较,得出PH值,并计算出相对精度。 2) 液位的测量方法:采用超声波传感器对水和白醋、纯净水进行液位检测,运用TI公司的高速比较器TL047对前级放大电路输出信号进行处理。如有信号返回,通过IO口ECHO输出一个高电平,高电平持续的时间就是超声波发射到返回的时间,测试距离=(高电平持续时间*声速(340M/S)) /2,最后将测量值与系统所得值进行对比,并计算得到相对误差。 3) 电压的测量方法:将万用表测量值与单片机所测值进行对比,得到相对精度。 4.1.2 测试仪器 表4.1 测试仪器 序号 | | | | | | | Agileat 33600A(DDS函数信号发生器) | | | | | | | |
4.2 测试结果4.2.1 PH值的测量测量结果:使用信号源输出不同幅度的直流电压信号,记录实际测量值。测试结果如表4.2所示 表4.2 PH测量 结论:向塑料容器中注入若干毫升的水和白醋,可以在规定时间内完成PH值测量并显示,测量偏差远小于0.5;多次向纯净水中注入若干白醋,能在2分钟内稳定显示每次的PH值,同时测量偏差不大于0.1。 4.2.2 液位的测量 不同待测物液位测试结果如表4.3所示。 表4.3 液位测量 结论:向塑料容器中注入若干毫升的水和白醋,在1分钟内完成水位测量并显示,测量偏差不大于5mm;将塑料容器清空,多次向塑料容器注入若干纯净水,在1分钟内稳定显示,每次的水位值偏差不大于2mm。 4.2.3 电压的测量 供电电池的电压测试结果如表4.4所示。 表4.4 电压测量 结论:可以完成供电电池的输出电压测量并显示,测量偏差小于0.01V。 综上所述,本设计已达到要求。 五总结该系统以STC15F60S2为核心测量器件,配合放大电路设计了高精度水情检测系统。经过最终的调试,实现了不同情况下水位、PH值及电压的测量。本系统性能优良,工作可靠,操作简单,使用方便,完全满足题目中所有的指标要求。在设计过程中要注意以下问题:电压小于5V时,采用光耦元器件误差较大,当电压高于5V时,应采用A/D转换电路减小误差。
单片机源程序如下:
- #include <stc15f2k60s2.h>
- #include <intrins.h>
- #include <lcd12864.h>
- //引脚定义
- sbit RX = P4^2;
- sbit TX = P4^4;
- #define FOSC 11059200L
- #define BAUD 9600
- #define uchar unsigned char
- #define uint unsigned int
- #define ulong unsigned long
- #define URMD 0 //0:使用定时器2作为波特率发生器
- //1:使用定时器1的模式0(16位自动重载模式)作为波特率发生器
- //2:使用定时器1的模式2(8位自动重载模式)作为波特率发生器
-
- #define ADC_POWER 0x80 //ADC电源控制位
- #define ADC_FLAG 0x10 //ADC完成标志
- #define ADC_START 0x08 //ADC起始控制位
- #define ADC_SPEEDLL 0x00 //540个时钟
- #define ADC_SPEEDL 0x20 //360个时钟
- #define ADC_SPEEDH 0x40 //180个时钟
- #define ADC_SPEEDHH 0x60 //90个时钟
- /*---全局变量声明---*/
- uchar code CharCode1[]="**水情监测系统**";
- uchar code CharCode2[]="液位值:";
- uchar code CharCode3[]="PH值:";
- uchar code CharCode4[]="电压值:";
- uchar number0[5]; //水位值储存
- uchar number1[5]; //PH值储存
- uchar number2[7]; //电压值储存
- uchar Test0; //标志
- uint ad_data1; //十位AD值
- unsigned int time=0;
- long S=0;
- bit flag =0;
- ulong distance; //距离显示
- /*---函数声明---*/
- void InitUart();
- void InitADC();
- void Timer0Init(void); //10毫秒@11.0592MHz
- void Timer2Init(void); //10毫秒@11.0592MHz
- ulong GetADCResult(uchar ch);
- void Delay(uint n);
- void IO_Init(); //I/O口初始
- void LCD12864(); //LCD初始化显示
- void PH_Value(); //PH监测化
- ulong PH_read(long PH); //PH值转换
- void Voltag_read(); //电压监测
- /*---液位---*/
- void Conut(void);
- void Distance_Value(); //液位监测
- void delayms(unsigned int ms);
- void Timer_Count(void);
- void StartModule(); //T1中断用来扫描数码管和计800MS启动模块
- ulong datas_Value(long datas); //数据修正
- /*---主函数---*/
- void main()
- {
- IO_Init(); //I/O口初始化
- LCD12864_Init(); //LCD初始化
- InitUart(); //初始化串口
- InitADC(); //初始化ADC
- Timer0Init(); //定时器0初始化
- Timer2Init(); //定时器2初始化
- while (1)
- {
- LCD12864();
- if(Test0==1)
- {
- Test0=0;
- PH_Value();
- Voltag_read();
- Distance_Value();
- }
- }
- }
- void IO_Init() //I/O口模式选择
- {
- P0M0 = 0x00;P0M1 = 0x00;P1M0 = 0x00;P1M1 = 0x00;
- P2M0 = 0x00;P2M1 = 0x00;P3M0 = 0x00;P3M1 = 0x00;
- P4M0 = 0x00;P4M1 = 0x00;P5M0 = 0x00;P5M1 = 0x00;
- P6M0 = 0x00;P6M1 = 0x00;P7M0 = 0x00;P7M1 = 0x00;
- }
- void LCD12864() //LCD初始化显示
- {
- uchar i=0;
- uchar j=0;
- uchar k=0;
- uchar t=0;
- LCD12864_SetWindow(0, 0); //系统名称显示
- while(CharCode1[i]!='\0')
- {
- LCD12864_WriteData(CharCode1[i]);
- i++;
- if(i==16)
- {
- i=0;
- break;
- }
- }
- LCD12864_SetWindow(1, 0); //液位显示
- while(CharCode2[j]!='\0')
- {
- LCD12864_WriteData(CharCode2[j]);
- j++;
- if(j==15)
- {
- j=0;
- break;
- }
- }
- LCD12864_SetWindow(2, 0); //PH值显示
- while(CharCode3[k]!='\0')
- {
- LCD12864_WriteData(CharCode3[k]);
- k++;
- if(k==8)
- {
- k=0;
- break;
- }
- }
- LCD12864_SetWindow(3, 0); //电压值显示
- while(CharCode4[t]!='\0')
- {
- LCD12864_WriteData(CharCode4[t]);
- t++;
- if(t==13)
- {
- t=0;
- break;
- }
- }
- }
- void PH_Value() //PH监测
- {
- long PH;
- long datas;
- long num;
- uint i;
- datas=GetADCResult(0);
- // num=datas*(4.5/1.0240);
- num=datas_Value(datas*(4.5/1.024));
- PH=PH_read(num); //PH-Value对比
- number0[0]=PH/1000; //求十位
- number0[1]=PH%1000/100; //求个位
- number0[2]='.';
- number0[3]=PH%100/10; //求十分位
- number0[4]=PH%10; //求百分位
- LCD12864_SetWindow(2,3);
- for(i=0;i<5;i++)
- {
- if(i==2)
- LCD12864_WriteData(number0[i]);
- else
- LCD12864_WriteData(number0[i]+48);
- }
- }
- ulong PH_read(long PH) //PH值转换
- {
- long datas;
- if(PH>4242)
- {
- datas=0;
- }else if(PH>4065)
- {
- datas=0+(PH-4065)/177.00;
- }else if(PH>3885)
- {
- datas=1+(PH-3885)/180.00;
- }else if(PH>3709.8)
- {
- datas=2+(PH-3709.8)/175.20;
- }else if(PH>3532.5)
- {
- datas=3+(PH-3532.5)/177.30;
- }else if(PH>3354)
- {
- datas=4+(PH-3354)/178.50;
- }else if(PH>3177.5)
- {
- datas=5+(PH-3177.5)/176.50;
- }else if(PH>3000)
- {
- datas=6+(PH-3000)/177.50;
- }else if(PH>2822.5)
- {
- datas=7+(PH-2822.5)/177.50;
- }else if(PH>2646)
- {
- datas=8+(PH-2646)/176.50;
- }else if(PH>2467.5)
- {
- datas=9+(PH-2467.5)/178.50;
- }else if(PH>2292)
- {
- datas=10+(PH-2292)/175.50;
- }else if(PH>2115)
- {
- datas=11+(PH-2115)/177.00;
- }else if(PH>1938)
- {
- datas=12+(PH-1938)/177.00;
- }else if(PH>1758)
- {
- datas=13+(PH-1758)/180.00;
- }
- else
- {
- datas=14;
- }
- datas=datas*100;
- return datas;
- }
- void Voltag_read() //电压监测
- {
- long voltag;
- long datas;
- uint i;
- datas=GetADCResult(1);
- // voltag=datas*(4.5/1.024);
- voltag=datas_Value(datas*(4.5/1.024));
- number1[0]=voltag/1000; //求个位
- number1[1]='.';
- number1[2]=voltag%1000/100; //求十分位
- number1[3]=voltag%100/10; //求百分位
- number1[4]='V';
- LCD12864_SetWindow(3, 4);
- for(i=0;i<5;i++)
- {
- if(i==1|i==4)
- LCD12864_WriteData(number1[i]);
- else
- LCD12864_WriteData(number1[i]+48);
- }
- }
- /*---液位---*/
- void Conut(void)
- {
- int i;
- time=TH0*256+TL0;
- TH0=0;
- TL0=0;
- S=(time*1.8)/10; //算出来是mm
- S=280-S;
- if((S>=7000)||flag==1) //超出测量范围显示“-”
- flag=0;
- else
- {
- number2[0]=S/1000;
- number2[1]=S/100%10;
- number2[2]=S/10%10;
- number2[3]=S%10;
- number2[4]='m';
- number2[5]='m';
- LCD12864_SetWindow(1,4);
- for(i=0;i<6;i++)
- {
- if(i==4|i==5)
- LCD12864_WriteData(number2[i]);
- else
- LCD12864_WriteData(number2[i]+48);
- }
- }
- }
- void delayms(unsigned int ms)
- {
- unsigned char i=100,j;
- for(;ms;ms--)
- {
- while(--i)
- {
- j=10;
- while(--j);
- }
- }
- }
- void Timer_Count(void)
- {
- TR0=1; //开启计数
- while(RX); //当RX为1计数并等待
- TR0=0; //关闭计数
- Conut(); //计算
- }
- void StartModule() //T1中断用来扫描数码管和计800MS启动模块
- {
- TX=1; //800MS 启动一次模块
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- _nop_();
- TX=0;
- }
- void Distance_Value()
- {
- unsigned int valA;
- delayms(60);
- RX=1;
- StartModule();
- for(valA=7510;valA>0;valA--)
- {
- if(RX==1)
- {
- Timer_Count();
- }
- }
- }
- ulong datas_Value(long datas) //数据修正
- {
- int i;
- long v,sum,value[20];
- for(i=0;i<20;i++)
- {
- value[i]=datas;
- sum+=value[i];
- }
- v=sum/50.00;
- return v;
- }
- /*---发送ADC结果到PC---*/
- /*
- void ShowResult(char ch)
- {
- // SendData(ch); //显示通道号
- // SendData(GetADCResult(ch)); //显示ADC高8位结果
- // SendData(ADC_RESL); //显示低2位结果
- }
- */
- /*---读取ADC结果---*/
- unsigned long GetADCResult(uchar ch)
- {
- ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ch | ADC_START;
- _nop_(); //等待4个NOP
- _nop_();
- _nop_();
- _nop_();
- while (!(ADC_CONTR & ADC_FLAG));//等待ADC转换完成
- ADC_CONTR &= ~ADC_FLAG; //Close ADC
- ad_data1=ADC_RES<<2; //因为是10位的AD,因此需要把AD转换后的低8位向高位移动2位
- ad_data1=ad_data1|ADC_RESL&0x03; //再把高8位和低2位相加。
- ad_data1=ad_data1/1.024;
- return ad_data1; //返回ADC结果
- }
- /*---初始化ADC---*/
- void InitADC()
- {
- P1ASF = 0xff; //设置P1口为AD口
- CLK_DIV &=0xdf;
- ADC_RES = 0; //清除结果寄存器
- ADC_RESL= 0;
- ADC_CONTR = ADC_POWER | ADC_SPEEDH;
- Delay(2); //ADC上电并延时
- }
- /*---初始化串口---*/
- void InitUart()
- {
- SCON = 0x5a; //设置串口为8位可变波特率
- #if URMD == 0
- T2L = 0xd8; //设置波特率重装值
- T2H = 0xff; //115200 bps(65536-18432000/4/115200)
- AUXR = 0x14; //T2为1T模式, 并启动定时器2
- AUXR |= 0x01; //选择定时器2为串口1的波特率发生器
- #elif URMD == 1
- AUXR = 0x40; //定时器1为1T模式
- TMOD = 0x00; //定时器1为模式0(16位自动重载)
- TL1 = 0xd8; //设置波特率重装值
- TH1 = 0xff; //115200 bps(65536-18432000/4/115200)
- TR1 = 1; //定时器1开始启动
- #else
- TMOD = 0x20; //设置定时器1为8位自动重装载模式
- AUXR = 0x40; //定时器1为1T模式
- TH1 = TL1 = 0xfb; //115200 bps(256 - 18432000/32/115200)
- TR1 = 1;
- #endif
- }
- /*---发送串口数据---*/
- /*
- void SendData(uchar dat)
- {
- while (!TI); //等待前一个数据发送完成
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
所有资料51hei提供下载(代码):
简易水情监测.zip
(90.36 KB, 下载次数: 44)
|