Bootstrap

[linux驱动开发] 基于linux的i2c总线的sht20驱动开发笔记

linux的i2c子系统

  • linux构建的2c驱动框架十分庞大,完全讲透驱动框架,需要代码和理论的深入理解,在此不过多展开,很多博客讲解十分透彻。
  • 记住四大块i2c的总线、设备、驱动、适配器,总线和适配器是厂家和内核提供好的,设备和驱动是由开发者进行编写的。
  • 设备 - 由设备树中信息过度而来,最终驱动之中以struct i2c_client数据结构体的方式呈现
  • 驱动 - 和platform驱动类似,提供probe钩子函数进行开发等常规操作。
  • 下面就可以就开始进行sht20 i2c驱动开发。

设备树

  • 在自己构建的设备树或者官方设备树文件中i2c1添加sht20设备节点,前提内核开启了i2c驱动支持
  • 追加的代码如下
&i2c1 {
	clock-frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c1>;
	status = "okay";

    sht20-i2c@40 {
        compatible = "ssr,sht20";
        reg = <0x40>;
    };
};
  • 然后加载设备树后,查看开发板的/sys下是否有i2c设备添加
    在这里插入图片描述

驱动代码部分

驱动描述

  • 该部分和platform驱动十分类似,compatible关键字进行匹配
  • 代码如下:
//设备数匹配列表
static const struct of_device_id of_sht20_match [] = {
    {.compatible="ssr,sht20"},
    {}
};
MODULE_DEVICE_TABLE(of, of_sht20_match);

static struct i2c_driver sht20_drv = {
    .probe = sht20_probe,
    .remove = sht20_remove,
    .driver = {
        .name = "sht20 driver 0.1", //name 不重要
        .owner = THIS_MODULE,
        .of_match_table = of_sht20_match,
    },
    .id_table = sht20_id,
};

probe函数

  • 驱动安装后,与设备进行匹配后执行的函数
  • 依然是常规操作,不过引入一个sysfs调试方法,步骤如下
    1、设备号创建
    2、注册字符设备
    3、创建类
    4、创建设备
    5、创建sys属性
    6、保存私有数据
  • 代码如下:
/* @description: i2c驱动probe函数,设备和驱动匹配后执行
 *
 * @parm  : client - i2c设备
 * @parm  : id - i2c设备ID
 * @return: 0 successfully , !0 failure
 */
static int sht20_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    struct sht20_priv *priv = NULL;
    //struct device *dev;
    dev_t devno; //设备号
    int rv = 0;

    //0.给priv分配空间
    priv = devm_kzalloc(&client->dev, sizeof(struct sht20_priv), GFP_KERNEL);
    if(!priv)
    {
        return -ENOMEM;
    }

    //1.创建设备号
    if(0 != dev_major)
    {
        devno = MKDEV(dev_major, 0);
        rv = register_chrdev_region(devno, 1, DEV_NAME); //静态创建
    }
    else
    {
        rv = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);//动态创建
        dev_major = MAJOR(devno);//获主设备号
    }
    
    if(rv < 0)
    {
        dev_err(&client->dev, "%s driver can't get major %d\n", DEV_NAME, dev_major);
        return rv;
    }

    //2.注册字符设备
    cdev_init(&priv->cdev, &sht20_fops); //初始化cdev
    priv->cdev.owner = THIS_MODULE;

    rv = cdev_add(&priv->cdev, devno, 1);
    if(0 != rv)
    {
        dev_err(&client->dev, "error %d add %s device failure.\n", rv, DEV_NAME);
        goto undo_major;
    }

    //3.创建类,驱动进行节点创建
    priv->dev_class = class_create(THIS_MODULE, DEV_NAME);
    if(IS_ERR(priv->dev_class))
    {
        dev_err(&client->dev, "%s driver create class failure.\n", DEV_NAME);
        rv = -ENOMEM;
        goto undo_cdev;
    }

    //4.创建设备 
    priv->dev = device_create(priv->dev_class, NULL, devno, NULL, DEV_NAME);
    if(IS_ERR(priv->dev))
    {
        rv = -ENOMEM;
        goto undo_class;
    }

    //5. 创建sys 属性 在platform下
    if(device_create_file(priv->dev, &dev_attr_sht20_temp))
    {
        rv = -ENOMEM;
        goto undo_device;
    }

    //6. 保存私有数据
    priv->client = client;
    i2c_set_clientdata(client, priv);
    dev_set_drvdata(priv->dev, priv);

    dev_info(&client->dev, "sht20 i2c driver probe okay.\n");
    return 0;

