百问网驱动大全学习(二)I2C驱动

百问网驱动大全学习(二)I2C驱动

i2c的驱动分为两个方面的驱动:

  1. 芯片I2C控制器的驱动
  2. I2C外设的驱动

一、I2C外设驱动

我们首先来看看I2C外设驱动如何编写,我们以100ask生产的STM32MP157板子为例,板子上有一个三合一的传感器模块,通过I2C协议来与它进行通信。

我们参考at24.c来编写一个简单的I2C驱动程序框架i2c_drv_example.c,完整的代码会在附录一种给出

(一)简单的I2C驱动程序框架

首先是复制头文件,然后是把入口函数和出口函数复制过来,如下所示

static int __init at24_init(void)
{
	if (!at24_io_limit) {
		pr_err("at24: at24_io_limit must not be 0!\n");
		return -EINVAL;
	}

	at24_io_limit = rounddown_pow_of_two(at24_io_limit);
	return i2c_add_driver(&at24_driver);
}
module_init(at24_init);

static void __exit at24_exit(void)
{
	i2c_del_driver(&at24_driver);
}
module_exit(at24_exit);

上面的一段代码是at24.c的入口函数和出口函数,在入口函数里面,调用i2c_add_driver注册了一个at24_driver的结构体变量,我们来看看这个at24_driver变量是个什么东西

static struct i2c_driver at24_driver = {
	.driver = {
		.name = "at24",
		.of_match_table = at24_of_match,
		.acpi_match_table = ACPI_PTR(at24_acpi_ids),
	},
	.probe_new = at24_probe,
	.remove = at24_remove,
	.id_table = at24_ids,
};

它是一个i2c_driver结构体变量,里面有id_table和of_match_table,这两个变量是I2C驱动I2C设备匹配的重要参考。当设备和驱动匹配上时,就会调用i2c_driver里面的probe_new函数(在4.9内核是probe函数)。在probe_new函数里完成字符设备的分配、设置、注册。

我们先来看看of_match_table

static const struct of_device_id at24_of_match[] = {
	{ .compatible = "atmel,24c00",		.data = &at24_data_24c00 },
	{ .compatible = "atmel,24c01",		.data = &at24_data_24c01 },
	{ .compatible = "atmel,24cs01",		.data = &at24_data_24cs01 },
	{ .compatible = "atmel,24c02",		.data = &at24_data_24c02 },
	{ .compatible = "atmel,24cs02",		.data = &at24_data_24cs02 },
	{ .compatible = "atmel,24mac402",	.data = &at24_data_24mac402 },
	{ .compatible = "atmel,24mac602",	.data = &at24_data_24mac602 },
	{ .compatible = "atmel,spd",		.data = &at24_data_spd },
	{ .compatible = "atmel,24c04",		.data = &at24_data_24c04 },
	{ .compatible = "atmel,24cs04",		.data = &at24_data_24cs04 },
	{ .compatible = "atmel,24c08",		.data = &at24_data_24c08 },
	{ .compatible = "atmel,24cs08",		.data = &at24_data_24cs08 },
	{ .compatible = "atmel,24c16",		.data = &at24_data_24c16 },
	{ .compatible = "atmel,24cs16",		.data = &at24_data_24cs16 },
	{ .compatible = "atmel,24c32",		.data = &at24_data_24c32 },
	{ .compatible = "atmel,24cs32",		.data = &at24_data_24cs32 },
	{ .compatible = "atmel,24c64",		.data = &at24_data_24c64 },
	{ .compatible = "atmel,24cs64",		.data = &at24_data_24cs64 },
	{ .compatible = "atmel,24c128",		.data = &at24_data_24c128 },
	{ .compatible = "atmel,24c256",		.data = &at24_data_24c256 },
	{ .compatible = "atmel,24c512",		.data = &at24_data_24c512 },
	{ .compatible = "atmel,24c1024",	.data = &at24_data_24c1024 },
	{ .compatible = "atmel,24c2048",	.data = &at24_data_24c2048 },
	{ /* END OF LIST */ },
};

这是at24.c的of_match_table,compatible属性一般格式为厂商,产品名,我们在i2c_drv_example.c里写成

static const struct of_device_id i2c_example_of_match[] = {
	{ .compatible = "lite-on,ap3216c",		.data = NULL },
	{ /* END OF LIST */ },
};

还有id_table,我们写成如下的形式

static const struct i2c_device_id i2c_example_ids[] = {
	{ "ap3216c", (long unsigned int)NULL },
	{ /* END OF LIST */ }
};

然后把probe函数和remove函数也拿过来,修改成我们自己的probe函数和remove函数,这里只做了简单的打印

