文章目录

  • 1- I2C协议介绍
    • (1)I2C协议简介
    • (2)I2C从设备地址
  • 2- I2C通信时序
    • (1)起始位
    • (2)读写地址
    • (3)I2C应答信号
    • (4)数据位发送与接收
  • 3- I2C协议主机收发数据流程
    • (1)主机发送数据
    • (2)主机接收数据
  • 4- SHT30传感器介绍
    • (1)SHT30简介
    • (2)SHT30工作原理
  • 5- HAL库中I2C发送接收数据函数
    • (1)HAL_I2C_Master_Transmit()
    • (2)HAL_I2C_Master_Receive()
  • 6- SHT30温湿度采样
    • (1)配置
    • (2)创建sht30.c
    • (3)创建sht30.h
    • (4)修改main.c
  • 7- 结果呈现

1- I2C协议介绍

(1)I2C协议简介

I2C总线是Philips公司在八十年代初推出的一种串行、半双工的总线,主要用于近距离、低速的芯片之间的通信。
12C Bus(IIC, Inter-Integrated Circuit Bus)是由一根数据线SDA用于收发数据,一根时钟线SCL用于通信双方时钟的同步,利用上拉电阻将它们拉成高电平(表示总线空闲),其典型的电压准位为+3.3V或+5V,具有电路简单、连接线少、控制简单、通信速率高等优点。

I2C总线是一种主从结构(Master/Slave)总线, I2C总线上的每一个设备都可以作为主设备或者从设备,但一个总线上一般只有一个主设备,可以带多个从设备。其中主设备用来产生允许传输的时钟信号,并初始化总线的数据传输,所以主设备通常是CPU,而从设备只能被动响应主设备发起的通信请求,所以各种12C接口芯片将作为从设备使用。

(2)I2C从设备地址

因为一个12C总线上可以有多个从设备,这样主设备需要通过地址来确定与哪个器件进行通信。I2C总线上每个从设备都有一个唯一的7bit地址物理识别,这个地址固化在芯片内部,并可以从芯片datasheet上找到。因为12C地址全0为广播地址,所以12C总线理论上最多能带2^7-1=127个从设备。

其中I2C的从器件地址(我只用到了7位的)的组成如下:

1byte = 7bit地址 + 1bit读写标志

注:1bit读写标志中,0-发送数据(写),1-请求数据(读)

有些时候一个总线上可能需要挂多个同一芯片,这样有些芯片还需要引出一个或几个引脚,由开发板设计电路来决定其具体地址,从而让不同芯片具有不同的7bit物理地址。如下图:

如果ADDR连VSS,则其7bit地址为0x44
如果ADDR连VDD,则其7bit地址为0x45


2- I2C通信时序

在12C总线上传送的每一位数据都由一个同步时钟脉冲相对应,即在SCL串行时钟的配合下,数据在SDA上从高位向低位依次串行传送每一位的数据。下面是12C通信的时序图:

(1)起始位

12C总线在空闲时SDA和SCL都处于高电平状态(由上拉电阻拉成高电平),当主设备要开始一次12C通信时就发送一个START(S)信号,这个起始位就可以告诉所有12C从机, “我”要开始进行12C通信了;当要结束一次12C通信时,则发送一个STOP§信号结束本次通信。

START(S):当SCL保持高电平时候,SDA出现下降沿,产生一个起始位,注意SCL一定要在高电平。
STOP( P ):当SCL保持高电平时候,SDA出现上升沿,产生一个停止位,注意SCL一定要在高电平。

(2)读写地址

主机在发送START信号之后,第2个时序应该立刻给出要通信的目标从机物理地址。此外,I2C总线是一种能够实现半双工通信的同步串行通信协议,站在主设备的角度来看应该具有读/写从设备的功能。

这时候12C的读写地址除了7bit物理地址以外,还有1bit用来标识读/写方向位。这样12C的从设备读写地址通常是一个字节,其中高7bit是上面描述的物理地址,最低位用来表示读写方向(0为写操作, 1为读操作)

(3)I2C应答信号

主机往12C总线上传输器件地址,所有的从机接收到这个地址后与自己的地址相比较若相同则发出一个应答ACK(Acknowledge)信号,主机收到这个应答信号后通讯连接建立成功,若未收到应答信号则表示寻址失败。

此外,主/从机在之后的数据通信中,数据接收方(可能是主机也可能是从机)收到传输的一个字节数据后,需要给出响应,此时处在第九个时钟,发送端释放SDA线控制权,将SDA电平拉高,由接收方控制。

  • 若希望继续,则给出“应答(ACK, Acknowledge)”信号,即SDA为低电平
  • 若不希望继续,则给出“非应答(NACK,Not Acknowledge) ”信号,即SDA为高电平