undo_device:
    device_destroy(priv->dev_class, devno);

undo_class:
    class_destroy(priv->dev_class);

undo_cdev:
    cdev_del(&priv->cdev);

undo_major:
    unregister_chrdev_region(devno, 1);

    devm_kfree(&client->dev, priv);
    return rv;
}

fops操作

open函数

  • 进行私有设备数据地址的查找
  • 进行sht20的初始化
/* @description: sht20 打开设备 
 *
 * @parm  : inode - 传递给驱动的inode
 * @parm  : filp - 设备文件,利用其私有数据成员
 * @return: 0 successfully , !0 failure
 */
static int sht20_open(struct inode *inode, struct file *filp)
{
    struct sht20_priv *priv = container_of(inode->i_cdev, struct sht20_priv, cdev);  
    int rv;

    //初始化sht20 
    rv = sht20_init(priv->client);
    if(rv < 0)
    {
        dev_err(priv->dev, "sht20 init failure.\n");
    }
    //dev_info(priv->dev, "sht20 init successfully.\n");

    filp->private_data = priv;

    return 0;
}

read接口

  • 准备工作,把读取温湿度的功能进行函数封装,在功能函数中调用适配器提供的传输算法实现底层的接口
  • 具体传输的数据根据数据手册来,相应的命令和收到数据的规格,都可以由数据手册查询得到
  • 主要将获取的温湿度数据进行倍数放大后,传递给用户buf中
  • 代码如下
/* @description: 从设备读取文件
 *
 * @parm  : filp - 设备文件,文件描述符
 * @parm  : buf - 返回给用户空间的数据缓冲区
 * @parm  : cnt - 要读取的数据长度
 * @parm  : offt - 相对于文件首地址的偏移
 * @return: 读取的字节数,负数 - 读取失败
 */
static ssize_t sht20_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
    int rv = 0;
    //struct i2c_client *client = filp->private_data;
    struct sht20_priv *priv = filp->private_data;
    unsigned char data[4] = {0,};

    if(!priv->client)
    {
        printk("failure to get i2c_client.\n");
        return -EFAULT;
    }

    rv = read_temperature(priv->client, data);
    if(rv)
    {
        dev_err(priv->dev, "read_temperature failure.\n");
    }

    data[3] = data[1];
    data[2] = data[0];

    rv = read_humidity(priv->client, data);
    if(rv)
    {
        dev_err(priv->dev, "read_humidity failure.\n");
    }
    
    //printk("test %x %x %x %X \n", data[3], data[2], data[1], data[0]);
    rv = copy_to_user(buf, data, sizeof(data));
    if(rv)
    {
        dev_err(priv->dev, "copy to user error.\n");
        return -EFAULT;
    }

    return sizeof(data);
}

sysfs功能

  • 作为调试技术的一部分,把数据直接通过sysfs文件系统,呈现给用户空间,加速调试
  • 使用步骤如下
    1、属性定义
    2、编写属性读写回调函数
    3、创建文件并与属性结合
  • 本文使用简单创建方式,单属性创建,使用设备提供的文件创建api
  • 代码如下
static ssize_t sht20_temp_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct sht20_priv *priv = dev_get_drvdata(dev);
    int rv = 0;
    unsigned char data[4] = {0,};

    if(!priv->client)
    {
        printk("failure to get i2c_client.\n");
        return -EFAULT;
    }

    rv = read_temperature(priv->client, data);
    if(rv)
    {
        dev_err(priv->dev, "read_temperature failure.\n");
    }

    data[3] = data[1];
    data[2] = data[0];

    rv = read_humidity(priv->client, data);
    if(rv)
    {
        dev_err(priv->dev, "read_humidity failure.\n");
    }
    
    //printk("test %x %x %x %X \n", data[3], data[2], data[1], data[0]);

    return sprintf(buf, "temperature=%d humidity=%d\n", ((data[3] << 8) | data[2]), ((data[1] << 8) | data[0]) );//1000倍
}

static ssize_t sht20_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    char k_buf[10] = {0,};
    snprintf(k_buf, sizeof(k_buf), "%s", buf);

    dev_info(dev, "Don't echo to me  -> [%s] size [%d]\n", k_buf, count);

    return count;
}