static int i2c_example_probe(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static int i2c_example_remove(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

把完整的i2c_drv_example.c编译成.ko文件,并且在157上安装好,还需要手动添加一个设备节点,以匹配我们的i2c_drv_example.c。我们使用如下命令进行添加i2c设备节点

echo ap3216c 0x1e > /sys/bus/i2c/devices/i2c-0/new_device

当运行这个命令时,就会和我们的驱动程序匹配,并且会打印出probe函数被调用的信息

要删除这个i2c设备节点也很简单,类似的有如下的命令

echo 0x1e > /sys/bus/i2c/devices/i2c-0/delete_device

运行这条指令,我们的驱动程序的remove函数就会被调用

我们简单的I2C驱动框架就到这里,接着,我们要去编写读取ap3216c的数据的驱动程序和测试应用

(二)ap3216c的驱动

我们在i2c_drv_example.c的基础上来编写ap3216c的驱动,这一步也很简单,就是字符设备驱动的套路,分配主设备号,创建类,创建设备节点,最核心的地方是file_operations结构体变量的设置,其中的open和read函数是我们字符设备驱动程序的核心,驱动代码和测试代码在附录二给出。

然后在open和read函数里面,就可以使用i2c-core-smbus.c里面的i2c_smbus_read_byte_data函数和i2c_smbus_write_byte函数与ap3216c模块进行通信,整个程序也很简单。

二、I2C控制器驱动

更新中

附录一、简单的I2C驱动程序框架

i2c_drv_example.c

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

static const struct i2c_device_id i2c_example_ids[] = {
	{ "ap3216c", (long unsigned int)NULL },
	{ /* END OF LIST */ }
};


static const struct of_device_id i2c_example_of_match[] = {
	{ .compatible = "lite-on,ap3216c",		.data = NULL },
	{ /* END OF LIST */ },
};
MODULE_DEVICE_TABLE(of, i2c_example_of_match);


static int i2c_example_probe(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static int i2c_example_remove(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


static struct i2c_driver i2c_example_driver = {
	.driver = {
		.name = "ap3216c",
		.of_match_table = i2c_example_of_match,
	},
	.probe_new = i2c_example_probe,
	.remove = i2c_example_remove,
	.id_table = i2c_example_ids,
};

static int __init i2c_example_init(void)
{
	return i2c_add_driver(&i2c_example_driver);
}
module_init(i2c_example_init);

static void __exit i2c_example_exit(void)
{
	i2c_del_driver(&i2c_example_driver);
}
module_exit(i2c_example_exit);

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

附录二、ap3216c的驱动

驱动代码

#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/fs.h>
#include <linux/uaccess.h>



static int major = 0;
static struct class *ap3216c_class;
static struct i2c_client * ap3216c_client;


int ap3216c_open(struct inode *inode, struct file *file)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	i2c_smbus_write_byte_data(ap3216c_client, 0, 0x4);
	mdelay(20); /* delay 20ms */
	i2c_smbus_write_byte_data(ap3216c_client, 0, 0x3);
	mdelay(250); /* delay 20ms */
	return 0;
}

ssize_t ap3216c_read(struct file *filp, char __user *buf,
		size_t size, loff_t *pos)
{
	char kernel_buf[6];
	int ret;
	
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	ret = i2c_smbus_read_word_data(ap3216c_client, 0xA);
	kernel_buf[0] = ret & 0xff;
	kernel_buf[1] = (ret >> 8) & 0xff;
	mdelay(20); /* delay 20ms */

	ret = i2c_smbus_read_word_data(ap3216c_client, 0xC);
	kernel_buf[2] = ret & 0xff;
	kernel_buf[3] = (ret >> 8) & 0xff;
	mdelay(20); /* delay 20ms */

	ret = i2c_smbus_read_word_data(ap3216c_client, 0xE);
	kernel_buf[4] = ret & 0xff;
	kernel_buf[5] = (ret >> 8) & 0xff;
	mdelay(20); /* delay 20ms */

	copy_to_user(buf, kernel_buf, size);
	return size;
}


static struct file_operations ap3216c_fops = {
	.owner = THIS_MODULE,
	.open = ap3216c_open,
	.read = ap3216c_read,
};

static const struct i2c_device_id ap3216c_ids[] = {
	{ "ap3216c", (long unsigned int)NULL },
	{ /* END OF LIST */ }
};


static const struct of_device_id ap3216c_of_match[] = {
	{ .compatible = "lite-on,ap3216c",		.data = NULL },
	{ /* END OF LIST */ },
};
MODULE_DEVICE_TABLE(of, ap3216c_of_match);


static int ap3216c_probe(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	ap3216c_client = client;

	/* 注册字符设备 */
	major = register_chrdev(0, "ap3216c", &ap3216c_fops);
	ap3216c_class = class_create(THIS_MODULE, "ap3216c");
	device_create(ap3216c_class, NULL, MKDEV(major, 0), NULL, "ap3216c"); // /dev/ap3216c
	
	return 0;
}

static int ap3216c_remove(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(ap3216c_class, MKDEV(major, 0));
	class_destroy(ap3216c_class);
	unregister_chrdev(major, "ap3216c");
	return 0;
}


static struct i2c_driver ap3216c_driver = {
	.driver = {
		.name = "ap3216c",
		.of_match_table = ap3216c_of_match,
	},
	.probe_new = ap3216c_probe,
	.remove = ap3216c_remove,
	.id_table = ap3216c_ids,
};

static int __init ap3216c_init(void)
{
	return i2c_add_driver(&ap3216c_driver);
}
module_init(ap3216c_init);

static void __exit ap3216c_exit(void)
{
	i2c_del_driver(&ap3216c_driver);
}
module_exit(ap3216c_exit);

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



测试代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>



/*
	./ap3216c_test <i2c_bus_num> r
*/

int main(int argc, char **argv)
{

	int fd = 0;
	char buf[6];
	int ret = 0;
	if(argc != 3)
	{
		printf("Usage:\n");
		printf("%s <i2c_bus_num> r\n", argv[0]);
		return -1;
	}

	fd = open("/dev/ap3216c", O_RDONLY);
	if(fd < 0)
	{
		printf("Can't open /dev/ap3216c\n");
		return -1;
	}

	ret = read(fd, buf, 6);
	if(ret < 0)
	{
		printf("Can't read /dev/ap3216c\n");
		return 0;
	}

	printf("IR:  %02x %02x\n", buf[0], buf[1]);
	printf("ALS: %02x %02x\n", buf[2], buf[3]);
	printf("PS:  %02x %02x\n", buf[4], buf[5]);

	return 0;
	
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值