[imx6ull]I2C协议-SHT20温湿度采样

文章目录

  • 一、I2C协议
    • 1.I2C物理层
    • 2.I2C协议层
      • ①起始位
      • ②I2C读写地址
      • ③I2C应答信号
      • ④数据位收发
      • ⑤总线速率
      • ⑥主机发送数据流程
      • ⑦主机接收数据流程
  • 二、I2C接口使能
  • 三、SHT20采样测试
    • 1.硬件连接
    • 2.测试代码
    • 3.Makefile
    • 4.运行测试


一、I2C协议

I2C Bus(IIC,Inter-Integrated Circuit Bus) 是由Philips公司(现被NXP 收购)推出的一种在电子通信控制领域常用的通信协议。它由时钟线(SCL)和数据线(SDA)两根线构成通信线路,利用上拉电阻将它们拉成高电平(表示总线空闲), 其典型的电压准位为+3.3V或+5V,具有电路简单、连接线少、控制简单、通信速率高等优点。

1.I2C物理层

[imx6ull]I2C协议-SHT20温湿度采样_第1张图片

  • I2C 总线是一种主从结构(Master/Slave)总线,I2C 总线上的每一个设备都可以作为主设备或者从设备,但一个总线上一般只有一个主设备,可以带多个从设备。
  • 主设备用来产生允许传输的时钟信号,并初始化总线的数据传输,所以主设备通常是CPU;而从设备只能被动响应主设备发起的通信请求,所以各种I2C 接口芯片将作为从设备使用。
  • 每个连接到总线的设备都有一个独立的设备地址,主机可以利用这个地址进行不同设备之间的访问。其中地址是一个七位或十位的数字。
  • 具有三种传输模式:标准模式传输速率为100kbit/s,快速模式为400kbit/s, 高速模式下可达 3.4Mbit/s,但目前大多I2C设备尚不支持高速模式。
  • 连接到相同总线的IC数量受到总线的最大电容400pF限制。

2.I2C协议层

在I2C总线上每一位数据都由一个同步时钟没冲所对应,也就是在SCL串行时钟的配合下,数据在SDA上从高位向低位一次串行传送每一位数据。I2C通信时序图如下:

[imx6ull]I2C协议-SHT20温湿度采样_第2张图片

①起始位

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

[imx6ull]I2C协议-SHT20温湿度采样_第3张图片

  • I2C 协议起始位:当 SCL 保持高电平时,SDA 出现一个下降沿,产生一个起始位;
  • I2C 协议停止位:当 SCL 保持高电平时,SDA 出现一个上升沿,产生一个停止位;

②I2C读写地址

[imx6ull]I2C协议-SHT20温湿度采样_第4张图片

首先在主机发送了起始信号后,第二个时序要给出通讯的目标从机的物理地址。并且I2C总线是一种能够实现半双工通信的同步串行通信协议,从主设备的角度来看具有读/写从设备的功能。

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

③I2C应答信号

当主机往I2C总线上传输器件的地址时,所有的从机接受到了这个地址后与自己的地址相比较,如果相同则发送一个应答ACK信号,不相同则不发送 应答信号,当主机收到了应答信号后表示建立连接成功,而未收到则表示寻址失败(可以在总线时序的第九个时钟时的电平高低进行判断)。

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

  • 若希望继续,则给出“应答(ACK, Acknowledge)”信号,即SDA 为低电平
  • 若不希望继续,则给出“非应答(NACK,Not Acknowledge)”信号,即 SDA 为高电平
    [imx6ull]I2C协议-SHT20温湿度采样_第5张图片

④数据位收发

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

I2C 总线通信时数据位传输采用MSB(最高位优先)方式发送,其中高电平表示数据位 1,低电平表示数据位 0。当传输的数据位需要改变时(如上一个位发送的是 1,下一个位要发送 0),必须发生在 SCL 为低电平期间。即规定所有设备在SCL高时候,进行数据的获取,SCL低时候,进行数据的变化。

[imx6ull]I2C协议-SHT20温湿度采样_第6张图片

⑤总线速率

I2C 总线是一种同步、半双工、采用电平信号收发的串行总线,其速率支持:

  • 标准模式(Standard-mode):速率高达 100kbit/s
  • 快速模式(Fast-mode):速率高达 400kbit/s
  • 快速模式+(Fast-mode Plus):速率高达 1Mbit/s。
  • 高速模式(High-speed mode):速率高达 3.4Mbit/s
  • 超快速模式(Ultra Fast-mode):速率高达 5Mbit/s (单向传输时支持)

⑥主机发送数据流程

  1. 主机在检测到总线为空闲时,发送一个启动信号“S”,开始一次通信的开始;

  2. 主机接着发送一个从设备地址,它由 7bit 物理地址和 1bit 读写控制位 R/W 组成(此时 R/W=0 写操作);

  3. 相对应的从机收到命令字节后向主机回馈应答信号 ACK(ACK=0);

  4. 主机收到从机的应答信号后开始发送第一个字节的数据;

  5. 从机收到数据后返回一个应答信号 ACK

  6. 主机收到应答信号后再发送下一个数据字节;

  7. 主机发完最后一个字节并收到 ACK 后,向从机发送一个停止信号 P 结束本次通信并释放总线;

  8. 从机收到 P 信号后也退出与主机之间的通信;