static DEVICE_ATTR(sht20_temp, 0644, sht20_temp_show, sht20_temp_store);
  • 创建过程在probe之中
  • 写属性的接口函数,由于不是主要功能,只做了数据回显功能
  • 读属性中进行温湿度的采样

驱动完整代码

完整代码如下

/*********************************************************************************
 *      Copyright:  (C) 2022 weihh
 *                  All rights reserved.
 *
 *       Filename:  sht20_i2c_drv.c
 *    Description:  This file is sht20 driver code.
 *                 
 *        Version:  1.0.0(2022年04月17日)
 *         Author:  Wei Huihong <[email protected]>
 *      ChangeLog:  1, Release initial version on "2022年04月17日 21时34分06秒"
 *                 
 ********************************************************************************/
#include <linux/module.h>       //所有模块都需要的头文件
#include <linux/init.h>         // init和exit相关宏
#include <linux/kernel.h>       // printk(),内核打印函数
#include <linux/delay.h>        // 延时函数头文件
#include <linux/device.h>       // 用于设备创建的函数头文件
#include <linux/fs.h>           //和fops相关的头文件
#include <linux/cdev.h>         //字符设备 初始化相关
#include <linux/version.h>
#include <linux/ide.h>
#include <linux/gpio.h>         //gpio子系统头文件
#include <linux/of_gpio.h>      //gpio子系统和设备树相关
#include <linux/platform_device.h>  //platform总线设备相关
#include <linux/err.h>          //错误码相关
#include <linux/timer.h>        //定时器相关
#include <linux/i2c.h>          //i2c子系统相关

#define DEV_NAME                        "sht20"  //设备名
#define SHT20_SOFERESET                 0xFE    // 软复位
#define SHT20_TEMPERATURE_NO_HOLD_CMD   0xF3    // 无主机模式触发温度测量
#define SHT20_HUMIDITY_NO_HOLD_CMD      0xF5    // 无主机模式触发湿度测量
#define SHT20_TEMPERATURE_HOLD_CMD      0xE3    // 主机模式触发温度测量
#define SHT20_HUMIDITY_HOLD_CMD         0xE5    // 主机模式触发湿度测量

#define CRC_MODEL                   0x131
#define CRC_SUCCESS                 0
#define CRC_FAIL                    1

#ifndef DEV_MAJOR
#define DEV_MAJOR       0
#endif

static int dev_major = DEV_MAJOR;   //主机号

struct sht20_priv {
    struct cdev         cdev;
    struct class        *dev_class;
    struct i2c_client   *client;
    struct device       *dev;
};

/* @description: 初始化sht20
 *
 * @parm  : client - i2c 设备
 * @parm  : 
 * @return: 0 successfully , !0 failure
 */
static int sht20_init(struct i2c_client *client)
{    
    int rv;
    char data = SHT20_SOFERESET;
    
    rv = i2c_master_send(client, &data, 1);
    if(rv < 0)
    {
        dev_err(&client->dev, "i2c send init cmd failure.\n");
        return -1;
    }

    msleep(50);
    return 0;
}

static int crc_check(unsigned char *data, int len, unsigned char checksum)
{
    unsigned char   crc = 0x00; 
    int             i, j;  
 
    for(i=0; i<len; i++)
    {
        crc ^= *data++;  		   
		
        for (j=0; j<8; j++)     
        { 
            if (crc & 0x80)
			{
				crc = (crc << 1) ^ CRC_MODEL;
			}    
            else
            {
                crc = (crc << 1);
            }
        }
    }
    // printk("crc clu data : [%x]\n", crc);
 
    if(checksum == crc)
	{
        return CRC_SUCCESS;
	}
	else 
    {
        return CRC_FAIL;
    }
}

/* @description: 读取sht20 的温度数据 
 *
 * @parm  : client - i2c 设备
 * @parm  : buf - 存储读取的数据
 * @return: 0 successfully , !0 failure
 */
