基于STM32的智能鱼缸控制系统的Proteus仿真

文章目录

  • 一、智能鱼缸控制系统
    • 1.题目要求
    • 2.思路
      • 2.1 主控
      • 2.2 传感器
      • 2.3 按键
      • 2.4 声光报警
      • 2.5 自动换水,喂食,供氧
      • 2.6 OLED显示
      • 2.7 电源部分
      • 2.8 远程终端
    • 3.电路仿真
      • 3.1 未仿真时
      • 3.2 开始仿真,正常显示
      • 3.3 按下设置按键,进入阈值界面
      • 3.4 通过阈值加减按键控制阈值大小
      • 3.5 温度大于温度阈值,声光报警
      • 3.6 远程终端设置阈值和换水时间,喂食时间
    • 4.仿真程序
      • 4.1 程序说明
      • 4.2 主程序
      • 4.3 OLED显示函数
      • 4.4 按键扫描函数
  • 二、总结



一、智能鱼缸控制系统

1.题目要求

1、检测温度、水位、PH值、浑浊度;
2、按键设定阈值,高于阈值,声光报警;
3、OLED双界面显示,第一界面正常显示温度等,第二界面显示阈值,可通过按键切换返回;
4、自动换水,自动喂食,自动供氧;
5、手机端设置各个阈值,换水时间和喂食时间。

在这里插入图片描述

2.思路

2.1 主控

由系统框图可知我们需要一个单片机,这里首选还是STM32。因为有好几个传感器在Proteus仿真里面是没有的,得用滑动变阻器模拟,到时候就需要用到AD外设。

在这里插入图片描述

2.2 传感器

温度传感器Proteus仿真里面有常用的温度传感器DS18B20和温湿度传感器DHT11,这里选择DS18B20.

剩下的水位传感器,PH传感器,浑浊度传感器用滑动变阻器去模拟。

2.3 按键

因为显示有两个界面和多个阈值,所以这边设定4个按键来切换。

按键一(设置按键):初始正常显示界面,按下进入阈值界面,阈值界面有4个阈值,再次按下选择下一个阈值,依次类推;
按键二(阈值加按键):除PH值阈值是+0.1(规定阈值范围:1.0-9.9),其他三者都是+1(规定阈值范围:10-99)
按键三(阈值减按键):除PH值阈值是-0.1(规定阈值范围:1.0-9.9),其他三者都是-1(规定阈值范围:10-99)
按键一(返回按键):按一下退出设置阈值界面,返回初始正常显示界面。
在这里插入图片描述

2.4 声光报警

在这里插入图片描述

2.5 自动换水,喂食,供氧

在这里插入图片描述

2.6 OLED显示

OLED显示温度、水位、PH、浊度,要显示中文,这需要用到取模软件,如下所示:

在这里插入图片描述
参数设置为:

在这里插入图片描述

2.7 电源部分

2.8 远程终端

手机端设置各个阈值,换水时间和喂食时间。

在这里插入图片描述

3.电路仿真

3.1 未仿真时

在这里插入图片描述

3.2 开始仿真,正常显示

OLED显示正常温度,水位,PH值,浊度的值,因为是鱼缸,默认是一直供氧的,喂食和进水默认时间是3s,也就是前3s进水,后3s出水,前3s喂食,后3s不喂食,换水时间和喂食时间都是可以通过远程终端去设置的。

在这里插入图片描述

3.3 按下设置按键,进入阈值界面

在这里插入图片描述

3.4 通过阈值加减按键控制阈值大小

在这里插入图片描述

3.5 温度大于温度阈值,声光报警

在这里插入图片描述

3.6 远程终端设置阈值和换水时间,喂食时间

具体协议见下面的仿真程序说明
在这里插入图片描述

4.仿真程序

4.1 程序说明

主控芯片:STM32F103C8
HICK:64MHZ
Systick: 1ms

水位传感器:ADC1(PA0)

PH传感器:ADC2(PA1)

浑浊度传感器:ADC2(PA1)

