栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

Linux驱动

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux驱动

 参考:Linux I2C驱动框架(超详细)_JT同学的博客-CSDN博客_i2c驱动

一、几个重要的对象 1、I2C总线
struct bus_type i2c_bus_type = {
	.name		= "i2c",
	.match		= i2c_device_match,
	.probe		= i2c_device_probe,
	.remove		= i2c_device_remove,
	.shutdown	= i2c_device_shutdown,
	.pm		= &i2c_device_pm_ops,
};

        I2C总线对应着/bus下的一条总线,这个i2c总线结构体管理着i2c设备与I2C驱动的匹配,删除等操作,I2C总线会调用i2c_device_match函数看I2C设备和I2C驱动是否匹配,如果匹配就调用i2c_device_probe函数,进而调用I2C驱动的probe函数。

2、I2C驱动
struct i2c_driver {
	int (*probe)(struct i2c_client *, const struct i2c_device_id *); //probe函数
	struct device_driver driver; //表明这是一个驱动
	const struct i2c_device_id *id_table; //要匹配的从设备信息(名称)
	int (*detect)(struct i2c_client *, struct i2c_board_info *); //设备探测函数
	const unsigned short *address_list; //设备地址
	struct list_head clients; //设备链表
};

对应的是I2C驱动程序 

3、I2C设备 
struct i2c_client {
	unsigned short addr; //设备地址
	char name[I2C_NAME_SIZE]; //设备名称
	struct i2c_adapter *adapter; //设配器,值I2C控制器
	struct i2c_driver *driver; //设备对应的驱动
	struct device dev; //表明这是一个设备
	int irq; //中断号
	struct list_head detected; //节点
};

对应的是I2C设备 

4、I2C设配器

I2C适配器是什么?

经过上面的介绍,知道有I2C驱动和I2C设备,我们需要通过I2C驱动去和I2C设备通讯,这其中就需要一个I2C设配器。

struct i2c_adapter {
	unsigned int id; //设备器的编号
	const struct i2c_algorithm *algo; //算法,发送时序
	struct device dev; //表明这是一个设备
};

其中的i2c_algorithm是算法的意思,对应的就是如何发送I2C时序 

struct i2c_algorithm {
    
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
			   int num);

    
	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
			   unsigned short flags, char read_write,
			   u8 command, int size, union i2c_smbus_data *data);
};
小结:


I2C驱动有4个重要的东西,I2C总线、I2C驱动、I2C设备、I2C设备器
I2C总线:维护着两个链表(I2C驱动、I2C设备),管理I2C设备和I2C驱动的匹配和删除等
I2C驱动:对应的就是I2C设备的驱动程序
I2C设备:是具体硬件设备的一个抽象
I2C设配器:用于I2C驱动和I2C设备间的通用,是SOC上I2C控制器的一个抽象
以注册I2C驱动为例,简单讲解I2C总线的运行机制(I2C设备道理相同)

1、注册I2C驱动
2、将I2C驱动添加到I2C总线的驱动链表中
3、遍历I2C总线上的设备链表,根据i2c_device_match函数进行匹配,如果匹配成功系统则会分配一个i2c_client结构体,并当做参数传递给i2c_device_probe函数。
4、i2c_device_probe函数会调用I2C驱动的probe函数

二、驱动编写测试

        对于i2c总线属于内核的内容,留着以后学~,i2c adapter已经由芯片厂商编写,因此我们只需学习设备驱动编写。

1、修改设备树

        从原理图中可以看出,UART4_RXD作为I2C1_SDA,UART4_TXD作为I2C1_SCL。

因此需要修改对应的pinctrl节点信息,使管脚复用为I2C1功能 。修改后结果如下:

随后在设备树i2c1接口上添加AP3216C设备节点信息,添加结束后如下:

        其中@后面的“1e”是 ap3216c 的器件地址,compatible属性用于驱动和设备的匹配,reg属性也用来保存ap3216c的器件地址。

        修改完设备树后,重启开发板可以发现i2c总线上已经可以看到ap3216c这个设备了。

2、 搭建i2c驱动框架
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "ap3216creg.h"


static struct i2c_device_id ap3216c_id[] = {
    {"ap3216c,idtable",0},
    {},
};


static struct of_device_id ap3216c_of_match[] = {
    {.compatible = "ap3216c,tree"},
    {},
};


static int ap3216c_probe(struct i2c_client *client,const struct i2c_device_id *id){
    printk("ap3216c initrn");
    
	return 0;
}

