当前位置: 首页 > news >正文

基于立创GD32E230C8T6开发板的GP2Y1014AU粉尘传感器ADC驱动与浓度计算实战

基于立创GD32E230C8T6开发板的GP2Y1014AU粉尘传感器ADC驱动与浓度计算实战

最近在做一个室内空气质量监测的小项目,需要检测空气中的粉尘浓度,正好手头有立创的GD32E230C8T6开发板和GP2Y1014AU粉尘传感器。网上资料虽然多,但直接能用在国产GD32芯片上的完整教程不多,调试过程踩了不少坑。今天我就把整个从硬件连接到软件驱动,再到浓度计算的完整过程整理出来,手把手教你怎么在立创开发板上实现粉尘浓度检测。

这篇文章适合正在学习GD32开发,或者想用国产MCU做环境监测项目的朋友。我会尽量用大白话把原理讲清楚,代码也会详细注释,保证你跟着做就能出结果。

1. 认识你的“眼睛”:GP2Y1014AU粉尘传感器

在写代码之前,咱们得先了解手里的传感器是怎么工作的。GP2Y1014AU这个小模块,别看它个头不大,原理挺巧妙的。

你拿到传感器后,会看到中间有个小洞,空气就是从这里流通的。在洞的两侧,一边装了一个红外发光二极管(可以理解为一个小型红外线手电筒),另一边装了一个光电晶体管(相当于一个红外线“眼睛”)。

它的工作原理是这样的:

  1. 发射:红外发光二极管持续发射一束红外光。
  2. 阻碍与反射:当空气中飘浮着粉尘颗粒(比如PM2.5)时,这些颗粒会挡住并散射红外光。
  3. 接收:一部分被散射的红外光会飞到对面的光电晶体管上。
  4. 转换:光电晶体管接收到光信号后,会把它转换成电信号。空气中的粉尘越多,散射的光就越多,光电晶体管输出的电压就越高

所以,这个传感器最终给我们的,是一个模拟电压信号(AO引脚输出)。我们的任务就是用GD32E230的ADC(模数转换器)去读取这个电压,再通过一个公式把它换算成我们关心的浓度值(比如毫克每立方米,mg/m³)。

注意:传感器需要5-7V供电,而我们的开发板GPIO是3.3V电平。所以你需要一个外部的5V电源(比如USB口或稳压模块)给传感器单独供电。传感器的输出信号(AO引脚)电压在0-3.3V范围内,可以直接接到开发板的ADC引脚。

传感器关键参数速查表:

参数数值说明
工作电压5-7V必须外接5V电源,不能从开发板3.3V取电
工作电流最大20mA功耗很低
检测粒径0.8微米可以检测到PM2.5级别的颗粒物
灵敏度0.5V/(0.1mg/m³)浓度每变化0.1mg/m³,输出电压变化约0.5V
清洁空气电压0.9V(典型值)在干净空气中,输出大约0.9V

2. 硬件连接:把传感器和开发板“牵上线”

硬件连接很简单,就三根线,但接错了可能烧东西,咱们仔细看一下。

所需材料:

  • 立创GD32E230C8T6开发板 一块
  • GP2Y1014AU粉尘传感器 一个
  • 5V电源(如USB充电头+MicroUSB线,或稳压模块) 一个
  • 杜邦线 若干

接线步骤:

  1. 传感器供电(VCC & GND)

    • 找到传感器的VCCGND引脚。
    • VCC连接到你的5V电源正极
    • GND连接到5V电源的负极同时,这个GND还必须用一根杜邦线连接到开发板的GND引脚。这一步至关重要!只有共地,ADC才能正确读取电压。
  2. 信号线连接(AO & LED)

    • 传感器的AO(模拟输出)引脚,连接到开发板的PA1引脚。这是我们ADC采集的入口。
    • 传感器的LED引脚,连接到开发板的PB1引脚。这个引脚很关键,它用来控制传感器内部的红外LED。传感器要求我们以特定的时序来点亮和熄灭这个LED,然后在这个时间窗口内读取AO的电压,这样才能得到有效的测量值。
  3. 开发板供电:开发板通过其自身的MicroUSB口供电(3.3V系统)。

连接好之后,你的接线应该是这样的:

  • 传感器端:VCC→5V, GND→5V地 & 开发板GND, AO→开发板PA1, LED→开发板PB1。
  • 开发板端:独立USB供电。