static int read_temperature(struct i2c_client *client, unsigned char *buf)
{
    int rv = 0;
    int temperature = 0;

    unsigned char tmp[3] = {0};
    char data = SHT20_TEMPERATURE_HOLD_CMD;

    //形参判断
    if(!client || !buf)
    {
        printk("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
        return -1;
    }

    //发送CMD
    rv = i2c_master_send(client, &data, 1);
    //rv = i2c_smbus_write_byte(client, SHT20_TEMPERATURE_NO_HOLD_CMD);
    if(rv < 0)
    {
        dev_err(&client->dev, "i2c send tmper cmd failure.\n");
        return -1;
    }

    //delay 85ms
    msleep(85);

    //读取数据
    rv = i2c_master_recv(client, tmp, sizeof(tmp));
    if(rv < 0)
    {
        dev_err(&client->dev, "i2c recv tmper data failure.\n");
        return -1; 
    }
    
    // printk("read temperature: tmp[0] %x tmp[1] %x ; crc : tmp[2] %x\n", tmp[0], tmp[1], tmp[2]); //验证 crc校验结果
    //数据处理
    temperature = (tmp[0] << 8) | (tmp[1]&0xFC);
    temperature = ((temperature * 175720) >> 16) - 46850;

    //printk("temperature : %d\n", temperature);
    //TODO: 可以加上CRC校验
    if(0 != crc_check(tmp, 2, tmp[2]))
    {
        dev_err(&client->dev, "tmperature data fails to pass cyclic redundancy check\n");
        return -1;
    }

    buf[0] = temperature & 0xFF;
    buf[1] = (temperature >> 8) & 0xFF;

    return 0;
}

/* @description: 读取sht20 的相对数据数据
 *
 * @parm  : client - i2c 设备/客户端
 * @parm  : buf - 存储读取的数据
 * @return: 0 successfully , !0 failure
 */
static int read_humidity(struct i2c_client *client, unsigned char *buf)
{
    int rv = 0;
    int humidity = 0;

    unsigned char tmp[3] = {0};
    char data = SHT20_HUMIDITY_HOLD_CMD;

    //形参判断
    if(!client || !buf)
    {
        printk("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
        return -1;
    }

    //发送CMD
    rv = i2c_master_send(client, &data, 1);
    //rv = i2c_smbus_write_byte(client, SHT20_HUMIDITY_NO_HOLD_CMD);
    if(rv < 0)
    {
        dev_err(&client->dev, "i2c send humidity cmd failure.\n");
        return -1;
    }

    //delay 29ms
    msleep(29);

    //读取数据
    rv = i2c_master_recv(client, tmp, sizeof(tmp));
    if(rv < 0)
    {
        dev_err(&client->dev, "i2c recv humidity data failure.\n");
        return -1; 
    }

    // printk("read humidity: tmp[0] %x tmp[1] %x ; crc : tmp[2] %x\n", tmp[0], tmp[1], tmp[2]);

    //数据处理
    humidity = (tmp[0] << 8) | (tmp[1]&0xFC);
    humidity = ((humidity * 125000) >> 16) - 6000;

    //crc-8 校验
    if(0 != crc_check(tmp, 2, tmp[2]))
    {
        dev_err(&client->dev, "tmperature data fails to pass cyclic redundancy check\n");
        return -1;
    }

    buf[0] = humidity & 0xFF;
    buf[1] = (humidity >> 8) & 0xFF;

    return 0;
}


/* @description: sht20 打开设备 
 *
 * @parm  : inode - 传递给驱动的inode
 * @parm  : filp - 设备文件,利用其私有数据成员
 * @return: 0 successfully , !0 failure
 */
static int sht20_open(struct inode *inode, struct file *filp)
{
    struct sht20_priv *priv = container_of(inode->i_cdev, struct sht20_priv, cdev);  
    int rv;

    //初始化sht20 
    rv = sht20_init(priv->client);
    if(rv < 0)
    {
        dev_err(priv->dev, "sht20 init failure.\n");
    }
    //dev_info(priv->dev, "sht20 init successfully.\n");

    filp->private_data = priv;

    return 0;
}

/* @description: 从设备读取文件
 *
 * @parm  : filp - 设备文件,文件描述符
 * @parm  : buf - 返回给用户空间的数据缓冲区
 * @parm  : cnt - 要读取的数据长度
 * @parm  : offt - 相对于文件首地址的偏移
 * @return: 读取的字节数,负数 - 读取失败
 */
static ssize_t sht20_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
    int rv = 0;
    //struct i2c_client *client = filp->private_data;
    struct sht20_priv *priv = filp->private_data;
    unsigned char data[4] = {0,};

    if(!priv->client)
    {
        printk("failure to get i2c_client.\n");
        return -EFAULT;
    }

    rv = read_temperature(priv->client, data);
    if(rv)
    {
        dev_err(priv->dev, "read_temperature failure.\n");
    }

    data[3] = data[1];
    data[2] = data[0];

    rv = read_humidity(priv->client, data);
    if(rv)
    {
        dev_err(priv->dev, "read_humidity failure.\n");
    }
    
    //printk("test %x %x %x %X \n", data[3], data[2], data[1], data[0]);
    rv = copy_to_user(buf, data, sizeof(data));
    if(rv)
    {
        dev_err(priv->dev, "copy to user error.\n");
        return -EFAULT;
    }

    return sizeof(data);
}

/* @description: 关闭设备
 *
 * @parm  : inode - 传递给驱动的inode
 * @parm  : filp - 设备文件,file结构体有个私有数据区可以使用
 * @return: 0 successfully , !0 failure
 */
static int sht20_release(struct inode *inode, struct file *filp)
{
    return 0;
}

//设备操作函数
static struct file_operations sht20_fops = {
    .owner = THIS_MODULE,
    .open  = sht20_open,
    .read  = sht20_read,
    .release = sht20_release,
};

/* @description: sysfs - 温度属性显示函数
 *
 * @parm  : dev - 设备指针,创建file时候会指定dev
 * @parm  : attr - 设备属性,创建时候传入
 * @parm  : buf - 传出给sysfs中显示的buf
 * @return: 显示的字节数
 * @TODO: 函数不够正规,了解PAGE_SIZE
 */
static ssize_t sht20_temp_humi_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct sht20_priv *priv = dev_get_drvdata(dev);
    int rv = 0;
    unsigned char data[4] = {0,};

    if(!priv->client)
    {
        printk("failure to get i2c_client.\n");
        return -EFAULT;
    }

    rv = read_temperature(priv->client, data);
    if(rv)
    {
        dev_err(priv->dev, "read_temperature failure.\n");
    }

    data[3] = data[1];
    data[2] = data[0];

    rv = read_humidity(priv->client, data);
    if(rv)
    {
        dev_err(priv->dev, "read_humidity failure.\n");
    }
    
    //printk("test %x %x %x %X \n", data[3], data[2], data[1], data[0]);

    return sprintf(buf, "temperature=%d humidity=%d\n", ((data[3] << 8) | data[2]), ((data[1] << 8) | data[0]) );//1000倍
}