(4)数据位发送与接收

主机在收到从机的应答信号之后,开始给从机发送数据。SDA数据线上的每个字节必须是8位,每次传输的字节数量没有限制,每个字节发送完成之后,从机必须跟一个应答信号。

12C总线通信时数据位传输采用MSB(最高位优先)方式发送,其中高电平表示数据位1,低电平表示数据位0。
当传输的数据位需要改变时(如上一个位发送的是1,下一个位要发送0),必须发生在SCL为低电平期间。另外在传输过程中, SDA上的数据位在SCL高电平期间必须保持稳定不变。

假设SCL在高电平,想一下是不是就会触发起始位或者终止位。想一想起始信号与停止信号是怎么发送的就会明白为什么SCl一定要在高电平才能改变SDA。


3- I2C协议主机收发数据流程

(1)主机发送数据

  • 主机在检测到总线为“空闲状态”(即 SDA、SCL 线均为高电平)时,发送一个启动信号“S”,开始一次通信的开始;
  • 主机接着发送一个从设备地址,它由7bit物理地址和1bit读写控制位R/w组成(此时R/W=0);
  • 相对应的从机收到命令字节后向主机回馈应答信号 ACK(ACK=0);
  • 主机收到从机的应答信号后开始发送第一个字节的数据;
  • 从机收到数据后返回一个应答信号 ACK;
  • 主机收到应答信号后再发送下一个数据字节;
  • 当主机发送最后一个数据字节并收到从机的 ACK 后,通过向从机发送一个停止信号P结束本次通信并释放总线。从机收到P信号后也退出与主机之间的通信。

注意:

  • 主机通过发送地址码与对应的从机建立了通信关系,而挂接在总线上的其它从机虽然同时也收到了地址码,但因为与其自身的地址不相符合,因此提前退出与主机的通信;
  • 主机的一次发送通信,其发送的数据数量不受限制。主机是通过 P 信号通知发送的结束,从机收到 P 信号后退出本次通信;
  • 主机的每一次发送后都是通过从机的 ACK 信号了解从机的接收状况,如果应答错误则重发。

(2)主机接收数据

  • 主机发送起始信号后,接着发送地址字节(其中R/W=1);
  • 对应的从机收到地址字节后,返回一个应答信号并向主机发送数据;
  • 主机收到数据后向从机反馈一个应答信号ACK;
  • 从机收到应答信号后再向主机发送下一个数据;
  • 当主机完成接收数据后,向从机发送一个NAK,从机收到非应答信号后便停止发送;
  • 主机发送非应答信号后,再发送一个停止信号,释放总线结束通信。

注意:

  • 主机所接收数据的数量是由主机自身决定,当发送“非应答信号NAK”时从机便结束传送并释放总线。
  • 非应答信号的两个作用:前一个数据接收成功,停止从机的再次发送。

4- SHT30传感器介绍

(1)SHT30简介

SHT30数字温湿度传感器采用业内知名的瑞士Sensirion公司推出的新一代SHT30温湿度传感器芯片,它能够提供极高的可靠性和出色的长期稳定性,具有功耗低、反应快、抗干扰能力强等优点。IIC通讯,兼容3.3V/5V,可以非常容易的集成到智能楼宇、天气站、仓库存储、养殖、孵化等应用场景中,其中小米的温湿度传感器使用的也是SHT30。

  • 高精度,内部自动校准,数字输出
  • 低功耗、响应速度快、抗干扰能力强
  • 兼容3.3V/5V控制器

(2)SHT30工作原理

SHT30 芯片有八个引脚,利用12C进行数据传输,具有两个可选地址,宽电源电压从2.4V到5.5V。下面是引脚说明:

下面是小熊座NB-loT开发板上SHT30传感器的原理图,其中12C接口连到了MCU的PB13和PB14两个引脚上,这两个引脚可以设置为12C2模式工作。


5- HAL库中I2C发送接收数据函数

开始实现HAL库实现I2C对SHT30温湿度采样我们首先了解一下HAL库中的两个函数。

(1)HAL_I2C_Master_Transmit()

(1)函数原型:

HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);

(2)函数功能:IIC发送数据,主机需要将数据通过IIC发送过去
(3)参数介绍:

  • *hi2c 设置使用的是那个IIC
  • DevAddress 写入的地址,设置写入数据的地址
  • *pData 需要写入的数据
  • Size 要发送的字节数
  • Timeout 最大传输时间,超过传输时间将自动退出传输函数

