如何在Linux应用层中使用i2c接口驱动外围芯片

目录

前言

1 detect内核中i2c模块

1.1 查看内核中I2C驱动

1.2 查看单个i2总线下设备地址

2 硬件电路

2.1 SHT-20模块电路

2.2 板卡上的i2c接口

3 开发环境

3.1 硬件系统参数

3.2 编译环境:Ubuntu

4 驱动程序实现

4.1 sht-20驱动代码

4.2 编写应用程序

4.3 编写Makefile 

5 验证

6 逻辑分析仪查看波形


前言

       本文主要介绍如何在应用层,调用内核中的I2C模块来驱动外围芯片,并且能正确的从这些芯片读取数据。文章以sht-20温湿度传感器为例,详细介绍I2C驱动接口的使用方法。

1 detect内核中i2c模块

1.1 查看内核中I2C驱动

进入/dev/目录下,可以看见,有两个驱动接口,i2c-0和i2c-1

如何在Linux应用层中使用i2c接口驱动外围芯片_第1张图片

可以使用i2c-tools工具,来查看每个i2c总线下设备的情况

1.2 查看单个i2总线下设备地址

1 )查看i2c-0下设备情况

使用命令:  i2cdetect -a 0

该i2c总线下有两个设备,其地址分别为:0x1a和0x1e

如何在Linux应用层中使用i2c接口驱动外围芯片_第2张图片

 2 ) 查看i2c-1下设备情况

使用命令: i2cdetect -a 1

该i2c总线下有两个设备,其地址分别为:0x40和0x50

地址0x40: 对应的设备是sht-20温湿度传感器

地址0x50:是AT24C02芯片

如何在Linux应用层中使用i2c接口驱动外围芯片_第3张图片

2 硬件电路

2.1 SHT-20模块电路

1)SHT20硬件接口图

如何在Linux应用层中使用i2c接口驱动外围芯片_第4张图片

2) sensor模块电路

如何在Linux应用层中使用i2c接口驱动外围芯片_第5张图片

2.2 板卡上的i2c接口

本文做测试时,使用的i2c接口位置

如何在Linux应用层中使用i2c接口驱动外围芯片_第6张图片

电路实物图如下:

如何在Linux应用层中使用i2c接口驱动外围芯片_第7张图片

SHT-20实物图:

如何在Linux应用层中使用i2c接口驱动外围芯片_第8张图片

3 开发环境

3.1 硬件系统参数

硬件: ATK-DL6Y2C开发板(芯片型号: IMX6ULL)

内核启动位置: eMMC

3.2 编译环境:Ubuntu

版本信息: 20.04.2

Linux version 5.15.0-84-generic (buildd@lcy02-amd64-005) 
(gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0, GNU ld (GNU Binutils for Ubuntu) 2.34) 
#93~20.04.1-Ubuntu SMP Wed Sep 6 16:15:40 UTC 2023

linux kernel 版本信息: 4.1.15

Linux内核: linux-imx-4.1.15-2.1.0-g3dc0a4b-v2.7.tar.bz2

交叉编译器版本信息: gcc version 4.9.4

4 驱动程序实现

4.1 sht-20驱动代码

       由于sht-20挂载在i2c-1上,所以,在驱动程序中直接调用i2c-1可操作sht-20的寄存器,其具体驱动代码如下:

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名     : drv_sht20.c
作者       : [email protected]
版本       : V1.0
描述       : sht-20 驱动程序
其他       : 无
日志       : 初版V1.0 2024/02/01

***************************************************************/
#include "drv-sht20.h"

/******************************************************************************
* LOCAL MACROS AND DEFINITIONS
******************************************************************************/
#define SHT2X_ADDR           0x40
#define POLY                 0x131          //P(x)=x^8+x^5+x^4+1 = 100110001

#define DELAY_CNT            4500            //for N32G45, sleep time is 6.4 ms 
#define DEV_FILE             "/dev/i2c-1"

