STM32 GPIO模拟I2C的SHT20温湿度采样

要用GPIO模拟I2C,那么首先就得弄清楚I2C的时序。

1.I2C总线通信时序

2.起始与结束时序

 

//起始信号
uint8_t I2c_StartCondition()
{
        uint8_t rv = NO_ERROR;
        SDA_OUT();
        SCL_OUT();
        /* StartCondition(S): A high to low transition on the SDA line while SCL is high.
        _______
        SCL:   |___
        _____
        SDA: |_____
        */
        SDA_H();
        delay_us(2);
        SCL_H();
        delay_us(2);
        SDA_L();//先于SCL前拉低
        delay_us(2);
        SCL_L();//拉低SCL便于之后的操作
        delay_us(2);
        return rv;
        
}  
//终止信号
uint8_t I2c_StopCondition(void)
{
        uint8_t rv = NO_ERROR;
        SDA_OUT();
        /* StopCondition(P): A low to high transition on the SDA line while SCL is high.
        ————————————
        SCL:        — — —
                 _____
        SDA: ___|
        */
        SCL_L();
        SDA_L();
        delay_us(2);
        SCL_H();
        delay_us(2);
        SDA_H();
        delay_us(2);
        return rv;
}

 3.读写时序

 

 

//写一个byte
uint8_t I2c_WriteByte(uint8_t byte)
{
		uint8_t rv = NO_ERROR;
		uint8_t mask;
		/* Data line changes must happened when SCL is low */
		SDA_OUT();
		SCL_L();
		/* 1Byte=8bit, MSB send: bit[7]-->bit[0] */
		for(mask=0x80; mask>0; mask>>=1)
		{
			if((mask & byte) == 0)
			{
				SDA_L();
			}
			else
			{
				SDA_H();
			}
			delay_us(5); // data set-up time (t_SU;DAT)
			SCL_H();
			delay_us(5); // SCL high time (t_HIGH)
			SCL_L();
			delay_us(5); // data hold time(t_HD;DAT)
		}
		/* clk #9 wait ACK/NAK from slave */
		SDA_IN();
		SCL_H(); // clk #9 for ack
		delay_us(5); // data set-up time (t_SU;DAT)
		//从机应答
		if( READ_SDA() )
			rv = ACK_ERROR;
OUT:
		SCL_L();
		delay_us(20);
		return rv;
}
//读一个byte
uint8_t I2c_ReadByte(uint8_t *byte, uint8_t ack)
{
		uint8_t rv = NO_ERROR;
		uint8_t mask;
		*byte = 0x00;
		SDA_IN();		
		/* 1Byte=8bit, MSB send: bit[7]-->bit[0] */
		for(mask = 0x80; mask > 0; mask >>= 1)
		{
				SCL_H(); // start clock on SCL-line
				delay_us(2); // clock set-up time (t_SU;CLK)
				if(READ_SDA())
					*byte |= mask; // read bit
				SCL_L();
				delay_us(2); // data hold time(t_HD;DAT)
		}
		/* clk #9 send ACK/NAK to slave */
		if(ack == ACK)
		{
				SDA_OUT();
				SDA_L(); // send Acknowledge if necessary
		}
		else if( ack == NAK )
		{
				SDA_OUT();
				SDA_H(); // send NotAcknowledge if necessary
		}
		delay_us(1); // data set-up time (t_SU;DAT)
		SCL_H(); // clk #9 for ack
		delay_us(2); // SCL high time (t_HIGH)
OUT:
		SCL_L();
		delay_us(2); // wait to see byte package on scope
		return rv;
}

注意:

1.无论是写入一个byte还是读出一个byte,都是从MSB到LSB

2.要改变SDA状态时,都是在SCL为低时改变,如果在SCL为高时改变,将被视为开始或结束信号。

 4.封装收发信号

//接受从机发来的信号
int I2C_Master_Receive(uint8_t addr, uint8_t *buf, int len)
{
		int i;
		int rv = NO_ERROR;
		uint8_t byte;
		I2c_StartCondition();
		rv = I2c_WriteByte(addr);
		if( rv )
		{
				sht20_printf("Send I2C read address failure, rv=%d\n", rv);
				goto OUT;
		}
		for (i=0; i<len; i++)
		{
				if( !I2c_ReadByte(&byte, ACK) )
				{
					buf[i] = byte;
				}
				else
					goto OUT;
		}
OUT:
		I2c_StopCondition();
		return rv;
}
//发送一个命令
int I2C_Master_Transmit(uint8_t addr, uint8_t *data, int bytes)
{
		int i;
		int rv = NO_ERROR;
	
		if(!data)
		{
				return PARM_ERROR;
		}
		sht20_printf("I2C Mastr start transimit [%d] bytes data to addr [0x%02x]\n", bytes, addr);
		I2c_StartCondition();
		rv = I2c_SendAddress(addr);
		if( rv )
		{
				goto OUT;
		}
		for (i=0; i<bytes; i++)
		{
			
				if( NO_ERROR != (rv=I2c_WriteByte(data[i])) )
				{
						break;
				}
		}
OUT:
		I2c_StopCondition();
		return rv;
}

5.主函数

采用默认的采样精度,温度采样精度14bit,湿度采样精度12bit,采样测量时间如下图1:

图1采样测量时间 

 

图2命令

注意:

1. I2C总线上的从机都有自己的地址,方便主机选择通信,sht20的地址为0x40,加上最后一位读写位的话,写地址为0x80,读地址为0x81。(非保持主机模式采样)

2. 发送采样命令后要等待采样测量时间之后再去读取数据。

//没有检查检验和
short sht20_sample_temp_or_hum(uint8_t cmd)
{
	uint8_t data[2];
	unsigned short sht20_data=0;
	int rv;
//	uint8_t tran_cmd=0xF3;
	rv=I2C_Master_Transmit(0x80,&cmd,1);
	if(0!=rv)
		return -1;
	if(cmd==0xf3)
		HAL_Delay(85);
	else if(cmd==0xf5)
		HAL_Delay(29);
	rv=I2C_Master_Receive(0x81,data,sizeof(data));
	if(0!=rv)
		return -1;
	
	sht20_data=data[0];
	sht20_data=(sht20_data<<8);
	sht20_data+=data[1]&0xFC;
	if(cmd==0xf3)
	{
		sht20_data=(short)((-46.85+175.72*sht20_data/65536)*(10));//计算
	}
	else if(cmd==0xf5)
	{
		sht20_data=(short)((-6.0+125.0*sht20_data/65536)*(10));//计算
	}
	printf("sht20_data=%d\n",sht20_data);
	return sht20_data;
}

运行结果:

温度测量:

湿度测量:(单位用H代替)

 

 

 

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值