(4) 使用到的函数参数讲解(举例子):

HAL_I2C_Master_Transmit(&hi2c2, SHT30_ADDR_WR, (uint8_t*)buf, 2, 0xFFFF);
  • &hi2c2:我们使用的是:hi2c2,传地址&hi2c2
  • SHT30_ADDR_WR:我们宏定义了写的地址,传写的地址c #define SHT30_ADDR_WR (SHT30_ADDR<<1)
  • (uint8_t*)buf:我们将需要传的数据保存在buf中
  • 2:传2个字节,16个位
  • 0xFFFF超时:oxFFFF(4 294 967 295也就是无符号整型所能表示的最大值)

(2)HAL_I2C_Master_Receive()

(1)函数原型:

HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);

(2)函数功能:IIC接收数据,从机发送给主机,主机需要将数据通过IIC接收
(3)函数参数和HAL_I2C_Master_Transmit()大概是一样的,只是取到的数据保存在pData中。
(4)使用到的函数参数讲解(举例子):

HAL_I2C_Master_Receive(&hi2c2, SHT30_ADDR_RD, buf, SHT30_DATA_SIZE, 0xFFFF);
  • &hi2c2:我们使用的是:hi2c2,传地址&hi2c2
  • SHT30_ADDR_RD:我们宏定义了读的地址,传读的地址c #define SHT30_ADDR_RD ((SHT30_ADDR<<1) | 0x01)
  • buf:我们将获取到的数据保存在buf中
  • SHT30_DATA_SIZE:宏定义,6个字节c #define SHT30_DATA_SIZE 6
  • 0xFFFF超时:oxFFFF(4 294 967 295也就是无符号整型所能表示的最大值)

6- SHT30温湿度采样

(1)配置

先配置SHT30连接的I2C管脚PB13 和PB14为I2C模式,此时因为I2C功能并没有使能,管脚状态为黄色。接下来再在Connectivity里选择12C2并配置为I2C模式,这是12C功能配置完成。配置好之后按Ctrl+S将会自动生成I2C总线初始化代码。

(2)创建sht30.c

/** sht30.c**  Created on: Nov 3, 2022*      Author: Administrator*/#include <stdio.h>
#include "stm32l4xx_hal.h"
#include "i2c.h"
#include "sht30.h"#define CONFIG_SHT30_DEBUG#ifdef CONGIF_SHT30_DEBUG
#define sht30_print(format, args...) printf(format, ##args)
#else
#define sht30_print(format, args...) do{} while(0)
#endifstatic int sht30_send_cmd(SHT30_CMD cmd)
{uint8_t buf[2];buf[0] = cmd >> 8;buf[1] = cmd & 0xFF;return HAL_I2C_Master_Transmit(&hi2c2, SHT30_ADDR_WR, (uint8_t*)buf, 2, 0xFFFF);
}static void sht30_soft_reset(void)
{sht30_send_cmd(SOFT_RESET_CMD);HAL_Delay(1);
}static int sht30_single_shot_measurement(uint8_t *buf, uint8_t buf_size)
{uint16_t cmd = HIGH_ENABLED_CMD;uint8_t  rv;if( !buf || buf_size < SHT30_DATA_SIZE ){sht30_print("%s(): Invalid input argument\n", __func__);return -1;}rv = sht30_send_cmd(cmd);if( rv ){sht30_print("ERROR: HST30 send messurement command failure, rv = &d\n", rv);sht30_soft_reset();return -2;}rv = HAL_I2C_Master_Receive(&hi2c2, SHT30_ADDR_RD, buf, SHT30_DATA_SIZE, 0xFFFF);if(rv){sht30_print("ERROR: SHT30 read measurement result failure, rv = %d\n", rv);return -3;}return 0;
}static uint8_t sht30_crc8(const uint8_t *data, int len)
{const uint8_t POLYNOMIAL = 0x31;uint8_t       crc = 0xFF;int           i,j;for (i=0; i<len; ++i){crc ^= *data++;for (j=0; j<8; j++){crc = (crc & 0x80)? (crc << 1) ^ POLYNOMIAL:(crc << 1);}}return crc;
}int SHT30_SampleData(float *temperature, float *humidity)
{uint8_t buf[SHT30_DATA_SIZE];int rv;uint16_t temp;uint16_t humd;uint16_t crc;if(!temperature || !humidity){sht30_print("%s(): Invalid input argument\n", __func__);return -1;}rv = sht30_single_shot_measurement(buf, SHT30_DATA_SIZE);if(rv){sht30_print("SHT30 Single Short measurement failure, rv=%d\n", rv);return -2;}#ifdef CONFIG_SHT30_DEBUG{int i;sht30_print("SHT30 get %d bytes sample data: \n", SHT30_DATA_SIZE);for(i=0; i<SHT30_DATA_SIZE; i++){sht30_print("0x%02x ", buf[i]);}sht30_print("\n");}
#endifcrc = sht30_crc8(buf, 2);sht30_print("SHT30 temperature Cal_CRC: [%02x] EXP_CRC: [%02x]\n", crc, buf[2]);if(crc != buf[2]){sht30_print("SHT30 measurement temperature got CRC error\n");return -3;}crc = sht30_crc8(&buf[3], 2);sht30_print("SHT30 humidity Cal_CRC: [%02x] EXP_CRC: [%02x]\n", crc, buf[5]);if(crc != buf[5]){sht30_print("SHT30 messurement temperature got CRC error\n");return -4;}temp = (buf[0]<<8) | buf[1];humd = (buf[3]<<8) | buf[4];*temperature = -45 + 175*((float)temp/65535);*humidity = 100 * ((float)humd/65535);return 0;
}