DS18B20温度传感器:DAT(PA4)

模拟Wifi模块(Uart1):9600(PA9:tx1,PA10:rx1)

模拟进水:Into(PA12)
模拟出水:Out(PA13)
模拟喂食:Feed(PA14)
模拟进水:Breath(PA15)

KEY按键:
KEY1(PB0)
KEY2(PB1)
KEY3(PB2)
KEY3(PB3)

时钟模块DS1302:
SCLK(PB5)
IO(PB6)
RST(PB7)

风扇:FAN(PB10)

OLED显示屏:SCL(PB12),SDA(PB13)

蜂鸣器:BUZZER(PB15)

在这里插入图片描述

串口协议
设置温度阈值TH1HH,HH取值范围为(10-99),如 TH150,即设置温度阈值为50.

设置水位阈值TH2HH,HH取值范围为(10-99)
设置PH阈值
TH3HH,HH取值范围为(1.0-9.9)
设置浊度阈值*TH4HH,HH取值范围为(10-99)

设置换水时间TM1HH,HH取值范围为(1-99)秒
设置喂食时间
TM2HH,HH取值范围为(1-99)秒

4.2 主程序

/* Includes ------------------------------------------------------------------*/
#include "Drv_UserSystem.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
/**
  * @brief  main function.
  * @param  none
  * @retval none
  */
int main(void)
{
	UserSystemInit();//用户配置初始化		
	while (1)
	{			
		if (stSysTime.flg._100ms + TEN_MILLISECOND < Time_millis()) //10ms
		{
			stSysTime.flg._10ms = Time_millis();
			Key_Scan();//按键扫描						
		}
		if (stSysTime.flg._50ms + FIFTY_MILLISECOND < Time_millis()) //50ms
		{
			stSysTime.flg._50ms = Time_millis();						
			ADC_Scan();	//检测							
		}				
		if(stSysTime.flg._100ms + BEST_MILLISECOND < Time_millis()) //100ms
		{
			stSysTime.flg._100ms = Time_millis();					
			Receive_data_Handel();//数据接收判断 			
			Warning_function();//预警							
			OLED_Handel();//OLED显示								
			IWDG_ReloadCounter();//清开门狗 				
		}
		if (stSysTime.flg._1s + THOUSAND_MILLISECOND < Time_millis()) //1s
		{
			stSysTime.flg._1s = Time_millis();	
            timer_task();//时间任务			
		}
  }
}

4.3 OLED显示函数