static int ap3216c_remove(struct i2c_client *client){
    printk("ap3216c exitrn");
    return 0;
}


static struct i2c_driver ap3216c_driver = {
    .probe = ap3216c_probe,
    .remove = ap3216c_remove,
    .driver = {
        .name = "ap3216c",
        .owner = THIS_MODULE,
        .of_match_table = of_match_ptr(ap3216c_of_match),
    },
    
    .id_table = ap3216c_id,
};


static int __init ap3216c_init(void){
    int ret = 0;

    ret = i2c_add_driver(&ap3216c_driver);  

    return ret;
}


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


module_init(ap3216c_init);
module_exit(ap3216c_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYC");

        i2c驱动框架采用,设备树匹配方式进行。当ap3216c_of_match[]中的compatible属性与设备树节点compatible属性匹配后就会执行.probe函数,卸载驱动时会执行.remove函数。

验证结果如下:

 注意:我遇到了一个问题,当我把传统id匹配方式注释掉,只保留设备树匹配方式以后发现,.probe函数无法执行????我还无法解决,因此暂时跳过!!!

完整驱动框架如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "ap3216creg.h"

#define AP3216C_NAME "AP3216C"
#define AP3216C_COUNT 1

 
struct ap3216c_dev{
    struct cdev cdev;   
    dev_t devid;    
    struct class *class;    
    struct device *device;  
    int major;  
    int minor;  
};

struct ap3216c_dev ap3216c;


static int ap3216c_open(struct inode *inode, struct file *filp){
        filp->private_data = &ap3216c;
        printk("ap3216c openrn");
        return 0;
}
static ssize_t ap3216c_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){
    printk("ap3216c readrn");
    return 0;
}
    
static int ap3216c_release(struct inode *inode, struct file *filp){
    printk("ap3216c releasern");
    return 0;
}


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



static struct i2c_device_id ap3216c_id[] = {
    {"ap3216c,idtable",0},
    {},
};


static struct of_device_id ap3216c_of_match[] = {
    {.compatible = "ap3216c,tree"},
    {},
};


static int ap3216c_probe(struct i2c_client *client,const struct i2c_device_id *id){
        int ret = 0;
    printk("ap3216c initrn");
    
    ap3216c.major = 0;  
    if(ap3216c.major){    
        ap3216c.devid = MKDEV(ap3216c.major, 0);    
        ret = register_chrdev_region(ap3216c.devid, AP3216C_COUNT, AP3216C_NAME);
    }
    else{   
        ret = alloc_chrdev_region(&ap3216c.devid, 0, AP3216C_COUNT, AP3216C_NAME);
        ap3216c.major = MAJOR(ap3216c.devid);
        ap3216c.minor = MINOR(ap3216c.devid);
    }
    if(ret < 0){
        printk("ap3216c register error!rn");
        goto fail_devid;
    }
    printk("ap3216c major = %d, minor = %d rn", ap3216c.major, ap3216c.minor);//打印主次设备号

    ap3216c.cdev.owner = THIS_MODULE;
    cdev_init(&ap3216c.cdev, &ap3216c_fops);
    ret = cdev_add(&ap3216c.cdev, ap3216c.devid, AP3216C_COUNT);
     if(ret < 0){
        goto fail_cdev;
    }
   
    
    ap3216c.class = class_create(THIS_MODULE, AP3216C_NAME);
    if(IS_ERR(ap3216c.class)){
        ret = PTR_ERR(ap3216c.class);
        goto fail_class;
    }

    ap3216c.device = device_create(ap3216c.class, NULL, ap3216c.devid, NULL, AP3216C_NAME);
     if(IS_ERR(ap3216c.device)){
         ret = PTR_ERR(ap3216c.device);
        goto fail_device;
    }
    return 0;

fail_device:
    class_destroy(ap3216c.class);
fail_class:
    cdev_del(&ap3216c.cdev);
fail_cdev:
    unregister_chrdev_region(ap3216c.devid, AP3216C_COUNT);
fail_devid:
    return ret;
}

static int ap3216c_remove(struct i2c_client *client){
    printk("ap3216c exitrn");
     
    cdev_del(&ap3216c.cdev);
    
    unregister_chrdev_region(ap3216c.devid, AP3216C_COUNT);

    
    device_destroy(ap3216c.class, ap3216c.devid);
    
    class_destroy(ap3216c.class);

    return 0;
}


static struct i2c_driver ap3216c_driver = {
    .probe = ap3216c_probe,
    .remove = ap3216c_remove,
    .driver = {
        .name = "ap3216c",
        .owner = THIS_MODULE,
        .of_match_table = of_match_ptr(ap3216c_of_match),
    },
    