(3)创建sht30.h

/** sht30.h**  Created on: Nov 3, 2022*      Author: Administrator*/#ifndef INC_SHT30_H_
#define INC_SHT30_H_#include "stm32l4xx_hal.h"#define SHT30_ADDR 0x44#define SHT30_ADDR_WR (SHT30_ADDR<<1)
#define SHT30_ADDR_RD ((SHT30_ADDR<<1) | 0x01)#define SHT30_DATA_SIZE 6typedef enum
{SOFT_RESET_CMD = 0x30A2,HIGH_ENABLED_CMD = 0x2C06,MEDIUM_ENABLED_CMD =0x2C0D,LOW_ENSABLED_CMD = 0x2C10,HIGH_DISABLED_CMD = 0x2400,MEDIUM_DISABLED_CMD = 0X240B,LOW_DISABLED_CMD = 0x2416,HIGH_0_5_CMD = 0x2032,MEDIUM_0_5_CMD = 0x2024,LOW_0_5_CMD = 0x202F,HIGH_1_CMD = 0x2130,MEDIUM_1_CMD = 0x2126,LOW_1_CMD = 0x212D,HIGH_2_CMD = 0x2236,MEDIUM_2_CMD = 0x2220,LOW_2_CMD = 0x222B,HIGH_4_CMD = 0x2334,MEDIUM_4_CMD = 0x2322,LOW_4_CMD = 0x2329,HIGH_10_CMD = 0x2737,MEDIUM_10_CMD = 0x2721,LOW_10_CMD = 0x272A,
}SHT30_CMD;extern int SHT30_SampleData(float *temperature, float *humidity);#endif /* INC_SHT30_H_ */

(4)修改main.c

printf()函数实现的代码前期博客中有,就不多说了。

/* USER CODE BEGIN Includes */
#include "sht30.h"
#include "string.h"
/* USER CODE END Includes */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
static int report_tempRH_json(void);
/* USER CODE END 0 */
  printf ("The sht30 starts to obtain temperature and humidity data.\r\n");while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */report_tempRH_json();HAL_Delay(3000);}/* USER CODE END 3 */
/* USER CODE BEGIN 4 */
int report_tempRH_json(void)
{char      buf[128];float     temperature, humidity;if(SHT30_SampleData(&temperature, &humidity) < 0){printf("ERROR: SHT30 Sample data failure\n");return -1;}memset(buf, 0, sizeof(buf));snprintf(buf, sizeof(buf), "{\"Temperature\":\"%.2f\", \"Humidity\":\"%.2f\"}", temperature, humidity);HAL_UART_Transmit(&huart1, (uint8_t*)buf, strlen(buf), 0xFFFF);return 0;
}
/* USER CODE END 4 */

7- 结果呈现

对着开发板上的温湿度传感器哈气,就会看见变化,说明成功。


