SHT30数据读取历程

首先我采用的是,周期性读取的方法。

实际效果如下:

需要注意在手册单中明确的精度,以及测量数据如何计算。

其中的ST主要就是我们两个拼接的16位寄存器数据。简单换算就行。

为了防止与库函数当中的I2C重名,我自己另起名位MYI2C,自己可以修改:

其中延时的函数需要自己添加。如果需要可以留言我会发源码。

另外对于其中一些检测我没有添加。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"                  // Device header
/**
  * @brief  SCL电平设置
  * @param  需要设置的高低电平
  * @retval 无返回值
  */
void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);
	Delay_us(10);
}
/**
  * @brief  SDA电平设置
  * @param  需要设置的高低电平
  * @retval 无返回值
  */

void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
	Delay_us(10);
}

/**
  * @brief  SDA电平读取
  * @param  无参数
  * @retval 读取的SDA高低电平
  */
uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
	Delay_us(10);
	return BitValue;
}

/**
  * @brief  I2C初始化
  * @param  无参数
  * @retval 无返回值
  */

void MYI2C_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOA的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//配置位开漏输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);					
	
	GPIO_SetBits(GPIOB,GPIO_Pin_10|GPIO_Pin_11);//使SDA和SCL均为高电平,空闲状态
}
/**
  * @brief  I2C开始信号
  * @param  无形参
  * @retval 无返回值
  */
void MYI2C_START(void)
{
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}
/**
  * @brief  I2C结束信号
  * @param  无形参
  * @retval 无返回值
  */
void MYI2C_STOP(void)
{
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}

/**
  * @brief  发送字节
  * @param  需要发送的字节
  * @retval 无返回值
  */
void MYI2C_SENDBYTE(uint8_t byte)
{
	uint8_t n=0;
	for(n=0;n<8;n++)
	{
		MyI2C_W_SDA(byte&(0x80>>n));
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);
	}
}

/**
  * @brief  接收字节
  * @param  无形参
* @retval 从SDA上接受的数据
  */
uint8_t MYI2C_RECEIVEBYTE(void)
{
	uint8_t Data=0X00;
	uint8_t n=0;
	MyI2C_W_SDA(1);
	for(n=0;n<8;n++)
	{
		MyI2C_W_SCL(1);
	  if( MyI2C_R_SDA()==1){Data|=(0x80>>n);}
		MyI2C_W_SCL(0);
	}
	return Data;
}

/**
  * @brief  发送应答位
  * @param  主机需要发送的应答位
  * @retval 无返回值
  */
void MYI2C_SENDACKBIT(uint8_t Ackbit)
{
//	  MyI2C_W_SCL(0);
		MyI2C_W_SDA(Ackbit);
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);
}


/**
  * @brief  接收从机应答位
  * @param  无形参
  * @retval 从机给的应答
  */
uint8_t MYI2C_RECEIVEAckbit(void)
{
		uint8_t  Ackbit;
		MyI2C_W_SDA(1);
		MyI2C_W_SCL(1);
		Ackbit=MyI2C_R_SDA();
		MyI2C_W_SCL(0);
		return Ackbit;
}


MYI2C.H

#ifndef _MYI2C__H__
#define __MYI2C_H__


void MyI2C_W_SCL(uint8_t BitValue);

void MyI2C_W_SDA(uint8_t BitValue);

uint8_t MyI2C_R_SDA(void);

void MYI2C_Init(void);

void MYI2C_START(void);

void MYI2C_STOP(void);

void MYI2C_SENDBYTE(uint8_t byte);

uint8_t MYI2C_RECEIVEBYTE(void);

void MYI2C_SENDACKBIT(uint8_t Ackbit);

uint8_t MYI2C_RECEIVEAckbit(void);

#endif

SHT30.c

其中数据读取部分,首先我们需要读取的是两个数据,但是返回值只能由一个。

方法我一时间想到两个,1.读取函数外创建数组,将指针传进去,我们操作指针自增存数据。

2.创建两个指针变量记得初始化,操作指针读取数据。由于数据就2个我采用第二个。

