news 2026/8/23 11:10:20

S12SD紫外线传感器模块在立创开发板上的ADC驱动与数据采集实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
S12SD紫外线传感器模块在立创开发板上的ADC驱动与数据采集实战

S12SD紫外线传感器模块在立创开发板上的ADC驱动与数据采集实战

最近在做一个户外环境监测的小项目,需要用到紫外线传感器。选来选去,发现S12SD这个模块性价比挺高,而且输出是模拟电压信号,可以直接用单片机的ADC来读取,非常适合咱们嵌入式开发者自己折腾。不过网上关于它的具体驱动资料不多,尤其是和具体开发板对接的教程更少。

今天我就以立创的开发板为例,手把手带大家走一遍完整的流程:从硬件怎么接线,到软件怎么配置ADC,再到最后怎么把ADC读到的数字转换成我们能看懂的紫外线强度等级。整个过程我会把原理讲清楚,代码一行行解释,顺便分享几个调试时容易踩的坑。如果你也在用或者打算用S12SD模块,这篇教程应该能帮你省下不少时间。

1. 认识S12SD紫外线传感器模块

在动手写代码之前,咱们先得搞清楚手头的“兵器”到底是个啥。S12SD模块的核心是一个对紫外线特别敏感的光电二极管,它能把接收到的紫外线强度转换成微弱的电流信号。

这个电流信号太小了,单片机根本“听”不见。所以模块内部用了一颗LM358运算放大器,把这个微弱的信号放大,变成一个单片机ADC引脚能轻松读取的电压信号。模块上的所有元器件都用了1%精度的电阻电容,保证了输出信号的稳定和准确。

它的用途很广,比如做紫外线测试仪、带紫外线检测的户外运动手表、手机上的紫外线指数提醒功能等等。

模块关键参数:

参数数值说明
工作电压2.7V - 5V直接用开发板的3.3V或5V供电都行
工作电流约 1mA非常省电,适合电池供电设备
检测角度130度视野比较广,不用精确对准光源
温度系数0.08%/°C温度变化对精度影响很小
检测波长240nm ~ 370nm覆盖UVA和UVB波段,正是我们需要监测的
输出方式模拟电压 (ADC)输出信号和紫外线强度成正比
引脚数量3 Pin (VCC, GND, SIG)接线非常简单

注意:模块检测的是紫外线强度,输出的是一个0到VCC之间的模拟电压。紫外线越强,输出电压越高。我们的任务就是用单片机的ADC把这个电压值“读”出来。

2. 硬件连接与开发板引脚选择

硬件连接是第一步,也是最简单的一步。S12SD模块就三根线,千万别接错了。

接线对照表:

传感器模块引脚立创开发板引脚说明
VCC3V3接3.3V电源,模块工作电压范围是2.7-5V,接3.3V最稳妥。
GNDGND接地,和开发板共地。
SIGP000这是关键!模块的信号输出线,必须接到开发板带ADC功能的引脚上。我选择的是P000引脚,它对应着ADC通道0。

为什么选P000?因为立创开发板的图形化配置工具里,ADC通道0默认就是映射到P000引脚的,用起来最方便。当然,你也可以根据自己项目的布线情况,选择其他支持ADC的引脚,比如P001、P002等,但记得在软件配置里也要相应修改。

接好线之后,硬件部分就搞定了。接下来进入重头戏——软件配置和驱动编写。

3. 软件工程配置(使用立创图形化工具)

立创的开发环境提供了图形化配置工具,能帮我们快速完成外设的初始化,非常方便。这里咱们主要配置ADC模块。

  1. 打开图形化界面:在你的工程里,找到并打开FSP(Flexible Software Package)配置界面。
  2. 配置ADC模块
    • 在“Stacks”标签页下,点击“New Stack” -> “Analog” -> “ADC (r_adc)”。这会为工程添加一个ADC驱动栈。
    • 在ADC的属性窗口中,进行关键配置:
      • Mode(模式):选择Scan Mode。因为我们要持续扫描读取一个通道的值。
      • Resolution(分辨率):选择12 Bits。这样ADC值范围是0-4095,精度足够。
      • Alignment(对齐方式):选择Right Aligned,数据右对齐方便处理。
      • 其他参数如时钟源、采样时间等,可以先保持默认。
  3. 配置ADC通道
    • 在“Pins”标签页下,找到P000引脚。
    • 将其功能Mode设置为Analog。这一步很重要,将引脚切换到模拟输入模式,才能正确读取电压。
    • 在下面的“Analog”设置中,将它分配到我们刚刚创建的ADC单元(比如ADC0)的通道0(Channel 0)。
  4. 保存并生成代码
    • Ctrl + S保存所有配置。
    • 点击右上角的Generate Project Content按钮。工具会自动根据你的配置,生成底层硬件初始化的代码(在hal_data.chal_data.h中)。这样我们就不用自己手动写一大堆寄存器配置了。

