
衡山派D133EBS开发板驱动S12SD紫外线传感器基于RT-Thread的ADC采集与电压分压处理实战最近在做一个户外环境监测的小项目需要用到紫外线传感器。我手头正好有一块衡山派D133EBS开发板和一个S12SD紫外线传感器模块但在移植过程中遇到了一个硬件上的“小麻烦”——开发板的ADC参考电压只有2.5V而传感器输出最高能达到3.3V。直接接上去读数肯定不准甚至可能损坏ADC。今天我就把整个解决问题的过程从硬件分压到软件补偿再到完整的RT-Thread驱动移植手把手分享给大家。这篇文章适合正在使用衡山派开发板做项目或者对RT-Thread下传感器驱动开发感兴趣的工程师和爱好者。咱们不绕弯子直接上干货。1. 硬件准备与核心问题分析1.1 认识S12SD紫外线传感器S12SD是一款常用的紫外线传感器模块它内部采用氮化镓基材料的光电二极管响应度高暗电流低。模块上集成了LM358运放能把光电二极管输出的微弱信号进行放大所有元器件都用了1%精度的器件整体性能比较稳定。它主要用在紫外线测试仪、紫外线手表、户外运动设备和手机移动电源等产品上。咱们先看看它的关键参数参数项规格工作电压2.7V - 5V工作电流约1mA测量角度130度温度系数0.08%/°C检测波长范围240nm ~ 370nm输出方式模拟电压输出 (ADC)管脚数量3 Pin (VCC, GND, AO)核心就是最后一点模拟电压输出 (AO引脚)。紫外线强度越强AO引脚输出的电压就越高。1.2 衡山派D133EBS开发板的ADC“瓶颈”这里就是问题的关键。咱们的衡山派D133EBS开发板其ADC模块的参考电压是2.5V。这意味着什么呢ADC就像一个只能测量0到2.5米高度的尺子。如果你把一个3.3米高的东西放上去尺子最多只能指到2.5米的位置超出的部分它“看不见”读出来的数值永远是最大值对应2.5V。在ADC的世界里如果输入电压超过参考电压读回来的数值就会一直停留在最大值对于12位ADC就是4095无法反映真实的电压变化。所以传感器最高输出3.3V而开发板ADC最高只能量到2.5V直接连接会导致ADC在紫外线较强时始终饱和测不准。1.3 解决方案硬件分压 软件补偿我的解决思路分两步走硬件分压在传感器AO输出引脚和开发板ADC输入引脚之间加一个电阻分压电路把最高3.3V的电压“压缩”到1.65V以内确保不会超过ADC的2.5V量程。软件补偿在程序里把ADC读到的电压值再乘以2还原回传感器真实的输出电压。注意分压电路会引入一定的精度损失因为电阻有误差。但对于紫外线强度测量这种应用这个精度通常是可接受的而且这是必须做的保护措施。分压电路原理 我们用一个简单的电阻分压器。假设R1连接传感器输出R2接地那么分压后的电压V_out V_in * [R2 / (R1 R2)]。 为了让3.3V输入变成1.65V输出我们需要R1 R2。比如两个10kΩ的电阻串联即可。传感器AO引脚 ---[R1]---|----[R2]--- GND | (连接到开发板ADC引脚)分压之后输入开发板ADC引脚的电压范围就变成了0V ~ 1.65V安全地落在了0~2.5V的量程内。2. 驱动代码移植与解析理解了硬件原理咱们来看看怎么在RT-Thread上把驱动跑起来。我已经把完整的驱动代码打包好了大家可以直接下载使用。2.1 获取与放置驱动文件首先下载驱动文件包。你可以从衡山派官方的资料下载中心找到它在“模块移植资料下载”栏目里搜索“S12SD”相关的压缩包。下载后你会得到一个文件夹里面包含以下几个关键文件bsp_ultraviolet.c// 传感器驱动核心文件bsp_ultraviolet.h// 驱动头文件Kconfig// RT-Thread menuconfig 配置选项SConscript// RT-Thread SCons 构建脚本test_s12sd_uv_sensor.c// 测试程序操作步骤打开你的衡山派工程目录路径通常是\luban-lite\application\rt-thread\helloworld\。将下载的整个驱动文件夹例如s12sd-uv-sensor复制到user-bsp目录下。如果找不到user-bsp文件夹说明你还没有进行模块移植的前置配置。需要先参考衡山派的使用手册完成必要的Kconfig路径配置操作。2.2 配置工程 (menuconfig)接下来我们需要在工程中启用这个传感器驱动。在工程根目录luban-lite文件夹下双击运行win_env.bat脚本打开RT-Thread的env工具。输入以下命令列出所有可用的默认配置scons --list-def选择衡山派开发板的默认配置。通常名为d13x_JLC_rt-thread_helloworld。输入以下命令应用它假设它在列表中是第7个scons --apply-def7或者直接使用配置名scons --apply-defd13x_JLC_rt-thread_helloworld_defconfig输入命令进入图形化配置菜单scons --menuconfig在配置菜单中用方向键找到并进入Porting code using the LCKFB module这个大类。在这个大类下找到USing S12SD UV Sensor选项。按Y键选中它前面会出现[*]星号。按左右方向键选择Save保存配置然后一路退出 (Exit) 即可。2.3 核心驱动代码解析配置好后咱们深入看看驱动是怎么工作的。核心逻辑在bsp_ultraviolet.c中。首先定义关键参数// adc设备名称衡山派D133的ADC设备名是gpai #define ADC_DEVICE_NAME gpai // adc通道根据你实际连接的ADC引脚选择这里示例是通道6 #define ADC_CHANNEL 6 // 电压基准D133EBS开发板的ADC参考电压是2.5V #define VREF_ADC_HSPI 2.5初始化函数Ultraviolet_Init 这个函数负责找到并打开ADC设备使能我们需要的那个ADC通道。int Ultraviolet_Init(void) { // 获取设备句柄 adc_dev (struct rt_adc_device *)rt_device_find(ADC_DEVICE_NAME); if (adc_dev RT_NULL) { LOG_E(Failed to open %s device, ADC_DEVICE_NAME); return -RT_ERROR; } // 使能ADC通道 int ret rt_adc_enable(adc_dev, ADC_CHANNEL); if(ret ! RT_EOK) { LOG_E(Failed to [rt_adc_enable] !!!); return -RT_ERROR; } aicos_mdelay(200); // 稍作延时等待ADC稳定 return RT_EOK; }核心数据读取与电压换算函数Ultraviolet_Get_Value 这是最关键的函数它完成了ADC数值读取、平均滤波以及最重要的电压还原计算。int Ultraviolet_Get_Value(float *Voltage) { int value 0; // 累计读取的数据 int count 5; // 采集次数 int valid_count 0; // 有效读取次数 // 循环读取5次ADC原始值并累加 while(count--) { uint32_t temp rt_adc_read(adc_dev, ADC_CHANNEL); // 过滤异常值0或大于4095 if((temp ! 0) (temp 4096)) { value temp; valid_count; } aicos_mdelay(5); // 每次读取间隔5ms } if(!valid_count) // 如果一次有效读数都没有 { return -RT_ERROR; } // 计算5次有效读数的平均值 int return_Value value / valid_count; /* 关键步骤电压还原计算 */ // 1. 将ADC原始值转换为分压后的电压: (基准电压 / 最大ADC值) * 原始值 // 2. 乘以2补偿硬件分压带来的1/2衰减 *Voltage (( VREF_ADC_HSPI / 4095.0 ) * return_Value) * 2; return RT_EOK; }这里需要仔细理解(VREF_ADC_HSPI / 4095.0) * return_Value这一步是把ADC读到的数字比如1500转换成它实际代表的电压值。因为我们的ADC是12位的最大值是4095对应参考电压2.5V。假设计算得到0.9V这其实是分压后的电压。由于我们用了两个等值电阻分压分压比例是1/2所以传感器真实的输出电压应该是0.9V * 2 1.8V。最后的* 2就是完成这个“还原”操作。2.4 测试程序解析驱动写好了怎么用呢test_s12sd_uv_sensor.c文件提供了一个完整的测试范例。它创建了一个线程ultraviolet_thread在这个线程里循环调用Ultraviolet_Get_Value函数读取电压并通过串口打印出来。static void ultraviolet_thread_entry(void *param) { int while_count 1; while(while_count) { float Voltage 0; if(RT_EOK ! Ultraviolet_Get_Value(Voltage)) { LOG_E(Failed to Ultraviolet_Get_Value !!!); } else { // 将电压值放大100倍方便分离整数和小数部分打印 uint32_t temp_voltage Voltage * 100; rt_kprintf(Read Voltage_Value %d.%02dV\n, temp_voltage/100, temp_voltage%100); } rt_thread_mdelay(1000); // 每秒读取一次 } }同时它通过MSH_CMD_EXPORT将两个函数导出为FinSH命令test_s12sd_uv_sensor: 启动传感器测试线程。test_exit_s12sd_uv_sensor: 停止测试并释放资源。3. 编译、烧录与测试3.1 编译工程保存并退出menuconfig后在env命令行中输入编译命令scons或者使用多核编译加快速度例如4核scons -j4编译成功后会在\luban-lite\output\d13x_JLC_rt-thread_helloworld\images\目录下生成一个d13x_JLC_v1.0.0.img镜像文件。3.2 烧录镜像将生成的.img文件烧录到衡山派D133EBS开发板上。具体的烧录方法请参考衡山派官方文档中的《镜像烧录》教程。3.3 串口测试使用USB转TTL模块连接开发板的调试串口到电脑。打开串口调试助手如Putty、MobaXterm等设置波特率为115200。给开发板上电在串口终端中先按回车键看到命令行提示符msh /。输入测试命令可以按TAB键自动补全test_s12sd_uv_sensor如果一切正常你将看到终端每秒打印一次读取到的电压值例如Read Voltage_Value 1.23V Read Voltage_Value 1.25V ...这个电压值就是经过软件乘以2补偿后传感器AO引脚的真实输出电压。你可以根据传感器手册中的“电压-紫外线强度对照表”将这个电压值转换为具体的紫外线强度指数。输入以下命令停止测试test_exit_s12sd_uv_sensor4. 总结与注意事项整个过程走下来核心就是处理ADC量程不匹配的问题。硬件上通过分压电路保护ADC软件上通过乘法运算还原真实电压。这个方法不仅适用于S12SD传感器任何输出电压超过你开发板ADC参考电压的模拟传感器都可以借鉴这个思路。几个容易踩的坑分压电阻精度尽量选择1%精度的电阻可以减少测量误差。ADC通道选择务必确认代码中的ADC_CHANNEL宏定义与你实际连接的开发板引脚对应的ADC通道一致。参考电压确认一定要确认你使用的开发板ADC参考电压是多少VREF_ADC_HSPI这个宏定义的值必须与之对应。分压比例本文示例是2分压R1R2。如果你用的电阻比例不同软件中乘的系数也要相应改变公式是补偿系数 (R1 R2) / R2。希望这篇教程能帮你顺利在衡山派开发板上驱动S12SD紫外线传感器。如果在移植过程中遇到其他问题多看看串口打印的日志那往往是解决问题的关键线索。