其中如果自己数据没有读取出来可以通过我自己添加的应答位显示检查时序非常直观,当然也可以用示波器如果条件允许。我一开始也是挨个检查发现问题在于没有给从机足够测量导致数据满格

如下。后面逐个打印应答位发现问题所在。

#include "stm32f10x.h"                  // Device header
#include "MYI2C.h"
#include "OLED.h"
#include "Delay.h"
#define SHT30_ADDRESS 0x44
#define MSBRegaddress 0Xe0
#define LSBRegaddress 0x00
void SHT30_Init(void)
{
	MYI2C_Init();
}	
/**
  * @brief  需要写入从机的数据
  * @param  地址,数据/数据,数据
  * @retval 无返回值
  */
void SHT30_WriteByte(uint8_t x,uint8_t y)
{
	uint8_t n;
	MYI2C_START();
	MYI2C_SENDBYTE((SHT30_ADDRESS<<1)|0);//写入
	MYI2C_RECEIVEAckbit();
	MYI2C_SENDBYTE(x);//0x21
	MYI2C_RECEIVEAckbit();
	MYI2C_SENDBYTE(y);//0x30
	n=MYI2C_RECEIVEAckbit();
//	OLED_ShowNum(1,5,n,3);
	MYI2C_STOP();
	Delay_ms(20);//给从机测量时间,非常重要!!!!
}
/**
  * @brief  温湿度读取
  * @param  传入温度,湿度指针
  * @retval 无返回值
  */
void SHT30_ReadByte(float *Temperature,float *Humidity)
{
	uint16_t temp,humi;
  uint16_t data[6];
	uint8_t n,m,a,b,c,d;
	MYI2C_START();
	MYI2C_SENDBYTE((SHT30_ADDRESS<<1)|0);//读取
	a=MYI2C_RECEIVEAckbit();
//	OLED_ShowNum(1,1,n,3);
	MYI2C_SENDBYTE(MSBRegaddress);//0xe0
	b=MYI2C_RECEIVEAckbit();
//	OLED_ShowNum(2,1,n,3);
	MYI2C_SENDBYTE(LSBRegaddress);//0x00
	c=MYI2C_RECEIVEAckbit();
//	OLED_ShowNum(3,1,n,3);
 
	MYI2C_START();
	MYI2C_SENDBYTE((SHT30_ADDRESS<<1)|1);//读取数据
	n=MYI2C_RECEIVEAckbit();
//	OLED_ShowNum(4,1,n,3);
	//温度
	data[0]=MYI2C_RECEIVEBYTE();
	MYI2C_SENDACKBIT(0);
	data[1]=MYI2C_RECEIVEBYTE();
   MYI2C_SENDACKBIT(0);
	//温度校验位
	data[2]=MYI2C_RECEIVEBYTE();
	 MYI2C_SENDACKBIT(0);
	//湿度
	data[3]=MYI2C_RECEIVEBYTE();
	 MYI2C_SENDACKBIT(0);
	data[4]=MYI2C_RECEIVEBYTE();
  MYI2C_SENDACKBIT(0);
	//CRC校验位
	data[5]=MYI2C_RECEIVEBYTE();
  MYI2C_SENDACKBIT(1);
	MYI2C_STOP();
	temp=(data[0]<<8)|data[1];
	humi=(data[3]<<8)|data[4];
	
	/*转换实际温度*/
	*Temperature=(175.0*(float)temp/65535.0-45.0) ;// T = -45 + 175 * tem / (2^16-1)
	*Humidity=(100.0*(float)humi/65535.0);// RH = hum*100 / (2^16-1);
}
/**
  * @brief  温湿度数据显示
  * @param  无形参
  * @retval 无返回值
  */