提示:如果手头有万用表,可以在传感器通电后测量一下AO对GND的电压。在静止空气中,它应该稳定在0.9V左右。吹一口气(增加粉尘),电压会瞬间升高然后回落。有这个现象,说明传感器本身是好的。

3. 软件驱动:配置GD32E230的ADC

硬件搞定,接下来就是重头戏——写代码。咱们的目标是让GD32的ADC能准确读取PA1(也就是AO)的电压。我把它分解成几个步骤。

3.1 建立工程与文件

首先,在你的GD32工程里(比如用Keil或VSCode+GCC),创建两个文件:

  • bsp_dust.c:传感器驱动源文件。
  • bsp_dust.h:传感器驱动头文件。

头文件bsp_dust.h里主要是引脚定义和函数声明,先把框架搭好:

#ifndef _BSP_DUST_H_ #define _BSP_DUST_H_ #include "gd32e23x.h" // 控制传感器LED的引脚定义 (连接传感器LED引脚) #define RCU_LED RCU_GPIOB #define PORT_LED GPIOB #define GPIO_LED GPIO_PIN_1 // 读取传感器AO信号的引脚定义 (连接传感器AO引脚) #define RCU_OUT RCU_GPIOA #define PORT_OUT GPIOA #define GPIO_OUT GPIO_PIN_1 // ADC相关定义 (PA1对应ADC通道1) #define RCU_OUT_ADC RCU_ADC #define PORT_OUT_ADC ADC #define CHANNEL_OUT_ADC ADC_CHANNEL_1 // 采样通道数,我们只用1个通道(PA1) #define CHANNEL_NUM 1 // 函数声明 void Dust_GPIO_Init(void); // 初始化函数 float Read_dust_concentration(void); // 读取浓度函数 #endif

3.2 初始化ADC与GPIO

接下来在bsp_dust.c里实现初始化函数Dust_GPIO_Init。这个函数要做的事情比较多,我一步步解释。

#include "bsp_dust.h" #include "systick.h" // 用于延时函数 #include "bsp_usart.h" // 用于printf调试(可选) #include "stdio.h" void Dust_GPIO_Init(void) { /* 第1步:打开相关时钟 */ rcu_periph_clock_enable(RCU_OUT); // 使能GPIOA时钟 (PA1) rcu_periph_clock_enable(RCU_LED); // 使能GPIOB时钟 (PB1) rcu_periph_clock_enable(RCU_OUT_ADC); // 使能ADC时钟 /* 第2步:配置ADC时钟源 */ // ADC模块工作频率不能太高。系统主频72MHz,这里选择4分频,ADC时钟=72/4=18MHz rcu_adc_clock_config(RCU_ADCCK_APB2_DIV4); /* 第3步:配置GPIO引脚模式 */ // 配置PB1为推挽输出,用来控制传感器LED gpio_mode_set(PORT_LED, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_LED); gpio_output_options_set(PORT_LED, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LED); gpio_bit_write(PORT_LED, GPIO_LED, SET); // 初始化为高电平,LED灭 // 配置PA1为模拟输入模式,这是ADC引脚的标准配置 gpio_mode_set(PORT_OUT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_OUT); /* 第4步:配置ADC工作模式 */ adc_special_function_config(ADC_CONTINUOUS_MODE, ENABLE); // 使能连续转换模式 adc_special_function_config(ADC_SCAN_MODE, ENABLE); // 使能扫描模式(多通道时才需要,单通道也建议开启) adc_data_alignment_config(ADC_DATAALIGN_RIGHT); // 数据右对齐(方便阅读) /* 第5步:配置ADC通道 */ adc_channel_length_config(ADC_REGULAR_CHANNEL, CHANNEL_NUM); // 规则组通道数设为1 // 将通道1(PA1)配置为规则组第0个转换序列,采样时间13.5个周期 adc_regular_channel_config(0, CHANNEL_OUT_ADC, ADC_SAMPLETIME_13POINT5); /* 第6步:其他ADC参数 */ adc_resolution_config(ADC_RESOLUTION_12B); // 12位分辨率,结果范围0-4095 // 禁用外部触发,使用软件触发转换 adc_external_trigger_source_config(ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_NONE); adc_external_trigger_config(ADC_REGULAR_CHANNEL, ENABLE); /* 第7步:使能ADC并校准 */ adc_enable(); // 使能ADC模块 delay_1ms(1); // 短暂延时等待ADC稳定 adc_calibration_enable(); // 执行自校准,消除内部误差,这一步很重要! /* 第8步:启动转换 */ adc_software_trigger_enable(ADC_REGULAR_CHANNEL); // 软件触发开始连续转换 }