/*******************************************************************************
 * 函数名:OLED_Handel
 * 描述  :OLED显示
 * 输入  :void
 * 输出  :void
 * 调用  :初始化
 * 备注  :100ms
*******************************************************************************/
void OLED_Handel(void)
{  	
   if(ADC_Flag)
	 {
		if(Interface_switching == 0) 
		{		
				OLED_Show_Character(1,1,0,16);//温
				OLED_Show_Character(1,2,1,16);//度	
				OLED_Show_Character(1,3,2,16);//:	 			
			
				temperature = DS18B20_Get_Temp();
				temperature = (-1) * temperature;
		 
//				printf("temperature=%d\r\n",temperature);	 
			 
				Temp_High = temperature / 10;
				if(Temp_High < 10)
				{
					if(Temperature_jump == 1)
					{
							Temperature_jump = 0;
							OLED_Part_Clear(1,8,9);
					}
					OLED_ShowNum(1,10,Temp_High,1);			
				}
				else
				{
					Temperature_jump = 1;
					OLED_ShowNum(1,9,Temp_High,2);				
				}
				
		    if(Temp_High > Temp_Threshold)
				{
		        Warning1 = 1;					
				}
				else
				{
		        Warning1 = 0;							
				}				
				
				Temp_Low = temperature % 10;
				OLED_ShowNum(1,12,Temp_Low,1);
				OLED_ShowString(1, 11, ".");	
				OLED_Show_Character(1,7,3,16);	
				OLED_ShowString(1, 15, "C");		

				OLED_Show_Character(2,1,4,16);//水
				OLED_Show_Character(2,2,5,16);//位
				OLED_Show_Character(2,3,2,16);//:		

		    if(ADC1_Value > ADC1_Threshold)
				{
		        Warning2 = 1;					
				}
				else
				{
		        Warning2 = 0;							
				}

			  if(ADC1_Value < 10)
				{
						OLED_Part_Clear(2,8,9);					
				    OLED_ShowNum(2,10,ADC1_Value,1);						
				}
				else
				{					
				    OLED_ShowNum(2,9,ADC1_Value,2);	
				}	

				OLED_ShowString(3, 1, "PH");	
				OLED_Show_Character(3,2,9,16);//值
				OLED_Show_Character(3,3,2,16);//:	

		    if(ADC2_Value > ADC2_Threshold)
				{
		        Warning3 = 1;					
				}
				else
				{
		        Warning3 = 0;							
				}

				OLED_ShowNum(3,9,ADC2_High,1);						
				OLED_ShowString(3, 10, ".");		
				OLED_ShowNum(3,11,ADC2_Low,1);														

				OLED_Show_Character(4,1,6,16);//浊
				OLED_Show_Character(4,2,7,16);//度
				OLED_Show_Character(4,3,2,16);//:		

		    if(ADC3_Value > ADC3_Threshold)
				{
		        Warning4 = 1;					
				}
				else
				{
		        Warning4 = 0;							
				}

			  if(ADC3_Value < 10)
				{
						OLED_Part_Clear(4,8,9);					
				    OLED_ShowNum(4,10,ADC3_Value,1);						
				}
				else
				{					
				    OLED_ShowNum(4,9,ADC3_Value,2);	
				}				
		}
    else
		{
				if(Interface_switching == 1)
				{
					OLED_Show_Character(1,8,10,16);//←					
				}
				else if(Interface_switching == 2)
				{
					OLED_Show_Character(2,8,10,16);//←							
				}
				else if(Interface_switching == 3)
				{
					OLED_Show_Character(3,8,10,16);//←							
				}				
				else if(Interface_switching == 4)
				{
					OLED_Show_Character(4,8,10,16);//←							
				}				
			
				OLED_Show_Character(1,1,0,16);//温
				OLED_Show_Character(1,2,1,16);//度	
				OLED_Show_Character(1,3,8,16);//阈
				OLED_Show_Character(1,4,9,16);//值				
				OLED_Show_Character(1,5,2,16);//:		

				OLED_ShowNum(1,12,Temp_Threshold,2);	

				OLED_Show_Character(2,1,4,16);//水
				OLED_Show_Character(2,2,5,16);//位
				OLED_Show_Character(2,3,8,16);//阈
				OLED_Show_Character(2,4,9,16);//值				
				OLED_Show_Character(2,5,2,16);//:			

				OLED_ShowNum(2,12,ADC1_Threshold,2);	
			
				OLED_ShowString(3, 1, "PH");	
				OLED_Show_Character(3,2,8,16);//阈			
				OLED_Show_Character(3,3,9,16);//值
				OLED_Show_Character(3,4,2,16);//:	
			
				OLED_ShowNum(3,12,ADC2_ThresholdHigh,1);						
				OLED_ShowString(3, 13, ".");		
				OLED_ShowNum(3,14,ADC2_ThresholdLow,1);	

				OLED_Show_Character(4,1,6,16);//浊
				OLED_Show_Character(4,2,7,16);//度
				OLED_Show_Character(4,3,8,16);//阈
				OLED_Show_Character(4,4,9,16);//值					
				OLED_Show_Character(4,5,2,16);//:		

				OLED_ShowNum(4,12,ADC3_Threshold,2);				
		}		
	}		
}

4.4 按键扫描函数

/*******************************************************************************
 * 函数名:Key_Scan
 * 描述  :按键扫描
 * 输入  :void
 * 输出  :void
 * 调用  :10ms
 * 备注  :
 *******************************************************************************/