图形化配置完成后,基础的ADC外设初始化代码就有了。接下来,我们需要自己编写应用层的驱动函数,来读取数据并处理。

4. 编写紫外线传感器驱动代码

为了代码结构清晰,我们新建两个文件:bsp_ultraviolet.cbsp_ultraviolet.h,把S12SD相关的操作都放在里面。记得把bsp_ultraviolet.h的头文件路径添加到你的编译器包含目录中。

4.1 头文件定义 (bsp_ultraviolet.h)

头文件里主要做三件事:包含必要的库、定义一些好用的宏、声明我们要用到的函数。

#ifndef BSP_CODE_BSP_ULTRAVIOLET_H_ #define BSP_CODE_BSP_ULTRAVIOLET_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 // 定义我们使用的ADC通道(对应P000) #define BSP_ADC_CHANNEL ADC_CHANNEL_0 // 定义采样次数,用于求平均值滤波 #define SAMPLES 5 // 函数声明 void S12SD_UV_GPIO_Init(void); // 初始化函数 int Get_ADC_Value(void); // 获取ADC原始值(已平均) char Get_Ultraviolet_Intensity(uint16_t value); // 将ADC值转换为紫外线强度等级 #endif /* BSP_CODE_BSP_ULTRAVIOLET_H_ */

4.2 初始化函数 (S12SD_UV_GPIO_Init)

这个函数负责打开和配置ADC硬件。它调用了FSP库提供的API。

void S12SD_UV_GPIO_Init(void) { fsp_err_t err = FSP_SUCCESS; // 用于检查函数执行状态 // 1. 打开ADC设备。g_adc0_ctrl和g_adc0_cfg是图形化工具自动生成的变量。 err = R_ADC_Open(&g_adc0_ctrl, &g_adc0_cfg); if (FSP_SUCCESS != err) { printf("ADC Open Failed! \n"); return; // 初始化失败,直接返回 } // 2. 配置ADC扫描。这里告诉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就准备好了 }

4.3 核心数据读取与处理函数

这里包含了三个层层调用的函数,是驱动的心脏部分。

首先,是单次读取ADC的底层函数ADC_GET

// 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; // 扫描完成,设置标志 } 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. 等待扫描完成。这里用循环查询标志位的方式。 while(!adc_flag && timeOut--) { delay_us(1); // 稍微延时一下,避免纯空转消耗CPU } // 3. 清除标志位,为下一次读取做准备 adc_flag = false; // 4. 检查是否超时 if(!timeOut) { printf("ADC_GET 超时!!!\r\n"); return 0; } // 5. 读取ADC转换结果 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转换完成后主动通知CPU。

然后,是带滤波的平均值函数Get_ADC_Value传感器读数可能会有微小波动,连续采样几次求平均,可以让数据更稳定。

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; }

最后,是最关键的映射函数Get_Ultraviolet_Intensity这个函数把ADC原始值(0-4095)映射到0-11级的紫外线强度等级。这里的阈值是根据模块特性实测出来的,是代码的核心逻辑。

