Linux驱动开发 之 i2C驱动开发

         本博客主要介绍的是在Samsung 4412平台进行i2C驱动开发。驱动开发最后都需要在文件系统中注册设备节点,我将i2c设备以字符设备的方式进行注册,当然你也可以注册为混杂设备。混杂设备驱动开发可以算是字符设备开发的一种,但是相对简单一点,不能体现出整个字符设备开发的的整体框架,因为我也是处在学习阶段,所以就将该设备注册为 字符设备,文中我会详细的介绍i2c驱动的整个架构,但不会详细介绍怎么注册字符驱动,好的,下面开始:

       i2c_adapter可以理解做一个i2c的主控制器,里面封装了i2c的所有信息,包括时序,地址等。该适配器由i2c的主机创建,使用i2c_client和i2c_driver相连,从而使内核通相应的方法与设备进行交互。

      i2c_algorithm描述一个i2c主机的发送时序的信息,该类的对象algo是i2c_adapter的一个域,其中的master_xfer()注册的函数最终被设备驱动端的i2c_transfer()回调

     i2c_client描述一个挂接在硬件i2c总线上的设备的设备信息,也就是你对应去要驱动的芯片,即i2c设备的设备对象,与i2c_driver对象匹配成功后通过detected和i2c_driver以及i2c_adapter相连,在控制器驱动与控制器设备匹配成功后被控制器驱动通过i2c_new_device()创建。

     i2c_driver描述一个挂接在硬件i2c总线上的设备的驱动方法,即i2c设备的驱动对象,通过i2c_bus_type和设备信息i2c_client匹配,匹配成功后通过clients和i2c_client对象以及i2c_adapter对象相连。

    i2c_msg描述一个在设备端和主机端之间进行流动的数据, 在设备驱动中打包并通过i2c_transfer()发送。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#include <linux/regulator/consumer.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/cdev.h>
#include <asm/uaccess.h> 
#define DEV_MAJOR 0  ///主设备号 次设备号
#define DEV_MINOR 0


#define DEVICE_NAME  "ADC_DAC"
#define DEVICE_MINOR_NUM  2




#define PCF8591T_ADDRESS  0x48     ////定义i2c从机地址
#define PCF8591T_ADC      0x40     // 读寄存器 滑动变阻器


int numdev_major=DEV_MAJOR;//主设备号
int numdev_minor=DEV_MINOR;//次设备号


dev_t dev_no;//用来申请设备号
struct class *clas;//注册设备类


struct PCF8591T_pri{
struct cdev dev;
struct i2c_client *client;

};

struct PCF8591T_pri dev;


static void PCF8591T_write_byte(struct i2c_client *client,const unsigned char reg,const unsigned char val)
{
char txbuf[2]={reg,val};

struct i2c_msg msg[1]=
{
[0]={

.addr=client->addr,
.flags=0,                   //写标志
.len =sizeof(txbuf),
.buf=txbuf,
},

};

i2c_transfer(client->adapter,msg,ARRAY_SIZE(msg));  //


static char  PCF8591T_read_byte(struct i2c_client *client,const unsigned char reg)
{
int  ret;
char txbuf[1]={reg};       //要写的寄存器
char rxbuf[1]={0};         //收到的数据
struct i2c_msg msg[2]=
{
[0]={

.addr=client->addr,
.flags=0,                   //写标志
.len =sizeof(txbuf),
.buf=txbuf,
},
[1]={

.addr=client->addr,
.flags=1,                   //读标志
.len =sizeof(rxbuf),          //接收到的数据
.buf=rxbuf,
},

};

ret=i2c_transfer(client->adapter,msg,ARRAY_SIZE(msg));  //判断数据传输是否完成
if(ret<0)
{
pr_err("read reg error!\n");

}
ret=rxbuf[0];
return  ret;



static const struct i2c_device_id i2c_PCF8591T_id[]=
{
   {"PCF8591T",0},
   {}
     
};
static int dev_open(struct inode *ip, struct file *fp)
{
printk("==%s:\n",__FUNCTION__); 
    return 0;
}
static int dev_release(struct inode *ip, struct file *fp)
{
    printk("==%s:\n",__FUNCTION__); 
    return 0;
}
static long dev_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
unsigned char adc_data;
unsigned char get_data;
 ///copy_from_user(&reg_data,buffer,1);  ////内核空间地址指针  用户空间地址指针  数据长度
int ret;
ret=PCF8591T_read_byte(dev.client,PCF8591T_ADC);      //////返回的数据
if(ret<0)
{
pr_err("read reg error!\n");
 
}
adc_data=ret;
ret = copy_to_user(&adc_data,&get_data,1);   ////内核空间地址指针  用户空间地址指针  数据长度


   return 0;
}
struct file_operations fops ={
.open=dev_open,
.release =dev_release,
.unlocked_ioctl= dev_ioctl,

};


static int i2c_PCF8591T_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
int ret;
    printk("==%s:\n",__FUNCTION__);     //模块加载的时候打印函数名
    
    dev.client=client; 
   
   
ret=alloc_chrdev_region(&dev_no,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NAME);
    numdev_major = MAJOR(dev_no);

numdev_minor=MINOR(dev_no);
printk(KERN_EMERG "adev_region numdev_major %d !\n",numdev_major); //注册主设备号
printk(KERN_EMERG "adev_region numdev_minor %d !\n",numdev_minor); //注册从设备号

if(ret<0){    ///注册失败
printk(KERN_EMERG "register_chrdev_region req %d is failed!\n",numdev_major);
}

cdev_init(&dev.dev,&fops);     ///device 初始化
ret=cdev_add(&dev.dev,dev_no,1);  //添加设备到内核
    if(ret<0){    ///注册失败
printk(KERN_EMERG "cdev_add %d is fail! ");
}
    printk(KERN_INFO"match OK!\r\n");
   
    clas = class_create(THIS_MODULE,DEVICE_NAME);         //创建设备类
    device_create(clas,NULL,dev_no,NULL,"%s%d",DEVICE_NAME,1);
  return 0;

}

static int __devexit i2c_PCF8591T_remove(struct i2c_client *client)
{
     i2c_set_clientdata(client,NULL);                                  ///模块退出的时候打印函数名
     printk("==%s:\n",__FUNCTION__);
    
 
cdev_del(&dev.dev);
/*摧毁设备节点函数d*/
device_destroy(clas,MKDEV(numdev_major,numdev_minor));
class_destroy(clas);
return 0;
}

static struct i2c_driver i2c_PCF8591T_driver ={ // driver_regiester


   .probe    =i2c_PCF8591T_probe,
   .remove   =__devexit_p(i2c_PCF8591T_remove),
   .id_table =i2c_PCF8591T_id,
   .driver={
             .name = "PCF8591T",
             .owner=THIS_MODULE,
           },


};
static int __init i2c_PCF8591T_init(void)
{
   
   printk("==%s:\n",__FUNCTION__);
   return i2c_add_driver(&i2c_PCF8591T_driver);  //添加驱动
}

static void __exit i2c_PCF8591T_exit(void)
{
printk("==%s:\n", __FUNCTION__);
i2c_del_driver(&i2c_PCF8591T_driver);
}

late_initcall(i2c_PCF8591T_init);   //内核初始化列表 内核初始化分为 7个等级
module_exit(i2c_PCF8591T_exit);

MODULE_AUTHOR("fanxiangqiang");
MODULE_DESCRIPTION("PCF8591_I2c");
MODULE_LICENSE("GPL");

猜你喜欢

转载自blog.csdn.net/a13698709128/article/details/80573896