    .id_table = ap3216c_id,
};


static int __init ap3216c_init(void){
    int ret = 0;
    ret = i2c_add_driver(&ap3216c_driver);

    return ret;
}


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


module_init(ap3216c_init);
module_exit(ap3216c_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYC");
3、 AP3216C读写函数编写

        通过IIC适配器,来向AP3216C发送或读取数据

①向AP3216C寄存器写数据

         如图是一个i2c的写数据时序图,从图中可以看出具体步骤:##器件地址为7位,末位0是写
开始信号-->发送i2c器件地址-->从机发送应答-->发送要写入寄存器地址-->从机发送应答-->发送要写入的数据-->从机发送应答-->停止信号。

具体函数为:

static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len)
{
	u8 b[256];
	struct i2c_msg msg;
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	
	b[0] = reg;					
	memcpy(&b[1],buf,len);		
		
	msg.addr = client->addr;	
	msg.flags = 0;				

	msg.buf = b;				
	msg.len = len + 1;			

	return i2c_transfer(client->adapter, &msg, 1);
}


static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)
{
	u8 buf = 0;
	buf = data;
	ap3216c_write_regs(dev, reg, &buf, 1);
}

         Linux内核采用msg这个参数描述一个消息。其中msg.addr保存i2c设备的器件地址。msg.flags是一个标志位,为0表示写数据。msg.buf保存要写数据的首地址。msg.len表示要写数据的长度,单位为字节。

        注意:为什么写数据会有两个函数?因为ap3216c的寄存器长度是8位,有的i2c寄存器长度可能不止8位,因此s32 ap3216c_write_regs其实是一个通用写多个寄存器的函数,ap3216c_write_reg才是针对ap3216c的寄存器写数据的函数。

②从AP3216C寄存器读数据

   如图是一个i2c的读数据时序图,从图中可以看出具体步骤:##器件地址为7位,末位1是读

开始信号-->发送i2c器件地址-->从机发送应答-->重新发送起始信号-->发送要读的寄存器地址-->从机发送应答-->重新发送起始信号-->重新发送i2c器件地址-->从机发送应答信号-->主机读取从机发送数据-->主机发送NO ACK表示读取完成-->主机发送STOP信号结束通讯

具体函数为:

static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
{
	int ret;
	struct i2c_msg msg[2];
	struct i2c_client *client = (struct i2c_client *)dev->private_data;

	
	msg[0].addr = client->addr;			
	msg[0].flags = 0;					
	msg[0].buf = ®					
	msg[0].len = 1;						

	
	msg[1].addr = client->addr;			
	msg[1].flags = I2C_M_RD;			
	msg[1].buf = val;					
	msg[1].len = len;					

	ret = i2c_transfer(client->adapter, msg, 2);
	if(ret == 2) {
		ret = 0;
	} else {
		printk("i2c rd failed=%d reg=%06x len=%dn",ret, reg, len);
		ret = -EREMOTEIO;
	}
	return ret;
}


static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
{
	u8 data = 0;

	ap3216c_read_regs(dev, reg, &data, 1);
	return data;
}

        i2c读数据的步骤比写数据麻烦一些。因为既包含写寄存器又包含读寄存器。因此需要建立一个msg[]数组保存两种信息。

4、完整驱动
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "ap3216creg.h"

#define AP3216C_CNT	1
#define AP3216C_NAME	"ap3216c"

struct ap3216c_dev {
	dev_t devid;			
	struct cdev cdev;		
	struct class *class;	
	struct device *device;	
	struct device_node	*nd; 
	int major;			
	void *private_data;	
	unsigned short ir, als, ps;		
};

static struct ap3216c_dev ap3216cdev;


static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
{
	int ret;
	struct i2c_msg msg[2];
	struct i2c_client *client = (struct i2c_client *)dev->private_data;

	
	msg[0].addr = client->addr;			
	msg[0].flags = 0;					
	msg[0].buf = ®					
	msg[0].len = 1;						

	
	msg[1].addr = client->addr;			
	msg[1].flags = I2C_M_RD;			
	msg[1].buf = val;					
	msg[1].len = len;					

	ret = i2c_transfer(client->adapter, msg, 2);
	if(ret == 2) {
		ret = 0;
	} else {
		printk("i2c rd failed=%d reg=%06x len=%dn",ret, reg, len);
		ret = -EREMOTEIO;
	}
	return ret;
}