char Get_Ultraviolet_Intensity(uint16_t value) { char ret = 0; // 根据ADC值,判断所处的紫外线强度等级区间 if( value < 227 ) //紫外线强度0级 { ret = 0; } if( value >= 227 && value < 318 ) //紫外线强度1级 { ret = 1; } if( value >= 318 && value < 408 ) //紫外线强度2级 { ret = 2; } if( value >= 408 && value < 503 ) //紫外线强度3级 { ret = 3; } if( value >= 503 && value < 606 ) //紫外线强度4级 { ret = 4; } if( value >= 606 && value < 696 ) //紫外线强度5级 { ret = 5; } if( value >= 696 && value < 795 ) //紫外线强度6级 { ret = 6; } if( value >= 795 && value < 881 ) //紫外线强度7级 { ret = 7; } if( value >= 881 && value < 976 ) //紫外线强度8级 { ret = 8; } if( value >= 976 && value < 1079 ) //紫外线强度9级 { ret = 9; } if( value >= 1079 && value < 1170 ) //紫外线强度10级 { ret = 10; } if( value >= 1170 ) //紫外线强度11级(最高) { ret = 11; } return ret; // 返回强度等级 0-11 }

重要:这些阈值(227, 318, 408...)是特定于S12SD模块在3.3V供电下的表现。如果你的供电电压不同,或者模块批次有差异,这个映射关系可能需要重新校准。校准方法很简单:在已知紫外线强度(用专业仪器)下,读取对应的ADC值即可。

5. 在主程序中调用与测试

驱动函数写好了,最后就是在主函数里调用它们。通常我们会在app.cmain.c的某个主循环函数里操作。

#include "app.h" #include "stdio.h" #include "bsp_uart.h" // 串口调试用 #include "bsp_ultraviolet.h" // 我们刚写的驱动 void Run(void) // 假设这是你的主应用函数 { // 1. 初始化调试串口,方便打印数据 UART0_Debug_Init(); printf("\r\n=== S12SD紫外线传感器测试程序 ===\r\n"); // 2. 初始化紫外线传感器(实质是初始化ADC) S12SD_UV_GPIO_Init(); printf("ADC通道0 (P000) 已用于检测紫外线强度\r\n"); // 3. 主循环,持续读取并打印 while(1) { // 获取ADC原始值(已平均) int adc_raw_value = Get_ADC_Value(); // 将原始值转换为紫外线强度等级 char uv_level = Get_Ultraviolet_Intensity(adc_raw_value); // 通过串口打印结果 printf("ADC原始值 = [ %d ]\r\n", adc_raw_value); printf("紫外线强度等级 = [ %d ]\r\n", uv_level); // 延时500ms再读下一次 delay_ms(500); } }

把代码编译下载到开发板,打开串口调试助手(波特率根据你的UART0_Debug_Init配置而定),你应该能看到类似这样的输出:

ADC原始值 = [ 450 ] 紫外线强度等级 = [ 3 ]

把传感器拿到阳光下和阴影处,数值应该会有明显变化。数值越大,等级越高,代表紫外线越强。

调试小贴士

  1. 如果读到的值一直是0或4095:检查硬件接线,尤其是SIG线是否接到了正确的ADC引脚,以及图形化配置中该引脚是否已设置为模拟模式。
  2. 如果数值跳动很厉害:可以尝试增加SAMPLES的平均次数(比如改成10),或者适当增加delay_ms(5)的延时时间。
  3. 关于等级划分:0-11级的划分是模块资料提供的。你可以根据这个等级,在你的项目中触发不同的动作,比如等级大于5时让蜂鸣器报警,或者在屏幕上显示“强紫外线,注意防护”。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 16:44:17

NPYViewer:零代码实现NumPy数据可视化的高效工具

NPYViewer&#xff1a;零代码实现NumPy数据可视化的高效工具 【免费下载链接】NPYViewer Load and view .npy files containing 2D and 1D NumPy arrays. 项目地址: https://gitcode.com/gh_mirrors/np/NPYViewer 在数据科学与科研工作中&#xff0c;你是否曾因需要编写…

作者头像 李华
网站建设 2026/7/14 16:44:19

基于JAVA与jssc实现跨平台Modbus RTU通信(一)

1. 为什么我们要抛弃RXTX&#xff0c;拥抱jssc&#xff1f; 如果你之前尝试过用Java搞Modbus RTU通信&#xff0c;大概率会搜到一堆“modbus4j RXTX”的组合方案。我刚开始也是这么干的&#xff0c;照着教程一顿操作&#xff0c;在Windows上跑得挺欢&#xff0c;结果一打包放到…

作者头像 李华
网站建设 2026/7/14 16:44:17

期货量化策略中的仓位加减仓_金字塔与倒金字塔

免责声明&#xff1a;本文基于个人使用体验&#xff0c;与任何厂商无商业关系。内容仅供技术交流参考&#xff0c;不构成投资建议。 一、前言 加仓、减仓方式会直接影响资金曲线和回撤。金字塔加仓与倒金字塔减仓是常见思路。做了二十年期货交易&#xff0c;我在仓位管理上用过…

作者头像 李华