硬件I2C sht3x温湿度传感器 学习过程记录
这里没什么好说的,电压,协议,精度,封装之类的。
直接跳过,到影响整体方案的地方。。。
时间上的问题,高精度的情况下典型值要12.5ms,其实在官方给的例程中则选用的是最大值15ms,相应精度的发送命令不同。(软件I2C利用的GPIO以及延时,即阻塞处理,如果说对系统实时性较高的,不用往下看了。)
SCL和SDA都是开漏输出,外部上拉电阻,PP推挽输出也是可以的。注意是否单片机内部已经打开上拉电阻了(这句话不鸟他)
意思就是你传输过程中,不要乱动他的地址,传完了再动。
地址有两个,引脚拉低即0x44,引脚拉高即0x45
复位的引脚,不用就建议浮空,或者>=2KΩ上拉到VDD
支持I2C的fast mode(200khz),实际上它可以1000khz,即0.1us/bit
命令的间隙是1ms,也就是你发一次命令要等一会才能发。
7bits的地址 + 1bit的0(write)
支持两种查询模式:单次查询、周期查询
两种查询模式的命令不同,查手册吧,不贴了。
github地址例程地址
sensirion_sw_i2c_implementation.c
#include
#include "sensirion_sw_i2c_gpio.h"
#include "sensirion_arch_config.h"
#include "Timer.h"
void sensirion_init_pins() {
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
sensirion_SDA_in();
sensirion_SCL_in();
}
void sensirion_SDA_in() {
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOG, &GPIO_InitStruct);
}
void sensirion_SDA_out() {
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOG, &GPIO_InitStruct);
GPIO_ResetBits(GPIOG, GPIO_Pin_3);
}
u8 sensirion_SDA_read() {
return (u8)GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_3) == 1;
}
void sensirion_SCL_in() {
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOG, &GPIO_InitStruct);
}
void sensirion_SCL_out() {
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOG, &GPIO_InitStruct);
GPIO_ResetBits(GPIOG, GPIO_Pin_2);
}
u8 sensirion_SCL_read() {
return (u8)GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_2) == 1;
}
void sensirion_sleep_usec(u32 useconds) {
BaseTimer::Instance()->delay_ms(useconds);
}
SDA、SCL引脚根据自己的实际情况配置
- sensirion_sleep_usec()函数需要自己实现(
微秒级延时
)
sensirion_sw_i2c.c
#include "sensirion_arch_config.h"
#include "sensirion_common.h"
#include "sensirion_i2c.h"
#include "sensirion_sw_i2c_gpio.h"
#define DELAY_USEC (SENSIRION_I2C_CLOCK_PERIOD_USEC / 50)
static u8 sensirion_wait_while_clock_stretching(void)
{
u8 timeout = 2;
while (--timeout) {
if (sensirion_SCL_read())
return STATUS_OK;
sensirion_sleep_usec(DELAY_USEC);
}
return STATUS_FAIL;
}
static s8 sensirion_i2c_write_byte(u8 data)
{
s8 nack, i;
for (i = 7; i >= 0; i--) {
sensirion_SCL_out();
if ((data >> i) & 0x01)
sensirion_SDA_in();
else
sensirion_SDA_out();
sensirion_sleep_usec(DELAY_USEC);
sensirion_SCL_in();
sensirion_sleep_usec(DELAY_USEC);
if (sensirion_wait_while_clock_stretching())
return STATUS_FAIL;
}
sensirion_SCL_out();
sensirion_SDA_in();
sensirion_sleep_usec(DELAY_USEC);
sensirion_SCL_in();
if (sensirion_wait_while_clock_stretching())
return STATUS_FAIL;
nack = (sensirion_SDA_read() != 0);
sensirion_SCL_out();
return nack;
}
static u8 sensirion_i2c_read_byte(u8 ack)
{
s8 i;
u8 data = 0;
sensirion_SDA_in();
for (i = 7; i >= 0; i--) {
sensirion_sleep_usec(DELAY_USEC);
sensirion_SCL_in();
if (sensirion_wait_while_clock_stretching())
return STATUS_FAIL;
data |= (sensirion_SDA_read() != 0) << i;
sensirion_SCL_out();
}
if (ack)
sensirion_SDA_out();
else
sensirion_SDA_in();
sensirion_sleep_usec(DELAY_USEC);
sensirion_SCL_in();
sensirion_sleep_usec(DELAY_USEC);
if (sensirion_wait_while_clock_stretching())
return STATUS_FAIL;
sensirion_SCL_out();
sensirion_SDA_in();
return data;
}
static u8 sensirion_i2c_start(void)
{
sensirion_SCL_in();
if (sensirion_wait_while_clock_stretching())
return STATUS_FAIL;
sensirion_SDA_out();
sensirion_sleep_usec(DELAY_USEC);
sensirion_SCL_out();
sensirion_sleep_usec(DELAY_USEC);
return STATUS_OK;
}
static void sensirion_i2c_stop(void)
{
sensirion_SDA_out();
sensirion_sleep_usec(DELAY_USEC);
sensirion_SCL_in();
sensirion_sleep_usec(DELAY_USEC);
sensirion_SDA_in();
sensirion_sleep_usec(DELAY_USEC);
}
s8 sensirion_i2c_write(u8 address, const u8* data, u16 count)
{
s8 ret;
u16 i;
ret = sensirion_i2c_start();
if (ret != STATUS_OK)
return ret;
ret = sensirion_i2c_write_byte(address << 1);
if (ret != STATUS_OK) {
sensirion_i2c_stop();
return ret;
}
for (i = 0; i < count; i++) {
ret = sensirion_i2c_write_byte(data[i]);
if (ret != STATUS_OK) {
sensirion_i2c_stop();
break;
}
}
sensirion_i2c_stop();
return ret;
}
s8 sensirion_i2c_read(u8 address, u8* data, u16 count)
{
s8 ret;
u8 send_ack;
u16 i;
ret = sensirion_i2c_start();
if (ret != STATUS_OK)
return ret;
ret = sensirion_i2c_write_byte((address << 1) | 1);
if (ret != STATUS_OK) {
sensirion_i2c_stop();
return ret;
}
for (i = 0; i < count; i++) {
send_ack = i < (count - 1);
data[i] = sensirion_i2c_read_byte(send_ack);
}
sensirion_i2c_stop();
return STATUS_OK;
}
void sensirion_i2c_init()
{
sensirion_init_pins();
sensirion_SCL_in();
sensirion_SDA_in();
}
DELAY_USEC
这个是根据手册1000khz推算的,也就是bit时间- sensirion_i2c_write()函数根据协议通过GPIO及延时
模拟
通信过程中的报文
sht3x.c
#include "sensirion_arch_config.h"
#include "sensirion_common.h"
#include "sensirion_i2c.h"
#include "sht.h"
#include "sht_common.h"
#include "Timer.h"
#include "Console.h"
/* all measurement commands return T (CRC) RH (CRC) */
#if USE_SENSIRION_CLOCK_STRETCHING
static const u8 CMD_MEASURE_HPM[] = { 0x2C, 0x06 };
static const u8 CMD_MEASURE_LPM[] = { 0x2C, 0x10 };
#else
static const u8 CMD_MEASURE_HPM[] = { 0x24, 0x00 };
static const u8 CMD_MEASURE_LPM[] = { 0x24, 0x16 };
#endif /* USE_SENSIRION_CLOCK_STRETCHING */
static const u8 CMD_READ_STATUS_REG[] = { 0xF3, 0x2D };
static const u8 COMMAND_SIZE = sizeof(CMD_MEASURE_HPM);
#ifdef SHT_ADDRESS
static const u8 SHT3X_ADDRESS = SHT_ADDRESS;
#else
static const u8 SHT3X_ADDRESS = 0x44;
#endif
static const u16 MEASUREMENT_DURATION_USEC = 15000;
static const u8 *cmd_measure = CMD_MEASURE_HPM;
s8 sht_measure_blocking_read(s32 *temperature, s32 *humidity)
{
#if DEBUGTRANSFER
int32_t start_time;
int32_t end_time;
int32_t period_time;
start_time = BaseTimer::Instance()->getTime();
Console::Instance()->printf("transfer time is %d \n",start_time);
s8 ret = sht_measure();
end_time = BaseTimer::Instance()->getTime();
Console::Instance()->printf("transfer_end time is %d \n",end_time);
period_time = end_time - start_time;
Console::Instance()->printf("transfer_period time is %d \n",period_time);
#else
s8 ret = sht_measure();
#endif
if (ret == STATUS_OK) {
sensirion_sleep_usec(MEASUREMENT_DURATION_USEC);
#if DEBUGREAD
int32_t start_time;
int32_t end_time;
int32_t period_time;
start_time = BaseTimer::Instance()->getTime();
Console::Instance()->printf("read_time is %d \n",start_time);
s8 ret = sht_measure();
end_time = BaseTimer::Instance()->getTime();
Console::Instance()->printf("read_end time is %d \n",end_time);
period_time = end_time - start_time;
Console::Instance()->printf("read_period time is %d \n",period_time);
#else
ret = sht_read(temperature, humidity);
#endif
}
return ret;
}
s8 sht_measure()
{
return sensirion_i2c_write(SHT3X_ADDRESS, CMD_MEASURE_HPM, COMMAND_SIZE);
}
s8 sht_read(s32 *temperature, s32 *humidity)
{
return sht_common_read_measurement(SHT3X_ADDRESS, temperature, humidity);
}
s8 sht_probe()
{
u8 data[3];
sensirion_i2c_init();
s8 ret = sensirion_i2c_write(SHT3X_ADDRESS, CMD_READ_STATUS_REG, COMMAND_SIZE);
if (ret)
return ret;
ret = sensirion_i2c_read(SHT3X_ADDRESS, data, sizeof(data));
if (ret)
return ret;
ret = sensirion_common_check_crc(data, 2, data[2]);
if (ret)
return ret;
return STATUS_OK;
}
s8 sht_disable_sleep(u8 disable_sleep)
{
return STATUS_FAIL; /* sleep mode not supported */
}
void sht_enable_low_power_mode(u8 enable_low_power_mode)
{
cmd_measure = enable_low_power_mode ? CMD_MEASURE_LPM : CMD_MEASURE_HPM;
}
//const char *sht_get_driver_version()
//{
// return 11;
//}
u8 sht_get_configured_sht_address()
{
return 11;
}
- const char *sht_get_driver_version()需要
注掉
,并没有定义驱动版本,你也可以自己定义,否则会报错。- 有很多封装好的函数可以调用,睡眠,低功耗模式等等(
其实鸟用也没用
,反正我没用)sht_probe()
探针,用来检测是不是正常的,主程序中会用到。- 还有一些开头定义的
命令数组及地址
等- 忽略那些宏定义debug下的console,我用来算时间用的。(以前大佬写的console就是好用,hahahah)
mian.c
while (sht_probe() != STATUS_OK) {
Console::Instance()->printf("SHT sensor probing failed\n");
}
Console::Instance()->printf("SHT sensor probing successful\n");
while (1) {
s32 temperature, humidity;
/* Measure temperature and relative humidity and store into variables
* temperature, humidity (each output multiplied by 1000).
*/
#if DEBUGMAIN
int32_t start_time;
int32_t end_time;
int32_t period_time;
start_time = BaseTimer::Instance()->getTime();
Console::Instance()->printf("start time is %d \n",start_time);
s8 ret = sht_measure_blocking_read(&temperature, &humidity);
end_time = BaseTimer::Instance()->getTime();
Console::Instance()->printf("end time is %d \n",end_time);
period_time = end_time - start_time;
Console::Instance()->printf("period time is %d \n",period_time);
#else
s8 ret = sht_measure_blocking_read(&temperature, &humidity);
#endif
if (ret == STATUS_OK) {
Console::Instance()->printf("measured temperature: %0.2f degreeCelsius, "
"measured humidity: %0.2f percentRH\n",
temperature / 1000.0f,
humidity / 1000.0f);
} else {
Console::Instance()->printf("error reading measurement\n");
}
BaseTimer::Instance()->delay_ms(280);
}
return 0;
最后一行的延时不用管它了,delay_ms()内部已经被我改成us级的了。(功能测试,各位大佬不要抓这个)。至于等待多少也没有意义了,手册中是1ms的间隔才能发送命令进行查询,实际上这其中的过程早就超过1ms了。
程序跑起来的输出:
发送所需的时间:0.6ms
读取所需的时间:0.5ms
完整读取的时间:16ms(传感器自身测量占了15ms
见手册)