#define QUERY_TEMP_CMD       TRIG_T_MEASUREMENT_POLL
#define QUERY_HUMIDY_CMD     TRIG_RH_MEASUREMENT_POLL


static int public_fd;


static void msleep(unsigned int time)
{
    struct timespec sleeper, temp;

    sleeper.tv_sec = (time_t)(time/1000);
    sleeper.tv_nsec = (long)(time%1000)*1000000;
    nanosleep(&sleeper, &temp);
}

static int sht2xdrv_CheckCrc(unsigned char data[], unsigned char nbrOfBytes, unsigned char checksum)
{
    int set = 0;
    unsigned char crc = 0;
    unsigned char byteCtr;
    unsigned char bit;
    
    //calculates 8-Bit checksum with given polynomial
    for (byteCtr = 0; byteCtr < nbrOfBytes; ++byteCtr)
    {
        crc ^= (data[byteCtr]);
        for ( bit = 8; bit > 0; --bit)
        {
            if (crc & 0x80)
            {
                crc = (crc << 1) ^ POLY;
            }
            else
            {
                crc = (crc << 1);
            }
        }
    }

    if (crc != checksum)
    {
        set = -1;
    }

    return set;
}

static int sht2xdrv_CalcTemperatureC(unsigned short u16sT)
{
    int temperatureC;       // variable for result

    u16sT &= ~0x0003;           // clear bits [1..0] (status bits)

    /*
    * Formula T = -46.85 + 175.72 * ST / 2^16 from data sheet 6.2,
    * optimized for integer fixed point (3 digits) arithmetic
    */

    temperatureC = ((17572 * u16sT) >> 16) - 4685;

    return temperatureC;
}

static int sht2xdrv_CalcRH(unsigned short u16sRH)
{
    int humidityRH;       // variable for result

    u16sRH &= ~0x0003;          // clear bits [1..0] (status bits)

    /*
    * Formula RH = -6 + 125 * SRH / 2^16 from data sheet 6.1,
    * optimized for integer fixed point (3 digits) arithmetic
    */

    humidityRH = ((12500 * u16sRH) >> 16) - 600;

    return humidityRH;
}

static int sht2x_send_cmd_wr( unsigned cmd)
{
    int              ret = -1;
    unsigned char    buf[2] = {0};

    switch (cmd)
    {
        case QUERY_TEMP_CMD:
            buf[0] = QUERY_TEMP_CMD;
            ret = write(public_fd, buf, 1);
            if( ret < 0 )
            {
                printf("write temper cmd to sht2x failure.\n");
                return -1;
            }
            msleep(85);  //datasheet typ=66, max=85
            break;

        case QUERY_HUMIDY_CMD:
            buf[0] = QUERY_HUMIDY_CMD;
            ret = write(public_fd, buf, 1);
            if( ret < 0 )
            {
                printf("write humidity cmd to sht2x failure.\n");
                return -1;
            }
            msleep(29);  //datasheet typ=22, max=29
            break;
        default:
            return -1;
    }

    return 0;
}


int sht2x_init(void)
{
    int fd = -1;
    
    fd = open(DEV_FILE, O_RDWR);
    if( fd < 0 )
    {
        close( fd );
        printf("%s %s i2c device open failure: %s\n", __FILE__, __FUNCTION__, strerror(errno));
        return -1;
    }

    ioctl(fd, I2C_TENBIT, 0);
    ioctl(fd, I2C_SLAVE, SHT2X_ADDR);
    public_fd = fd;

    return fd;
}

int sht2x_softreset( void )
{
    int           ret = -1;
    unsigned char buf[2] = {0};
    unsigned char cmd = SOFT_RESET;
    
    buf[0] = cmd;
    ret = write(public_fd, buf, 1);
    if( ret < 0 )
    {
        printf("%s %s write softrest cmd to sht2x failure \n", __FILE__, __FUNCTION__);
        return -1;
    }

    msleep(50);

    return 0;
}

