// 引入I2C通信库和LCD驱动库
#include <Wire.h> // I2C通信协议支持
#include <LiquidCrystal_I2C.h> // I2C接口的LCD显示屏驱动
// 初始化LCD对象(地址0x27,16列2行显示)
LiquidCrystal_I2C lcd(0x27, 16, 2); // 参数:I2C地址、列数、行数
/******************** 硬件引脚定义 ********************/
#define STEP_PIN 9 // 步进电机脉冲引脚(控制转速)
#define DIR_PIN 8 // 方向控制引脚(固定正转,low=正转)
#define ENABLE_PIN 7 // 驱动器使能引脚(低电平有效)
#define PRESSURE_PIN A0 // 压力传感器模拟输入引脚(0-5V对应0-0.8MPa)
#define LOW_PIN 2 // 低水位开关(低电平有效)
#define MID_PIN 3 // 中水位开关(低电平有效)
#define HIGH_PIN 4 // 高水位开关(低电平有效)
#define DANGER_PIN 5 // 危险水位开关(低电平有效)
#define MODE_PIN 6 // 模式切换开关(低电平=手动模式)
#define MANUAL_PUMP 10 // 手动模式水泵控制开关(低电平=运行)
#define MANUAL_VALVE 11 // 手动模式补气阀控制开关(低电平=开启)
#define VALVE_LED 12 // 补气阀状态指示灯(高电平点亮)
#define SAFETY_LED A1 // 安全阀报警指示灯(高电平点亮)
#define ALARM_LED A2 // 危险水位报警指示灯(高电平点亮)
/******************** 全局变量与常量 ********************/
#define FILTER_SIZE 5 // 压力滤波窗口大小
float targetPressure = 0.4; // 目标压力设定值(单位:MPa)
float pressureBuffer[FILTER_SIZE]; // 压力滤波缓冲区
int bufferIndex = 0; // 滤波缓冲区索引
float currentPressure = 0.0; // 滤波后的当前压力值
int currentMode = 0; // 运行模式(0=自动,1=手动)
unsigned long lastStepTime = 0; // 最后一次步进脉冲时间(微秒)
int stepInterval = 500; // 步进脉冲间隔(单位:微秒,控制转速)
bool pumpRunning = false; // 水泵运行状态标志(true=运行中)
// PID控制参数
float Kp = 2.0; // 比例系数(响应速度)
float Ki = 0.05; // 积分系数(消除稳态误差)
float Kd = 0.5; // 微分系数(抑制振荡)
float error = 0; // 当前误差
float lastError = 0; // 上一次误差
float integral = 0; // 积分累积值
/******************** 初始化函数 setup() ********************/
void setup() {
// 配置步进电机控制引脚
pinMode(STEP_PIN, OUTPUT); // 脉冲引脚设为输出
pinMode(DIR_PIN, OUTPUT); // 方向引脚设为输出
pinMode(ENABLE_PIN, OUTPUT); // 使能引脚设为输出
digitalWrite(DIR_PIN, LOW); // 固定方向为正转(low)
// 配置LED指示灯引脚
pinMode(VALVE_LED, OUTPUT); // 补气阀LED
pinMode(SAFETY_LED, OUTPUT); // 安全阀LED
pinMode(ALARM_LED, OUTPUT); // 危险水位LED
// 配置输入引脚(启用内部上拉电阻)
pinMode(LOW_PIN, INPUT_PULLUP); // 低水位开关
pinMode(MID_PIN, INPUT_PULLUP); // 中水位开关
pinMode(HIGH_PIN, INPUT_PULLUP); // 高水位开关
pinMode(DANGER_PIN, INPUT_PULLUP); // 危险水位开关
pinMode(MODE_PIN, INPUT_PULLUP); // 模式切换开关
pinMode(MANUAL_PUMP, INPUT_PULLUP);// 手动水泵开关
pinMode(MANUAL_VALVE, INPUT_PULLUP);// 手动补气阀开关
// 初始化压力滤波缓冲区(填充初始值)
for (int i = 0; i < FILTER_SIZE; i++) {
pressureBuffer[i] = analogRead(PRESSURE_PIN) * 0.8 / 1023.0;
}
// 初始化LCD显示屏
lcd.init(); // 初始化LCD
lcd.backlight(); // 开启背光
lcd.print("System Ready"); // 显示启动信息
delay(1000); // 保持启动信息1秒
lcd.clear(); // 清屏准备显示运行数据
}
/******************** 主循环函数 loop() ********************/
void loop() {
// 1. 读取模式开关状态(低电平=手动模式)
currentMode = digitalRead(MODE_PIN) ? 0 : 1;
// 2. 读取并滤波压力值
updatePressure(); // 调用压力更新函数
// 3. 获取当前水位状态
int waterLevel = getWaterLevel();
// 4. 根据模式执行控制逻辑
if (currentMode == 0) {
autoControl(waterLevel); // 自动模式
} else {
manualControl(); // 手动模式
}
// 5. 更新LCD显示
updateLCD();
}
/******************** 自定义函数:updatePressure() ********************/
void updatePressure() {
// 读取原始压力值(0-1023对应0-5V)
float rawValue = analogRead(PRESSURE_PIN);
// 转换为MPa单位(0-0.8MPa)
float rawPressure = rawValue * 0.8 / 1023.0;
// 更新滤波缓冲区(移动平均滤波)
pressureBuffer[bufferIndex] = rawPressure;
bufferIndex = (bufferIndex + 1) % FILTER_SIZE;
// 计算滤波后的压力值
currentPressure = 0;
for (int i = 0; i < FILTER_SIZE; i++) {
currentPressure += pressureBuffer[i];
}
currentPressure /= FILTER_SIZE;
}
/******************** 自定义函数:getWaterLevel() ********************/
int getWaterLevel() {
// 读取水位开关状态(低电平=接通)
int low = !digitalRead(LOW_PIN); // 低水位开关
int mid = !digitalRead(MID_PIN); // 中水位开关
int high = !digitalRead(HIGH_PIN); // 高水位开关
int danger = !digitalRead(DANGER_PIN); // 危险水位开关
// 水位状态优先级判断
if (danger) return 3; // 危险水位(最高优先级)
if (high && mid && low) return 2; // 高水位(需三个开关接通)
if (mid && low) return 1; // 中水位(需两个开关接通)
if (low) return 0; // 低水位(仅低开关接通)
return -1; // 无效状态
}
/******************** 自动控制函数:autoControl() ********************/
void autoControl(int level) {
// 1. 危险水位处理
if (level == 3) {
digitalWrite(ENABLE_PIN, HIGH); // 禁用驱动器(停泵)
digitalWrite(ALARM_LED, HIGH); // 点亮危险报警灯
digitalWrite(VALVE_LED, HIGH); // 强制开启补气阀
pumpRunning = false; // 标记水泵停止
return; // 退出函数
} else {
digitalWrite(ALARM_LED, LOW); // 非危险水位关闭报警灯
}
// 2. 补气阀控制(高水位及以上开启)
digitalWrite(VALVE_LED, (level >= 2) ? HIGH : LOW);
// 3. 压力控制逻辑
if (currentPressure >= 0.5) { // 超压保护
digitalWrite(ENABLE_PIN, HIGH); // 停泵
digitalWrite(SAFETY_LED, HIGH); // 点亮安全阀报警灯
pumpRunning = false;
} else { // 正常压力范围
digitalWrite(SAFETY_LED, LOW); // 关闭安全阀报警灯
digitalWrite(ENABLE_PIN, LOW); // 启用驱动器
// PID控制计算
error = targetPressure - currentPressure; // 计算误差
integral += error * (stepInterval / 1000000.0); // 积分项(考虑时间)
float derivative = (error - lastError) / (stepInterval / 1000000.0); // 微分项
float output = Kp * error + Ki * integral + Kd * derivative; // PID输出
// 根据输出调整脉冲间隔(限制在200-2000μs)
stepInterval = constrain(map(output, -1.0, 1.0, 200, 2000), 200, 2000);
// 生成步进脉冲
if (micros() - lastStepTime > stepInterval) {
digitalWrite(STEP_PIN, HIGH); // 脉冲上升沿
delayMicroseconds(10); // 保持10μs脉宽
digitalWrite(STEP_PIN, LOW); // 脉冲下降沿
lastStepTime = micros(); // 记录当前时间
pumpRunning = true; // 标记水泵运行中
}
lastError = error; // 保存当前误差供下次计算
}
}
/******************** 手动控制函数:manualControl() ********************/
void manualControl() {
// 1. 水泵控制(低电平=运行)
if (!digitalRead(MANUAL_PUMP)) {
digitalWrite(ENABLE_PIN, LOW); // 启用驱动器
digitalWrite(STEP_PIN, HIGH); // 生成脉冲上升沿
delayMicroseconds(10); // 保持10μs脉宽
digitalWrite(STEP_PIN, LOW); // 生成脉冲下降沿
delay(10); // 固定间隔(约100Hz)
pumpRunning = true; // 标记水泵运行中
} else {
digitalWrite(ENABLE_PIN, HIGH); // 禁用驱动器
pumpRunning = false; // 标记水泵停止
}
// 2. 补气阀控制(低电平=开启)
digitalWrite(VALVE_LED, !digitalRead(MANUAL_VALVE) ? HIGH : LOW);
// 3. 复位安全指示灯
digitalWrite(SAFETY_LED, LOW); // 关闭安全阀灯
digitalWrite(ALARM_LED, LOW); // 关闭危险水位灯
}
/******************** LCD显示更新函数:updateLCD() ********************/
void updateLCD() {
// 第一行显示模式与压力
lcd.setCursor(0, 0); // 光标定位到第0列第0行
lcd.print("Mode:"); // 显示模式标签
lcd.print(currentMode ? "MAN" : "AUTO"); // 显示当前模式(MAN/AUTO)
lcd.setCursor(8, 0); // 光标移动到第8列
lcd.print("P:"); // 显示压力标签
lcd.print(currentPressure, 1); // 显示压力值(保留1位小数)
lcd.print("MPa"); // 显示单位
// 第二行显示水位与频率
lcd.setCursor(0, 1); // 光标定位到第0列第1行
lcd.print("Level:"); // 显示水位标签
switch (getWaterLevel()) { // 根据水位值显示状态
case 0: lcd.print("LOW "); break; // 低水位
case 1: lcd.print("MID "); break; // 中水位
case 2: lcd.print("HIGH"); break; // 高水位
case 3: lcd.print("DANG"); break; // 危险水位
default: lcd.print("----"); break; // 无效状态
}
lcd.setCursor(10, 1); // 光标移动到第10列
lcd.print("Freq:"); // 显示频率标签
if (!pumpRunning) {
lcd.print("0Hz "); // 停泵时显示0Hz
} else {
// 将脉冲间隔映射为等效频率(200μs=50Hz,2000μs=10Hz)
lcd.print(map(stepInterval, 200, 2000, 50, 10));
lcd.print("Hz"); // 显示单位
}
}