初始化完成后,ADC就会自动在后台连续将PA1的电压值转换成数字量,我们随时可以去读取。

3.3 编写数据滤波函数

直接读取一次ADC值往往跳动很大,因为信号里有噪声。为了得到稳定的数据,我们需要做软件滤波。这里我采用一个简单的滑动平均滤波,取最近10次采样的平均值。

// 滑动平均滤波器,参数m是新的ADC采样值,返回值是滤波后的值 int Filter(int m) { static int flag_first = 0; // 首次运行标志 static int _buff[10]; // 存储最近10次值的数组 static int sum; // 数组元素总和 const int _buff_max = 10; // 窗口大小 int i; // 如果是第一次调用,用当前值m初始化整个数组 if (flag_first == 0) { flag_first = 1; for (i = 0, sum = 0; i < _buff_max; i++) { _buff[i] = m; sum += _buff[i]; } return m; } else { // 1. 从总和中减去最旧的数据(_buff[0]) sum -= _buff[0]; // 2. 将数组所有元素向前移动一位 for (i = 0; i < (_buff_max - 1); i++) { _buff[i] = _buff[i + 1]; } // 3. 将新数据m放入数组末尾 _buff[_buff_max - 1] = m; // 4. 将新数据加入总和 sum += _buff[_buff_max - 1]; // 5. 返回平均值 i = sum / 10.0; return i; } }

这个滤波器的效果就是“平滑”数据,让显示出来的浓度值不会跳来跳去。你可以通过修改_buff_max来调整平滑程度,值越大越平滑但反应越慢。

4. 核心逻辑:读取并计算粉尘浓度

GP2Y1014AU传感器对测量时序有严格要求,不能一直亮着LED读数据。必须按照它数据手册里的时序来:先点亮LED,等待一段特定时间后读取电压,然后熄灭LED,再等待一个周期结束。我们的Read_dust_concentration函数就是干这个的。

