由瑞士Sensirion推出的 SHT20数字温湿度传感器,基于领先世界的CMOSens ® 数字传感技术,具有极高的可靠性和卓越的长期稳定性。全量程标定,两线数字接口,可与单片机直接相连,大大缩短研发时间、简化外围电路并降低费用。此外,体积微小、响应迅速、低能耗、可浸没、抗干扰能力强、温湿一体,兼有露点测量,性价比高,使该产品能够适于多种场合的应用。
模块来源
规格参数
工作电压:2.1~3.6V
工作电流:0.1~1000uA
温度精度:±0.3℃
温度范围:-40~125℃
湿度范围:0~100 %RH
湿度精度:±3%RH
输出方式: IIC
管脚数量:4 Pin
以上信息见厂家资料文件
移植过程
我们的目标是将例程移植至开发板上【能够测量环境温湿度】。首先要获取资料,查看数据手册应如何实现读取数据,再移植至我们的工程。
查看资料
开始传输后,随后先传输首字节包括I2C设备地址(7bit)和一个SDA方向位(R:1, W:0)。
一个时钟发送一个位,在第8个下降沿之后,通过拉低SDA引脚(ACK位 为0),只是传感器数据接收正常。在发出测量命令之后(‘1110’‘0011’代表温度测量,‘1110’‘0101’代表相对湿度测量 ,这种为主机模式),MCU必须等待测量完成。
主机模式:
测量过程中,SCL线被封锁(由传感器控制),在测量时,SHT2x将SCL拉低强制主机进入等待状态。当释放SCL线,表示传感器内部工作接收,可以继续进行数据传送
灰色的部分是传感器控制的,当传感器给MCU返回数据时,每返回一个字节,MCU要返回一个ACK信号,当接收完毕之后,返回一个NACK并接着传输停止时序(P)。
注:校验和可以不需要,不需要则就在数据接收完之后返回NACK
非主机模式:
测量过程中,SCL线是开发状态,可进行其他通信,可以在总线上处理其他I2C总线通信任务。
当MCU要对传感器状态进行查询时,先发起一个开始信号,在发送从机地址和SDA方向位(写),此时从机匹配地址成功,则发送ACK信号,并开始测量。如果传感器完成了测量过程,并且发送ASK信号,那么MCU就可以读取相关数据。如果测量没有完成,传感器发送NACK信号,此时MCU必须重新发送启动传输时序,直至测量完成,MCU读取数据。
注意:测量的数据,温度和湿度均为两个字节。而且无论哪一种传输模式,测量的最大分辨率最大是14bit,数据的第二个字节SDA上最后两位是用来标记相关状态信息。其中bit1表示测量类型(0是温度,1是湿度)
灰色的区域是传感器控制的,如果不需要校验和,那么在接收完两个字节的数据之后就MCU直接发出NACK信号再接着发出停止时序(P),则结束通信。
通常测量的最长时间取决于测量类型和分辨率.
在计算MCU通信时间时,测量温度选择最长测量时间是85ms,而测量相对湿度选择最长的时间是29ms。
传感器内部设置的默认分辨率为相对湿度12位和温度14位。SDA的输出数据被转换成两个字节的数据包,高字节MSB在前(左对齐)。每个字节后面都跟随一个应答位。两个状态位,即 LSB的后两位在进行物理计算前须置0。 计算湿度:其中SRH表示我们读取到的湿度原始数据。
计算温度:其中ST表示我们读取到的温度原始数据。
引脚选择
这里选择的引脚见引脚接线表
代码移植
下载为大家准备的驱动代码文件夹,复制到自己工程中\luban-lite\application\rt-thread\helloworld\user-bsp
文件夹下
提示
如果未找到 user-bsp
这个文件夹,说明你未进行模块移植的前置操作。请转移到手册使用必要操作(点击跳转)中进行必要的配置操作!!!
接下来打开自己的工程,开始修改Kconfig文件。
1、在 VSCode 中打开 application\rt-thread\helloworld\Kconfig 文件
2、在该文件的 #endif
前面添加该模块的 Kconfig路径语句
# SHT20温湿度传感器
source "application/rt-thread/helloworld/user-bsp/sht20-temp-humi-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 SHT20 temperature and humidity 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_sht20.c
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <getopt.h>
#include <sys/time.h>
#include <rtthread.h>
#include <inttypes.h>
#include <finsh.h>
#include "hal_adcim.h"
#include "rtdevice.h"
#include "aic_core.h"
#include "aic_log.h"
#include "hal_gpai.h"
#include "aic_hal_gpio.h"
#include "hal_i2c.h"
#include "bsp_sht20.h"
#define I2C_BUS_NAME "i2c0" /* I2C总线设备名称 */
#define SLAVE_ADDR 0x40 /* 器件地址 */
static struct rt_i2c_bus_device *i2c_bus = RT_NULL; /* I2C总线设备句柄 */
/* 发送数据 */
static rt_err_t write_data(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *data)
{
struct rt_i2c_msg msgs;
msgs.addr = SLAVE_ADDR;
msgs.flags = RT_I2C_WR;
msgs.buf = data;
msgs.len = len;
/* 调用I2C设备接口传输数据 */
if (rt_i2c_transfer(bus, &msgs, 1) == 1)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
/* 读取数据 */
static rt_err_t read_data(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *buf)
{
struct rt_i2c_msg msgs;
msgs.addr = SLAVE_ADDR; // 器件地址
msgs.flags = RT_I2C_RD; // 读写标志的设定
msgs.buf = buf; // 发送缓存区地址
msgs.len = len; // 发送的字节长度
/* 调用I2C设备接口传输数据 */
if (rt_i2c_transfer(bus, &msgs, 1) == 1)
{
return RT_EOK;
}
else
{
return -RT_ERROR;
}
}
/**********************************************************
* 函 数 名 称:SHT20_Init
* 函 数 功 能:初始化SHT20
* 传 入 参 数:无
* 函 数 返 回:RT_OK:完成 -RT_ERROR:错误
* 作 者:LCKFB
* 备 注:
**********************************************************/
int SHT20_Init(void)
{
/* 查找I2C总线设备,获取I2C总线设备句柄 */
i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(I2C_BUS_NAME);
if(i2c_bus == RT_NULL)
{
LOG_E("no device: %s",I2C_BUS_NAME);
return -RT_ERROR;
}
else
{
rt_kprintf("\nfind device: %s\n",I2C_BUS_NAME);
}
return RT_EOK;
}
/******************************************************************
* 函 数 名 称:SHT20_Read
* 函 数 说 明:测量温湿度
* 函 数 形 参:Temperature温度数据地址 Humidity湿度数据地址
* 函 数 返 回:RT_EOK:成功 -RT_ERROR:错误
* 作 者:LC
* 备 注:无
******************************************************************/
int SHT20_Read(float *Temperature, float *Humidity)
{
uint8_t temp_reg_addr = 0xF3; // 温度
uint8_t humi_reg_addr = 0xF5; // 湿度
uint8_t send_buff[2] = {temp_reg_addr, humi_reg_addr};
uint8_t recv_buff[2] = {0};
if(Temperature != RT_NULL) // 如果温度地址不为空
{
/* 发送寄存器地址 */
if(RT_EOK != write_data(i2c_bus, 1, &send_buff[0]))
{
LOG_E("write_data for temperature failed: [%d]", __LINE__);
return -RT_ERROR;
}
aicos_mdelay(100);
/* 读取16位数据 */
if(RT_EOK != read_data(i2c_bus, 2, recv_buff))
{
LOG_E("read_data for temperature failed: [%d]", __LINE__);
return -RT_ERROR;
}
/* 转换数据 */
*Temperature = ((recv_buff[0]<<8)|recv_buff[1]) / 65536.0 * 175.72 - 46.85;
/* 清空接收缓存区 */
if(RT_NULL == rt_memset(recv_buff, 0, sizeof(recv_buff)))
{
LOG_E("rt_memset for temperature failed: [%d]", __LINE__);
return -RT_ERROR;
}
}
aicos_mdelay(100);
if(Humidity != RT_NULL) // 如果湿度地址不为空
{
/* 发送寄存器地址 */
if(RT_EOK != write_data(i2c_bus, 1, &send_buff[1]))
{
LOG_E("write_data for humidity failed: [%d]", __LINE__);
return -RT_ERROR;
}
aicos_mdelay(100);
/* 读取16位数据 */
if(RT_EOK != read_data(i2c_bus, 2, recv_buff))
{
LOG_E("read_data for humidity failed: [%d]", __LINE__);
return -RT_ERROR;
}
/* 转换数据 */
*Humidity = ((recv_buff[0]<<8)|recv_buff[1]) / 65536.0 * 125.0 - 6;
}
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
bsp_sht20.h
/*
* 立创开发板软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 文档网站:wiki.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 嘉立创社区问答:https://www.jlc-bbs.com/lckfb
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*/
#ifndef __BSP_SHT20_H__
#define __BSP_SHT20_H__
#include "stdio.h"
int SHT20_Init(void);
int SHT20_Read(float *Temperature, float *Humidity);
#endif
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Kconfig
这个是一个menuconfig中的选项,如果在菜单中选中该选项,就会在rtconfig.h
中定义一个语句,用来if判断条件编译之类的。
config LCKFB_SHT20_TEMP_HUMI_SENSOR
bool "Using SHT20 temperature and humidity sensor"
select AIC_USING_I2C0
default n
help
More information is available at: https://wiki.lckfb.com/
2
3
4
5
6
7
SConscript
自动化构建文件,如果定义了 LCKFB_SHT20_TEMP_HUMI_SENSOR
和 USING_LCKFB_TRANSPLANT_CODE
就自动编译当前目录下的文件!!
Import('RTT_ROOT')
Import('rtconfig')
import rtconfig
from building import *
cwd = GetCurrentDir()
CPPPATH = [cwd]
src = []
if GetDepend('LCKFB_SHT20_TEMP_HUMI_SENSOR') and GetDepend('USING_LCKFB_TRANSPLANT_CODE'):
src = Glob(os.path.join(cwd, '*.c'))
group = DefineGroup('lckfb-sht20-temp-humi-sensor', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
test_sht20_temp_humi_sensor.c
这个文件是一个基于RT-Thread操作系统的SHT20温湿度传感器处理模块。它包含了对SHT20传感器的初始化、数据读取以及线程管理相关的代码。
全局变量
- 声明了一个线程控制块指针
sht20_thread
,用于管理SHT20传感器读取线程。
线程入口函数逻辑
sht20_thread_entry
是线程的入口函数,它不接受任何参数。- 在函数内部,首先调用
SHT20_Init
函数初始化SHT20传感器。 - 如果初始化失败,则打印错误信息并返回。
- 使用一个无限循环来持续读取传感器的数据。
- 在循环中,定义了温度和湿度变量,并尝试通过
SHT20_Read
函数读取传感器数据。 - 如果读取成功,将数据转换为整数和小数部分,并打印到控制台。
- 如果读取失败,打印错误信息。
- 通过
while_count
计数器控制循环次数,并在计数器达到100次时提示用户可以通过命令退出读取循环。 - 在每次循环结束后,线程会挂起2秒(如果是第一次循环)或1秒。
SHT20启动函数
test_sht20_sensor
函数用于启动SHT20传感器读取线程。- 它首先尝试创建一个名为
sht20_thread1
的线程,并设置其入口函数、参数、栈大小、优先级和时间片。 - 如果线程创建成功,则启动该线程。
SHT20退出函数
test_exit_sht20_sensor
函数用于删除SHT20传感器读取线程。- 它尝试删除
sht20_thread
线程,并根据删除操作的结果打印相应的信息。
命令行接口
- 使用
MSH_CMD_EXPORT
宏导出test_sht20_sensor
和test_exit_sht20_sensor
函数,使其可以通过命令行接口调用。 - 用户可以通过输入
test_sht20_sensor
命令来启动SHT20传感器读取线程。 - 用户可以通过输入
test_exit_sht20_sensor
命令来退出SHT20传感器读取线程。
文件提供了一个RT-Thread操作系统下的SHT20传感器数据处理模块,通过命令行接口控制传感器数据的读取和退出操作。
#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_sht20.h"
#define THREAD_PRIORITY 25 // 线程优先级
#define THREAD_STACK_SIZE 1024 // 线程大小
#define THREAD_TIMESLICE 20 // 时间片
static rt_thread_t sht20_thread = RT_NULL; // 线程控制块
// 线程入口函数
static void sht20_thread_entry(void *param)
{
int ret = 0;
int while_count = 1; // 循环次数
/* SHT20初始化 */
ret = SHT20_Init();
if(ret != RT_EOK)
{
LOG_E("failed to SHT20_Init !!");
return;
}
rt_kprintf("Start Loop !!\n");
while(while_count++)
{
float Temperature = 0;
float Humidity = 0;
int ret = SHT20_Read(&Temperature, &Humidity); //读取
if(ret != RT_EOK)
{
LOG_E("failed to SHT20_Read !");
}
else
{
uint32_t value_temp = Temperature * 100;
uint32_t value_humi = Humidity * 100;
rt_kprintf("\nRead [SHT20] Temperature = %d.%02d\n", value_temp/100, value_temp%100);
rt_kprintf("Read [SHT20] Humidity = %d.%02d\n", value_humi/100, value_humi%100);
}
if(while_count >= 100)
{
while_count = 1;
rt_kprintf("\nType [test_exit_sht20_sensor] command to exit SHT20 to read data\n");
rt_kprintf("Note: Pressing [TAB] as you type will autocomplete the command\n");
rt_thread_mdelay(2000);
}
rt_thread_mdelay(1000);
}
rt_kprintf("\nEND!!\n");
}
/* SHT20启动函数 */
static void test_sht20_sensor(int argc, char **argv)
{
/* 创建线程,名称是 sht20_thread,入口是 sht20_thread_entry */
sht20_thread = rt_thread_create("sht20_thread1",
sht20_thread_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
/* 如果获得线程控制块,启动这个线程 */
if (sht20_thread != RT_NULL)
rt_thread_startup(sht20_thread);
}
// 导出函数为命令
MSH_CMD_EXPORT(test_sht20_sensor, run SHT20 sensor);
/* SHT20退出函数 */
static void test_exit_sht20_sensor(void)
{
int ret = rt_thread_delete(sht20_thread);
if(ret != RT_EOK)
{
LOG_E("failed to test_exit_sht20_sensor !!");
}
else
{
rt_kprintf("\n========SHT20 exit successful !!========\n");
}
}
// 导出函数为命令
MSH_CMD_EXPORT(test_exit_sht20_sensor, quit SHT20);
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
移植验证
我们使用串口调试,将 USB转TTL模块 连接到衡山派开发板上面!!
具体的教程查看:串口调试(点击跳转🚀)
串口波特率默认为
115200
我们在输入下面的命令运行该模块的线程:
输入的时候按下
TAB键
会进行命令补全!!
test_sht20_sensor
模块上电效果: