简介:这是一款基于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,电导G=1/R,单位是西门子S。把电导乘上由电极结构决定的比例系数K,才能得到不依赖探头尺寸的物理量:
κ = G × K = (1/R) × (L/A)
式中的K就是电导池常数,单位cm⁻¹,L是极板间距,A是电极有效面积。实际探头出厂时标称K值,但装配误差和电极表面状态会让真实K和标称值有偏差,所以后面校准过程本质上就是在修正这个常数。选电极时按下表粗定量程:
| 被测对象 | 典型电导率范围 | 推荐量程档 | 典型电极常数K |
|---|---|---|---|
| 去离子水 / 超纯水 | 0.05 ~ 10 µS/cm | 200 µS/cm | 0.01 |
| 生活饮用水 | 300 ~ 1500 µS/cm | 2 mS/cm | 1.0 |
| 农业用水 / 一般废水 | 1 ~ 10 mS/cm | 20 mS/cm | 1.0 |
| 海水 / 高浓度工业液 | 30 ~ 60 mS/cm | 200 mS/cm | 10 |
同一支电导池,极板面积越大或间距越小,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;取Rf=510Ω时,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.c | ADC+DMA连续采集 | ADC_Start_DMA() |
| app_cond.c | 电导率计算、滤波、量程切换 | Conductivity_Process() |
| app_temp.c | NTC温度读取 | 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,设PSC=71得到1MHz计数时钟,ARR=999时PWM频率正好1kHz,占空比设置为50%产生对称方波。初始化代码如下:
TIM_HandleTypeDef htim2; htim2.Instance = TIM2; htim2.Init.Period = 999; // ARR, 决定频率 htim2.Init.Prescaler = 71; // 72MHz / (71+1) = 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做分频得到目标频率。PSC=71、ARR=999时频率为1kHz;要切到5kHz,把ARR改成199,占空比参数Pulse同步改成100即可。引脚输出0~3.3V的单极性方波,后面串一个隔直电容后,变成约±1.65V的交流信号,幅度取决于电容和负载阻抗的分压关系。占空比必须保持50%,否则隔直电容会充上直流偏置,破坏激励对称性。
3.3 ADC用DMA连续采样并分离正负半周
激励频率1kHz,用100kHz采样率一个周期能采100个点。为了不占CPU,ADC配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 | 帧头 | 0xA5 |
| 1 | 数据长度 | uint8 |
| 2~5 | 电导率值×100 | float |
| 6~9 | 温度值×100 | float |
| 10 | 校验字节 | 累加和 |
帧结构不宜做得太复杂,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的现场修正
买来的电极标称K=1.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做曲线分析,这一步能让“源代码+文档说明”的交付显得更完整。
本文还有配套的精品资源,点击获取