void SHT30_SHOWVALUE(void)
{
	float Temperature=0x0000;
	float Humidity=0x0000;
	float*PT=&Temperature;
	float*PH=&Humidity;
	SHT30_Init();
	SHT30_WriteByte(0x21,0x30);
	SHT30_ReadByte(PT,PH);//建立两个指针变量,操作指针读取数据
	OLED_ShowString(1,1,"Temperature:");
	OLED_ShowString(3,1,"Humidity:");
	OLED_ShowNum(2,1,Temperature,3);//整数部分
	OLED_ShowChar(2,4,'.');
	OLED_ShowNum(2,5,(uint16_t)(Temperature*1000)%1000,2);//取出小数部分显示
	OLED_ShowNum(4,1,Humidity,2);//整数部分
	OLED_ShowChar(4,3,'.');
	OLED_ShowNum(4,4,(uint16_t)(Humidity*1000)%1000,2);//取出小数部分显示
	Delay_s(1);//防止数据测量太快不断闪烁,加1s延迟;
}

个人总结一下I2C需要注意地方:

1.从机地址是否正确;

2.要写入寄存器地址是否正确;

3.读取数据之前要查手册了解最短测量时间,留充足。

4.每个应答位是否发送正确,特别注意在读取的时候每读取一个主机要向从机发送应答0表示受到。以及在不读取之前主机要发送应答位1表示停止接收,不然容易出现数据紊乱。

5.考虑电平拉高低的时间,SHT30是1MHZ的通信速度考虑就比较少,实际要注意。

检查方法:

有钱的可以用逻辑分析仪/示波器直接看波形,没有的或者觉得麻烦看不懂的可以学我打印应答位直观明了。

相比较硬件简单配置而言,很多地方,譬如不好检查错误,不好查看波形,以及通用性不强,很多设备不具备I2C硬件外设。当然模拟有助于自己理解I2C通信。也可以看我之前写的I2C介绍了解更多。仅仅代表个人拙见,欢迎指出错误。需要具体源码可以留言。

补充实验现象:

我这是以前做的一个项目:小型气象站系统,写的温湿度代码,实际测量情况如下:

这里面包含了对于SHT30,BH1750 等等的测量。

  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
sht30的基于c51单片机驱动程序:#include #include #include "I2C.h" #include "SHT30.h" #define uint unsigned int #define uchar unsigned char void display(); unsigned char code tableduan[]= { 0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71 }; uchar data DIS_ROME[6]= {0,0,0,0,0,0}; //显示缓存区(4) uchar DISP=0;//缓存区指针 uchar SCANF=0xDF;//扫描指针 sbit LED1=P1^0; sbit LED2=P1^1; sbit LED3=P1^2; sbit LED4=P1^3; sbit VOC_A=P3^5; sbit VOC_B=P3^6; sbit dula=P2^6; //IO口定义 sbit wela=P2^7; sbit key=P3^4; sbit beep_dr=P2^3; uint pm1 = 0; uint pm2 = 0; uint pm10 = 0; uchar vr=0; uint intrcnt=0; bit F_1HZ; uint voice_time_cnt; uchar Uart_Buf; uchar Rec_Addr=0; uchar mode=0; uchar Rec_Uart=0; uchar Recive_Buf[30]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; #define key P34 #define const_key_time1 50 unsigned char ucKeySec=0; //被触发的按键编号 unsigned int uiKeyTimeCnt1=0; //按键去抖动延时计数器 unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志 unsigned char displaycnt=0; void keyscan() { if(key==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位 { ucKeyLock1=0; //按键自锁标志清零 uiKeyTimeCnt1=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。 } else if(ucKeyLock1==0)//有按键按下,且是第一次被按下 { uiKeyTimeCnt1++; //累加定时中断次数 if(uiKeyTimeCnt1>const_key_time1) { uiKeyTimeCnt1=0; ucKeyLock1=1; //自锁按键置位,避免一直触发 ucKeySec=1; //触发1号键 } } } void keyservice() { if(ucKeySec) { displaycnt=!displaycnt; } ucKeySec=0; } void UartInit(void) //9600bps@12.000MHz { TMOD=0x01; //设置定时器0为工作方式1 TH0=0xf8; //重装初始值(65535-500)=65035=0xfe0b TL0=0x2f; SCON=0x50; TMOD=0X21; IP =0x10; //把串口中断设置为最高优先级, EA=1; ES=1; ET0=1; TR0=1; } void T0_time(void) interrupt 1 //定时中断 { TF0=0; //清除中断标志 TR0=0; //关中断 keyscan(); keyservice(); display();

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值