float Read_dust_concentration(void) { unsigned int value = 0; float f_value = 0, density = 0; /* 严格按照传感器时序操作 */ gpio_bit_write(PORT_LED, GPIO_LED, RESET); // 1. 拉低PB1,点亮传感器LED delay_1us(280); // 2. 等待280微秒 (关键延时!) value = adc_regular_data_read(); // 3. 读取此刻的ADC值 delay_1us(40); // 4. 再等待40微秒 gpio_bit_write(PORT_LED, GPIO_LED, SET); // 5. 拉高PB1,熄灭传感器LED delay_1us(9680); // 6. 等待9680微秒,完成一个测量周期 // 对原始ADC值进行滤波 value = Filter(value); // 将ADC值转换为电压值 (GD32E230的ADC参考电压Vref+通常是3.3V) // ADC值范围0-4095对应电压0-3.3V f_value = (value / 4095.0) * 3.3; // 将电压值转换为粉尘浓度 (mg/m³) // 公式: 浓度 = (电压 - 清洁空气电压) / 灵敏度 // 根据资料:清洁空气典型电压Vc = 0.9V, 灵敏度K = 0.5V / 0.1mg/m³ // 推导出:浓度 = (V - 0.9) / 0.5 * 0.1 = 0.2 * (V - 0.9) // 注意:原始代码中使用了 0.17*value-0.1,这是将ADC值直接换算的简化公式。 // 为了更清晰,我们使用基于电压的公式: if(f_value > 0.9) { density = 0.2 * (f_value - 0.9); } else { density = 0.0; // 电压低于或等于清洁空气电压,认为浓度为零 } return density; // 返回浓度值,单位 mg/m³ }

重要提示delay_1us(280)delay_1us(40)这两个延时非常关键,必须尽可能精确。你需要确保你的systick或定时器提供的delay_1us函数是准确的。如果不准,测量值会有偏差。delay_1us(9680)是保证两次测量间隔至少10ms,让传感器内部准备好下一次测量。

5. 上机测试与现象观察

最后,我们在主函数里调用这些功能,并把浓度值打印出来看看。

#include "gd32e23x.h" #include "systick.h" #include "bsp_usart.h" // 用于printf #include "stdio.h" #include "bsp_dust.h" int main(void) { systick_config(); // 初始化系统滴答定时器,提供延时函数 usart_gpio_config(115200U); // 初始化串口,用于打印数据到电脑 printf("GP2Y1014AU Dust Sensor Demo Start!\r\n"); Dust_GPIO_Init(); // 初始化粉尘传感器相关硬件 while(1) { // 读取并打印粉尘浓度 float dust = Read_dust_concentration(); printf("Dust Concentration: %.3f mg/m³\r\n", dust); delay_1ms(1000); // 每秒读取一次 } }

将代码编译下载到开发板,打开串口助手(波特率115200),你应该能看到类似这样的输出:

Dust Concentration: 0.023 mg/m³ Dust Concentration: 0.021 mg/m³ ...

测试现象

  • 在静止、干净的空气中,数值应该比较低且稳定(接近0或零点零几)。
  • 对着传感器的进气孔轻轻吹一口气(注意别把口水吹进去),数值会有一个明显的脉冲式上升,然后缓慢下降。这是因为你吹出的气体中含有水分、皮屑等颗粒物,被传感器检测到了。
  • 点一支香或产生一些烟雾靠近传感器,数值会持续升高。

如果现象符合,恭喜你,驱动移植成功!这个传感器比较适合检测相对浓度变化,用于判断空气质量变好还是变差。如果需要绝对精确的测量,可能需要进行校准,并考虑温湿度补偿。但用于大多数DIY项目或定性监测,已经完全够用了。实际项目中,我一般会把滤波窗口再调大一点,让显示更稳定,同时把打印间隔改为2-3秒一次,避免串口数据刷得太快。

http://www.jsqmd.com/news/479373/

相关文章:

  • 【仅限首批读者】MCP-SDK 0.9.4内测版修复的6个VS Code插件集成崩溃点(含vscode-mcp-extension v0.7.1热修复补丁下载链接)
  • ESP32-CAM + YOLOv5实战:5分钟搭建智能安防监控系统(附Python代码)
  • 零基础玩转Live Avatar:用一张照片+一段音频生成数字人视频
  • CLIP-GmP-ViT-L-14生产环境部署:Docker镜像免配置+Gradio高并发优化方案
  • 从Simulink/Stateflow官方案例出发:构建一个可扩展的自动变速器控制模型
  • YOLO12效果实测:对比传统YOLO,注意力架构精度提升展示
  • Cube-443示波镊子:嵌入式调试用差分便携示波器设计
  • MogFace-large在嵌入式Linux平台(如树莓派)的移植与优化
  • 3步攻克金融数据壁垒:面向量化分析师的通达信数据读取指南
  • 颠覆传统播放模式:XiaoMusic让本地音乐焕发智能新生
  • 解锁AI视频合成新范式:ComfyUI-VideoHelperSuite的图像序列处理应用指南
  • Qwen2.5-7B微调教程:十分钟打造专属AI,开箱即用实战
  • wan2.1-vae生产环境实践:中小企业AI内容创作平台落地完整指南
  • Qwen3-ASR-0.6B真实案例:电力巡检语音→设备编号/缺陷类型/处置建议生成
  • SecGPT-14B开发者友好:提供OpenAPI Schema、Postman集合、SDK示例
  • DeOldify服务在AI编程教育中的应用:设计图像处理实验课
  • Qwen2.5-VL-7B-Instruct惊艳案例:模糊截图文字识别+逻辑推理+分步解答全过程
  • Flux.1-Dev深海幻境赋能内容社区:为CSDN博客自动生成头图
  • ANIMATEDIFF PRO文旅应用:景区宣传动画自动生成
  • ESP8266桌面小狗:嵌入式软硬协同学习平台
  • FaceFusion保姆级教程:一键部署,轻松实现高清视频换脸
  • 立创开源:基于STM32F103RCT6的三合一USB读卡器,支持拖拽文件升级与WS2812灯带控制
  • Qwen3.5-35B-A3B-AWQ-4bit多场景落地:零售货架图商品识别+缺货预警生成
  • CLIP-GmP-ViT-L-14中小企业AI方案:低成本部署跨模态语义搜索
  • 3大突破:WarcraftHelper让魔兽争霸3重获新生的现代解决方案
  • Phi-4-reasoning-vision-15B一文详解:视觉多模态模型在数字孪生系统中的感知中枢作用
  • 视频资源管理新范式:douyin-downloader的效率革命
  • Hunyuan-MT-7B-WEBUI新手必看:从部署到翻译,完整操作流程解析
  • 八卦键盘:面向嵌入式开发的模块化USB多主机键盘平台
  • MT4进阶实战:从EA策略编写到自动化交易部署