【全志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

然后将该工具拷贝到开发板,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: -- -- -- -- -- -- -- -- 

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 <stdio.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <errno.h>
#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);
    }
}

4、测试

# ./iic_gy30_test
光照度:  75.42lx
光照度:  95.14lx
光照度:  311.47lx

(二)使用设备树操作

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>;
	};

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

# 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
#

3、编写驱动

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

#include <linux/ide.h>
#include <linux/module.h>
#include <linux/i2c.h>

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");

4、编写测试程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
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;
}

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

采集数据

# ./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

测试完成。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

第四维度4

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值