基于51单片机的水质水位PH值溶解率电导率水温浊度检测proteus仿真原理图PCB

功能介绍:
0.本系统采用STC89C52作为单片机
1.液晶实时显示当前检测到的温度/浊度/电导率/PH信息,同时蓝牙串口上报
2.系统实时监测水箱水位和水箱盖状态(假设水箱最大深度1m)
3.当浊度/电导率超过设定阈值时,启动水泵排水,直至低于最低水位,停止排水,并启动抽水,抽水至最高水位停止
4.当浊度/电导率/PH值超过设定范围,蜂鸣器报警
5.当水箱盖打开蜂鸣器报警
6.可通过蓝牙发送*CS#命令抽水,*PS#命令排水
7.采用DC002作为电源接口可直接输入5V给整个系统供电

原理图:
在这里插入图片描述
在这里插入图片描述

PCB :
在这里插入图片描述

主程序:

#include "main.h"

/*******************变量定义*********************/
enum _MODE_DF_ dispMode;
bit modeFlag = AUTO; //模式标记
uchar setIndex = 0;

bit dispFlag = 0;
bit sendFlag = 0;

char dis[17];

float temperature; //温度
float distance = 0;//水位距离
int distance_H = 500;//水位上限
int distance_L = 50;//水位下限
float PH_Value = 0;//PH
int PH_Value_Max = 9;//PH上限
int PH_Value_Min = 6;//PH下限
int turbidity_Value = 0;//浊度
int turbidity_Value_Max = 20;//浊度上限
float TDS_Value = 0;//TDS
int TDS_Value_Max = 100;//TDS上限

bit drainFlag1 = 0; //排水标志1
bit drainFlag2 = 0; //排水标志2
bit drawFlag1 = 0; //抽水标志1
bit drawFlag2 = 0; //抽水标志2
bit buzzerFlag1 = 0; //蜂鸣器标志1
bit buzzerFlag2 = 0; //蜂鸣器标志2
bit buzzerFlag3 = 0; //蜂鸣器标志3

/********************************************************
函数名称:void mian()
函数作用:主函数
参数说明:
********************************************************/
void main()
{
    TRIG = 0;

    Timer0_Init();
    Timer1_Init(); //初始化定时器0
    UART_Init(); //初始化串口

    LCD_Init();   //初始化液晶
    DelayMs(200); //延时有助于稳定
    LCD_DispStr(4, 0, "Welcome!");

	DelayS(1);
    LCD_Clear();  //清屏


    while (1) //死循环
    {
        if (dispFlag == 1)
        {
            dispFlag = 0;

            if (dispMode == NORMAL) //正常显示模式
            {
                Measuring(&distance);
                DispNormal();
            }
        }

        if (sendFlag == 1)
        {
            sendFlag = 0;
            SendData();
        }

        if (turbidity_Value > turbidity_Value_Max || TDS_Value > TDS_Value_Max) //浊度超高或TDS超高
        {
            drainFlag1 = 1; //开启排水
            buzzerFlag1 = 1; //蜂鸣器报警标志
        }
        else
        {
            buzzerFlag1 = 0; //蜂鸣器报警标志
        }

        if (PH_Value > PH_Value_Max || PH_Value < PH_Value_Min) //PH值超过设定
        {
            buzzerFlag2 = 1; //蜂鸣器报警标志
        }
        else
        {
            buzzerFlag2 = 0; //蜂鸣器报警标志
        }

        if (distance > (1050 - distance_L)) //达到水位下限 1050mm的是考虑超声波探头的盲区50mm
        {
            drainFlag1 = 0; //停止排水
            drainFlag2 = 0; //停止排水
            drawFlag1 = 1; //开启抽水
        }
        else if (distance < (1050 - distance_H)) //达到水位上限
        {
            drawFlag1 = 0; //停止抽水
            drawFlag2 = 0; //停止抽水
        }

        if (LID == 1) //水箱盖打开
        {
            buzzerFlag3 = 1;
        }
        else
        {
            buzzerFlag3 = 0;
        }

        if (buzzerFlag1 || buzzerFlag2 || buzzerFlag3) //蜂鸣器报警
        {
            BUZZER = ON;
        }
        else
        {
            BUZZER = OFF;
        }

        if (drainFlag1 || drainFlag2) //排水
        {
            RELAY_DRAIN_WATER = ON;
        }
        else
        {
            RELAY_DRAIN_WATER = OFF;
        }

        if (drawFlag1 || drawFlag2) //抽水
        {
            RELAY_DRAW_WATER = ON;
        }
        else
        {
            RELAY_DRAW_WATER = OFF;
        }

        KeyProcess();
    }   
}

