此紫外线检测模块采用氮化家基材料的肖特基光电二极管,具有高的响应度和低的暗电流,板载LM358放大器对光电二极管输出的微弱信号进行放大,所有元器件采用1%精度元器件制造。应用于紫外线测试仪,紫外线手表,户外运动设备,手机移动电话等。
模块来源
采购链接:
S12SD 高灵敏UV紫外线传感器模块 太阳光照强度感应检测电路板
资料下载链接:
https://pan.baidu.com/s/1YuwoCsbiJPaYH-8TaHEwVg
提取码:8888
规格参数
工作电压:2.7-5V
工作电流:1mA
测量角度:130度
温飘:0.08%/℃
检测波长范围:240nm~370nm
输出方式: ADC
管脚数量:3 Pin
以上信息见厂家资料文件
移植过程
我们的目标是将例程移植至开发板上【能够测量紫外线强度】。首先要获取资料,查看数据手册应如何实现读取数据,再移植至我们的工程。
查看资料
电路图中,SIG引脚是经过放大模拟电压后进行模拟信号输出,采集到模拟量后将其转换为电压,根据下图电压与紫外线强度对照表,则可得知紫外线强度。
引脚选择
这里选择的引脚见引脚接线表
☠ 特别注意
我们的芯片是 D133EBS
它的ADC参考电压是2.5V
, 最高只能读到2.5V(也就是输入3.3V它显示出来的也是2.5V) ,所以我们需要在外面给它进行分压,将模块输出的最高3.3V电压分压成最高1.65V,然后在程序中将ADC读到的数据乘2
得到真实的数据。
进行分压会损失一定的精度,但这是必要的!
分压计算公式:
原理图结构:
根据计算公式,我们可以算出来分压之后的电压为模块AO引脚输出的一半!!
代码移植
下载为大家准备的驱动代码文件夹,复制到自己工程中\luban-lite\application\rt-thread\helloworld\user-bsp
文件夹下
提示
如果未找到 user-bsp
这个文件夹,说明你未进行模块移植的前置操作。请转移到手册使用必要操作(点击跳转)中进行必要的配置操作!!!
接下来打开自己的工程,开始修改Kconfig文件。
1、在 VSCode 中打开 application\rt-thread\helloworld\Kconfig 文件
2、在该文件的 #endif
前面添加该模块的 Kconfig路径语句
# S12SD紫外线传感器
source "application/rt-thread/helloworld/user-bsp/s12sd-uv-sensor/Kconfig"
2
menuconfig操作
1、我们 双击 luban-lite
文件夹下的 win_env.bat
脚本打开env工具:
2、输入以下命令列出所有可用的默认配置:
scons --list-def
3、选择 d13x_JLC_rt-thread_helloworld
这个配置!这个是我们衡山派开发板的默认配置!输入以下命令即可:
scons --apply-def=7
或者
scons --apply-def=d13x_JLC_rt-thread_helloworld_defconfig
这两个命令作用是一样的,一个是 文件名 ,一个是 编号 !!!
4、输入以下命令进入menuconfig菜单
scons --menuconfig
进入以下界面:
5、选中 Porting code using the LCKFB module
按
Y
选中按
N
取消选中方向键
左右
调整 最下面菜单的选项方向键
上下
调整 列表的选项
回车
执行最下面菜单的选项
6、回车进入 Porting code using the LCKFB module
菜单
7、按方向键 上下
选中 USing S12SD UV Sensor
后按 Y
键,看到前面括号中出现一个 *
号,就可以下一步了。
8、按方向键 左右
选中 <Save>
然后一路回车
,然后 退出
即可
编译
我们 保存并退出menuconfig菜单 之后,输入以下命令进行编译:
scons
或
scons -j16
-j 用来选择参与编译的核心数: 我这里是选择16
大家可以根据自己的电脑来选择
核心越多编译越快
如果写的数量高于电脑本身,那么就自动按照最高可用的来运行!
镜像烧录
编译完成之后会在 \luban-lite\output\d13x_JLC_rt-thread_helloworld\images
文件夹下生成一个 d13x_JLC_v1.0.0.img
镜像文件!
然后我们烧录镜像,具体的教程请查看:镜像烧录(点击跳转🚀)
到这里完成了,请移步到 最后一节 进行移植验证。
工程代码解析
bsp_ultraviolet.c
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <sys/time.h>
#include <rtthread.h>
#include "hal_adcim.h"
#include "rtdevice.h"
#include "aic_core.h"
#include "aic_log.h"
#include "hal_gpai.h"
#include <stdio.h>
#include "aic_hal_gpio.h"
#include "bsp_ultraviolet.h"
// adc设备名称
#define ADC_DEVICE_NAME "gpai"
// adc通道
#define ADC_CHANNEL 6
// 电压基准
#define VREF_ADC_HSPI 2.5
static struct rt_adc_device *adc_dev = NULL;
/**********************************************************
* 函 数 名 称:ULTRAVIOLET_Init
* 函 数 功 能:初始化ADC
* 传 入 参 数:无
* 函 数 返 回:RT_EOK成功 -RT_ERROR失败
* 作 者:LCKFB
* 备 注:LP
**********************************************************/
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);
return RT_EOK;
}
/**********************************************************
* 函 数 名 称:Ultraviolet_DeInit
* 函 数 功 能:清除ADC初始化
* 传 入 参 数:无
* 函 数 返 回:RT_EOK成功 -RT_ERROR失败
* 作 者:LCKFB
* 备 注:LP
**********************************************************/
int Ultraviolet_DeInit(void)
{
int ret = rt_adc_disable(adc_dev, ADC_CHANNEL);
if(ret != RT_EOK)
{
LOG_E("[%d]:Failed to [rt_adc_disable] !!!", __LINE__);
return -RT_ERROR;
}
return RT_EOK;
}
/**********************************************************
* 函 数 名 称:Ultraviolet_Get_Value
* 函 数 功 能:获得某个通道的值
* 传 入 参 数:电压存储地址
* 函 数 返 回:RT_EOK成功 -RT_ERROR失败
* 作 者:LCKFB
* 备 注:
**********************************************************/
int Ultraviolet_Get_Value(float *Voltage)
{
int value = 0; // 累计读取的数据
int count = 5; // 采集次数
int valid_count = 0; // 有效读取次数
int return_Value = 0; // 分压之后还原的数据
while(count--)
{
uint32_t temp = rt_adc_read(adc_dev, ADC_CHANNEL);
if((temp != 0) && (temp < 4096)) // 确保不会把校验数据计算进来
{
// rt_kprintf("[%d]adc temp = [%d]\n",valid_count+1,temp);
value += temp;
valid_count++;
}
aicos_mdelay(5); // 延时5ms
}
// 如果没有有效的读取
if(!valid_count)
{
return -RT_ERROR; // 返回一个错误代码
}
return_Value = value / valid_count; // 计算平均值
/* 因为电压分压为了二分之一所以需要还原 */
*Voltage = (( VREF_ADC_HSPI / 4095.0 ) * return_Value) * 2; // 换算成电压
return RT_EOK;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
bsp_ultraviolet.h
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#ifndef __BSP_ULTRAVIOLET_H__
#define __BSP_ULTRAVIOLET_H__
#include "stdio.h"
int Ultraviolet_Init(void);
int Ultraviolet_DeInit(void);
int Ultraviolet_Get_Value(float *Voltage);
#endif
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Kconfig
这个是一个menuconfig中的选项,如果在菜单中选中该选项,就会在rtconfig.h
中定义一个语句,用来if判断条件编译之类的。
config LCKFB_S12SD_UV_SENSOR
bool "USing S12SD UV Sensor"
select AIC_USING_GPAI
select AIC_USING_GPAI6
default n
help
More information is available at: https://wiki.lckfb.com/
2
3
4
5
6
7
SConscript
自动化构建文件,如果定义了 LCKFB_S12SD_UV_SENSOR
和 USING_LCKFB_TRANSPLANT_CODE
就自动编译当前目录下的文件!!
Import('RTT_ROOT')
Import('rtconfig')
import rtconfig
from building import *
cwd = GetCurrentDir()
CPPPATH = [cwd]
src = []
if GetDepend('LCKFB_S12SD_UV_SENSOR') and GetDepend('USING_LCKFB_TRANSPLANT_CODE'):
src = Glob(os.path.join(cwd, '*.c'))
group = DefineGroup('lckfb-s12sd-uv-sensor', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
test_s12sd_uv_sensor.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <sys/time.h>
#include <rtthread.h>
#include "rtdevice.h"
#include "aic_core.h"
#include "aic_hal_gpio.h"
#include "bsp_ultraviolet.h"
#define THREAD_PRIORITY 25 // 线程优先级
#define THREAD_STACK_SIZE 1024 // 线程大小
#define THREAD_TIMESLICE 25 // 时间片
static rt_thread_t ultraviolet_thread = RT_NULL; // 线程控制块
// 线程入口函数
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); // 电压
}
/* 循环提示 */
if(while_count >= 100)
{
while_count = 1;
rt_kprintf("\nType [test_exit_s12sd_uv_sensor] command to exit \n");
rt_kprintf("Note: Pressing [TAB] as you type will autocomplete the command\n");
rt_thread_mdelay(2000);
}
rt_thread_mdelay(1000);
}
}
static void test_s12sd_uv_sensor(int argc, char **argv)
{
int ret = Ultraviolet_Init();
if(ret != RT_EOK)
{
LOG_E("Failed to [Ultraviolet_Init] !!!");
return;
}
/* 创建线程,名称是 ultraviolet_thread,入口是 ultraviolet_thread_entry */
ultraviolet_thread = rt_thread_create("ultraviolet_thread",
ultraviolet_thread_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
/* 如果获得线程控制块,启动这个线程 */
if (ultraviolet_thread != RT_NULL)
rt_thread_startup(ultraviolet_thread);
}
// 导出函数为命令
MSH_CMD_EXPORT(test_s12sd_uv_sensor, s12sd ultraviolet sensor test);
/* 退出函数 */
void test_exit_s12sd_uv_sensor(void)
{
int ret = rt_thread_delete(ultraviolet_thread);
if(ret != RT_EOK)
{
LOG_E("failed to test_exit_ultraviolet_sensor !!");
return;
}
ret = Ultraviolet_DeInit();
if(ret != RT_EOK)
{
LOG_E("Failed to [Ultraviolet_DeInit] !!!");
return;
}
else
{
rt_kprintf("\nUltraviolet_DeInit successful!!!\n");
}
rt_kprintf("\n========ultraviolet sensor exit successful !!========\n");
}
// 导出函数为命令
MSH_CMD_EXPORT(test_exit_s12sd_uv_sensor, exit s12sd ultraviolet sensor);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
ultraviolet_thread_entry
函数逻辑
- 函数入口是
ultraviolet_thread_entry
,它是一个线程的入口点,参数param
在这里没有被使用。 - 定义了一个局部变量
while_count
并初始化为1,用于控制循环的次数。 - 进入一个无限循环,每次循环
while_count
递增。 - 在循环内部,定义了一个浮点变量
Voltage
,用于存储读取的电压值。 - 调用
Ultraviolet_Get_Value
函数尝试获取电压值,如果失败,则使用LOG_E
宏记录错误信息。 - 如果读取成功,将
Voltage
乘以100并转换为uint32_t
类型的temp_voltage
,用于将电压值分解为整数和小数部分。 - 使用
rt_kprintf
函数打印电压值,格式为整数部分和小数部分,小数部分固定为两位。 - 如果
while_count
达到100,则重置while_count
为1,并打印提示信息,告知用户如何退出传感器测试。
test_s12sd_uv_sensor
函数逻辑
- 函数入口是
test_s12sd_uv_sensor
,它接收参数argc
和argv
,这些参数通常用于命令行参数解析,但在本函数中未使用。 - 调用
Ultraviolet_Init
函数初始化紫外线传感器,如果初始化失败,则使用LOG_E
宏记录错误信息并返回。 - 使用
rt_thread_create
函数创建一个线程,命名为 "ultraviolet_thread",入口函数为ultraviolet_thread_entry
,参数为RT_NULL
,栈大小为THREAD_STACK_SIZE
,优先级为THREAD_PRIORITY
,时间片为THREAD_TIMESLICE
。 - 如果线程创建成功,则调用
rt_thread_startup
函数启动线程。 - 使用
MSH_CMD_EXPORT
宏将test_s12sd_uv_sensor
函数导出为命令行接口可用的命令。
test_exit_s12sd_uv_sensor
函数逻辑
- 函数入口是
test_exit_s12sd_uv_sensor
,它没有参数。 - 调用
rt_thread_delete
函数尝试删除ultraviolet_thread
线程,如果失败,则使用LOG_E
宏记录错误信息并返回。 - 调用
Ultraviolet_DeInit
函数尝试去初始化紫外线传感器,如果失败,则使用LOG_E
宏记录错误信息。 - 如果去初始化成功,则使用
rt_kprintf
函数打印成功信息。 - 打印退出成功的提示信息。
- 使用
MSH_CMD_EXPORT
宏将test_exit_s12sd_uv_sensor
函数导出为命令行接口可用的命令。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <sys/time.h>
#include <rtthread.h>
#include "rtdevice.h"
#include "aic_core.h"
#include "aic_hal_gpio.h"
#include "bsp_ultraviolet.h"
#define THREAD_PRIORITY 25 // 线程优先级
#define THREAD_STACK_SIZE 1024 // 线程大小
#define THREAD_TIMESLICE 25 // 时间片
static rt_thread_t ultraviolet_thread = RT_NULL; // 线程控制块
// 线程入口函数
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); // 电压
}
/* 循环提示 */
if(while_count >= 100)
{
while_count = 1;
rt_kprintf("\nType [test_exit_s12sd_uv_sensor] command to exit \n");
rt_kprintf("Note: Pressing [TAB] as you type will autocomplete the command\n");
aicos_mdelay(2000);
}
aicos_mdelay(1000);
}
}
static void test_s12sd_uv_sensor(int argc, char **argv)
{
int ret = Ultraviolet_Init();
if(ret != RT_EOK)
{
LOG_E("Failed to [Ultraviolet_Init] !!!");
return;
}
/* 创建线程,名称是 ultraviolet_thread,入口是 ultraviolet_thread_entry */
ultraviolet_thread = rt_thread_create("ultraviolet_thread",
ultraviolet_thread_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
/* 如果获得线程控制块,启动这个线程 */
if (ultraviolet_thread != RT_NULL)
rt_thread_startup(ultraviolet_thread);
}
// 导出函数为命令
MSH_CMD_EXPORT(test_s12sd_uv_sensor, s12sd ultraviolet sensor test);
/* 退出函数 */
void test_exit_s12sd_uv_sensor(void)
{
int ret = rt_thread_delete(ultraviolet_thread);
if(ret != RT_EOK)
{
LOG_E("failed to test_exit_ultraviolet_sensor !!");
return;
}
ret = Ultraviolet_DeInit();
if(ret != RT_EOK)
{
LOG_E("Failed to [Ultraviolet_DeInit] !!!");
return;
}
else
{
rt_kprintf("\nUltraviolet_DeInit successful!!!\n");
}
rt_kprintf("\n========ultraviolet sensor exit successful !!========\n");
}
// 导出函数为命令
MSH_CMD_EXPORT(test_exit_s12sd_uv_sensor, exit s12sd ultraviolet sensor);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
移植验证
我们使用串口调试,将 USB转TTL模块 连接到衡山派开发板上面!!
具体的教程查看:串口调试(点击跳转🚀)
串口波特率默认为
115200
我们在输入下面的命令运行该模块的线程:
输入的时候按下
TAB键
会进行命令补全!!
test_s12sd_uv_sensor