void Key_Scan(void)
{				
		if(Key1_IN_Read() == 0)//key1
		{
			KeyState.value1 = 1;
			if(!KeyState.Press1)
			{			
				  if(Interface_switching == 0)
					{
					    OLED_Clear();							
					}
				  Interface_switching++;					
				  if(Interface_switching > 4)
					{
						Interface_switching = 1;
					}
					if(Interface_switching == 2)
					{
							OLED_Part_Clear(1,15,16);						
					}
					else if(Interface_switching == 3)
					{
							OLED_Part_Clear(2,15,16);							
					}
					else if(Interface_switching == 4)
					{
							OLED_Part_Clear(3,15,16);							
					}
					else if(Interface_switching == 1)
					{
							OLED_Part_Clear(4,15,16);							
					}					
					printf("按键1按下 \r\n");					
			}
			KeyState.Press1 = 1;					
		}		
		else
		{		
			if(KeyState.value1)
			{
					KeyState.value1= 0;
					printf("按键1松开 \r\n");									
			}
			KeyState.Press1 = 0;
		}		
		
		if(Key2_IN_Read() == 0)//key2
		{
			KeyState.value2 = 1;
			if(!KeyState.Press2)
			{						
				  if(Interface_switching == 1)
					{		
						Temp_Threshold++;
						if(Temp_Threshold > 98)
						{
							Temp_Threshold = 99;					
						}						
					}
					else if(Interface_switching == 2)
					{
						ADC1_Threshold++;
						if(ADC1_Threshold > 98)
						{
							ADC1_Threshold = 99;					
						}												
					}				
					else if(Interface_switching == 3)
					{
						ADC2_Threshold++;
						if(ADC2_Threshold > 98)
						{
							ADC2_Threshold = 99;					
						}		
						ADC2_ThresholdHigh = ADC2_Threshold/10;
						ADC2_ThresholdLow = ADC2_Threshold%10;								
					}	
					else if(Interface_switching == 4)
					{
						ADC3_Threshold++;
						if(ADC3_Threshold > 98)
						{
							ADC3_Threshold = 99;					
						}						
					}						
					printf("按键2按下 \r\n");						
			}
			KeyState.Press2 = 1;
		}		
		else
		{		
			if(KeyState.value2)
			{
					KeyState.value2= 0;
					printf("按键2松开 \r\n");								
			}					
			KeyState.Press2 = 0;
		}	

		if(Key3_IN_Read() == 0)//key3
		{
			KeyState.value3 = 1;					
			if(!KeyState.Press3)
			{					
				  if(Interface_switching == 1)
					{
						if(Temp_Threshold < 11)
						{
							Temp_Threshold = 10;					
						}
						else
						{
							Temp_Threshold--;
						}						
					}
					else if(Interface_switching == 2)
					{
						if(ADC1_Threshold < 11)
						{
							ADC1_Threshold = 10;					
						}
						else
						{
							ADC1_Threshold--;
						}							
					}				
					else if(Interface_switching == 3)
					{
						if(ADC2_Threshold < 11)
						{
							ADC2_Threshold = 10;					
						}
						else
						{
							ADC2_Threshold--;
						}	
						ADC2_ThresholdHigh = ADC2_Threshold/10;
						ADC2_ThresholdLow = ADC2_Threshold%10;							
					}	
					else if(Interface_switching == 4)
					{
						if(ADC3_Threshold < 11)
						{
							ADC3_Threshold = 10;					
						}
						else
						{
							ADC3_Threshold--;
						}							
					}							
					printf("按键3按下 \r\n");						
			}
			KeyState.Press3 = 1;
		}		
		else
		{		
			if(KeyState.value3)
			{
					KeyState.value3= 0;
					printf("按键3松开 \r\n");									
			}						
			KeyState.Press3 = 0;
		}	

		if(Key4_IN_Read() == 0)//key4
		{
			KeyState.value4 = 1;					
			if(!KeyState.Press4)
			{			
				  if(Interface_switching != 0)
					{
					    OLED_Clear();	 
  						Interface_switching = 0;							
					}						
					printf("按键4按下 \r\n");						
			}
			KeyState.Press4 = 1;
		}		
		else
		{		
			if(KeyState.value4)
			{
					KeyState.value4= 0;
					printf("按键4松开 \r\n");									
			}						
			KeyState.Press4 = 0;
		}			
}