/* @description: sysfs - echo写入属性函数
 *
 * @parm  : dev - 设备指针,创建file时候会指定dev
 * @parm  : attr - 设备属性,创建时候传入
 * @parm  : buf - 用户空间的buf
 * @parm  : count - 传入buf的size
 * @return: 写入的buf大小
 */
static ssize_t sht20_temp_humi_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    char k_buf[10] = {0,};
    snprintf(k_buf, sizeof(k_buf), "%s", buf);

    dev_info(dev, "Don't echo to me  -> [%s] size [%d]\n", k_buf, count);

    return count;
}

//初始化属性值
static DEVICE_ATTR(sht20_temp_humi, 0644, sht20_temp_humi_show, sht20_temp_humi_store);

/* @description: i2c驱动probe函数,设备和驱动匹配后执行
 *
 * @parm  : client - i2c设备
 * @parm  : id - i2c设备ID
 * @return: 0 successfully , !0 failure
 */
static int sht20_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    struct sht20_priv *priv = NULL;
    //struct device *dev;
    dev_t devno; //设备号
    int rv = 0;

    //0.给priv分配空间
    priv = devm_kzalloc(&client->dev, sizeof(struct sht20_priv), GFP_KERNEL);
    if(!priv)
    {
        return -ENOMEM;
    }

    //1.创建设备号
    if(0 != dev_major)
    {
        devno = MKDEV(dev_major, 0);
        rv = register_chrdev_region(devno, 1, DEV_NAME); //静态创建
    }
    else
    {
        rv = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);//动态创建
        dev_major = MAJOR(devno);//获主设备号
    }
    
    if(rv < 0)
    {
        dev_err(&client->dev, "%s driver can't get major %d\n", DEV_NAME, dev_major);
        return rv;
    }

    //2.注册字符设备
    cdev_init(&priv->cdev, &sht20_fops); //初始化cdev
    priv->cdev.owner = THIS_MODULE;

    rv = cdev_add(&priv->cdev, devno, 1);
    if(0 != rv)
    {
        dev_err(&client->dev, "error %d add %s device failure.\n", rv, DEV_NAME);
        goto undo_major;
    }

    //3.创建类,驱动进行节点创建
    priv->dev_class = class_create(THIS_MODULE, DEV_NAME);
    if(IS_ERR(priv->dev_class))
    {
        dev_err(&client->dev, "%s driver create class failure.\n", DEV_NAME);
        rv = -ENOMEM;
        goto undo_cdev;
    }

    //4.创建设备 
    priv->dev = device_create(priv->dev_class, NULL, devno, NULL, DEV_NAME);
    if(IS_ERR(priv->dev))
    {
        rv = -ENOMEM;
        goto undo_class;
    }

    //5. 创建sys 属性 在platform下
    if(device_create_file(priv->dev, &dev_attr_sht20_temp_humi))
    {
        rv = -ENOMEM;
        goto undo_device;
    }

    //6. 保存私有数据
    priv->client = client;
    i2c_set_clientdata(client, priv);
    dev_set_drvdata(priv->dev, priv);

    dev_info(&client->dev, "sht20 i2c driver probe okay.\n");
    return 0;