[imx6ull]I2C协议-SHT20温湿度采样_第7张图片

⑦主机接收数据流程

  1. 主机发送启动信号后,接着发送地址字节(其中 R/W=1 读操作);

  2. 对应的从机收到地址字节后,返回一个应答信号并向主机发送数据;

  3. 主机收到数据后向从机反馈一个应答信号 ACK

  4. 从机收到应答信号后再向主机发送下一个数据;

  5. 当主机完成接收数据后,向从机发送一个 NAK,从机收到非应答信号后便停止发送;

  6. 主机发送非应答信号后,再发送一个停止信号,释放总线结束通信;

[imx6ull]I2C协议-SHT20温湿度采样_第8张图片

二、I2C接口使能

I.MX6ull有四个I2C外设,但是我们的IGKBoard开发板只使能了两路I2C外设,如下:

I2C2 ---> 触摸屏控制器,rtc时钟,摄像头控制器
I2C1 ---> 40pin扩展口使用,由DToverlay进行开启
	GPIO01_IO03 ---> I2C1_SDA
	GPIO01_IO03 ---> I2C1_SCL

我们需要使能I2C1,所以我们需要和之前一样修改开发板的DTOverlay配置文件,添加管脚对I2C的支持,eMMC介质的boot分区下的config.txt的路径:/run/media/mmcblk1p1/config.txt 修改如下:

# Enable I2C overlay
dtoverlay_i2c1=yes

[imx6ull]I2C协议-SHT20温湿度采样_第9张图片

重启后可以在/dev下查看I2C的设备节点是否存在,或者我们可以查看/sys/bus/i2c/devices中设备文件来查看I2C驱动是否加载:

ls -l /dev/i2c-*

ls /sys/bus/i2c/devices/

[imx6ull]I2C协议-SHT20温湿度采样_第10张图片
当中由于驱动是从0开始编写的,所以当中i2c-0是对饮I2C1的设备节点,而还有1开头的链接文件是I2C2下的设备。

三、SHT20采样测试

1.硬件连接

2.测试代码

sht20.c

/*********************************************************************************
 *      Copyright:  (C) 2023 Deng Yonghao
 *                  All rights reserved.
 *
 *       Filename:  sht20.c
 *    Description:  This file is SHT20 get temperature and humidity code
 *                 
 *        Version:  1.0.0(2023年03月24日)
 *         Author:  Deng Yonghao 
 *      ChangeLog:  1, Release initial version on "2023年03月24日 20时52分18秒"
 *                 
 ********************************************************************************/

/*
 * Hardware connection:
 *
 *   Sht20 Module       RaspberryPi/IGKBoard
 *      VCC      <----->      3.3V
 *      SDA      <----->      SDA
 *      SCL      <----->      SCL
 *      GND      <----->      GND
 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SOFTRESET                        0xFE
#define TRIGGER_TEMPERATURE_NO_HOLD      0xF3
#define TRIGGER_HUMIDITY_NO_HOLD         0xF5

static inline void msleep(unsigned long ms);
static inline void dump_buf(const char *prompt, uint8_t *buf, int size);
int sht2x_init(char *i2c_dev);
int sht2x_get_serialnumber(int fd, uint8_t *serialnumber, int size);
int sht2x_get_temp_humidity(int fd, float *temp, float *rh);

int main(int argc, char **argv)
{
    int         fd;
    float       temp;
    float       rh;
    uint8_t     serialnumber[8];

    if( argc != 2)
    {
        printf("This program used to do I2C test by sht20 temperature & humidity module.\n");
        printf("Usage: %s /dev/i2c-x\n", argv[0]);
        return 0;
    }

    fd = sht2x_init(argv[1]);
    if(fd < 0)
    {
        printf("SHT2x initialize failure\n");
        return 1;
    }

    if( sht2x_get_serialnumber(fd, serialnumber, 8) < 0)
    {
        printf("SHT2x get serial number failure\n");
        return 3;
    }

    if( sht2x_get_temp_humidity(fd, &temp, &rh) < 0 )
    {
        printf("SHT2x get get temperature and relative humidity failure\n");
        return 3;
    }

    printf("Temperature=%lf ℃ relative humidity=%lf% \n", temp, rh);

    close(fd);
}

/* ms级休眠函数 */
static inline void msleep(unsigned long ms)
{
    struct timespec cSleep;
    unsigned long ulTmp;

    cSleep.tv_sec = ms / 1000;
    if (cSleep.tv_sec == 0)
    {
        ulTmp = ms * 10000;
        cSleep.tv_nsec = ulTmp * 100;
    }
    else
    {
        cSleep.tv_nsec = 0;
    }

    nanosleep(&cSleep, 0);
}

/* 打印 buf值*/
static inline void dump_buf(const char *prompt, uint8_t *buf, int size)
{
    int          i;

    if( !buf )
    {
        return ;
    }

    if( prompt )
    {
        printf("%s ", prompt);
    }

    for(i=0; i<size; i++)
    {
        printf("%02x ", buf[i]);
    }
    printf("\n");

    return ;
}