/************************* 定时器0初始化 *************************/
void Timer0_Init(void)
{
    TMOD |= 0x01; //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响
    // TH0 = (65536 - 9216) / 256; //重新赋值 10ms
    // TL0 = (65536 - 9216) % 256;
    TH0 = 0;
    TL0 = 0;
    EA = 1;  //总中断打开
    ET0 = 1; //定时器中断打开
    TR0 = 0; //定时器开关打开
}

/************************* 定时器0中断 *************************/
void Timer0_Interrupt(void) interrupt 1
{
    // static unsigned int time10ms  = 0;
    // TH0 = (65536 - 9216) / 256; //重新赋值 10ms
    // TL0 = (65536 - 9216) % 256;
}


/************************* 定时器1初始化 *************************/
void Timer1_Init(void)
{
    TMOD |= 0x10; //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响
    TH1 = (65536 - 9216) / 256; //重新赋值 10ms
    TL1 = (65536 - 9216) % 256;
    EA = 1;  //总中断打开
    ET1 = 1; //定时器中断打开
    TR1 = 1; //定时器开关打开
}

/************************* 定时器1中断 *************************/
void Timer1_Interrupt(void) interrupt 3
{
    static unsigned int time10ms  = 0;
    TH1 = (65536 - 9216) / 256; //重新赋值 10ms
    TL1 = (65536 - 9216) % 256;
    time10ms++;

    if (time10ms % 50 == 0)
    {
        dispFlag = 1; //显示标志
    }

    if (time10ms > 500)
    {
        time10ms = 0;
        sendFlag = 1; //发送信息标志
    }
}

/************************* 蓝牙串口发送数据 *************************/
void SendData(void)
{
    sprintf(dis, "T:%5.1f'C PH:%2d\r\n", temperature, (int)PH_Value);
    UART_SendStr(dis, 17); //发送数据
    DelayMs(10);
    sprintf(dis, "ZD:%2d%% TDS:%4d\r\n", turbidity_Value, (int)TDS_Value);
    UART_SendStr(dis, 17); //发送数据
    DelayMs(10);
    sprintf(dis, "SW:%7.2fmm   \r\n", (1050-distance));
    UART_SendStr(dis, 17); //发送数据
}


/************************* 超声波测距 *************************/
void Measuring(float *distance)
{
    static long cnt = 0; //定时器计数
    
    TR1 = 0;
    
    TRIG = 1; //启动一次模块		//不可以使用其他终端 容易造成死循环
    DelayUs10x(1);
    TRIG = 0;
    while (!ECHO)
        ;	 //当RX为零时等待
    TR0 = 1; //开启计数
    while (ECHO)
        ; //当RX为1计数并等待
    TR0 = 0;
    cnt = (long)(TH0 * 256 + TL0);
    TH0 = 0;
    TL0 = 0;
    *distance = (float)cnt * 17 / 100.0 * 1.102; //算出来是mm (g_cnt * 340 / 2) / 1000.0 * 1.102;系数
    cnt = 0;
    TR1 = 1;

    if (*distance > 1050)
    {
        *distance = 1050;
    }

//    sprintf(dis, "    %7.2fmm   ", *distance);
//    LCD_DispStr(0, 1, dis);
}

