预警器件控制思考
最小示例思想
当读取到环境信息与环境阈值的时候, 我们预警系统就要根据这些信息做出判断,是否要启动器件。
![image-20240801174851481](https://img-blog.csdnimg.cn/img_convert/799e0526eba9b64e4203cd8d935c4ee9.png)
最简单的就是, 举温度temp的例子, temp(温度)与temp_th(阈值), 通过判断, 得出是否要启动器件.
![image-20240802061601931](https://img-blog.csdnimg.cn/img_convert/aacdb2a6841a745d60bdf4c21ba56e1b.png)
如果在一段时间内, 一直是环境异常, 我们那只需要启动一次器件就可以了, 按照上面的方法, 就会一直重复启动器件. 所以我们在启动器件前, 需要判断一下, 器件的状态, 如果要切换状态的话, 那么我们就操作硬件.
(1)当temp > temp_th 时, 当环境刚异常, 并且器件没有启动的时候, 此时我们判断出此状态, 那么我们就启动硬件.
那下一次判断的时候, 环境仍然异常, 但是器件刚才已经启动了, 那么通过判断, 我们就不用启动了.
(2)当temp < temp_th 时, 当环境刚恢复正常时, 并且器件启动的时候, 此时我们判断出此状态, 那么我们就可以关闭硬件了.
那下一次判断的时候, 环境仍然正常, 但是器件刚才已经关闭了, 那么通过判断, 我们就不用重复关闭了.
(3) 所以, 当我们要切换器件状态的时候, 我们首先判断器件当前的状态是否和我们所需一致, 如果一致, 那么我们就不进行切换即可.
(4)最小例程的代码, 是用 red_led 和 blue_led 来存储温湿度应急器件的状态的, 并且这个标志位是和器件io口绑定状态, 我们在操作io口后,我们都要更新这个状态。只是为了避免频繁操作器件,我们在判断预警操作器件的时候,都需要判断当前器件状态,是否已经符合, 如果符合,那么就不操作。我们这里功能不是太复杂,所以就用这种方法来实现。
后续如果加入操作系统的思想的话,尤其是硬件的操作,需要多级容错判断,来保证绝对的安全,这里只做简单分析。
![image-20240801175047736](https://img-blog.csdnimg.cn/img_convert/dd6d33befdb1a120df208b4514a5b983.png)
器件状态绑定
我们通过观察发现 ,red_led和 PBout(3)深度绑定, 所以直接新建一个文件, 然后新建一个结构体, 来保存器件的状态, 操作器件开关.
我们先观察下面示例, 然后分析学习, 构建我们的工程:
(1)观察, 这个函数,是操作器件的, 然后每次操作的时候, 都会传入一个status, 就是我们所需的状态,
但是 传入状态并不代表着我们就要频繁操作io口, 因为在一段时间内环境一直异常,我们100ms钟检测一次,当我们检测到异常, 我们就启动器件, 因为要保证预警系统的实时性, 所以下一个100ms检测的时候, 器件已经运行了,我们就无需重复启动了. 所以当我们传入状态后, 想开器件, 就判断一下器件是否已经启动, 如果启动, 就说明在上一个100ms的时候, 就已经启动了,我们这次进入只是为了保证实时性, 从而重复确认.
同理, 关闭器件也一样, 如果我们检测到 环境 从 异常->正常后,
我们就关闭器件,操作io口前, 也同样判断一下, 器件是否已经处于关闭状态, 如果关闭,则无需操作. (注意我们上图运用的是结构体变量, 其函数状态和io口状态深度绑定, 所以通过判断变量,就可以判断出我们所处的状态,从而操作io口)
代码实操:
在加dht11和oled最小例程的基础上,手把手改写代码, 加思路导向
改前的工程:
https://ww0.lanzoul.com/i1jH22680p3a
修改完预警的工程:
https://ww0.lanzoul.com/ihhjp26avqub
失效联系qq:2958360390 回复关键词 最小例程系统预警系统构建
(1) 我们解压打开改之前的工程
![image-20240802071443939](https://img-blog.csdnimg.cn/img_convert/f27841d82dd669a45e1090462ef8659c.png)
(2)然后去锁定预警处理部分的代码
会发现, 我们这里是直接在main函数里面访问temp和humi的, 直接做出预警判断
![image-20240802071703229](https://img-blog.csdnimg.cn/img_convert/6a10d7e4e3b52d63790ed1a54d029c89.png)
(3)并且, 和上述说的一样, red_led和 PBout(3) 并没有深度绑定, 所以我们先去 构建一下, 操作器件的函数, 我们构建两个文件一个是temp预警器件, 一个是 humi预警器件。
首先,还和之前一样, 在品字哪里, 新建一个alarm的文件夹, 来存放预警器件的文件, 然后再去add, 逐个创建文件, 我们起名, temp_led.c 和 temp_led.h , humi_led.c和 humi_led.h吧。
详细步骤:(为了避免篇幅过长)
https://blog.csdn.net/qq_57484399/article/details/140862473
(4)如下图,我们先做temp_led.c的预警,
这里我们不谈c语言格式, 只谈随心写代码, 甚至我们可以用中文来代替, 至于格式问题, 是编译器和al的事情, 我们只谈逻辑.
![image-20240802081906579](https://img-blog.csdnimg.cn/img_convert/6b79a9d0f81cc9f8a7b3eb32af922e13.png)
(5)先看之前原始的预警的时候, 操作器件的时候, 是怎么操作的
我们是先判断状态, 让着当状态切换的时候, 我们才操作器件.
但是这些好像和我们用户没什么关系, 我们用户, 只是想, 在报警的时候, 你操作器件就行了, 至于启动不启动器件, 是我们底层控制启动器件的代码通过判断, 才做的事情, 所以, 我们此处进行替换.
(6)至于启动不启动器件, 是函数内部通过判断器件是否处于运行状态, 才操作io口的.我们这里复制 temp_led_Set, 然后直接跳转temp_led.c函数
(7)看到这个是传入的状态, 所以我们还得给led_red绑定一个结构体状态, 通过调用判断这个状态, 再确定是否需要操作io口,
我们去temp_led.h去定义状态结构体
![image-20240802124853840](https://img-blog.csdnimg.cn/img_convert/6d915b09d5687e0ed4bd5cb734d1fb43.png)
#define red_led_ON 1
#define red_led_OFF 0
typedef struct
{
_Bool red_led_Status;
} TEMP_ALARM_INFO;
(8)定义完, 之后, 去temp_led.c里面去创建一个temp_alarm_info的变量, 然后 再去构造函数. 相当于定一个了一个状态了
TEMP_ALARM_INFO temp_info = {0};
(9) 然后我们接着构造控制函数
传入的状态, 我们修改为 _Bool status
void temp_led_Set(_Bool status)
{
}
(10)还是根据流程图所示, 我们传入的状态, 就是我们想要的状态, 目前如果 机器是此状态, 我们就跳过不操作io口, 相当于我们切换状态的时候, 我们才操作io口
![image-20240802130517065](https://img-blog.csdnimg.cn/img_convert/a168c668b66112bb2751343bf89cbc5f.png)
(11)直接上代码, 如果需要开预警设备, 并且当前状态不是开,则操作io口
如果需要关应急设备, 并且当前状态不是关,则操作io口.
(为什么要判断切换状态呢? 因为我们并不能确保这是 第一次从正常->应急, 或者从应急->预警, 第一次操作,当然可以直接操作io口, 因为在一段时间内, 我们只需要操作一次io口,就行了, 但是为了保证实时性, 我们会一直操作判断器件状态, 从而达到实时性, 后续我们采用操作系统思想解决, 我们这里比较简单粗暴.)
下面我把完善的代码奉上, 请直接复制黏贴即可
temp_led.c
//单片机头文件
#include "stm32f10x.h"
#include "temp_led.h"
#include "sys.h"
TEMP_ALARM_INFO temp_info = {0};
void temp_led_Init(void)
{
//初始化小灯, PB3(温度) PB4(湿度) PB5(烟雾浓度)
GPIO_InitTypeDef GPIO_InitStructure;
//开启硬件时钟 PB5
/* GPIOD Periph clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
//开启AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//禁用JTAGD端口
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
//配置 pB 3 4 5 GPIO工作模式(推挽输出)
/* Configure PD0 and PD2 in output pushpull mode */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure); //配置端口
//控制端口寄存器GPIO输出电平
PBout(3) = 0;//temp_led
}
void temp_led_Set(_Bool status)
{
if(status == red_led_ON && temp_info.red_led_Status != red_led_ON)
{
//操作小灯开
PBout(3) = 1;//temp_led
//状态设置为开
temp_info.red_led_Status = status;
}
else
if(status == red_led_OFF && temp_info.red_led_Status != red_led_OFF)
{
//操作小灯关
PBout(3) = 0;//temp_led
//状态设置为关
temp_info.red_led_Status = status;
}
}
temp_led.h
#ifndef _TEMP_LED_H_
#define _TEMP_LED_H_
#define red_led_ON 1
#define red_led_OFF 0
typedef struct
{
_Bool red_led_Status;
} TEMP_ALARM_INFO;
extern TEMP_ALARM_INFO temp_info;
void temp_led_Init(void);
void temp_led_Set(_Bool status);
#endif
humi_led.c
//单片机头文件
#include "stm32f10x.h"
#include "humi_led.h"
#include "sys.h"
HUMI_ALARM_INFO humi_info = {0};
void humi_led_Init(void)
{
//初始化小灯, PB3(温度) PB4(湿度) PB5(烟雾浓度)
GPIO_InitTypeDef GPIO_InitStructure;
//开启硬件时钟 PB5
/* GPIOD Periph clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
//开启AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//禁用JTAGD端口
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
//配置 pB 3 4 5 GPIO工作模式(推挽输出)
/* Configure PD0 and PD2 in output pushpull mode */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure); //配置端口
//控制端口寄存器GPIO输出电平
PBout(4) = 0;//humi_led
}
void humi_led_Set(_Bool status)
{
if(status == blue_led_ON && humi_info.blue_led_Status != blue_led_ON)
{
//操作小灯开
PBout(4) = 1;//blue_led
//状态设置为开
humi_info.blue_led_Status = status;
}
else
if(status == blue_led_OFF && humi_info.blue_led_Status != blue_led_OFF)
{
//操作小灯关
PBout(4) = 0;//blue_led
//状态设置为关
humi_info.blue_led_Status = status;
}
}
humi_led.h
#ifndef _HUMI_LED_H_
#define _HUMI_LED_H_
#define blue_led_ON 1
#define blue_led_OFF 0
typedef struct
{
_Bool blue_led_Status;
} HUMI_ALARM_INFO;
extern HUMI_ALARM_INFO humi_info;
void humi_led_Init(void);
void humi_led_Set(_Bool status);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "sys.h"
#include "delay.h"
#include "dht11.h"
#include "OLED.h"
#include "humi_led.h"
#include "temp_led.h"
uint8_t temp; //读取的环境温度
uint8_t humi; //读取的环境湿度
uint8_t temp_th; //设置的温度阈值
uint8_t humi_th; //设置的湿度阈值
_Bool red_led; //(温度指示灯)
_Bool blue_led; //(湿度指示灯)
_Bool set_limit; //管理员是否设置阈值
led小灯初始化
//void led_init(void);
int main()
{
unsigned short timeCount = 0; //发送间隔变量
//手动设置环境温度(模拟上帝)
temp = 66; //温度是28度
humi = 60; //湿度是60%
//手动设置阈值(代替远程)
temp_th = 30; //温度阈值是30度
humi_th = 60; //湿度阈值是60%
//小灯状态初始化
red_led = 0;
blue_led = 0;
//测试变量: 管理设置阈值符号位
set_limit = 1;
// //小灯初始化
// led_init();
//滴答定时器
Delay_Init();
//dht11初始化
DHT11_Init();
// while(DHT11_Init())
// {
printf("DHT11 Error \r\n");
// OLED_ShowString(0,48, "DHT11 Error", 8);
// OLED_Update();
// DelayMs(2000);
// }
//oled初始化
OLED_Init();
humi_led_Init();
temp_led_Init();
while(1)
{
//(1)设置环境信息阈值(手动输入代替远程控制)
if(set_limit == 1)
{
temp_th = 29;
humi_th = 88;
//设置完阈值就清零,下次不设置了, 直到set_limit被管理员置1
//然后再次进入此功能函数里,进行设置阈值
set_limit = 0;
}
//(2)读取环境信息 (手动输入代替器件读取)
//if(过了100毫秒) 每隔100毫秒,读取一次, 保证实时性,减轻负担
if(++timeCount >= 10)
{
DHT11_Read_Data(&temp,&humi);
// temp = 99; //人手输入,代替器件,忽略底层,重视逻辑
// humi = 99;
OLED_Clear();
OLED_ShowChinese(0, 0, "温度");
OLED_ShowChinese(0, 24, "湿度:");
OLED_Printf(48,0,OLED_8X16,"%2d",temp);
OLED_Printf(48,24,OLED_8X16,"%2d",humi);
OLED_ShowChinese(80,0 , "℃");
OLED_ShowChinese(80,24 , "%");
OLED_Update();
timeCount = 0; //从新计时`
}
DelayXms(10); //10*10 = 100ms
//后面跟 1ms的延时, 我们一切从简,先留着
//(3)每时每刻判断环境信息是否异常
if(temp >= temp_th)
{
//启动器件
temp_led_Set(red_led_ON);
}
else //同理,环境正常时候
{
temp_led_Set(red_led_OFF);
}
//判断湿度是否报警
//每时每刻判断环境信息是否异常
if(humi >= humi_th)
{
humi_led_Set(blue_led_ON);
// if(blue_led == 0) //如果是关闭状态则启动,开启则无操作
// {
// blue_led = 1;
// PBout(4) = 1;
// }
}
else //同理,环境正常时候
{
humi_led_Set(blue_led_OFF);
// if(blue_led == 1) //如果是开启状态则启动,关闭则无操作
// {
// blue_led = 0;
// PBout(4) = 0;
// }
}
}
}
///**************************************************
//函数名: led_init
//功 能: 温湿度,烟雾浓度小灯初始化
//参 数: 无
//配置参数: PB3(温度) PB4(湿度) PB5(烟雾浓度)
// 推挽输出
// 小灯正极接io口, 负极接地
//返回值: 无
//**************************************************/
//void led_init(void)
//{
// //初始化小灯 , PB3(温度) PB4(湿度) PB5(烟雾浓度)
// GPIO_InitTypeDef GPIO_InitStructure;
// //开启硬件时钟 PB5
// /* GPIOD Periph clock enable */
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// //开启AFIO时钟
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// //禁用JTAGD端口
// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
// //配置 pB 3 4 5 GPIO工作模式(推挽输出)
// /* Configure PD0 and PD2 in output pushpull mode */
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
// GPIO_Init(GPIOB, &GPIO_InitStructure); //配置端口
// //控制端口寄存器GPIO输出电平
// PBout(3) = 0;//temp_led
// PBout(4) = 0; //humi_led
PBout(5) = 0; //mq2_led
//
//}