PH传感器基于玻璃电极法,通过测量溶液中氢离子浓度对应的电压信号,实现PH值的精确检测。
PH传感器内部包含一个对氢离子(H⁺)敏感的玻璃电极和一个参比电极。当玻璃电极浸入待测溶液时,玻璃膜两侧会因氢离子浓度差产生一个电位差。该电位差遵循能斯特方程(Nernst Equation):E = E₀ - (2.303RT/nF) × pH。在25°C条件下,每变化1个PH单位,电极输出电压变化约59.16mV。传感器模块内置信号调理电路,将微弱的电极信号放大并转换为0~3.3V的模拟电压输出(AO),供STM32的ADC采集。
该模块输出的模拟电压与PH值呈线性反比关系:电压越高,PH值越低(偏酸性);电压越低,PH值越高(偏碱性)。本程序中通过标定得到的线性公式为:PH = -5.8887 × V + 21.677,其中V为ADC采集并换算后的实际电压值(单位:V)。
通过玻璃电极实时测量溶液的氢离子浓度,输出对应的模拟电压信号,支持PH 0~14全量程检测。
AO引脚输出0~3.3V线性模拟电压,直接连接STM32的ADC通道进行采集,无需复杂通信协议。
配合STM32定时采集(200ms周期),可在OLED屏幕上实时显示当前PH值,适用于水质连续监测场景。
采用5V供电,内置信号调理电路,适用于酸碱溶液、水质检测、实验室分析等多种应用场景。
| 引脚号 | 引脚名称 | 功能说明 | STM32连接 |
|---|---|---|---|
| 1 | VCC | 电源正极(5V) | 5V |
| 2 | GND | 电源地 | GND |
| 3 | AO | 模拟电压输出(0~3.3V) | PA0(ADC1_IN0) |
PH传感器模块包含PH探头、信号转换板和连接线,下方为模块各角度实物展示。
PH传感器模块的电路原理图与PCB封装设计。
基于STM32 HAL库开发,使用ADC1通道0采集PH传感器模拟电压,经线性换算得到PH值并在OLED上显示。
系统使用外部高速晶振(HSE)8MHz作为时钟源,通过PLL倍频9倍得到72MHz系统时钟(SYSCLK)。AHB总线不分频(72MHz),APB1总线2分频(36MHz),APB2总线不分频(72MHz)。ADC时钟由APB2再8分频得到9MHz,满足ADC最高14MHz的限制。
void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; /* HSE 8MHz外部晶振, PLL倍频9倍 = 72MHz */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; HAL_RCC_OscConfig(&RCC_OscInitStruct); /* SYSCLK=72MHz, AHB=72MHz, APB1=36MHz, APB2=72MHz */ RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2); /* ADC时钟 = APB2(72MHz) / 8 = 9MHz */ PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV8; HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit); }
使用ADC1通道0(PA0引脚)采集PH传感器AO输出的模拟电压。配置为12位分辨率(0~4095)、右对齐、软件触发、单次转换模式。采样时间设为239.5个ADC时钟周期(约26.6μs),确保高阻抗信号源的采样精度。PA0引脚配置为模拟模式(GPIO_MODE_ANALOG),避免数字电路干扰。
ADC_HandleTypeDef hadc1; void MX_ADC1_Init(void) { ADC_ChannelConfTypeDef sConfig = {0}; hadc1.Instance = ADC1; hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; // 禁用扫描模式 hadc1.Init.ContinuousConvMode = DISABLE; // 单次转换 hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 软件触发 hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; // 右对齐(12位) hadc1.Init.NbrOfConversion = 1; // 1个转换通道 HAL_ADC_Init(&hadc1); sConfig.Channel = ADC_CHANNEL_0; // 通道0 (PA0) sConfig.Rank = ADC_REGULAR_RANK_1; // 规则组第1 sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; // 239.5周期采样 HAL_ADC_ConfigChannel(&hadc1, &sConfig); } /* PA0 引脚配置为模拟模式 */ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(adcHandle->Instance == ADC1) { __HAL_RCC_ADC1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /* PA0-WKUP -----> ADC1_IN0 */ GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } }
在MX_GPIO_Init中使能GPIOD和GPIOA时钟。PA0的模拟模式配置在ADC的MspInit回调中完成。OLED使用的PB14(SDA)和PB15(SCL)引脚在OLED_Init函数内部通过软件I2C方式配置。
void MX_GPIO_Init(void) { __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); }
采用0.96寸OLED显示屏(128×64),通过软件模拟I2C通信。SCL连接PB15,SDA连接PB14。使用OLED_CenterPrintf函数在Y=24位置居中显示PH值,显示格式为"PH:%.1f"(保留1位小数)。每次更新前调用OLED_Clear清屏,更新后调用OLED_Update刷新显存到屏幕。
#define SCL_Port GPIOB #define SCL_Pin GPIO_PIN_15 #define SDA_Port GPIOB #define SDA_Pin GPIO_PIN_14 /* OLED核心函数 */ void OLED_Init(void); // 初始化 void OLED_Clear(void); // 清屏 void OLED_CenterPrintf(int16_t Y, char *format, ...); // 居中打印 void OLED_Update(void); // 更新显示
主函数首先完成HAL初始化、时钟配置、GPIO/ADC外设初始化,然后执行ADC自校准(HAL_ADCEx_Calibration_Start)和OLED初始化。进入while(1)主循环后,通过HAL_GetTick()实现200ms定时:启动ADC转换 → 等待转换完成 → 读取12位ADC原始值 → 换算为电压 → 线性公式计算PH值 → 限幅处理 → OLED显示。最后更新时间戳,等待下一个采集周期。
uint32_t Time_Now; float PH; int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); HAL_ADCEx_Calibration_Start(&hadc1); // ADC自校准 OLED_Init(); // OLED初始化 while (1) { if(HAL_GetTick() - Time_Now >= 200) // 200ms更新一次 { // 单通道数据获取 HAL_ADC_Start(&hadc1); if(HAL_ADC_PollForConversion(&hadc1, 999) == HAL_OK) PH = (HAL_ADC_GetValue(&hadc1)/4095.0)*3.3; HAL_ADC_Stop(&hadc1); PH = -5.8887*PH+21.677; // 计算PH值 // 限幅 if(PH > 14.0) PH = 14.0; if(PH < 0.0) PH = 0.0; OLED_Clear(); // 清屏 OLED_CenterPrintf(24, "PH:%.1f", PH); OLED_Update(); // 更新显示 Time_Now = HAL_GetTick(); // 更新时间戳 } } }
| ADC原始值 | 电压 (V) | PH值 | 溶液状态 |
|---|---|---|---|
| 4095 | 3.300 | 2.24 | 强酸性 |
| 3072 | 2.475 | 7.10 | 中性 |
| 2048 | 1.650 | 11.95 | 碱性 |
| 1024 | 0.825 | 14.00 (限幅) | 强碱性 |
| 0 | 0.000 | 14.00 (限幅) | 强碱性 |
从系统初始化到主循环采集的完整程序执行流程,虚线框表示while(1)循环体。
模拟PH传感器探头浸入不同酸碱性溶液,观察ADC采集、PH值计算与OLED显示的实时联动。
烧杯中的溶液颜色随PH值变化:红色(强酸)→ 橙色(弱酸)→ 绿色(中性)→ 蓝色(弱碱)→ 紫色(强碱)。PH探头浸入溶液中,AO引脚输出对应的模拟电压。