/* sht20命令复位 */
int sht2x_softreset(int fd)
{
    uint8_t           buf[4];

    if( fd<0 )
    {
        printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
        return -1;
    }

    /* software reset SHT2x */
    memset(buf, 0, sizeof(buf));

    /* 按照手册命令表进行命令发送 并休眠需要的时间 */
    buf[0] = SOFTRESET;
    write(fd, buf, 1);

    msleep(50);

    return 0;
}

/* 初始化sht20 设备新参为i2c总线节点 */
int sht2x_init(char *i2c_dev)
{
    int          fd;

    if( (fd=open(i2c_dev, O_RDWR)) < 0)
    {
        printf("i2c device open failed: %s\n", strerror(errno));
        return -1;
    }

    /* set I2C mode and SHT2x slave address */
    ioctl(fd, I2C_TENBIT, 0);    /* Not 10-bit but 7-bit mode */
    ioctl(fd, I2C_SLAVE, 0x40); /* set SHT2x slava address 0x40*/

    if( sht2x_softreset(fd) < 0 )
    {
        printf("SHT2x softreset failure\n");
        return -2;
    }

    return fd;
}

/* 获取sht20的温度湿度值 */
int sht2x_get_temp_humidity(int fd, float *temp, float *rh)
{
    uint8_t           buf[4];

    if( fd<0 || !temp || !rh )
    {
        printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
        return -1;
    }

    /* send trigger temperature measure command and read the data */
    memset(buf, 0, sizeof(buf));
    /* 发送非主机保持的温度测量命令 */
    buf[0]=TRIGGER_TEMPERATURE_NO_HOLD;
    write(fd, buf, 1);

    /* 等待需要的时间 数据手册可知 */
    msleep(85); /* datasheet: typ=66, max=85 */

    memset(buf, 0, sizeof(buf));
    /* 读取三个字节的数据,最后一个字节为crc检验值 */
    read(fd, buf, 3);
    dump_buf("Temperature sample data: ", buf, 3);
    /* 计算实际温度值*/
    *temp = 175.72 * (((((int) buf[0]) << 8) + (buf[1] & 0xfc)) / 65536.0) - 46.85;

    /* send trigger humidity measure command and read the data */
    memset(buf, 0, sizeof(buf));
    /* 发送非主机保持的湿度测量命令 */
    buf[0] = TRIGGER_HUMIDITY_NO_HOLD;
    write(fd, buf, 1);

    msleep(29); /* datasheet: typ=22, max=29 */
    memset(buf, 0, sizeof(buf));

    read(fd, buf, 3);
    dump_buf("Relative humidity sample data: ", buf, 3);
    /* 计算实际湿度值*/
    *rh = 125 * (((((int) buf[0]) << 8) + (buf[1] & 0xfc)) / 65536.0) - 6;

    return 0;
}

/* 获取sht20的唯一序列号 详情可以查看数据手册*/
int sht2x_get_serialnumber(int fd, uint8_t *serialnumber, int size)
{
    uint8_t           buf[4];

    if( fd<0 || !serialnumber || size!=8 )
    {
        printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
        return -1;
    }

    /* Read SerialNumber from Location 1 */
    memset(buf, 0, sizeof(buf));
    buf[0] = 0xfa;  /* command for readout on-chip memory */
    buf[1] = 0x0f;  /* on-chip memory address */
    write(fd, buf, 2);

    memset(buf, 0, sizeof(buf));
    read(fd, buf, 4);

    serialnumber[5]=buf[0]; /* Read SNB_3 */
    serialnumber[4]=buf[1]; /* Read SNB_2 */
    serialnumber[3]=buf[2]; /* Read SNB_1 */
    serialnumber[2]=buf[3]; /* Read SNB_0 */

    /* Read SerialNumber from Location 2 */
    memset(buf, 0, sizeof(buf) );
    buf[0]=0xfc;  /* command for readout on-chip memory */
    buf[1]=0xc9;  /* on-chip memory address */
    write(fd, buf, 2);

    memset(buf, 0, sizeof(buf) );
    read(fd, buf, 4);

    serialnumber[1]=buf[0]; /* Read SNC_1 */
    serialnumber[0]=buf[1]; /* Read SNC_0 */
    serialnumber[7]=buf[2]; /* Read SNA_1 */
    serialnumber[6]=buf[3]; /* Read SNA_0 */

    dump_buf("SHT2x Serial number: ", serialnumber, 8);

    return 0;
}

3.Makefile

CC=arm-linux-gnueabihf-gcc
APP_NAME=sht20

all:clean
    @${CC} ${APP_NAME}.c -o ${APP_NAME}

clean:
    @rm -f ${APP_NAME}

4.运行测试

下载交叉编译后的可执行文件,并赋予它可执行权限:

在这里插入图片描述

然后运行程序,并可以向sht20传感器哈一口气,可以见得温湿度变化:

[imx6ull]I2C协议-SHT20温湿度采样_第11张图片
测试成功。

你可能感兴趣的:(linux,arm开发)