/************************* 正常显示模式 *************************/
void DispNormal(void)
{
    static char cnt = 0;
    int tempBuf;

    /************************* 18b20数据读取显示 *************************/
    DS18B20_Start();
    DS18B20_GetTemp(&tempBuf);
    temperature = tempBuf * 0.0625;
    sprintf(dis, "T:%5.1f", temperature);
    LCD_DispStr(0, 0, dis);
    LCD_DispOneChar(7, 0, 0xdf);
    LCD_DispOneChar(8, 0, 'C');

    /************************* PH读取显示 *************************/
    PH_Value = 5 * (float)ReadADC_1(AIN0_GND) / 255; //读取PH
    PH_Value = -5.7541 * PH_Value + 16.654;
    if (PH_Value < 0)
    {
        PH_Value = 0;
    }
    sprintf(dis, " PH:%2d", (int)PH_Value);
    LCD_DispStr(9, 0, dis);

    /************************* 浊度读取显示 *************************/
    turbidity_Value = 100 - 100 * ReadADC(AIN0_GND) / 255; //读取浊度

    /************************* TDS读取显示 *************************/
    TDS_Value = 5 * ReadADC(AIN1_GND) / 255; //读取TDS电压
    TDS_Value = 66.71*TDS_Value*TDS_Value*TDS_Value - 127.93*TDS_Value*TDS_Value + 428.7*TDS_Value; //计算TDS
    
    cnt++;
    if (cnt <5) //循环显示
    {
        sprintf(dis, "ZD:%2d%% TDS:%4d", turbidity_Value, (int)TDS_Value);
        LCD_DispStr(0, 1, dis);
    }
    else if (cnt < 10)
    {
        sprintf(dis, "SW:%7.2fmm   ", (1050-distance));
        LCD_DispStr(0, 1, dis);
    }
    else
    {
        cnt = 0;
    }


}

/************************* 设置水位阈值 *************************/
void DispSetWater(unsigned char setIndex)
{

    LCD_DispStr(0, 0, "   Set Water    ");
    sprintf(dis, " H:%4d  L:%4d  ", distance_H, distance_L);
    LCD_DispStr(0, 1, dis);
    
    switch (setIndex)
    {
        case 1: LCD_SetCursor(6, 1, 1); break;
        case 2: LCD_SetCursor(14, 1, 1); break;
        default:;
    } 
}

/************************* 设置PH阈值 *************************/
void DispSetPH(unsigned char setIndex)
{

    LCD_DispStr(0, 0, "  Set PH Limit  ");
    sprintf(dis, "  H:%3d  L:%3d  ", PH_Value_Max, PH_Value_Min);
    LCD_DispStr(0, 1, dis);
    
    switch (setIndex)
    {
        case 1: LCD_SetCursor(6, 1, 1); break;
        case 2: LCD_SetCursor(13, 1, 1); break;
        default:;
    } 
}

/************************* 设置浊度阈值 *************************/
void DispSetTurbidity(unsigned char setIndex)
{

    LCD_DispStr(0, 0, "  Set Turbidity ");
    sprintf(dis, "    Max:%3d   ", turbidity_Value_Max);
    LCD_DispStr(0, 1, dis);
    
    switch (setIndex)
    {
        case 1: LCD_SetCursor(10, 1, 1); break;
        default:;
    } 
}

/************************* 设置TDS阈值 *************************/
void DispSetTDS(unsigned char setIndex)
{

    LCD_DispStr(0, 0, "  Set TDS Limit ");
    sprintf(dis, "    Max:%4d   ", TDS_Value_Max);
    LCD_DispStr(0, 1, dis);
    
    switch (setIndex)
    {
        case 1: LCD_SetCursor(11, 1, 1); break;
        default:;
    } 
}

/************************* 串口配置 *************************/
void UART_Init(void)
{
	SCON = 0x50;
	TH2 = 0xFF;
	TL2 = 0xDC;
	RCAP2H = 0xFF;  //(65536-(FOSC/32/BAUD))   BAUD = 9600 FOSC = 11059200
	RCAP2L = 0xDC;

	/*****************/
	TCLK = 1;
	RCLK = 1;
	C_T2 = 0;
	EXEN2 = 0;

	/*****************/
	TR2 = 1;
	ES   = 1; //打开串口中断
	EA   = 1; //打开总中断

}

/************************* 串口发送字节 *************************/
void UART_SendByte(unsigned char dat) //串口发送单字节数据
{
	unsigned char time_out;
    
	time_out = 0;
	SBUF = dat;						  //将数据放入SBUF中
	while ((!TI) && (time_out < 100)) //检测是否发送出去
	{
		time_out++;
		DelayUs10x(2);
	}		//未发送出去 进行短暂延时
	TI = 0; //清除ti标志
}

/************************* 串口发送字符串 *************************/
void UART_SendStr(unsigned char *s, unsigned char length)
{
	unsigned char num;
	num = 0x00;
	while (num < length) //发送长度对比
	{
		UART_SendByte(*s); //放松单字节数据
		s++;			  //指针++
		num++;			  //下一个++
	}
}