二、总结

今天主要讲了基于STM32的智能鱼缸控制系统的Proteus仿真。

感谢你的观看!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/922527.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Python爬虫】Scrapy框架实战---百度首页热榜新闻

如何利用Scrapy框架实战提取百度首页热榜新闻的排名、标题和链接 一、安装Scrapy库 二、创建项目&#xff08;以BaiduSpider为例&#xff09; scrapy startproject BaiduSpider生成每个文件的功能&#xff1a; 二、 创建爬虫脚本&#xff08;爬虫名&#xff1a;news&#xff…

mysql-分析MVCC原理

一、MVCC简介 MVCC是一种用来解决读写冲读的无锁并发控制&#xff0c;也就是为事务分配单增长的时间戳&#xff0c;为每个修改保存一个版本&#xff0c;版本与事务时间戳关联&#xff0c;读操作只读该事务开始前的数据库的快照&#xff0c;所以MVCC可以为数据库解决一些问题。…

论文笔记:Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks

1. 挑战/问题&#xff08;Challenges/Issues&#xff09;&#xff1a; 这篇论文探讨了大型预训练语言模型在处理知识密集型自然语言处理&#xff08;NLP&#xff09;任务时面临的挑战。尽管这些模型在参数中存储了大量事实知识&#xff0c;并在微调后能够在下游NLP任务中取得很…

嵌入式硬件电子电路设计(六)LDO低压差线性稳压器全面详解

引言&#xff1a; LDO&#xff08;Low Dropout Regulator&#xff0c;低压差线性稳压器&#xff09;是一种常用的电源管理组件&#xff0c;用于提供稳定的输出电压&#xff0c;同时允许较小的输入电压与输出电压之间的差值。LDO广泛应用于各种电子设备中&#xff0c;特别是在对…

Spring:AOP面向切面案例讲解AOP核心概念

Spring的AOP&#xff0c;在不惊动(改动)原有设计(代码)的前提下&#xff0c;想给谁添加功能就给谁添加。这个也就是Spring的理念&#xff1a; 无入侵式/无侵入式 AOP中核心概念分别指的是什么? 连接点切入点通知通知类切面 下面以一个例子进行讲解&#xff0c;直接上代码&a…

禁止Chrome的自动升级

一、需求分析 因为用Chromeselenium做了网页自动化填写任务&#xff0c;如果Google Chrome浏览器自动升级&#xff0c;就会导致chromedriver加载失败&#xff0c;自动化任务失效&#xff0c;因此需要禁止Chrome浏览器的自动升级。 二、当前环境 三、实际配置 运行注册表编辑…

2024年wordpress、d-link等相关的多个cve漏洞poc

⚠️ 漏洞 ✅ CVE-2024-10914 在D-Link DNS-320、DNS-320LW、DNS-325和DNS-340L中发现的漏洞&#xff0c;版本直到20241028 GET /cgi-bin/account_mgr.cgi?cmdcgi_user_add&name%27;id;%27 HTTP/1.1✅ CVE-2024-11305 在Altenergy Power Control Software中发现的关键…

Spring框架特性及包下载(Java EE 学习笔记04)

1 Spring 5的新特性 Spring 5是Spring当前最新的版本&#xff0c;与历史版本对比&#xff0c;Spring 5对Spring核心框架进行了修订和更新&#xff0c;增加了很多新特性&#xff0c;如支持响应式编程等。 更新JDK基线 因为Spring 5代码库运行于JDK 8之上&#xff0c;所以Spri…

从搭建uni-app+vue3工程开始