undo_device:
    device_destroy(priv->dev_class, devno);

undo_class:
    class_destroy(priv->dev_class);

undo_cdev:
    cdev_del(&priv->cdev);

undo_major:
    unregister_chrdev_region(devno, 1);

    devm_kfree(&client->dev, priv);
    return rv;
}


/* @description: i2c驱动的remove函数,移除时候执行
 *
 * @parm  : client - i2c 
 * @parm  : 
 * @return: 0 successfully , !0 failure
 */
static int sht20_remove(struct i2c_client *client)
{
    struct sht20_priv *priv = i2c_get_clientdata(client);
    dev_t devno = MKDEV(dev_major, 0);

    //删除sys中的属性
    device_remove_file(priv->dev, &dev_attr_sht20_temp_humi);

    //设备销毁
    device_destroy(priv->dev_class, devno);

    //注销类
    class_destroy(priv->dev_class);

    //删除字符设备
    cdev_del(&priv->cdev);
    unregister_chrdev_region(devno, 1);

    //释放堆
    devm_kfree(&client->dev, priv);
    dev_info(&client->dev, "sht20 driver remove.\n");
    
    return 0;
}

//传统方式ID列表
static const struct i2c_device_id sht20_id[] = {
    {"ssr,sht20", 0},
    {}
};

//设备树匹配列表
static const struct of_device_id of_sht20_match [] = {
    {.compatible="ssr,sht20"},
    {}
};
MODULE_DEVICE_TABLE(of, of_sht20_match);

static struct i2c_driver sht20_drv = {
    .probe = sht20_probe,
    .remove = sht20_remove,
    .driver = {
        .name = "sht20 driver 0.1", //name 不重要
        .owner = THIS_MODULE,
        .of_match_table = of_sht20_match,
    },
    .id_table = sht20_id,
};

//注册sht20驱动
module_i2c_driver(sht20_drv);

MODULE_AUTHOR("Wei Huihong <[email protected]>");
MODULE_DESCRIPTION("i.MX6ULL sht20 driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("i2c:imx_sht20_i2c_driver");

测试

  • app即为使用open、read系统调用测试
  • 代码如下
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define DEV_STH20   "/dev/sht20"

int main(int argc, char *argv[])
{
    float temperature = 0.0;
    float humidity = 0.0;
    unsigned char data[4] = {0,};

    int rv = -1;
    int fd_sht20 = -1;

    fd_sht20 = open(DEV_STH20, O_RDONLY);
    if(fd_sht20 < 0)
    {
        printf("open %s failure.\n", DEV_STH20);
        return -1;
    }
    printf("open %s successful.\n", DEV_STH20);    

    while(1)
    {
        //printf("start read temperature and humidity.\n");
        memset(data, 0, sizeof(data));
        rv = read(fd_sht20, data, sizeof(data));
        if(rv < 0)
        {
            printf("read data failure.\n");
        }
        else
        {
            //printf("data[0-3] : %x %x %x %x\n",data[0], data[1], data[2], data[3]);
            humidity = ((data[1] << 8) | data[0]) / 1000.0;
            temperature = ((data[3] << 8) | data[2]) /1000.0;

            printf("temperature : %.4f℃ humidity : %.4f %%\n", temperature, humidity);
        }
        sleep(5);
    }
    close(fd_sht20);

    return 0;
}
  • cat 属性 即为sysfs调试方式
  • 效果图如下
    在这里插入图片描述
  • 误差在接受范围以内
;