资讯动态

基于Proteus的STM32污水处理测控系统仿真设计与实现

发布时间:2026/9/16 19:06:46 来源:尧图企业网站定制
简介面向STM32与Proteus仿真学习者这份污水处理测控系统仿真资源提供了从入门到调试的完整闭环。资源以Keil5与Proteus 8.15为运行环境完整还原题目要求安全开关控制、低中高水位阀门联动、水质参数浑度、氨氮、溶氧、PH值、重金属、水温实时监测并支持通过串口调试助手远程修改阈值、10秒无操作自动进入处理判断、警报水位时关闭出水阀等关键逻辑适合课程设计、毕业设计或竞赛备赛参考。压缩包共277个文件以c与h源码、hex可执行文件、uvprojx工程文件以及proteus的pdsprj仿真文件为主另含文档及备份文件约13.55MB目录结构清晰便于按工程、源码、仿真分类查找。目前已有89人学习下载。内容涵盖程序源代码与仿真电路文件可对照学习按键处理、ADC采集、串口通信、阀门控制等具体实现细节是一份可直接运行验证的完整仿真方案。1. 先用Proteus把污水处理测控跑起来再谈硬件污水处理测控系统如果在实物上调试传感器标定、阀门驱动、通讯线缆任何一环出问题都会让排查成本翻倍。用Proteus 8.15做STM32仿真把传感器信号用可调电阻和信号源模拟阀门的开关状态通过LED和逻辑电平直接观察这等于把现场问题前置到了开发阶段。这套资源包含完整的Keil5工程源码和仿真电路运行环境是Keil5加Proteus 8.15覆盖水位联动、水质检测、串口阈值修改、超时自动判定出水这一整条控制链路。适合正在做STM32课程设计、毕业设计或者想用仿真方式验证控水逻辑的开发者。开头这句话可以直接记住这仿真的核心不是画电路而是把一套状态机逻辑调通。2. 系统拆解与仿真工程装配Keil5与Proteus 8.15的对接拿到工程文件后先别急着点仿真运行把项目里各个文件的角色搞清楚后面改起来才顺手。工程文件名对应关系如下stm32f10x_tim.c负责定时器配置水位检测的采样周期和10秒无操作超时都依赖它stm32f10x_flash.c用于掉电保存用户修改过的阈值参数这在实际工程里属于标配stm32f10x_rcc.c是时钟树配置串口波特率和ADC采样时钟都从这儿分频出来stm32f10x_adc.c是水质检测的核心浊度、氨氮、溶解氧、pH、重金属、水温这六路信号全部通过ADC采集stm32f10x_i2c.c和stm32f10x_can.c在这个仿真里属于预留模块如果后续要把数据上报给上位机或者接入现场总线可以直接在这两个文件的基础上扩展。2.1 仿真电路与源码的模块映射打开Proteus工程文件后你会看到电路主要由三部分组成STM32F103单片机最小系统、传感器信号模拟区、执行机构指示区。传感器模拟区用滑动变阻器POT或者信号发生器输出0到3.3V的模拟电压分别代表六路水质参数的实时值。执行机构指示区用LED表示进水阀、出水阀、报警指示灯蜂鸣器模块表示声音报警。需要注意排水阀默认是常开的只有检测到警报水位时才会关闭。编译之前先检查Keil5工程里的Output选项卡确认勾选了Create HEX File仿真调试需要的是Project.axf文件Proteus的芯片属性设置里有一项专门加载这个文件。最常见的装不上程序的问题就出在这儿Keil生成的AXF路径改动过或者工程名带中文导致Proteus找不到调试符号。2.2 Proteus芯片选型与时钟配置Proteus 8.15里的STM32库支持F103系列但型号后缀和内部资源有差异代码里如果用到了USART1和ADC1就直接选STM32F103R6或者STM32F103R8这两个型号在Proteus里的外设映射是完整的。双击芯片打开属性面板在Program File里指定AXF文件路径Processor Clock Frequency填8M这是Proteus里STM32仿真时最稳定的时钟值。晶振电容计算在这里不需要操心仿真模型内部已经做了简化不会出现实物上晶振不起振的问题。时钟树这边要留意Keil工程里默认使用外部8MHz晶振经过PLL倍频到72MHz作为系统主频。Proteus仿真时如果开启RCC配置但是外部时钟源没有接好程序会卡在SystemInit()里面现象是虚拟终端没有任何打印输出。稳妥的做法是保持原工程里的启动文件startup_stm32f10x_hd.s不变同时把Proteus里芯片的时钟频率设置和工程里的HSE_VALUE宏保持一致这是仿真能跑起来的基本前提。2.3 编译到仿真的一键链路配置Keil5侧需要把Debug选项里的设置指向Proteus但这套仿真资源里运行方式更简单直接在Proteus里点左下角运行按钮即可前提是AXF文件已经烧录进去。每次修改完代码在Keil里重新编译生成新的AXF然后回到Proteus重新运行芯片会加载最新的固件。如果想要一步到位可以在Keil的Utilities选项卡里配置仿真器的路径让它调用Proteus的PDS.EXE自动载入但这属于进阶玩法手动加载足够应付课程设计。Proteus的虚拟串口COMPIM是一个值得提前确认的节点。串口调试助手在这套系统的角色是远程终端用来发送阈值设置命令。在Proteus里双击COMPIM组件Physical Port选择自己电脑上实际存在的串口编号或者选COM1留给虚拟串口软件处理。Physical Baud Rate和Virtual Baud Rate都要和代码里的波特率一致工程里默认是115200 8 N 1这个参数如果对不上串口调试助手发送的命令会变成乱码表现为屏幕上没有任何反馈。3. ADC多通道水质采集与阈值比对实现水质参数的采集在这套仿真里的实现方式非常直观六路传感器的模拟电压分别接到STM32的PA0到PA5引脚对应的ADC通道是ADC1的Channel0到Channel5。代码里使用扫描模式开启DMA传输这样六路数据不需要CPU逐个读取DMA会把结果连续搬运到一个数组里。这种多通道采集方式在实物项目和仿真里都是通用做法区别只在于实物需要额外的信号调理电路仿真里用一个电位器就解决了。ADC配置采用规则组扫描加连续转换模式采样时间设置为55.5个周期即ADC_SampleTime_55Cycles5这个值对低速变化的水质信号来说精度足够而且能有效抑制信号抖动。3.1 六路传感器的信号接入与通道映射六路信号线的通道映射关系如下表所示这个对应关系在PCB布局和Proteus连线时都适用保持通道编号和数组下标的同步后面数据处理会省掉很多麻烦。通道编号GPIO引脚ADC通道模拟量含义Proteus信号源0PA0ADC_Channel_0浊度NTUPOT-HG1PA1ADC_Channel_1氨氮浓度mg/LPOT-LIN2PA2ADC_Channel_2溶解氧mg/LPOT-HG3PA3ADC_Channel_3pH值POT-HG4PA4ADC_Channel_4重金属含量mg/LPOT-LIN5PA5ADC_Channel_5水温℃POT-HG电位器输出范围是0到3.3VADC把电压转换为0到4095的原始数字量。电压对应关系的换算公式为物理量 (ADC值 / 4095) × 3.3V然后再根据每个传感器的线性标定系数推导出实际的工程物理量。比如pH传感器如果标定为0.5V对应pH 03.0V对应pH 14那么换算公式就是pH (voltage - 0.5) × (14 / 2.5)。仿真里电位器的转动就相当于改变了传感器的输出这比实物传感器要调试起来要快得多而且不会出现电极老化的问题。3.2 ADC规则组扫描与DMA搬运配置ADC配置的核心代码在stm32f10x_adc.c里下面是工程里的关键配置段void ADC_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; DMA_InitTypeDef DMA_InitStructure; // 开启GPIOA、ADC1、DMA1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 配置PA0-PA5为模拟输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_Init(GPIOA, GPIO_InitStructure); // ADC1 独立模式扫描开启连续转换 ADC_InitStructure.ADC_Mode ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode ENABLE; ADC_InitStructure.ADC_ContinuousConvMode ENABLE; ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel 6; ADC_Init(ADC1, ADC_InitStructure); // 通道配置序号1-6采样时间55.5周期 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_55Cycles5); // DMA1 通道1 绑定ADC1 DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)ADC1-DR; DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)adc_raw_values; DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize 6; DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode DMA_Mode_Circular; DMA_InitStructure.DMA_Priority DMA_Priority_High; DMA_InitStructure.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel1, DMA_InitStructure); ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); // 校准 ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); // 连续转换启动 ADC_SoftwareStartConvCmd(ADC1, ENABLE); }这段配置里有几个容易写错的地方逐个说明。ADC_NbrOfChannel必须和ADC_RegularChannelConfig里配置的通道数一致填5的话DMA只搬运前5路数据第6路永远不会更新。DMA的DMA_PeripheralBaseAddr必须是ADC1-DR这个寄存器地址不能写错写错了会导致DMA从错误的地址搬运数据。DMA_BufferSize填6和通道数对应这里填多大DMA就搬运多少个数。完成配置后启动ADC校准这一步在STM32F103上必须做跳过校准读出来的数据会整体偏差几十个LSB。3.3 从ADC原始值到工程量值的换算处理DMA搬运过来的原始数据存放在adc_raw_values[6]数组里对应通道0到通道5的数据。接下来要做的就是把原始值转换成实际物理量。代码里一般会定义一个数据类型为中转结构体统一存放原始值和换算后的工程值。typedef struct { float turbidity; // 浊度单位 NTU float ammonia; // 氨氮单位 mg/L float dissolved_oxygen; // 溶解氧单位 mg/L float ph_value; // pH 值范围 0-14 float heavy_metal; // 重金属单位 mg/L float water_temp; // 水温单位 ℃ } WaterQualityData; WaterQualityData g_water_quality; void WaterQuality_Convert(void) { float voltage; // 通道0 浊度传感器0.5V 对应 0 NTU3.0V 对应 100 NTU voltage (float)adc_raw_values[0] * 3.3f / 4095.0f; g_water_quality.turbidity (voltage - 0.5f) * 100.0f / 2.5f; if(g_water_quality.turbidity 0.0f) g_water_quality.turbidity 0.0f; // 通道1 氨氮传感器1.0V 对应 0 mg/L3.0V 对应 5 mg/L voltage (float)adc_raw_values[1] * 3.3f / 4095.0f; g_water_quality.ammonia (voltage - 1.0f) * 5.0f / 2.0f; if(g_water_quality.ammonia 0.0f) g_water_quality.ammonia 0.0f; // 通道2 溶解氧0.5V 对应 0 mg/L2.5V 对应 10 mg/L voltage (float)adc_raw_values[2] * 3.3f / 4095.0f; g_water_quality.dissolved_oxygen (voltage - 0.5f) * 10.0f / 2.0f; // 通道3 pH 值0.5V 对应 pH 03.0V 对应 pH 14 voltage (float)adc_raw_values[3] * 3.3f / 4095.0f; g_water_quality.ph_value (voltage - 0.5f) * 14.0f / 2.5f; // 通道4 重金属0V 对应 0 mg/L3.3V 对应 2 mg/L voltage (float)adc_raw_values[4] * 3.3f / 4095.0f; g_water_quality.heavy_metal voltage * 2.0f / 3.3f; // 通道5 水温0V 对应 -20℃3.3V 对应 80℃ voltage (float)adc_raw_values[5] * 3.3f / 4095.0f; g_water_quality.water_temp -20.0f voltage * 100.0f / 3.3f; }这段换算逻辑相当于一层信号调理ADC采集到的是原始电压传感器标定曲线把电压映射到工程物理量。标定系数是根据Proteus仿真中的信号源范围设定的实际项目中每个传感器都有自己的标定表。在仿真里通过调整电位器观察LCD或者串口输出就能验证这段换算的线性度是否满足要求。这里对边界值做了钳位处理避免电位器拧到两端时出现负值或者超大值这个处理在实际项目中就是传感器量程保护。3.4 阈值设置与超限判断逻辑水质检测不只是采集数据更重要的是把数据跟阈值比较判断当前水样是否达标。工程里为用户提供了六个可调阈值通过串口命令可以随时修改。系统运行时的判断逻辑是每100毫秒执行一次水质评估uint8_t WaterQuality_Check(void) { uint8_t pass_count 0; if(g_water_quality.turbidity g_threshold.turbidity_max) pass_count; if(g_water_quality.ammonia g_threshold.ammonia_max) pass_count; if(g_water_quality.dissolved_oxygen g_threshold.do_min) pass_count; if(g_water_quality.ph_value g_threshold.ph_min g_water_quality.ph_value g_threshold.ph_max) pass_count; if(g_water_quality.heavy_metal g_threshold.metal_max) pass_count; if(g_water_quality.water_temp g_threshold.temp_min g_water_quality.water_temp g_threshold.temp_max) pass_count; if(pass_count 6) return 1; // 全部达标 else return 0; }这段代码里溶解氧的判定逻辑和其它参数不一样。浊度、氨氮、重金属是越少越好所以是上限判断溶解氧是越多越好所以是下限判断pH和水温是区间判断过高过低都不行。如果调整电位器时发现出水阀条件始终不满足大概率是某个通道的极性搞反了把下限判断写成了上限判断或者区间判断的范围设置不合理。4. 水位联动与串口阈值改写状态机与中断解析这套系统的控制核心不是简单的前后台轮询而是一个明确的四档水位状态机。水位信号在仿真里通过开关或者电压比较器模拟实际项目中用的是浮球开关或者压力传感器。代码里把水位定义为低阶、中阶、高阶、警报四档每个档位触发不同的阀门动作。串联逻辑要理解到位低水位时补水的优先级最高其他动作都要让路中高水位时停止补水把ADC采集切换到水质检测流程警报水位直接进入安全保护模式不管水处理流程进行到哪一步都先关阀门。水位状态的判定依赖于GPIO输入或者ADC阈值判断这取决于工程里的具体实现。4.1 四档水位状态机的状态定义与迁移条件状态机的状态定义和迁移路径这组对应关系必须理清。低水位状态下1号进水阀打开这是整个流程的起点。当水位升到中阶或高阶时关闭1号进水阀此时系统认为进水量已经足够进入水质检测阶段。检测完成后如果水质达标开出水阀排水如果不达标系统保持等待状态。警报水位表示水位过高存在溢出风险此时系统会关闭所有进水和出水阀门并触发报警。typedef enum { ST_SYSTEM_BOOT 0, // 开机打印阈值 ST_SAFETY_LOCK, // 安全开关未开启 ST_LOW_LEVEL_FILL, // 低水位进水 ST_MID_HIGH_DETECT, // 中/高水位水质检测 ST_QUALITY_JUDGE, // 判断水质是否达标 ST_DRAIN_OPEN, // 打开出水阀 ST_ALARM_TRIGGER // 警报水位 } WaterSystemState; WaterSystemState g_current_state ST_SYSTEM_BOOT; void WaterSystem_StateMachine(void) { switch(g_current_state) { case ST_SYSTEM_BOOT: uart_printf(System Boot OK\r\n); g_current_state ST_SAFETY_LOCK; break; case ST_SAFETY_LOCK: // 安全开关未开启阀门全关 VALVE1_OFF(); VALVE2_OFF(); if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) 1) { uart_printf(Safety Switch ON\r\n); g_current_state ST_LOW_LEVEL_FILL; } break; case ST_LOW_LEVEL_FILL: // 低水位1号进水阀打开 VALVE1_ON(); if(water_level LEVEL_MID) { VALVE1_OFF(); g_current_state ST_MID_HIGH_DETECT; } break; case ST_MID_HIGH_DETECT: // 显示水质参数等待按键或10s超时 uart_display_water_quality(); if(water_level LEVEL_ALARM) { g_current_state ST_ALARM_TRIGGER; } else if(key_pressed || timeout_flag) { g_current_state ST_QUALITY_JUDGE; } break; case ST_QUALITY_JUDGE: if(WaterQuality_Check()) { g_current_state ST_DRAIN_OPEN; } else { uart_printf(Water Quality Not Pass\r\n); g_current_state ST_MID_HIGH_DETECT; } break; case ST_DRAIN_OPEN: VALVE2_ON(); if(water_level LEVEL_LOW) { VALVE2_OFF(); g_current_state ST_LOW_LEVEL_FILL; } break; case ST_ALARM_TRIGGER: VALVE1_OFF(); VALVE2_OFF(); BEEP_ON(); LED_ALARM_ON(); break; } }这个状态机的核心设计思路是把工艺处理过程拆解成离散状态每个状态在执行完自己的任务后根据条件和输入跳转到下一个状态。安全开关的设计是一个容易漏掉的细节ST_SAFETY_LOCK状态下即使检测到低水位也不会开阀门这在工业控制里叫急停优先必须放在状态机开头处理不能放在主循环尾部。代码里用了VALVE1_ON和VALVE1_OFF宏定义来操作阀门GPIO后续如果要换成继电器控制或者加PWM调速只需要改宏定义的底层实现状态机逻辑不用动。4.2 USART1中断接收与阈值命令帧解析远程终端通过串口调试助手直接发命令设置阈值这个需求在实物项目里对应的是上位机下发参数的场景。工程里用USART1作为通信接口中断方式接收数据命令帧格式设计为#T50;这种紧凑文本形式。分号作为帧结束符这样可以避免使用复杂的Modbus协议栈在资源受限的STM32F103上也能轻松处理。char rx_buffer[16]; uint8_t rx_index 0; volatile uint8_t rx_complete 0; void USART1_IRQHandler(void) { char ch; if(USART_GetITStatus(USART1, USART_IT_RXNE) ! RESET) { ch USART_ReceiveData(USART1); if(ch #) { rx_index 0; rx_complete 0; } else if(ch ;) { rx_buffer[rx_index] \0; rx_complete 1; } else if(rx_index 15) { rx_buffer[rx_index] ch; } } } void UART_ProcessCommand(void) { if(rx_complete) { if(strncmp(rx_buffer, T, 1) 0) { g_threshold.turbidity_max atof(rx_buffer[1]); } else if(strncmp(rx_buffer, A, 1) 0) { g_threshold.ammonia_max atof(rx_buffer[1]); } else if(strncmp(rx_buffer, D, 1) 0) { g_threshold.do_min atof(rx_buffer[1]); } else if(strncmp(rx_buffer, P, 1) 0) { g_threshold.ph_min atof(rx_buffer[1]); // 这里只设置下限实际工程也可以成对设置 } else if(strncmp(rx_buffer, M, 1) 0) { g_threshold.metal_max atof(rx_buffer[1]); } else if(strncmp(rx_buffer, C, 1) 0) { g_threshold.temp_max atof(rx_buffer[1]); } uart_printf(Threshold Set OK\r\n); uart_printf(Turbidity Max: %.1f NTU\r\n, g_threshold.turbidity_max); uart_printf(Ammonia Max: %.2f mg/L\r\n, g_threshold.ammonia_max); uart_printf(DO Min: %.1f mg/L\r\n, g_threshold.do_min); rx_complete 0; } }中断接收的设计里把#当作帧头分号当作帧尾中间部分是命令类型和数据。命令类型用单个字母区分参数数据部分通过atof()转成浮点数。串口调试助手发送格式示例#T30;表示把浊度阈值设置为30 NTU#A0.5;表示氨氮阈值0.5 mg/L#D2.0;表示溶解氧下限2.0 mg/L。UART_ProcessCommand()在主循环里不断被调用处理完命令后把新的阈值打印出来这样操作人员在远程终端就能确认参数是否正确写入。4.3 10秒无操作自动处理的实现细节题目要求里有一条核心逻辑按键触发处理或者无操作10秒后自动触发处理。这个10秒的计时不能在主循环里用延时函数实现否则在等待期间无法扫描开关和串口。工程里的做法是用定时器中断产生一个1秒的软件时钟主循环里检查该标志位。TIM2配置为1Hz中断每进一次中断就对计数器加1当计数值达到10时置位timeout_flag。在ST_MID_HIGH_DETECT状态里如果有按键按下或者timeout_flag为1都允许进入ST_QUALITY_JUDGE状态。这里的key_pressed是通过扫描按键状态得到的用了软件防抖一般是延时20ms再连续读两次确认电平稳定防止机械抖动造成误触发。定时器配置代码在stm32f10x_tim.c里这里给出关键参数TIM2的预分频系数设为7200自动重装载值设为9999这样在72MHz主频下产生1秒的中断周期。启动定时器后在中断服务程序里做计数器累加并设定一个复位机制一旦检测到串口有数据或者按键被按下计数器清零重来。这样就保证了最后操作时间的语义是连续无操作10秒而不是从系统启动开始算10秒。5. Proteus仿真排错三板斧虚拟终端、时序观测与报警可读性Proteus仿真跑不通的情况大多集中在三个点虚拟终端没输出、串口波特率对不上、报警不直观。前两个问题可以通过检查连线确定位第三个问题就得从代码层想办法。这套资源里提到一个很实际的细节蜂鸣器报警有时候听不到所以加了一个LED作为视觉辅助。这个思路在工业现场也同样适用声音报警容易被环境噪音掩盖必须搭配光报警或者短信通知才能保证及时响应。5.1 虚拟终端不显示打印信息的经典排查流程仿真启动后屏幕和终端全黑是最常见的情况。优先检查芯片有没有加载程序双击STM32芯片确认Program File路径指向了Keil生成的AXF文件注意这个文件在Keil工程目录的Obj文件夹下。再确认单片机VDD和VSS引脚是否接上了5V和GNDProteus里STM32模型必须完整接入电源否则芯片不运行。接着检查复位电路NRST引脚通过10k电阻上拉到3.3V如果悬空可能会导致芯片反复复位打印信息会闪一下就消失。最后查看串口连接STM32的PA9TX必须接虚拟终端的RXD引脚PA10RX接TXD引脚接反了只会有接收没发送。从uart_printf的实现看底层通过USART1发送字符虚拟终端打印乱码通常是波特率不一致检查COMPIM和虚拟终端的波特率设置是否都改成了代码里初始化的数值。5.2 仿真运行时的阈值验证方法验证系统功能的最直接手段是配合串口调试助手动态修改阈值观察阀门动作逻辑是否跟随变化。具体步骤是先拖动滑动变阻器让浊度输出为3V左右此时理论上浊度大约100NTU再发送#T50;把阈值设为50系统判断水质超标不会打开出水阀。然后把滑动变阻器调到1V左右对应浊度约20NTU低于阈值再按一下按键触发处理出水阀打开。反复调整电位器和发送命令就能确认ADC采集、阈值比较、状态机判定这三维逻辑是联通的。如果发现改了阈值但数据显示不变优先看串口命令解析里有没有处理这个字母类型的命令漏了分支就要在UART_ProcessCommand()里补上。5.3 报警可读性与仿真调试的综合优化最后说明报警模块的优化点代码里BEEP_ON()和LED_ALARM_ON()是分离控制的两个宏在Proteus里蜂鸣器模块的频率参数建议设置为2kHz到4kHz这个范围人耳敏感仿真中也能在虚拟终端上看到对应的电平翻转输出。同时把指示灯LED的引脚配置为推挽输出串接一个330Ω限流电阻接到电源防止过流烧毁仿真元件。这套系统后续要做实物可以直接把LED和蜂鸣器的驱动逻辑移植到继电器模块和声光报警器上GPIO口不变只调整驱动电路的功率等级。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价