技术栈 uni-app、vue3、typescript、vite、sass、uview-plus、pinia、axios 一、项目搭建 1、创建以 typescript 开发的工程 npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project2、安装sass npm install -D sass// 安装sass-loader&#xff0c;注意需要版本10&…

WPF中的登录界面

创建如下的目录结构&#xff1a; 2.在App.xaml.cs中设置为先登录验证之后再进入主页面 using Prism.Ioc; using System.Windows; using 校园访客系统.Views;namespace 校园访客系统 {/// <summary>/// Interaction logic for App.xaml/// </summary>public partia…

ros2学习日记_241124_ros相关链接

前言 提醒&#xff1a; 文章内容为方便作者自己后日复习与查阅而进行的书写与发布&#xff0c;其中引用内容都会使用链接表明出处&#xff08;如有侵权问题&#xff0c;请及时联系&#xff09;。 其中内容多为一次书写&#xff0c;缺少检查与订正&#xff0c;如有问题或其他拓展…

ETAS工具导入DBC生成Com协议栈

文章目录 前言DBC配置关键属性Cobra参数配置Cobra使用isolar工程配置总结前言 ETAS工具导入DBC主要也是生成arxml用的,ETAS推荐使用Cobra导入,本文介绍导入过程及注意事项 DBC配置关键属性 对于普通Com报文,配置为周期发送,及其周期,NmMessage配置为No,示例如下: 对…

Kafka 工作流程解析:从 Broker 工作原理、节点的服役、退役、副本的生成到数据存储与读写优化

Kafka&#xff1a;分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析&#xff1a;从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析&#xff1a…

如果在docker 容器中安装ros遇到的问题

1.在容器内部无法修改时间&#xff0c;需要在宿主机外边修改时钟。修改时钟&#xff1a; hwclock --systohc或者执行 date -s "2024-11-24 19:25:10"2.容器内部内置有opencv4.5版本&#xff0c;需要卸载&#xff0c;重新安装4.2.0版本。记录折腾好久的卸载过程。 …

排序(Java数据结构)

1. 排序的概念及引用 1.1 排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。(所有的排序都是默认从小到大排序) 稳定性&#xff1a;假定在待排序的记录序列中&#xff…

AutoDL安装docker问题

在AutoDL上租了卡&#xff0c;安装docker遇到一些问题&#xff1a; 1.执行 sudo docker run hello-world 报错 docker: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? 解决方法 先查看docker有没有启动&#xff0c;…

ArcGIS定义投影与投影的区别(数据和底图不套合的原因和解决办法)

今天介绍一下ArcGIS中定义投影与投影的区别。 给大家解惑一下为什么经常出现自己的数据无法和底图套合的情况。 一 目录 1、ArcGIS定义投影与投影的概念区别 2、ArcGIS定义正确的坐标系 3、ArcGIS动态投影实现套合 4、ArcGIS地理坐标系转投影坐标系&#xff08;错误做法&am…

ChatGPT 桌面版发布了,如何安装?

本章教程教大家如何进行安装。 一、下载安装包 官网地址地址&#xff1a;https://openai.com/chatgpt/desktop/ 支持Windows和MacOS操作系统 二、安装步骤 Windows用户下载之后&#xff0c;会有一个exe安装包&#xff0c;点击运行安装即可。 注意事项&#xff0c;如果Windows操…

鸿蒙开发——根据背景图片来构建特定颜色的蒙版

效果图如下(文字部分马赛克处理)&#xff1a; 最近突然发现网易云和QQ音乐这些图片上方的蒙版颜色不是固定的&#xff0c;而是跟着图片内容走的&#xff0c;想看看能不能在鸿蒙实现&#xff0c;最后凭借俺寻思之力寻思出了一套流程(有bug&#xff0c;有时候蒙版直接透明了&…

clipboard

clipboard 现代复制到剪贴板。无闪光。只有 3kb 的 gzip 压缩。 安装 npm install clipboard --save第三方cdn提供商 <script src"https://cdn.jsdelivr.net/npm/clipboard2.0.11/dist/clipboard.min.js"></script>使用 data-clipboard-target"…