嵌入式Linux的两种I2C驱动方式

        i2c接口常见于各种慢速ic,特别是传感器。本文以I2C的CO2 sensor为例,

 

如上可以看到该i2c设备的从机地址,最大通信速率以及通信时序。因只要读取co2值,因此只需关注读的时许即可。

i2c设备接到实际硬件上后可以通过i2ctool进行侦测硬件接线是否有误。


root@TinaLinux:/# i2cdetect -y -r 1       //查询接到i2c总线1的从设备,如下可以看到接了一个从机地址为0x41的设备,即准备驱动co2设备
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- 41 -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --


root@TinaLinux:/# i2cdump -f -y 1 0x41   //把co2 设备的寄存器内容都dump出来
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: 01 78 04 3f 10 ff 2f 3f 36 3f 01 3f 01 3f 00 3f    ?x???./?6?????.?
10: 00 3f 01 3f 00 3f 00 3f 00 3f 13 3f 00 3f 20 3f    .???.?.?.???.? ?
20: 78 3f 78 3f 78 3f 00 3f 3f 3f 10 3f 00 3f 00 3f    x?x?x?.?????.?.?
30: 7f 3f 7b 3f 04 3f 28 3f 28 3f 68 3f 68 3f 7b 3f    ??{???(?(?h?h?{?
40: 7b 3f 7f 3f 7c 3f 7b 3f 06 3f 00 3f 00 3f 00 3f    {???|?{???.?.?.?
50: 55 3f 01 3f 3f 3f 04 3f 3f 3f 00 3f 70 3f 3f 3f    U?????????.?p???
60: 00 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 00 3f    .?????????????.?
70: 3f 3f b2 3f 00 3f 03 3f 00 3f 00 3f 00 3f 7b 3f    ????.???.?.?.?{?
80: 7b 3f 04 3f 3f 3f 00 3f 00 3f 00 3f 3f 3f 3f 3f    {?????.?.?.?????
90: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f    ????????????????
a0: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f    ????????????????
b0: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f    ????????????????
c0: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f    ????????????????
d0: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f    ????????????????
e0: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f    ????????????????
f0: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f    ????????????????
root@TinaLinux:/# i2cget -f -y 1 0x41 0x00   //读取寄存器00的值
0x01
root@TinaLinux:/# i2cget -f -y 1 0x41 0x02    //读取寄存器02的值
0x04
root@TinaLinux:/#

通过i2ctool确认可以识别到硬件上没问题后既可以开始进行驱动开发。

1、用户态驱动

        即在用户层直接驱动iic从机,不需要涉及到编写内核驱动,好处是不用涉及到编译内核,只需要用户态驱动

//直接在用户态上通过read write 标准接口去读取
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <stdio.h>
#define CHIP "/dev/i2c-1"
#define CHIP_ADDR 0x41
int main()
{
	unsigned char rddata[4];
	unsigned char rdnofilterregaddr = 0x02;
	unsigned char rdfilterregaddr = 0x34;
	int co2ppm;
	printf("hello, this is i2c tester\n");
	int fd = open(CHIP, O_RDWR);
	if (fd < 0)
	{
		printf("open "CHIP"failed\n");
		goto exit;
	}
	if (ioctl(fd, I2C_SLAVE_FORCE, CHIP_ADDR) < 0)
	{ 	
		/* 璁剧疆鑺墖鍦板潃 */
		printf("oictl:set slave address failed\n");
		goto closelabel;
	}
	while(1)
	{
		printf("write nofilteraddress return: %d\n",write(fd, &rdnofilterregaddr, 1)); 
		if(read(fd, &rddata, 3))
		{
			printf("rawdata:%x %x %x\r\n",rddata[0],rddata[1],rddata[2]);
			co2ppm = rddata[0]*256 + rddata[1];
			printf("co2ppm:%d\n",co2ppm);
		}
		else
		{
			printf("noneread\n");
		}
		sleep(3);
		printf("write filteraddress return: %d\n",write(fd, &rdfilterregaddr, 1)); 
		if(read(fd, &rddata, 2))
		{
			printf("rawdata:%x %x\r\n",rddata[0],rddata[1],rddata[2]);
			co2ppm = rddata[0]*256 + rddata[1];
			printf("co2ppmfilter:%d\n",co2ppm);
		}
		else
		{
			printf("noneread\n");
		}
		sleep(3);
	}

closelabel:	
	close(fd);
	exit:
	return 0;
}

//编译用户态程序
 arm-openwrt-linux-gcc i2cuser.c

//拷贝到板子上运行
root@TinaLinux:/# ./a.out
hello, this is i2c tester
write nofilteraddress return: 1
rawdata:3 a8 55
co2ppm:936
write filteraddress return: 1
rawdata:3 78
co2ppmfilter:888
write nofilteraddress return: 1
rawdata:3 9e 55
co2ppm:926
write filteraddress return: 1
rawdata:a 22
co2ppmfilter:2594
write nofilteraddress return: 1
rawdata:5 d1 55
co2ppm:1489
write filteraddress return: 1
rawdata:8 6
co2ppmfilter:2054


2、内核态驱动

        编写设备树,依据平台设备驱动模型去编写sensor driver驱动。

设备树

&twi1 {
	clock-frequency = <100000>;//按co2 sensor支持的最大100khz填写
	pinctrl-0 = <&twi1_pins_a>;
	pinctrl-1 = <&twi1_pins_b>;
	pinctrl-names = "default", "sleep";
	status = "okay";
 
  co2lp3@41{   //co2子设备定义
    compatible = "co2lp3";//用于drive probe
    reg = <0x41>;//设备地址
  };
};

驱动代码

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/mod_devicetable.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/property.h>
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/nvmem-provider.h>
#include <linux/regmap.h>
#include <linux/pm_runtime.h>
#include <linux/gpio/consumer.h>
#include <linux/uaccess.h>
#include <linux/fs.h>

static int major = 0;
static struct class *lp3co2_class;
static struct i2c_client *lp3co2_client;


static int lp3co2_read_regs( void *val, int len)
{
    int ret;
    struct i2c_msg msg;
    /* msg 为读取数据 */
    msg.addr = lp3co2_client->addr;
    msg.flags = I2C_M_RD;
    msg.buf = val;
    msg.len = len;
    ret = i2c_transfer(lp3co2_client->adapter, &msg, 1);
    if(ret == 1){
        ret = 0;
    }else{
        printk("i2c rd failed=%d len=%d \n",ret, len);
        ret = -EREMOTEIO;
    }
    return ret;
}

static unsigned int lp3co2_write_regs( unsigned char reg, unsigned char*buf, unsigned char len)
{
    unsigned char *b;
    unsigned char i;
    struct i2c_msg msg;
    b = kzalloc(len+1,GFP_KERNEL);
    b[0] = reg;
    msg.addr = lp3co2_client->addr;//i;
    msg.flags = 0;//write
    msg.buf = b;
    msg.len = len+1;
    return i2c_transfer(lp3co2_client->adapter, &msg, 1);
}


static ssize_t lp3co2_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int err;
	char kernel_buf[4];

	lp3co2_write_regs(0x02,NULL,0);
	lp3co2_read_regs(kernel_buf,2);
  lp3co2_write_regs(0x34,NULL,0);
	lp3co2_read_regs(&kernel_buf[2],2);
	err = copy_to_user(buf, kernel_buf, size);
	return size;
}


static int lp3co2_open (struct inode *node, struct file *file)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


static struct file_operations lp3co2_ops = {
	.owner = THIS_MODULE,
	.open  = lp3co2_open,
	.read  = lp3co2_read,
};

static const struct of_device_id of_match_ids_lp3co2[] = {
	{ .compatible = "co2lp3",		.data = NULL },
	{ /* END OF LIST */ },
};

static const struct i2c_device_id lp3co2_ids[] = {
	{ "lp3co2",	(kernel_ulong_t)NULL },
	{ /* END OF LIST */ }
};

static int lp3co2_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	lp3co2_client = client;
	
	/* register_chrdev */
	major = register_chrdev(0, "lp3co2", &lp3co2_ops);

	lp3co2_class = class_create(THIS_MODULE, "lp3co2_class");
	device_create(lp3co2_class, NULL, MKDEV(major, 0), NULL, "lp3co2"); /* /dev/lp3co2 */

	return 0;
}

static int lp3co2_remove(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(lp3co2_class, MKDEV(major, 0));
	class_destroy(lp3co2_class);
	
	/* unregister_chrdev */
	unregister_chrdev(major, "lp3co2");

	return 0;
}

static struct i2c_driver i2c_lp3co2_driver = {
	.driver = {
		.name = "co2lp3",
		.of_match_table = of_match_ids_lp3co2,
	},
	.probe = lp3co2_probe,
	.remove = lp3co2_remove,
	.id_table = lp3co2_ids,
};


static int __init i2c_driver_lp3co2_init(void)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return i2c_add_driver(&i2c_lp3co2_driver);
}
module_init(i2c_driver_lp3co2_init);

static void __exit i2c_driver_lp3co2_exit(void)
{
	i2c_del_driver(&i2c_lp3co2_driver);
}
module_exit(i2c_driver_lp3co2_exit);

MODULE_AUTHOR("www.100ask.net");
MODULE_LICENSE("GPL");

应用层测试代码

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>

int main(int argc, char **argv)
{
    int fd,ret;
    unsigned char databuf[4] = {0};
    int co2ppm;

    fd = open("/dev/lp3co2", O_RDWR);
    if (fd < 0)
    {
        perror("open device lp3co2 error");
    }
    while (1)
    {
        
        ret = read(fd, databuf, 4);
        printf("rawdata:%x %x %x %x\r\n",databuf[0],databuf[1],databuf[2],databuf[3]);
  			co2ppm = databuf[0]*256 + databuf[1];
  			printf("co2ppm:%d\n",co2ppm);
        co2ppm = databuf[2]*256 + databuf[3];
  			printf("co2filterppm:%d\n",co2ppm);
        sleep(3);

    
    }
    return 0;
}

 linux i2c既可以通过i2c硬件总线驱动,也可以通过模拟io驱动,模拟io驱动linux内核也有做好了对应的驱动,只要设备树按要求的定义后就可以直接使用。不需要自己像mcu级别额外去控制模拟scl和sda时序。

gpio模拟iic驱动需要内核开启相应配置

 同时添加对应的设备树

 i2cgpio{
		compatible = "i2c-gpio";//必须为该名称才可以让内核的驱动识别到
    gpios = < &pio PB 6 0 /*sda*/
              &pio PB 7 0 /*scl*/
    >;
  
    i2c-gpio,delay-us = <30>;
    #address-cells = <1>;
    #size-cells = <0>;
    
    co2lp3@41{
    compatible = "co2lp3";
    reg = <0x41>;
  };
	};
root@TinaLinux:/# dmesg | grep i2c
[    2.361954] i2c /dev entries driver
[    3.059727] sunxi-i2c sunxi-i2c1: sunxi-i2c1 supply twi not found, using dummy regulator
[    3.069550] sunxi-i2c sunxi-i2c1: probe success
[    3.075112] sunxi-i2c sunxi-i2c2: sunxi-i2c2 supply twi not found, using dummy regulator
[    3.084907] sunxi-i2c sunxi-i2c2: probe success
[    3.091982] gpio-38 (i2cgpio): enforced open drain please flag it properly in DT/ACPI DSDT/board file
[    3.102457] gpio-39 (i2cgpio): enforced open drain please flag it properly in DT/ACPI DSDT/board file
[    3.113282] i2c-gpio i2cgpio: using lines 38 (SDA) and 39 (SCL)

i2c-1   i2c             sunxi-i2c1                              I2C adapter
i2c-2   i2c             sunxi-i2c2                              I2C adapter
i2c-0   i2c             i2cgpio                                 I2C adapter

//i2c-0即为模拟iic的adapter

root@TinaLinux:/# i2cdump -y -f 0
root@TinaLinux:/# i2cdetect -y -r 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- 41 -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
root@TinaLinux:/# i2cdump -y -f 0x41
root@TinaLinux:/# i2cdump -y -f 0 0x41
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: 01 78 04 3f 10 ff 2f 3f 36 3f 01 3f 01 3f 00 3f    ?x???./?6?????.?
10: 00 3f 01 3f 00 3f 00 3f 00 3f 13 3f 00 3f 20 3f    .???.?.?.???.? ?
20: 78 3f 78 3f 78 3f 00 3f 3f 3f 10 3f 00 3f 00 3f    x?x?x?.?????.?.?
30: 7f 3f 7a 3f 05 3f 28 3f 28 3f 67 3f 67 3f 7a 3f    ??z???(?(?g?g?z?
40: 7a 3f 7f 3f 7b 3f 7a 3f 06 3f 00 3f 00 3f 00 3f    z???{?z???.?.?.?
50: 55 3f 01 3f 3f 3f 04 3f 3f 3f 00 3f 70 3f 3f 3f    U?????????.?p???
60: 00 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 00 3f    .?????????????.?
70: 3f 3f b2 3f 00 3f 03 3f 00 3f 00 3f 00 3f 7b 3f    ????.???.?.?.?{?
80: 7b 3f 05 3f 3f 3f 00 3f 00 3f 00 3f 3f 3f 3f 3f    {?????.?.?.?????
90: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f    ????????????????
a0: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f    ????????????????
b0: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f    ????????????????
c0: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f    ????????????????
d0: 3f 3f 3f 3f 3f 3f 3f 3f XX 3f 3f 3f 3f 3f 3f 3f    ????????X???????
e0: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f    ????????????????
f0: 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f 3f    ????????????????
root@TinaLinux:/#

i2c更换成模拟io后内核会生成一个新的adapter,用户态驱动只需打开的设备文件名改为生成的i2c-0后就可以直接读取,

root@TinaLinux:/# ./a.out
hello, this is i2c tester
write nofilteraddress return: 1
rawdata:4 6a 55
co2ppm:1130
write filteraddress return: 1
rawdata:4 8e
co2ppmfilter:1166
write nofilteraddress return: 1
rawdata:4 62 55
co2ppm:1122

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值