基于立创开发板的土壤湿度传感器模块移植与ADC/GPIO双模式读取实战
基于立创开发板的土壤湿度传感器模块移植与ADC/GPIO双模式读取实战
最近在做一个智能浇花的小项目,需要用到土壤湿度传感器。手头正好有一块立创的开发板(主控是R7FA6E2BB3CNE)和一个常见的土壤湿度模块。网上资料虽然多,但直接能用在立创板子上的完整教程却很少。今天我就把整个移植和调试的过程记录下来,从硬件连接到软件驱动,再到数据读取,手把手带你走一遍。无论你是刚接触嵌入式的新手,还是想快速在立创平台上验证传感器的朋友,这篇教程都能帮到你。
咱们的目标很明确:让开发板既能通过ADC读取精确的模拟湿度值(百分比),也能通过GPIO快速判断土壤是“干”还是“湿”。下面就开始吧。
1. 认识你的土壤湿度传感器模块
在动手写代码之前,咱们先得搞清楚手头的传感器是怎么工作的。我用的这个模块非常常见,淘宝上几块钱就能买到,长这样:
- 核心功能:检测土壤含水量。
- 输出模式:它有两个输出口,这是关键!
- AO(模拟输出):输出一个连续的电压信号,土壤越湿,电压越高(或越低,取决于模块设计)。这个信号需要接到单片机的ADC引脚,经过模数转换得到一个数字值,从而计算出更精确的湿度百分比。
- DO(数字输出):输出一个简单的高低电平。模块上有个蓝色的电位器,你可以调节一个阈值。当土壤湿度超过这个阈值时,DO引脚输出一种电平(比如低电平);低于阈值时,输出另一种电平(比如高电平)。它相当于一个简单的开关,告诉你“干了”还是“没干”。
- 工作原理:模块附带两个叉子状的探针。把它插入土壤,土壤中的水分会让两个探针之间导电。水分越多,导电性越好,输出的模拟电压就会变化。
- 硬件参数:
- 工作电压:3.3V - 5V(和立创开发板兼容)
- 工作电流:约150mA
- 接口:4个2.54mm间距的排针(VCC, GND, DO, AO)
提示:你可以做个简单测试,用一根导线或金属镊子同时接触两个探针,模拟土壤非常湿润的状态,这时模块上的DO指示灯应该会亮起。这能快速验证模块好坏。
2. 硬件连接与引脚配置
首先,把传感器和立创开发板连起来。连接很简单,就是四根线:
| 传感器模块引脚 | 连接到立创开发板 |
|---|---|
| VCC | 5V0 (5V电源输出) |
| GND | GND (地) |
| DO | P407 (用作GPIO输入) |
| AO | P000 (用作ADC输入) |
这里我选择P000作为ADC输入通道0,P407作为普通GPIO输入。你当然可以根据自己的板子资源选择其他引脚,只要功能对应就行。
接下来,我们需要在开发环境里配置这些引脚。立创的开发环境通常有图形化配置工具(如RASC),操作如下:
- 打开项目的图形化配置界面。
- 配置P000为ADC功能:找到P000引脚,将其功能选择为“ADC0”或对应的ADC输入通道。
- 配置P407为GPIO输入:找到P407引脚,将其功能设置为“GPIO Input”。
- 按下
Ctrl + S保存配置。 - 点击界面上的
Generate Project Content按钮生成底层驱动代码。
完成这一步后,工具会自动生成初始化ADC和GPIO的代码框架,我们后续的代码会基于这个框架来写。
3. 驱动代码编写:创建传感器专属文件
为了代码整洁和可复用,我们新建两个文件:bsp_soilhumidity.c和bsp_soilhumidity.h(“bsp”意为板级支持包)。记得把包含头文件的路径添加到你的编译器设置里。
3.1 头文件定义 (bsp_soilhumidity.h)
头文件里主要做宏定义和函数声明。
#ifndef BSP_CODE_BSP_SOILHUMIDITY_H_ #define BSP_CODE_BSP_SOILHUMIDITY_H_ #include "hal_data.h" #include <stdio.h> // 一些常用的延时宏定义,方便使用 #ifndef delay_ms #define delay_ms(x) R_BSP_SoftwareDelay(x, BSP_DELAY_UNITS_MILLISECONDS) #endif #ifndef delay_us #define delay_us(x) R_BSP_SoftwareDelay(x, BSP_DELAY_UNITS_MICROSECONDS) #endif // 类型简写 #ifndef u8 #define u8 uint8_t #endif #ifndef u16 #define u16 uint16_t #endif // 硬件相关宏定义(根据你的配置修改) #define BSP_ADC_CHANNEL ADC_CHANNEL_0 // 对应P000,ADC通道0 #define BSP_DO_GPIO_PIN BSP_IO_PORT_04_PIN_07 // 对应P407 // 采样次数,用于软件滤波 #define SAMPLES 5 // 函数声明 void Module_BSP_Init(void); // 初始化ADC int Get_Adc_Value(void); // 获取ADC原始值(多次平均) uint8_t Get_SH_Percentage_value(void); // 获取湿度百分比 char Get_SH_DO_value(void); // 获取DO数字状态 #endif /* BSP_CODE_BSP_SOILHUMIDITY_H_ */3.2 源文件实现 (bsp_soilhumidity.c)
这里是具体的功能实现,咱们一步步看。
首先,是ADC初始化和回调函数。ADC扫描完成时,会触发回调函数,我们用一个标志位来通知主程序。
#include "bsp_soilhumidity.h" #include <stdio.h> // ADC扫描完成标志 volatile bool adc_flag = false; // ADC回调函数 void adc_callback(adc_callback_args_t *p_args) { FSP_PARAMETER_NOT_USED(p_args); // 避免未使用参数警告 adc_flag = true; // 设置标志位,表示一次ADC采样完成 } // 初始化ADC模块 void Module_BSP_Init(void) { fsp_err_t err = FSP_SUCCESS; // 1. 打开ADC设备 err = R_ADC_Open(&g_adc0_ctrl, &g_adc0_cfg); if (FSP_SUCCESS != err) { printf("ADC Open Failed! \n"); return; } // 2. 配置ADC扫描(图形化工具已生成配置,这里直接应用) err = R_ADC_ScanCfg(&g_adc0_ctrl, &g_adc0_channel_cfg); if (FSP_SUCCESS != err) { printf("ADC Scan Config Failed! \n"); R_ADC_Close(&g_adc0_ctrl); // 如果失败,关闭已打开的ADC return; } }接着,是核心的ADC读取函数。这个函数负责启动一次ADC转换,并等待读取结果。
// 内部函数:执行一次ADC采样并读取结果 static int ADC_GET(void) { int gAdcResult = 0; int timeOut = 1000; // 超时计数器,防止卡死 fsp_err_t err = FSP_SUCCESS; // 1. 启动ADC扫描 err = R_ADC_ScanStart(&g_adc0_ctrl); if (FSP_SUCCESS != err) { printf("ADC扫描启动失败! \n"); return 9999; // 返回一个明显的错误值 } // 2. 等待ADC转换完成(标志位由回调函数设置) while(!adc_flag && timeOut--) { delay_us(1); // 短暂延时,避免空循环耗尽CPU } // 3. 清除标志位,为下一次采样做准备 adc_flag = false; // 4. 检查是否超时 if(!timeOut) { printf("ADC_GET Failed!!!\r\n"); return 0; } // 5. 读取ADC通道0的结果 err = R_ADC_Read(&g_adc0_ctrl, BSP_ADC_CHANNEL, (uint16_t *)&gAdcResult); if (FSP_SUCCESS != err) { printf("ADC Read Failed!\n"); return 9999; } return gAdcResult; // 返回12位ADC原始值(0-4095) }单次ADC采样可能受噪声干扰,所以我们需要一个函数来读取多次并求平均值,这样数据更稳定。
// 获取ADC原始值(经过多次采样平均) int Get_Adc_Value(void) { int Data = 0; int i; // 采样SAMPLES次(例如5次),然后累加 for(i = 0; i < SAMPLES; i++) { Data += ADC_GET(); delay_ms(5); // 每次采样间隔5ms } Data = Data / SAMPLES; // 计算平均值 return Data; }有了稳定的ADC原始值,我们就可以把它转换成更直观的湿度百分比了。这里假设ADC值越大代表湿度越高(请根据你的模块实际特性调整,有些模块是反比的)。
// 将ADC值转换为湿度百分比(0%~100%) uint8_t Get_SH_Percentage_value(void) { int adc_max = 4095; // 立创开发板ADC是12位,最大值4095 int adc_new = 0; uint8_t Percentage_value = 0; adc_new = Get_Adc_Value(); // 获取平均后的ADC值 // 计算百分比:(当前值 / 最大值) * 100 Percentage_value = ((float)adc_new / (float)adc_max) * 100.0f; return Percentage_value; }最后,是读取DO数字引脚状态的函数。这个就简单多了,直接读取GPIO电平即可。
// 读取DO引脚电平状态 char Get_SH_DO_value(void) { bsp_io_level_t pin_level; fsp_err_t err = R_IOPORT_PinRead(&g_ioport_ctrl, BSP_DO_GPIO_PIN, &pin_level); if(err != FSP_SUCCESS) { printf("GPIO Input Read Failed!!\r\n"); return -1; // 读取失败返回-1 } // 根据模块逻辑返回。假设DO输出低电平表示湿度足够(LED亮) if(pin_level == BSP_IO_LEVEL_LOW) { return 0; // 可以定义为“湿” } else { return 1; // 可以定义为“干” } }注意:
Get_SH_DO_value函数返回值的含义(0是湿还是1是湿)取决于你的模块逻辑和电位器调节方向。调试时,用手捏住探针(模拟湿润),观察返回值和模块指示灯的变化来确定。
4. 主程序验证与数据读取
驱动写好了,最后就是在主函数里调用它们,并打印出结果看看。在app.c或你的主循环文件里,可以这样写:
#include "app.h" #include "stdio.h" #include "bsp_uart.h" // 用于串口打印 #include "bsp_soilhumidity.h" void Run(void) { // 初始化调试串口(用于打印信息到电脑) UART0_Debug_Init(); printf("\r\n=== 土壤湿度传感器测试开始 ===\r\n"); // 初始化土壤湿度传感器(主要是ADC) Module_BSP_Init(); printf("土壤湿度传感器初始化完成.\r\n"); while(1) { // 读取并打印三种数据 int raw_adc = Get_Adc_Value(); uint8_t percentage = Get_SH_Percentage_value(); char do_state = Get_SH_DO_value(); printf("\r\n"); printf("ADC原始值: %d\r\n", raw_adc); printf("湿度百分比: %d%%\r\n", percentage); printf("DO状态(0湿/1干): %d\r\n", do_state); printf("----------------------------\r\n"); delay_ms(1000); // 每秒读取一次 } }编译代码并烧录到立创开发板。打开串口调试助手(如SecureCRT、Putty等),设置好波特率(通常是115200),你就能看到每秒输出的传感器数据了。
如何调试和校准?
- 测试ADC:将传感器探针完全置于空气中(干燥状态),记录下ADC原始值和百分比。然后将其浸入水中(小心别淹到电路板),再记录一组值。你就得到了湿度和ADC值的对应范围。
- 调节DO阈值:旋转模块上的蓝色电位器。在土壤处于你想要的“临界干湿状态”时,调节电位器直到模块上的DO指示灯刚好改变状态(亮或灭)。这时
Get_SH_DO_value()的返回值也会随之改变。 - 实际应用:在自动浇花系统中,你可以用
Get_SH_DO_value()进行快速判断(低于阈值就启动水泵),同时用Get_SH_Percentage_value()来记录更精细的土壤湿度变化曲线。
好了,整个移植和双模式读取的过程就完成了。代码我已经在实际项目里跑过,稳定可用。遇到问题多看看串口打印的调试信息,硬件连接确保牢靠,剩下的就是根据你的具体应用场景调整逻辑了。
