简介:这是一份基于STM32的污水处理测控系统的Proteus仿真资源,涵盖Keil5程序源代码与Proteus 8.15仿真电路文件,适合嵌入式学习者及自动化相关专业学生进行环境监测与流程控制实践。系统实现低中高水位判断、进水阀与出水阀联动、水质参数检测(浑度、氨氮、溶氧、PH、重金属、水温),支持通过串口调试助手远程修改阈值,并具备10秒无操作自动处理及警报水位指示等功能,逻辑完整贴近实际工程应用。资源共277个文件,以C源码、头文件及编译构建文件为主,同时包含Keil工程文件、可烧录的HEX文件和Proteus仿真工程等,压缩包约13.55MB,便于直接打开学习或二次开发。目前已有89人学习查看,适合需要快速获取可运行仿真方案、理解STM32嵌入式系统与Proteus联合调试思路的读者参考。
1. 在 Proteus 里跑通污水处理测控系统,难点不在画电路而在控制节拍
很多人在 Proteus 里用 STM32 做污水处理测控,最后只实现了“把 pH、液位、浊度显示到 LCD 上”就停了。真正的难点其实是那些按时序切换的控制动作:污水泵什么时候启动、曝气机转多快、加药阀开几度、沉淀多久才排水。这些动作不是靠传感器读数硬顶出来的,而是由一套流程状态机来调度。Proteus 的价值恰好在这里——把传感器、执行器、通信接口全部虚拟化,不接一条真实导线,就能在实物投产之前验证整条控制逻辑。它适合自动化、测控、环境工程背景的开发者,也适合需要用 Proteus 仿真支撑毕业设计或技术方案预演的工程师。下面会从系统架构、STM32 侧控制代码、Proteus 联调步骤,一直讲到仿真中真正值得记下来的排错办法。
2. 污水处理测控系统架构:先拆成 Proteus 能仿真的信号链
2.1 工艺状态怎么划分才能放进状态机
常规的小型污水处理线可以简化成提升泵、调节池、曝气池、沉淀池、消毒排放五个环节。想在 STM32 里实现控制逻辑,不能把整条线做成“一个大循环”,而应该把工艺切成若干状态,每个状态只做固定动作。常见做法是分成:待机、进水、曝气、加药、沉淀、排水、排泥、异常报警。我用一张表来描述每个状态需要考虑的输入输出:
| 状态 | 触发条件 | 执行器动作 | 典型时间常数 |
|---|---|---|---|
| 待机 | 上电或异常复位 | 所有泵、阀、曝气机关闭 | 无 |
| 进水 | 液位低于下限 | 提升泵启动,进水阀打开 | 5~10 分钟 |
| 曝气 | 液位到达中位且定时到 | 曝气风机启动,鼓风机 PWM 输出 | 30~60 分钟 |
| 加药 | pH 偏差超过阈值 | 加药泵以占空比运行 | 1~5 分钟 |
| 沉淀 | 曝气时间结束 | 停止曝气,关闭搅拌 | 20~40 分钟 |
| 排水 | 沉淀时间结束 | 排水阀打开,液位到下限关闭 | 5~10 分钟 |
这张表就是状态机的骨架。每个状态除了动作外,还要记录进入该状态的时刻,以便通过定时器判断此状态是否已经达到目标时长。Proteus 仿真时间尺度可以比真实工艺快得多,但代码里仍然用真实时间常数来写,方便今后移植到实物。
2.2 传感器信号在 Proteus 里的等效建模方式
Proteus 本身不认识“pH 值”和“溶解氧浓度”,它只认识电压和电流。所以做仿真前要先约定信号链:传感器输出的真实物理量通过变送器转换为 4~20mA 或 0~3.3V,再用一个精度合适的电位器或信号源去模拟。对大多数毕业设计和原理验证来说,把工艺量映射到电压是最可靠的。下面是一个常用映射:
| 工艺量 | 量程 | 对应电压范围 | 输入引脚 |
|---|---|---|---|
| pH 值 | 0~14 | 0~3.3V | PA0 |
| 液位 | 0~5m | 0~3.3V | PA1 |
| 浊度 | 0~200NTU | 0~3.3V | PA2 |
| 溶解氧 | 0~10mg/L | 0~3.3V | PA3 |
这样做的直接好处是,传感器动态变化可以用一个信号源的分段电压模拟,例如让 pH 电压从 1.65V 缓慢下降到 1.20V,就等效于 pH 值从 7.0 降到 5.1。执行器侧同理,水泵和阀门的开关状态在仿真中用 LED 或电机模型指示即可,PWM 型执行器则直接观察占空比波形。
2.3 为什么控制节拍要由定时中断驱动而不是 delay 阻塞
如果整个控制逻辑放在 while 循环里用 delay 顺序执行,调节定时器频率时所有状态都会失真。更隐蔽的问题是,Modbus 通信、按键输入、报警扫描这些任务会被互相阻塞,仿真运行速度一变,整条流程的节奏全部错乱。我一般会在 STM32 中开启一个 50ms 的定时器中断作为控制节拍,主循环只处理低优先级的事务,例如 LCD 刷新。
volatile uint8_t tick_50ms = 0; void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); tick_50ms++; if (tick_50ms >= 20) { tick_50ms = 0; Control_Task_1s(); /* 每秒执行一次工艺控制 */ } } }说明:TIM3 配置为 50ms 中断,软件再对 tick 计数,凑出 1 秒的工艺控制节拍。之所以在中断里只置标记和调用轻量级任务函数,是因为生产工艺切换并不需要微秒级响应,1 秒钟扫一次已经足够工程使用。
3. 基于 STM32 的测控代码:状态机、PWM 与 Modbus 通信怎么落
3.1 工程初始化要点
在 Proteus 中配合 Keil 做 STM32 仿真,常使用标准外设库,因为它的编译结构简单,与 Proteus 的兼容性也容易控制。晶振配置要留意:Proteus 里双击 STM32 芯片设定外部晶振频率,代码里的 SystemInit 时钟配置最好固定为 72MHz,否则串口波特率会有偏差。GPIO 配置时,把传感器输入引脚设置为模拟输入、执行器引脚设置为推挽输出、串口引脚设置为复用功能。
3.2 工艺状态机的实现与切换条件
状态机是整个测控系统的核心。这里给出一个可以直接在工程中改写的框架:
typedef enum { ST_IDLE, ST_INLET, ST_AERATION, ST_DOSING, ST_SEDIMENT, ST_DRAIN } ProcessState_t; ProcessState_t current_state = ST_IDLE; uint32_t state_start_time = 0; void Control_Task_1s(void) { float ph_value = ADC_GetValue(ADC_CHANNEL_0); float level = ADC_GetValue(ADC_CHANNEL_1); uint32_t now_sec = Get_System_Seconds(); switch (current_state) { case ST_IDLE: if (level < LEVEL_LOW) Enter_State(ST_INLET, now_sec); break; case ST_INLET: Pump_Set(ON); if (level >= LEVEL_HIGH) { Pump_Set(OFF); Enter_State(ST_AERATION, now_sec); } break; case ST_AERATION: Blower_Set(0.75f); if (now_sec - state_start_time >= 3600) { Blower_Set(0.0f); Enter_State(ST_DOSING, now_sec); } break; case ST_DOSING: if (ph_value < PH_LOW_LIMIT) { Dosing_Set(0.3f); } else { Dosing_Set(0.0f); Enter_State(ST_SEDIMENT, now_sec); } break; case ST_SEDIMENT: if (now_sec - state_start_time >= 2400) Enter_State(ST_DRAIN, now_sec); break; case ST_DRAIN: if (level <= LEVEL_LOW) { Valve_Set(OFF); Enter_State(ST_IDLE, now_sec); } break; default: Enter_State(ST_IDLE, now_sec); break; } }这里所有动作都基于状态和数据采集值。每次切换状态时记录起始时间,后续通过“当前时间减起始时间”判断是否达到工艺时长。这样写的好处是任何时刻系统都能响应,不会被某个阶段的定时循环堵死。
3.3 执行器控制:继电器开关与 PWM 输出
控制执行器有两种常见输出方式:开关量直接控制泵和阀,PWM 控制曝气量和加药比例。PWM 频率一般放在 100Hz 左右,太低能看到明显闪烁,太高在 Proteus 里观察波形时反而不方便。下面是 PWM 初始化与设置的示例:
void PWM_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBase; TIM_OCInitTypeDef TIM_OC; TIM_TimeBase.TIM_Period = 999; TIM_TimeBase.TIM_Prescaler = 71; TIM_TimeBase.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBase.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBase); TIM_OC.TIM_OCMode = TIM_OCMode_PWM1; TIM_OC.TIM_OutputState = TIM_OutputState_Enable; TIM_OC.TIM_Pulse = 0; TIM_OC.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM2, &TIM_OC); TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_Cmd(TIM2, ENABLE); }代码将 TIM2 配置在 72MHz 下产生 1kHz 的 PWM。改变 TIM_Pulse 就可以改变占空比。对曝气风机来说,占空比 0.75 意味着风机出力 75%,对应曝气强度比较高;对加药泵来说,0.3 的占空比已经能维持缓慢加药。Proteus 的虚拟示波器接在 PA0/PWM 输出引脚上,可以直接看到占空比变化。
3.4 Modbus 通信与寄存器映射
污水处理测控系统一般需要把状态上报给上位机或触摸屏。通常在 STM32 上实现 Modbus RTU 从站,用串口连接 Proteus 里的虚拟终端,实现数据的连续读取与写入。Modbus 寄存器映射可以这样规划:
| 寄存器地址 | 内容 | 读写属性 |
|---|---|---|
| 40001 | pH 值,放大 100 倍 | 只读 |
| 40002 | 液位值,放大 100 倍 | 只读 |
| 40003 | 当前工艺状态码 | 只读 |
| 40004 | 曝气 PWM 占空比 | 只读 |
| 40005 | 排水阀开关命令 | 读写 |
串口中断收到完整一帧后,用 CRC16 校验,校验通过再更新保持寄存器。这里的关键点是在定时器中断里保证一个字符间隔计时,Proteus 仿真时如果波特率较高,容易因为中断开销而丢字节。推荐使用 9600bps,处理一个小型测控系统完全够用。
4. Proteus 与 Keil 的联调步骤:从元件选型到仿真跑通
4.1 Proteus 元件选型与 STM32 型号匹配
打开 Proteus 8 系列的元件搜索窗口,输入“STM32F103C8”便能找到对应的 MCU 模型。需要留意的是,Proteus 的 STM32 模型在外设支持上并不完全一致,部分型号的 ADC、定时器数量有限,如果项目里要用多个 ADC 通道和多路 PWM,优先选择引脚数量较多的 STM32F103VCT6 模型。另外在设计原理图时,一定要给 MCU 加上 VDD、VSS、VDDA、VSSA 的电源连接,并放置一个 100nF 去耦电容,否则仿真时可能出现随机复位。
4.2 把 Keil 工程编译出的 HEX 文件装载到 Proteus
在 Keil 中勾选 Options for Target -> Output -> Create HEX File,编译完成后得到工程目录下的 .hex 文件。回到 Proteus,双击原理图中的 STM32 芯片,在 Program File 一栏选择该 HEX 文件。这里有一个容易踩的坑:芯片属性里的 CKS 外部晶振频率必须与 Keil 工程里配置的 HSE_VALUE 一致。如果在代码中使用了 8MHz 外部晶振,Proteus 里就该填 8MHz,否则串口波特率和定时周期全部偏离。
4.3 用虚拟终端和虚拟示波器做联调
运行 Proteus 仿真后,点击调试菜单里的 Virtual Terminal,给 STM32 的 USART1 引脚接上虚拟终端。这样可以实时看到 Modbus 报文,例如每次下发寄存器读取时,终端会显示十六进制字节。虚拟示波器则需要从左侧仪表栏拖入,探头接到 PWM 输出引脚或传感器信号引脚。观察到的现象主要有三种:PWM 波形占空比稳定、ADC 电压随电位器变化、虚拟终端有规律输出。这三点都满足,说明控制链路已经打通。
下面是一张仿真问题排查表:
| 现象 | 可能原因 | 处理办法 |
|---|---|---|
| 程序不运行 | HEX 文件路径错误、外部晶振频率不匹配 | 重新装载并检查 CKS 属性 |
| 串口无输出 | USART 引脚接错、虚拟终端波特率不匹配 | 核查 TX/RX 引脚,核对波特率 |
| ADC 一直满量程 | 引脚浮空、电位器未连接 | 将传感器引脚接电位器中点 |
| PWM 无波形 | 定时器未使能或占空比寄存器为 0 | 检查 TIM_Cmd 调用和执行器状态 |
5. 让 Proteus 仿真带“动态漂移”,让控制逻辑接受更真实的考验
很多仿真项目停在一个固定电位器电压上,pH 值不会变、液位不会涨,状态机永远只停在一个状态下。想验证控制逻辑是否可靠,需要让传感器信号像真实工艺那样缓慢变化。常见做法是在 Proteus 中使用信号发生器,把波形设为三角波或梯形波,接到 STM32 的 ADC 输入引脚。比如液位信号用周期 30 秒的三角波模拟,从 0V 缓慢升到 3.0V,等效液位从 0m 涨到 4.5m,状态机就会自动从待机进入进水状态;当电压回落,系统进入排水状态。这样一来,整个测控系统在一个仿真周期内便能把所有工艺阶段走一遍。
为了让信号更接近真实传感器,还可以在 STM32 代码里加一个滑动平均滤波:
#define FILTER_LEN 8 uint16_t adc_buf[FILTER_LEN]; uint8_t adc_index = 0; uint32_t adc_sum = 0; uint16_t ADC_FilteredRead(void) { uint16_t val = ADC_GetValue(ADC_CHANNEL_0); adc_sum -= adc_buf[adc_index]; adc_buf[adc_index] = val; adc_sum += val; adc_index = (adc_index + 1) % FILTER_LEN; return (uint16_t)(adc_sum / FILTER_LEN); }滑动平均对周期性的锯齿信号有一定平滑效果,但要特别注意:如果滤掉真实突变的报警信号,反而会耽误动作,所以采样周期保持 50ms 才合理。最后在 Proteus 中分别以固定电压、斜坡电压、三角波电压三种信号驱动同一套程序,对比状态机切换时间和 Modbus 报文内容,就能确认控制参数是否合适。
本文还有配套的精品资源,点击获取