• 【全志T113-S3_100ask】6-编写IIC驱动GY-302(twi)


    前言

    在100ask的板子上预留了一个IIC接口,下面通过这个IIC接口来采集光照强度传感器GY-302(BH1750)。
    在这里插入图片描述

    (一)不使用设备树操作

    1、预操作

    通过查看设备,我们可以看到上面挂载着一个i2c-2设备,且没有其他的i2c。
    在这里插入图片描述

    2、使用i2c tools测试iic

    i2c-tools编译,安装步骤

    //1、下载i2c-tools-4.0
    wget  https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/i2c-tools-4.0.tar.gz  
    //2、解压
    tar -xzvf i2c-tools-4.0.tar.gz
    //3、修改交叉编译:Makefile中的CC该为使用的交叉编译器
    CC      ?= arm-linux-gnueabi-gcc
    //4、编译
    Make
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    然后将该工具拷贝到开发板,nfs也好,U盘也行。
    然后使用该工具检测设备

    # ls
    i2c-tools-4.0
    
    # cd i2c-tools-4.0/
    # ls
    CHANGES       Makefile      eeprom        lib           tools
    COPYING       README        eepromer      py-smbus      version.h
    COPYING.LGPL  eeprog        include       stub
    
    # cd tools/
    # ls
    Module.mk      i2cdetect.c    i2cget         i2cset.c       util.c
    i2cbusses.c    i2cdetect.o    i2cget.8       i2cset.o       util.h
    i2cbusses.h    i2cdump        i2cget.c       i2ctransfer    util.o
    i2cbusses.o    i2cdump.8      i2cget.o       i2ctransfer.8
    i2cdetect      i2cdump.c      i2cset         i2ctransfer.c
    i2cdetect.8    i2cdump.o      i2cset.8       i2ctransfer.o
    
    !目前只挂载i2c-2
    # i2cdetect -l
    i2c-2   i2c             twi2                                    I2C adapter
    
    # i2cdetect -y 2
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
     00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     20: -- -- -- 23 -- -- -- -- -- -- -- -- -- -- -- --
     30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
     70: -- -- -- -- -- -- -- -- 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    3、编写测试应用

    打开设备文件然后设置i2c设备的地址,按照GY-302数据手册中操作流程,连续写0x01和0x11,就可以读出光照信息。
    a、初始化 写寄存器0x01 上电
    b、设置 0x11 设置成高精度模式 Continuously H-Resolution Mode,即连续高分辨率模式。
    c、 读数据 unsigned char Buf[3] data=Buf[0]; data=(data<<8)+Buf[1] tmp=data/1.2

    #include 
    #include 
    #include 
    #include 
    #define I2C_ADDR 0x23
    
    int main(void)
    {
        int fd;
        char buf[3];
        char val, value;
        float flight;
        fd = open("/dev/i2c-2", O_RDWR);
        if (fd < 0)
        {
            printf("打开文件错误:%s\r\n", strerror(errno));
            return 1;
        }
        if (ioctl(fd, I2C_SLAVE, I2C_ADDR) < 0)
        {
            printf("ioctl 错误 : %s\r\n", strerror(errno));
            return 1;
        }
        val = 0x01;
        if (write(fd, &val, 1) < 0)
        {
            printf("上电失败\r\n");
        }
        val = 0x11;
        if (write(fd, &val, 1) < 0)
        {
            printf("开启高分辨率模式2\r\n");
        }
        usleep(200000);
        while (1)
        {
            if (read(fd, &buf, 3))
            {
                flight = (buf[0] * 256 + buf[1]) * 0.5 / 1.2;
                printf("光照度: %6.2flx\r\n", flight);
            }
            else
            {
                printf("读取错误\r\n");
            }
            sleep(3);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    4、测试

    # ./iic_gy30_test
    光照度:  75.42lx
    光照度:  95.14lx
    光照度:  311.47lx
    
    • 1
    • 2
    • 3
    • 4

    (二)使用设备树操作

    1、TWI 两线串行接口简介

    • 在设备树中,找不到任何i2c的节点信息,只找到twi。
    • TWI(Two-wire Serial Interface)两线串行接口,TWI 完全兼容 I2C 总线。
    • 由于TWI总线与传统的I2C总线极其相似。因此不少人误以为TWI总线就是I2C总线,其
      实这只是一种简单化的理解。TWI总线是对I2C总线的继承和发展。它定义了自已的功能
      模块和寄存器,寄存器各位功能的定义与I2C总线并不相同;而且TWI总线引入了状奁寄
      存器,使得TWI总线在操作和使用上比I2C总线更为灵活。

    2、添加节点

    添加到 &twi2 下

    light-sensor@23 {
    			compatible = "gy,bh1750";
    			reg = <0x23>;
    	};
    
    • 1
    • 2
    • 3
    • 4

    如图所示:
    在这里插入图片描述
    编译烧录后在以下目录可以找得到:

    # cd /sys/firmware/devicetree/base/soc@3000000/twi@2502800/
    
    # ls
    #address-cells   compatible       interrupts       pinctrl-names
    #size-cells      ctp@14           light-sensor@23  reg
    clock-frequency  device_type      name             resets
    clock-names      dma-names        pinctrl-0        status
    clocks           dmas             pinctrl-1
    #
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3、编写驱动

    参考正点原子以及https://blog.csdn.net/qq_18737805/article/details/86427845

    #include 
    #include 
    #include 
    
    struct class *class;    /* 类 		*/
    int major;              /* 主设备号 */
    struct i2c_client *gy_sensor_client;
    
    // 构造i2c_msg通过这个client调用i2c_tansfer来读写
    static int gy_sensor_write_reg(unsigned char addr)
    {
        int ret = -1;
        struct i2c_msg msgs;
    
        printk("gy_sensor_client -> addr=%d\n", gy_sensor_client->addr);
        msgs.addr = gy_sensor_client->addr; //   GY302_ADDR,直接封装于i2c_msg
        msgs.buf = &addr;
        msgs.len = 1;   //长度1 byte
        msgs.flags = 0; //表示写
    
        ret = i2c_transfer(gy_sensor_client->adapter, &msgs, 1); 
        //这里都封装好了,本来根据i2c协议写数据需要先写入器件写地址,然后才能读
        if (ret < 0)
        {
            printk("i2c_transfer write err\n");
            return -1;
        }
        return 0;
    }
    static int gy_sensor_read_reg(unsigned char *buf)
    {
        int ret = -1;
        struct i2c_msg msg;
        msg.addr = gy_sensor_client->addr; // GY30_ADDR
        msg.buf = buf;
        msg.len = 2;                                            //长度1 byte
        msg.flags = I2C_M_RD;                                   //表示读
        ret = i2c_transfer(gy_sensor_client->adapter, &msg, 1); //这里都封装好了,本来根据i2c协议读数据需要先写入读地址,然后才能读
        if (ret < 0)
        {
            printk("i2c_transfer write err\n");
            return -1;
        }
        return 0;
    }
    
    // 初始化光线传感器
    int gy_sensor_open(struct inode *inode, struct file *file)
    {
        printk("open gy_sensor\n");
        gy_sensor_write_reg(0x01); // power up
        gy_sensor_write_reg(0x11);
        return 0;
    }
    
    // 读出传感器的两个字节
    static ssize_t gy_sensor_read(struct file *file, char __user *buf, size_t count, loff_t *off)
    {
        unsigned char addr = 0, data[2];
        gy_sensor_read_reg(data);
        copy_to_user(buf, data, 2);
        return 1;
    }
    
    /* bh1750 操作函数 */
    static const struct file_operations gy_sensor_fops = {
        .owner = THIS_MODULE,
        .open = gy_sensor_open, 
        .read = gy_sensor_read, 
    };
    
    /* 构造一个platform_driver,
    其中的of_match_table字段需要与 light-sensor@23 节点的compatible属性值一致,
    当匹配时则调用platform_driver的probe函数 */
    static const struct of_device_id ids[] =
    {
        {.compatible = "gy,bh1750"},
        {  }
    };
    
    // 在i2c_driver的probe函数中得到在总线驱动程序中解析得到的i2c_client,
    // 并为该光线传感器注册一个字符设备
    static int gy_sensor_probe(struct i2c_client *client,
                               const struct i2c_device_id *id)
    {
        gy_sensor_client = client;
    
        printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
        major = register_chrdev(0, "gy_sensor", &gy_sensor_fops);
        class = class_create(THIS_MODULE, "gy_sensor");
        device_create(class, NULL, MKDEV(major, 0), NULL, "gy_sensor"); /* /dev/gy_sensor */
        return 0;
    }
    
    // 在platform_driver的remove函数中,注销该字符设备
    static int gy_sensor_remove(struct i2c_client *client)
    {
        device_destroy(class, MKDEV(major, 0));
        class_destroy(class);
        unregister_chrdev(major, "gy_sensor");
    
        return 0;
    }
    
    /* 分配/设置i2c_driver */
    static struct i2c_driver gy_sensor_driver = {
        .driver = {
            .name = "bh1750",
            .owner = THIS_MODULE,
            .of_match_table = ids,
        },
        .probe = gy_sensor_probe,
        .remove = gy_sensor_remove,
    };
    
    /*
     * @description	: 驱动入口函数
     */
    static int __init bh1750_init(void)
    {
        int ret = 0;
    
        ret = i2c_add_driver(&gy_sensor_driver);
        return ret;
    }
    
    /*
     * @description	: 驱动出口函数
     */
    static void __exit bh1750_exit(void)
    {
        i2c_del_driver(&gy_sensor_driver);
    }
    
    /* module_i2c_driver(bh1750_driver) */
    module_init(bh1750_init);
    module_exit(bh1750_exit);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("zhu");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139

    4、编写测试程序

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
     
    int main(int argc, char **argv)
    {
    	int fd;
    	char val;
    	unsigned char buf[3];
    	float flight;
    	fd = open("/dev/gy_sensor", O_RDWR);
    	if (fd < 0)
    	{
    		printf("can't open /dev/gy_sensor\n");
    		return -1;
    	}
     
    	usleep(200000);
    	while(1)
    		{
    	     if(read(fd,&buf,3)){
    	         flight=(buf[0]*256+buf[1])*0.5/1.2;
    	         printf("light: %6.2flx\r\n",flight);
    	     }
    	     else{
    	         printf("read err!\r\n");
    	     }
    	    sleep(4);
    	}
     
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    5、测试

    加载驱动

    # insmod iic_gy302_dts.ko
    [   62.979905] iic_gy302_dts: loading out-of-tree module taints kernel.
    [   62.988058] /disk/vsCode/09_iic_gy302_dts/iic_gy302_dts.c gy_sensor_probe 120
    
    
    # lsmod
    Module                  Size  Used by    Tainted: G
    iic_gy302_dts          16384  0
    sunxi_ce               57344  0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    采集数据

    # ./iic_gy302_dts_test
    [  164.872116] open gy_sensor
    [  164.875291] gy_sensor_client -> addr=35
    [  164.879870] gy_sensor_client -> addr=35
    light:  46.67lx
    light:  46.25lx
    light:  40.42lx
    light:  61.67lx
    light:  65.42lx
    light: 2098.33lx
    light: 4914.17lx
    light: 17047.50lx
    light:  40.83lx
    light:  40.42lx
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    测试完成。

  • 相关阅读:
    剑指 Offer 03. 数组中重复的数字
    数据结构(C语言版)01
    Mybatis 快速入门之 动态sql和分页
    netapp3210存储更换故障硬盘过程
    物联网通信技术|课堂笔记|week10-2 11月1日|应用层协议|域名解析
    Vue 双向绑定、diff和nextTick原理
    使用 ModSecurity 虚拟修补 Apache Struts CVE-2017-5638
    SCM系统是什么?供应链管理系统有哪些优势?
    PIC12F510作为PMBus主机
    低版本浏览器使用最新渲染模式以免IE不支持CSS3属性
  • 原文地址:https://blog.csdn.net/qq_46079439/article/details/126005394