static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len)
{
	u8 b[256];
	struct i2c_msg msg;
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	
	b[0] = reg;					
	memcpy(&b[1],buf,len);		
		
	msg.addr = client->addr;	
	msg.flags = 0;				

	msg.buf = b;				
	msg.len = len + 1;			

	return i2c_transfer(client->adapter, &msg, 1);
}


static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
{
	u8 data = 0;

	ap3216c_read_regs(dev, reg, &data, 1);
	return data;
}


static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)
{
	u8 buf = 0;
	buf = data;
	ap3216c_write_regs(dev, reg, &buf, 1);
}


void ap3216c_readdata(struct ap3216c_dev *dev)
{
	unsigned char i =0;
    unsigned char buf[6];
	
	
    for(i = 0; i < 6; i++)	
    {
        buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);	
    }

    if(buf[0] & 0x80) 	
		dev->ir = 0;					
	else 				
		dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0x03); 			
	
	dev->als = ((unsigned short)buf[3] << 8) | buf[2];	  
	
    if(buf[4] & 0x40)	
		dev->ps = 0;    													
	else 				
		dev->ps = ((unsigned short)(buf[5] & 0x3F) << 4) | (buf[4] & 0x0F); 
}


static int ap3216c_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &ap3216cdev;

	
	ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x04);		
	mdelay(50);														
	ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x03);		
	return 0;
}


static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
	short data[3];
	long err = 0;

	struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;
	
	ap3216c_readdata(dev);

	data[0] = dev->ir;
	data[1] = dev->als;
	data[2] = dev->ps;
	err = copy_to_user(buf, data, sizeof(data));
	return 0;
}


static int ap3216c_release(struct inode *inode, struct file *filp)
{
	return 0;
}


static const struct file_operations ap3216c_ops = {
	.owner = THIS_MODULE,
	.open = ap3216c_open,
	.read = ap3216c_read,
	.release = ap3216c_release,
};

 
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	
	if (ap3216cdev.major) {
		ap3216cdev.devid = MKDEV(ap3216cdev.major, 0);
		register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME);
	} else {
		alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME);
		ap3216cdev.major = MAJOR(ap3216cdev.devid);
	}

	
	cdev_init(&ap3216cdev.cdev, &ap3216c_ops);
	cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);

	
	ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME);
	if (IS_ERR(ap3216cdev.class)) {
		return PTR_ERR(ap3216cdev.class);
	}

	
	ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL, AP3216C_NAME);
	if (IS_ERR(ap3216cdev.device)) {
		return PTR_ERR(ap3216cdev.device);
	}

	ap3216cdev.private_data = client;

	return 0;
}


static int ap3216c_remove(struct i2c_client *client)
{
	
	cdev_del(&ap3216cdev.cdev);
	unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);

	
	device_destroy(ap3216cdev.class, ap3216cdev.devid);
	class_destroy(ap3216cdev.class);
	return 0;
}


static const struct i2c_device_id ap3216c_id[] = {
	{"ap3216c,idtable",0}, 
	{}
};


static const struct of_device_id ap3216c_of_match[] = {
	{ .compatible = "ap3216c,tree" },
	{  }
};

	
static struct i2c_driver ap3216c_driver = {
	.probe = ap3216c_probe,
	.remove = ap3216c_remove,
	.driver = {
			.owner = THIS_MODULE,
		   	.name = "ap3216c",
		   	.of_match_table = ap3216c_of_match, 
		   },
	.id_table = ap3216c_id,
};
		   

static int __init ap3216c_init(void)
{
	int ret = 0;

	ret = i2c_add_driver(&ap3216c_driver);
	return ret;
}


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



module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");



5、应用 
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include 
#include 
#include 
#include 
#include 


int main(int argc, char *argv[])
{
	int fd;
	char *filename;
	unsigned short databuf[3];
	unsigned short ir, als, ps;
	int ret = 0;

	if (argc != 2) {
		printf("Error Usage!rn");
		return -1;
	}

	filename = argv[1];
	fd = open(filename, O_RDWR);
	if(fd < 0) {
		printf("can't open file %srn", filename);
		return -1;
	}

	while (1) {
		ret = read(fd, databuf, sizeof(databuf));
		if(ret == 0) { 			
			ir =  databuf[0]; 	
			als = databuf[1]; 	
			ps =  databuf[2]; 	
			printf("ir = %d, als = %d, ps = %drn", ir, als, ps);
		}
		usleep(200000); 
	}
	close(fd);		
	return 0;
}

结果测试:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/747105.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号