/************************* 串口中断 *************************/
void UART_Interrupt(void) interrupt 4 //串行中断服务程序
{
    static unsigned char i = 0;
    static unsigned char firstBit = 0;
    static unsigned char R_buf[4];
    
    if (RI)//判断是接收中断产生
    {
        RI = 0; //标志位清零
        if (SBUF == '*')
        {
            firstBit = 1; //接收标志成功
            i = 0;
            R_buf[1] = 0;
            R_buf[2] = 0;
            R_buf[3] = 0;
        }
        if (firstBit == 1)
        {
            R_buf[i] = SBUF;
            SBUF = SBUF;
            i++;
            if (i == 4)
            {
                i = 0;
                if (R_buf[0] == '*' && R_buf[3] == '#')
                {
                    if (R_buf[1] == 'C' && R_buf[2] == 'S') //抽水
                    {
                        drawFlag2 = 1; 
                    }
                    else if (R_buf[1] == 'P' && R_buf[2] == 'S') //排水
                    {
                        drainFlag2 = 1;
                    }
                }
                firstBit = 0;
            }
        }
    }
}

仿真演示视频:
https://www.bilibili.com/video/BV1AZ4y1b7C1/

实物演示视频:
https://www.bilibili.com/video/BV1xT4y1q7Jf/

  • 2
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然,我可以给你提供一个基本的示例代码,但请注意这只是一个简单的框架,你需要根据你使用的具体浊度传感器型号和单片机型号进行适当的修改和调整。 ```c #include "stm32f4xx.h" #include "stdio.h" // 定义浊度传感器相关的引脚和通信协议等信息 #define SENSOR_SPI SPI1 #define SENSOR_CS_PIN GPIO_Pin_4 #define SENSOR_CS_PORT GPIOA // 初始化SPI总线 void SPI_Init(void) { // 使能SPI时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); // 配置SPI引脚 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStruct); // 将SPI引脚映射到SPI功能 GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1); // 配置SPI参数 SPI_InitTypeDef SPI_InitStruct; SPI_InitStruct.SPI_Mode = SPI_Mode_Master; SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; SPI_Init(SPI1, &SPI_InitStruct); // 使能SPI SPI_Cmd(SPI1, ENABLE); } // 初始化浊度传感器引脚 void Sensor_Init(void) { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = SENSOR_CS_PIN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(SENSOR_CS_PORT, &GPIO_InitStruct); } // 选浊度传感器 void Sensor_Select(void) { GPIO_ResetBits(SENSOR_CS_PORT, SENSOR_CS_PIN); } // 取消选浊度传感器 void Sensor_Deselect(void) { GPIO_SetBits(SENSOR_CS_PORT, SENSOR_CS_PIN); } // 从浊度传感器读取数据 uint16_t Sensor_ReadData(void) { uint16_t data = 0; Sensor_Select(); // 发送读取数据的命令 SPI_I2S_SendData(SENSOR_SPI, 0x01); while (SPI_I2S_GetFlagStatus(SENSOR_SPI, SPI_I2S_FLAG_TXE) == RESET); while (SPI_I2S_GetFlagStatus(SENSOR_SPI, SPI_I2S_FLAG_RXNE) == RESET); SPI_I2S_ReceiveData(SENSOR_SPI); // 读取传感器数据 SPI_I2S_SendData(SENSOR_SPI, 0xFF); while (SPI_I2S_GetFlagStatus(SENSOR_SPI, SPI_I2S_FLAG_TXE) == RESET); while (SPI_I2S_GetFlagStatus(SENSOR_SPI, SPI_I2S_FLAG_RXNE) == RESET); data = SPI_I2S_ReceiveData(SENSOR_SPI); Sensor_Deselect(); return data; } int main(void) { // 初始化系统时钟和外设 // ... // 初始化SPI总线 SPI_Init(); // 初始化浊度传感器引脚 Sensor_Init(); while(1) { // 读取浊度传感器数据 uint16_t sensorData = Sensor_ReadData(); // 处理浊度传感器数据 // ... // 延时一段时间 Delay(1000); } } ``` 这是一个简单的示例代码,通过SPI总线与浊度传感器进行通信,并周期性地读取传感器数据。你需要根据具体的传感器型号和单片机型号进行适当的修改,包括引脚配置、SPI参数设置和数据处理等。另外,还需要根据你的具体需求添加其他功能,例如延时函数、数据处理算法等。 请注意,在实际开发过程,你可能还需要处理错误和异常情况,以及进行适当的优化和调试。希望这个示例能对你有所帮助!如有任何疑问,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值