
简介这是一款基于STM32F429的电导率测量仪完整工程资源面向嵌入式、电子、自动化等专业的开发者与学生旨在以较低成本实现工业级溶液电导率检测。项目从实际实验需求出发设计了正弦波驱动、滤波、放大及负电源电压转换等硬件模块并配套温度传感器、ADC、LCD显示、触摸屏交互、SD卡存储和串口打印等完整软件驱动可完成数据采集、处理、显示与保存。压缩包共101个文件以C/H源代码、原理图与PCB设计文档、Keil工程文件、HEX固件、说明文档、图片及演示视频为主整体约72.96MB目录结构清晰便于对照学习与二次开发。目前已有648人学习下载。资源内项目代码经过运行测试功能可靠适合用于课程设计、毕业设计或入门进阶。通过学习可以掌握STM32外设驱动、传感器信号调理、模拟电路设计与嵌入式系统集成方法也可在此基础进行功能扩展具有较高的参考价值。1. 基于STM32电导率测量仪的项目定位与可复现路径用STM32做电导率测量仪在“基于STM32的毕业设计”里属于模拟链路相对完整、又不需要复杂外设的选题。这个项目不光是写代码它要求开发者把电极、运放、ADC和定时器串成一条能自洽的信号链电极输出的电流是微安级模拟前端要有足够增益同时激励信号不能是直流否则极化效应会让读数一直漂。本文从测量原理出发把模拟前端参数、STM32源代码实现、校准和排错一次讲透适合手里已经有一份“源代码文档说明”工程包、但不知道怎么改参数的人看。整条链路下来你会发现真正决定测量精度的不是主控而是你选的反馈电阻和采样相位。2. 电导测量原理与STM32模拟前端电路设计2.1 电导率公式与电导池常数电导率不是直接测出来的数字它是通过电极几何结构换算出来的。两个平行金属极板浸在溶液里等效成一个电阻R电导G1/R单位是西门子S。把电导乘上由电极结构决定的比例系数K才能得到不依赖探头尺寸的物理量κ G × K (1/R) × (L/A)式中的K就是电导池常数单位cm⁻¹L是极板间距A是电极有效面积。实际探头出厂时标称K值但装配误差和电极表面状态会让真实K和标称值有偏差所以后面校准过程本质上就是在修正这个常数。选电极时按下表粗定量程被测对象典型电导率范围推荐量程档典型电极常数K去离子水 / 超纯水0.05 ~ 10 µS/cm200 µS/cm0.01生活饮用水300 ~ 1500 µS/cm2 mS/cm1.0农业用水 / 一般废水1 ~ 10 mS/cm20 mS/cm1.0海水 / 高浓度工业液30 ~ 60 mS/cm200 mS/cm10同一支电导池极板面积越大或间距越小K越小测低电导率的敏感度越高。设计时先想清楚目标水质再去买电极和定增益顺序不能反。2.2 为什么必须用交流激励如果给电极加恒定的直流电压溶液里的正负离子会向两极定向迁移在电极表面形成极化层。这层极化等效成一个反向电动势叠加在测量回路上表现就是电流随时间衰减读数反复漂移无法稳定。所以所有电导率仪都用交流激励极板上的电压极性周期性反转极化层来不及建立就被打断。激励频率的选择分两条线。低电导率溶液低于200 µS/cm用1kHz左右就够频率太高时电极界面的双电层电容会变成低阻抗旁路一部分激励电流不经过溶液而走电容读数偏低。高电导率溶液高于10 mS/cm要提高到5kHz甚至更高因为这时候体电阻小需要靠高频来减小双电层电容的影响。这个取舍直接决定定时器的ARR值和低通滤波器的截止频率是源代码里第一组要调整的参数。2.3 跨阻放大器把电流变成电压信号链路的骨架如下STM32定时器PWM输出方波经隔直电容变成双极性的交流激励加到电极一端电极另一端的电流进入跨阻放大器TIA电流在这一级被转换成电压再经交流放大和低通滤波送进STM32的ADC引脚。TIA是整条链路里最关键的一级。电极一端接激励另一端接运放反相输入端反相输入端的“虚地”特性保证电极对地电位恒定这能避免连接电缆的寄生电容和电极极板电容对激励形成分压。输出电压为Vout -I × Rf负号只表示反相关系。如果激励电压峰峰值为1V电导G为2mS那么电流就是2mA取Rf510Ω时TIA输出约±1.02V。后级再接一个放大倍数为2的同相放大器把信号变成约±2V接近3.3V ADC的满量程但仍留10%裕量。如果做低量程档把Rf换成10kΩ即可所谓量程切换本质就是切换Rf。2.3.1 运放参数和地线处理TIA运放要选输入偏置电流小的型号普通LM358会在高阻抗反馈电阻上产生明显的失调误差。常见做法是选偏置电流在pA级别的CMOS运放低成本型号里可选项很多。电源去耦要在运放供电脚就近放10Ω电阻和100nF电容组成RC滤波把数字部分的高频噪声隔离掉。地线采用单点星形连接TIA区域的模拟地不要跟STM32的GND大面积铺铜混在一起否则ADC采样值会出现随串口发包而跳动的毛刺。2.4 ADC采样参数与频率对齐STM32F103系列内置12位ADC转换速率最快1MHz对1kHz的激励信号来说有充足的过采样空间。用100kHz采样率一个周期能采到100个点经过软件求峰峰值之后噪声能被平均掉不少。如果追求更高分辨率可以用过采样加求平均的方式把12位数据提升到等效14位。要注意的是ADC的采样时序最好由定时器触发保证采样时刻和PWM激励在同一相位基准上。如果各采各的电极电容充放电曲线的相位差会导致幅度计算不稳定。3. STM32源代码实现激励PWM、DMA采样、温度补偿3.1 工程结构与文件划分拿到一份“源代码文档说明”的STM32项目先别急着编译花十分钟看文件划分。常见做法是CubeMX生成初始化代码Keil MDK5编译调试业务代码按模块拆开文件职责关键接口main.c时钟、外设初始化、主循环main()bsp_pwm.c激励方波生成PWM_Init(), PWM_SetFreq()bsp_adc.cADCDMA连续采集ADC_Start_DMA()app_cond.c电导率计算、滤波、量程切换Conductivity_Process()app_temp.cNTC温度读取NTC_ReadTemp()app_uart.c结果输出UART_SendFrame()文档说明里如果附了引脚分配表第一件事就是对一下CubeMX的引脚配置TIM2_CH1映射到哪个引脚ADC1_INx又是哪个引脚这两处最容易被初始化代码覆盖掉。曾经遇到过PWM和ADC复用同一个引脚波形根本出不来原因就是CubeMX重新生成代码时把GPIO模式覆盖了。3.2 用定时器PWM生成1kHz激励信号用STM32的定时器做PWM输出不需要额外芯片频率还能在代码里直接改。STM32F103的APB1定时器时钟为72MHz设PSC71得到1MHz计数时钟ARR999时PWM频率正好1kHz占空比设置为50%产生对称方波。初始化代码如下TIM_HandleTypeDef htim2; htim2.Instance TIM2; htim2.Init.Period 999; // ARR, 决定频率 htim2.Init.Prescaler 71; // 72MHz / (711) 1MHz htim2.Init.CounterMode TIM_COUNTERMODE_UP; htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_TIM_PWM_Init(htim2); TIM_OC_InitTypeDef sConfigOC {0}; sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 500; // 占空比 500/1000 50% sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim2, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1);这段代码的逻辑是先通过PSC把定时器时钟降到1MHz再用ARR做分频得到目标频率。PSC71、ARR999时频率为1kHz要切到5kHz把ARR改成199占空比参数Pulse同步改成100即可。引脚输出0~3.3V的单极性方波后面串一个隔直电容后变成约±1.65V的交流信号幅度取决于电容和负载阻抗的分压关系。占空比必须保持50%否则隔直电容会充上直流偏置破坏激励对称性。3.3 ADC用DMA连续采样并分离正负半周激励频率1kHz用100kHz采样率一个周期能采100个点。为了不占CPUADC配DMA循环模式采样结果直接写入数组传满一轮后进回调。代码框架如下#define N_SAMPLES 1024 uint16_t adc_buf[N_SAMPLES]; volatile uint8_t dma_done 0; void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { if (hadc-Instance ADC1) { dma_done 1; // 通知主循环处理数据 } } // 主循环中判断标志位 if (dma_done) { dma_done 0; HAL_ADC_Stop_DMA(hadc1); process_conductivity(adc_buf, N_SAMPLES); HAL_ADC_Start_DMA(hadc1, (uint32_t *)adc_buf, N_SAMPLES); }之所以不在回调里直接处理是因为数据处理涉及浮点运算和滤波放在中断里会拖长中断响应时间还可能影响定时器PWM的抖动。主循环在DMA传输完成的间隙处理数组处理完立刻重启DMA中间丢掉的几个采样周期对结果无影响。process_conductivity里的核心工作是分离信号的正半周和负半周。假设模拟输出以1.65V为中心偏置方波波形在正半周高于中心值、负半周低于中心值用边沿检测和阈值判断分组累加void process_conductivity(uint16_t *buf, uint32_t len) { uint32_t sum_pos 0, sum_neg 0; uint32_t cnt_pos 0, cnt_neg 0; uint8_t stage 0; // 0: 负半周, 1: 正半周 for (uint32_t i 0; i len; i) { if (buf[i] 1800) { stage 1; // 高于阈值视为正半周 } else if (buf[i] 1500) { stage 0; // 低于阈值视为负半周 } if (stage) { sum_pos buf[i]; cnt_pos; } else { sum_neg buf[i]; cnt_neg; } } float vpos (float)sum_pos / cnt_pos * 3.3f / 4095.0f; float vneg (float)sum_neg / cnt_neg * 3.3f / 4095.0f; float vpp (vpos - vneg) * 2.0f; // 正负半周差再乘2得到峰峰值 // 后续根据增益换算成电导率 }阈值1500和1800的选取要配合实际波形的中心值。如果ADC参考电压是3.3V偏置在中点1.65V那么ADC码值大约在2048附近正半周应明显高于2048负半周明显低于。这两个常数不是通用的换板子改电源电压时要重新标定。更可靠的做法是在代码里维护一个滑动平均中心值实时计算阈值区间但这种自适应方案调试成本较高工程包自带的文档说明里若没写就不要自行加上去。3.4 温度补偿与25℃基准换算电导率的温度系数很大溶液温度每升高1℃电导率大约增加2%。如果仪器不做温度补偿夏天和冬天的读数差异会很夸张。常见做法是仪器内放一个NTC热敏电阻测溶液温度再把实际电导率折算回25℃下的标准值float ntc_to_temp(uint16_t adc_raw) { const float r_ref 10.0f; // 串联电阻 10kΩ const float b_const 3435.0f; // NTC B值 float r_ntc r_ref * ((4095.0f / adc_raw) - 1.0f); float t_k 1.0f / (1.0f / 298.15f logf(r_ntc / 10.0f) / b_const); return t_k - 273.15f; } float cond_to_25(float cond_t, float temp_c) { const float alpha 0.02f; // 温度系数约 2%/℃ return cond_t / (1.0f alpha * (temp_c - 25.0f)); }NTC计算用的B值公式是近似模型在0~50℃范围内误差可以控制在0.5℃以内对电导率补偿来说足够。alpha取0.02是大多数中性盐溶液的近似值若被测溶液是强酸强碱alpha会偏差较大这时候需要在文档里注明用户可配置。3.5 文档说明要写清楚的接口与协议一套能给别人复用的工程包文档至少要覆盖引脚映射、校准参数表、串口协议和量程切换逻辑。串口输出建议定成固定字节帧方便上位机解析字节偏移含义格式0帧头0xA51数据长度uint82~5电导率值×100float6~9温度值×100float10校验字节累加和帧结构不宜做得太复杂Modbus-RTU对毕设项目来说略重简单帧头加校验就能满足调试和上位机显示需求。源代码里对应的发送函数要和这个表格严格对齐否则文档和代码对不上后面调起来非常痛苦。4. 电导率仪校准、参数标定与现场排错4.1 两点校准的具体操作仪表装完第一件事不是测水样而是校准。常见做法是买标准电导率溶液用两点法校准低点用1413µS/cm的KCl标准液高点用12.88mS/cm的标准液。流程上先把电极放进1413µS/cm标准液稳定30秒后让仪器记录一个ADC原始峰峰值再换到12.88mS/cm标准液记录第二个点。软件里拿两个点的实测值与标准值做线性回归求出斜率和截距写入STM32内部Flash或外部EEPROM。typedef struct { float gain; // 斜率, 单位 (µS/cm) / (ADC码值) float offset; // 截距, 单位 µS/cm float pool_k; // 实测电导池常数, 默认1.0 } calib_t;4.2 电极常数K的现场修正买来的电极标称K1.0但实际装到PCB上、线缆长短变了之后K会有几个百分点的偏差。两点校准算出的gain实际上已经包含了K的影响所以工程里不必单独修K直接用量程系数就行。如果换了不同K值的电极把之前的校准参数清掉重新做两点校准不能沿用旧参数否则高量程会满偏。4.3 反复出现的漂移问题漂移是电导率仪最常见的故障。把电极放在标准液里读数缓慢上升或下降先排除极化现象确认激励是交流信号且频率没有被初始化代码无意间改掉。然后把电极拿出来检查极板上有油污或氧化物时有效面积改变读数会持续漂移处理方法是清洗电极并用标准液重新校准。还有一种隐蔽情况是被测液体里有气泡附着在极板表面这会让有效接触面积变小测出的电导率偏低。4.4 常见的测量异常排查表现象可能原因处理方式读数始终接近零PWM没输出或隔直电容虚焊用示波器量TIM引脚和运放输出读数偏高且不稳Rf太小增益不足信噪比差切换到高阻Rf档重新采样读数随串口发送跳变数字地噪声串入模拟地检查地线布线运放电源加RC滤波温度读数偏差超过2℃NTC分压电阻选错核对NTC B值和10kΩ串联电阻换水样后读数反应慢电极响应时间或气泡附着搅动溶液排出气泡检查RC时间常数给仪器做排错时最好先把ADC原始值通过串口打印出来避免看经滤波后的结果。原始值能直接反映模拟链路是否有信号如果原始值是一条直线后面再多的算法补偿都没意义。5. 进阶验证多量程自动切换与模拟电极自检5.1 用双反馈电阻实现自动量程切换单量程仪表测自来水与测海水时动态范围不够要么小信号被噪声淹没要么大信号让运放饱和。常见方案是在TIA反馈回路并联两支电阻用GPIO控制模拟开关切换低量程档用高阻值Rf高量程档切到低阻值Rf。主循环里判断ADC电压幅度float vpp_mv get_vpp(); // 当前峰峰值, 单位mV if (vpp_mv 200.0f) { switch_rf(HIGH_RF); // 切到 100kΩ 档适合低电导率 } else if (vpp_mv 2900.0f) { switch_rf(LOW_RF); // 切到 10kΩ 档防止饱和 }设置200mV和2.9V两个切换点留出滞回区间避免在临界点反复切换。切换后要等模拟链路稳定再采集一组数据重新计算切换瞬间的毛刺不能被滤波器记录进去。5.2 用精密电阻代替电极做链路自检校准完电极后想确认模拟前端本身没有误差有个很实用的技巧把电极拆下来在电极接口两端并接一个精度0.1%的500Ω电阻模拟一个固定电导负载。这时如果激励幅度和Rf都已知理论输出电压应为Vout Vin × (Rf / R_test)实测峰峰值和理论值偏差在0.5%以内说明整个模拟链路没有问题误差出在电极或校准参数上偏差超过2%就要回头查运放供电、Rf精度和ADC参考电压。这个技巧在工程交付时特别好用做验收测试时不需要临时配标准液只用电阻就能把硬件链路验证一遍。5.3 长时间稳定性观察法把模拟电极换成精密电阻后开机连续记录两小时观察串口输出的电导率数值。正常情况漂移应小于±1%。如果漂移超过这个范围大概率是运放温漂或ADC参考电压不稳先在代码里检查是否启用了内部参考电压模拟参考电压VREF需要外部基准源才稳定。稳定性测试的数据保存成CSV文件可直接导入Excel做曲线分析这一步能让“源代码文档说明”的交付显得更完整。本文还有配套的精品资源点击获取