I2C协议介绍以及HAL库实现I2C对SHT30温湿度采样相关推荐

  1. IIC协议之SHT30温湿度采样

    IIC协议之SHT30温湿度采样 1.IIC协议介绍 1.1 协议简介 1.2 SHT30设备地址 2.协议时序 2.1 IIC通信时序图 2.2 起始信号 2.3 结束信号 2.4 应答信号 2.5 ...

  2. I2C协议详解 (Based Philips I2C spec)

    前言 Version 1.0 - 1992 此版本的 1992 I2C 总线规范包括以下修改: 省略了软件对从地址的编程. 这个功能的实现比较复杂,没有使用过. 省略了"低速模式" ...

  3. STM32V107VCT6,基于HAL库的用IO口模拟I2C的0.96寸OLED驱动

    前言 作为一个初学者,当然希望有比较好的范例可以学习,但在CSDN和众多家的单片机学习网站,没发现现成的I2C模拟接口的HAL库的0.96寸的OLED驱动,经过努力,把这个代码调试出来,为此,基于技术 ...

  4. 14-HAL库之I2C通信

    1.I2C协议介绍 I2C是一个支持多设备的总线,可连接多个I2C通讯设备,每个总线包括一条双向串行数据线(SDA)和一条串行时钟线(SCL).数据线用来表示数据,时钟线用来数据收发同步.通讯过程如下 ...

  5. I2C协议靠这16张图彻底搞懂(超详细)

    文章目录 背景 硬件层 数据传输协议 实际上如何工作? 单个主设备连接多个从机 多个主设备连接多个从机 如何编程? 总结 背景 I²C(Inter-Integrated Circuit),中文应该叫集 ...

  6. 通过KXTF9-2050芯片分析I2C协议

    1.I2C协议 参见博客:<I2C通信协议详解和通信流程分析>. 2.I2C驱动的框架分析 (1)驱动框架分为两层:物理层和协议层.物理层是通用的,取决于主设备:协议层则每个从设备都不同. ...

  7. I2C 协议抓包,学习,总结

    文章目录 I2C 调查 标准I2C协议介绍 I2C时序介绍 起始信号 停止信号 重启信号 总线闲信号 总线忙信号 读写信号 应答信号 非应答信号 标准I2C协议读写介绍 I2C写一个寄存器 I2C 读 ...

  8. 沧小海详解面试的必答题——I2C协议

    目录 第一部分:I2C协议的概述 第二部分:I2C协议的阐述 第三部分:AT24C04简述 第四部分:基于verilog的程序设计(暂无) 对于大多从事FPGA行业的应届生来说,在面试过程中很可能会被 ...

  9. 【08】STM32·HAL库开发-HAL库介绍 | STM32Cube固件库介绍 | HAL库框架结构 | 如何使用HAL库及使用注意事项

    目录 1.初识HAL库(了解) 1.1CMSIS简介 1.2HAL库简介 2.STM32Cube固件包浅析(了解) 2.1如何获取STM32Cube固件包? 2.2STM32Cube固件包文件夹简介 ...

最新文章

  1. 后端传输大量log数据_京东智联云MySQL读写分离最佳实践 ,轻松搞定数据库高性能扩展...
  2. 中考计算机应用技术有前途吗,被中考“淘汰”的职高生,未来的就业前景怎么样,你知道吗?...
  3. K8S报错:controller-manager Unhealthy Get http://127.0.0.1:10252/healthz: dial tcp 127.0.0.1:10252
  4. 多目标遗传优化算法nsga2求解复杂约束问题【python源码实现,实数编码】
  5. linux开端口不重启,Linux 防火墙开放端口(有时不生效可能是没有保存、重启导致)...
  6. JavaFX 中的像素、分辨率与缩放比
  7. 什么是迭代器,JS如何实现迭代器
  8. 买房为什么要少出首付,多贷款?
  9. 【FFMPEG源码终极解析】void av_packet_unref(AVPacket *pkt)
  10. php xml相关函数方法,php中对xml读取的相关函数的介绍一
  11. Linux下查看某个进程的网络带宽占用情况
  12. Python--网络编程-----传输层tcp/udp协议
  13. java读取与写入excel(.xls .xlsx)
  14. 如何将视频生成M3U8文件格式?
  15. UE4编辑器修改界面显示语言
  16. matlab改变图像像素吗,在图像中随机更改像素值程序——matlab
  17. 埃森哲:数字化转型新阶段,企业需要什么新能力
  18. 经纬财富:乌海黄金白银暂缓跌势
  19. 计算机信息技术课程论文,信息技术教学的论文范文
  20. 中国青年报:有多少网络论坛永远免费

热门文章

  1. eclipse介绍,环境配置,mysql安装
  2. 实验7.5 数据选择器及应用
  3. Java接收solr动态域_Spring Data Solr创建动态域报错:org.springframework.data.solr.UncategorizedSolrException...
  4. linux下电信拨号器
  5. electron学习
  6. 233网校java,二级计算机重点
  7. Android 实现屏幕的亮屏与锁屏
  8. activiti报错 cvc-datatype-valid.1.2.1: ‘‘ 不是 ‘NCName‘ 的有效值。
  9. Matlab/simulink仿真,直驱永磁风机并网chopper低电压穿越,版本matlab2018a
  10. 一个普通码农无法避免的时代碾压