int sht2x_get_tempeture( float *temper)
{
    unsigned char   buf[4] = {0}, checksum;
    int             ret = -1;
    int tempval;

    ret = sht2x_send_cmd_wr( QUERY_TEMP_CMD);
    if( ret < 0 )
    {
        return -1;
    }

    ret = read(public_fd, buf, 3);
    if( ret < 0 )
    {
        printf("get the temper failure.\n");
        return -1;
    }

    // match crc here
    checksum = buf[2];
    ret = sht2xdrv_CheckCrc(buf, 2, checksum);
    if( ret < 0 )
    {
        printf("match check sum error.\n");
        return -1;
    }

    tempval = (buf[0] << 8) | buf[1];
    *temper = sht2xdrv_CalcTemperatureC( tempval );
  
    return 0;
}

int sht2x_get_humidy( float *humidy )
{
    unsigned char   buf[4] = {0}, checksum;
    int             ret = -1;
    int tempval;

    ret = sht2x_send_cmd_wr( QUERY_HUMIDY_CMD );
    if( ret < 0 )
    {
        return -1;
    }

    ret = read(public_fd, buf, 3);
    if( ret < 0 )
    {
        printf("get the humidy failure.\n");
        return -1;
    }

    // match crc here
    checksum = buf[2];
    ret = sht2xdrv_CheckCrc(buf, 2, checksum);
    if( ret < 0 )
    {
        printf("match check sum error.\n");
        return -1;
    }

    tempval = (buf[0] << 8) | buf[1];
    *humidy = sht2xdrv_CalcRH( tempval );
  
    return 0;
}

int sht2x_open( void )
{
    int set;

    set = sht2x_init();
    if( set < 0 ){
        return -1;
    }

    set = sht2x_softreset();
    if( set < 0 ){
        return -1;
    }  

    return 0;
} 


int sht2x_release( void )
{
    close( public_fd );

    return 0;
}

/** End of this file */

头文件部分代码:

#ifndef __DRV_SHT20_H
#define __DRV_SHT20_H

#ifdef __cplusplus
extern "C" {
#endif

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

typedef enum sht2xOptErrorCode
{
    SHT2x_RES_I2C_ERR = 0X01,
}sht2xOptErrorCode;

/******************************************************************************
* EXPORTED MACROS AND DEFINITIONS
******************************************************************************/ 
typedef enum sht2xResolution_t
{
  SHT2x_RES_12_14BIT       = 0x00, // RH=12bit, T=14bit
  SHT2x_RES_8_12BIT        = 0x01, // RH= 8bit, T=12bit
  SHT2x_RES_10_13BIT       = 0x80, // RH=10bit, T=13bit
  SHT2x_RES_11_11BIT       = 0x81, // RH=11bit, T=11bit
  SHT2x_RES_MASK           = 0x81  // Mask for res. bits (7,0) in user reg.
} sht2xResolution_t;

typedef enum sht2xStatusCode {
  SHT2x_STATUS_OK                = 0x00,
  SHT2x_STATUS_VALID_DATA        = 0x01,
  SHT2x_STATUS_NO_CHANGE         = 0x02,
  SHT2x_STATUS_ABORTED           = 0x03,
  SHT2x_STATUS_BUSY              = 0x04,
  SHT2x_STATUS_SUSPEND           = 0x05,
  SHT2x_STATUS_ERR_IO            = 0x06,
  SHT2x_STATUS_ERR_BAD_DATA      = 0x07,
  SHT2x_STATUS_ERR_TIMEOUT       = 0x08
}sht2xStatusCode;

// sensor command
typedef enum{
  TRIG_T_MEASUREMENT_HM    = 0xE3, // command trig. temp meas. hold master
  TRIG_RH_MEASUREMENT_HM   = 0xE5, // command trig. humidity meas. hold master
  TRIG_T_MEASUREMENT_POLL  = 0xF3, // command trig. temp meas. no hold master
  TRIG_RH_MEASUREMENT_POLL = 0xF5, // command trig. humidity meas. no hold master
  USER_REG_W               = 0xE6, // command writing user register
  USER_REG_R               = 0xE7, // command reading user register
  SOFT_RESET               = 0xFE  // command soft reset
}sht2xCommand_t;

typedef enum {
  SHT2x_EOB_ON             = 0x40, // end of battery
  SHT2x_EOB_MASK           = 0x40, // Mask for EOB bit(6) in user reg.
} sht2xEob_t;

typedef enum {
  SHT2x_HEATER_ON          = 0x04, // heater on
  SHT2x_HEATER_OFF         = 0x00, // heater off
  SHT2x_HEATER_MASK        = 0x04, // Mask for Heater bit(2) in user reg.
} etSHT2xHeater;

// measurement signal selection
typedef enum{
  HUMIDITY,
  TEMP
}etSHT2xMeasureType;

typedef enum{
  I2C_ADR_W                = 128,   // sensor I2C address + write bit
  I2C_ADR_R                = 129    // sensor I2C address + read bit
}etI2cHeader;


typedef struct {
    unsigned char _step;
    unsigned char ret;
    
    unsigned char finish;     //1: finished, 0: idle
    unsigned char dataValid;  //1: valid, 0: invalid
    
    int _tryCnt;
    int _binValue;   // primordial value from sht20 register 
    int outValue;    // true temperature or humidity
} shtdrv;

typedef struct {
    shtdrv st_Temp;
    shtdrv st_RH;
    int errorCode;
}shtOpt;


int sht2x_open( void );

int sht2x_get_humidy( float *humidy );

int sht2x_get_tempeture( float *temper);

int sht2x_release( void );


#ifdef __cplusplus
}
#endif

#endif /* __DRV_SHT20_H */

4.2 编写应用程序

该程序主要调用sht-20驱动程序中的接口,读取温湿度值。

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名     : test_sht20.c
作者       : [email protected]
版本       : V1.0
描述       : 验证dev_sht20.c 
其他       : 无
日志       : 初版V1.0 2024/02/01

***************************************************************/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "drv-sht20.h"

int main(void)
{
    float temper, rh;
    int count_run = 100;
    int set;

    set = sht2x_open();
    if( set < 0){
        printf("initial sht20 failure.\n");
        return -1;
    }

    while( count_run > 0){
        set = sht2x_get_tempeture( &temper );
        if(set > -1)
        {
            printf( "TM(C):  %.2f ", temper*0.01);
        }

        set = sht2x_get_humidy( &rh );
        if(set > -1)
        {
               printf("HM(\%): %.2f \r\n", rh*0.01);
        }
        count_run--;
    }

    sht2x_release();

    return 0;
}

4.3 编写Makefile 

CFLAGS= -Wall -O2
CC=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc
STRIP=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip

test-sht20: test-sht20.o drv-sht20.o
	$(CC) $(CFLAGS) -o test-sht20 test-sht20.o drv-sht20.o
	$(STRIP) -s test-sht20

clean:
	rm -f test-sht20 test-sht20.o drv-sht20.o

5 验证

1)编译测试代码,然后将其copy值NFS的挂载目录下,方便在板卡中运行该程序

2)在板卡中运行测试程序。在测试过程中,为了保证数据能有变化,可以对着sensor哈气,可以看见温度和湿度的值有明显的变化。说明驱动程序能够正常的工作。

如何在Linux应用层中使用i2c接口驱动外围芯片_第9张图片

6 逻辑分析仪查看波形

如何在Linux应用层中使用i2c接口驱动外围芯片_第10张图片

1)抓取一段i2c运行的波形图:

如何在Linux应用层中使用i2c接口驱动外围芯片_第11张图片

2) 详细波形图:

如何在Linux应用层中使用i2c接口驱动外围芯片_第12张图片

3)逻辑分析仪捕捉到的数据

如何在Linux应用层中使用i2c接口驱动外围芯片_第13张图片

你可能感兴趣的:(linux,驱动开发,芯片驱动分析,linux,驱动开发)