您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 管理学资料 > LinuxI2C设备驱动编写
LinuxI2C设备驱动编写(一)在Linux驱动中I2C系统中主要包含以下几个成员:I2Cadapter即I2C适配器I2Cdriver某个I2C设备的设备驱动,可以以driver理解。I2Cclient某个I2C设备的设备声明,可以以device理解。I2Cadapter是CPU集成或外接的I2C适配器,用来控制各种I2C从设备,其驱动需要完成对适配器的完整描述,最主要的工作是需要完成i2c_algorithm结构体。这个结构体包含了此I2C控制器的数据传输具体实现,以及对外上报此设备所支持的功能类型。i2c_algorithm结构体如下:structi2c_algorithm{int(*master_xfer)(structi2c_adapter*adap,structi2c_msg*msgs,intnum);int(*smbus_xfer)(structi2c_adapter*adap,u16addr,unsignedshortflags,charread_write,u8command,intsize,unioni2c_smbus_data*data);u32(*functionality)(structi2c_adapter*);};如果一个I2C适配器不支持I2C通道,那么就将master_xfer成员设为NULL。如果适配器支持SMBUS协议,那么需要去实现smbus_xfer,如果smbus_xfer指针被设为NULL,那么当使用SMBUS协议的时候将会通过I2C通道进行仿真。master_xfer指向的函数的返回值应该是已经成功处理的消息数,或者返回负数表示出错了。functionality指针很简单,告诉询问着这个I2C主控器都支持什么功能。在内核的drivers/i2c/i2c-stub.c中实现了一个i2cadapter的例子,其中实现的是更为复杂的SMBUS。SMBus与I2C的区别通常情况下,I2C和SMBus是兼容的,但是还是有些微妙的区别的。时钟速度对比:I2CSMBus最小无10kHz最大100kHZ(标准)400kHz(快速模式)2MHz(高速模式)100kHz超时无35ms在电气特性上他们也有所不同,SMBus要求的电压范围更低。I2Cdriver具体的I2C设备驱动,如相机、传感器、触摸屏、背光控制器常见硬件设备大多都有或都是通过I2C协议与主机进行数据传输、控制。结构体如下:structi2c_driver{unsignedintclass;/*Notifiesthedriverthatanewbushasappearedorisabouttobe*removed.Youshouldavoidusingthis,itwillberemovedina*nearfuture.*/int(*attach_adapter)(structi2c_adapter*)__deprecated;//旧的与设备进行绑定的接口函数int(*detach_adapter)(structi2c_adapter*)__deprecated;//旧的与设备进行解绑的接口函数/*Standarddrivermodelinterfaces*/int(*probe)(structi2c_client*,conststructi2c_device_id*);//现行通用的与对应设备进行绑定的接口函数int(*remove)(structi2c_client*);//现行通用与对应设备进行解绑的接口函数/*drivermodelinterfacesthatdon'trelatetoenumeration*/void(*shutdown)(structi2c_client*);//关闭设备int(*suspend)(structi2c_client*,pm_message_tmesg);//挂起设备,与电源管理有关,为省电int(*resume)(structi2c_client*);//从挂起状态恢复/*Alertcallback,forexamplefortheSMBusalertprotocol.*Theformatandmeaningofthedatavaluedependsontheprotocol.*FortheSMBusalertprotocol,thereisasinglebitofdatapassed*asthealertresponse'slowbit(eventflag).*/void(*alert)(structi2c_client*,unsignedintdata);/*aioctllikecommandthatcanbeusedtoperformspecificfunctions*withthedevice.*/int(*command)(structi2c_client*client,unsignedintcmd,void*arg);structdevice_driverdriver;//I2C设备的驱动模型conststructi2c_device_id*id_table;//匹配设备列表/*Devicedetectioncallbackforautomaticdevicecreation*/int(*detect)(structi2c_client*,structi2c_board_info*);constunsignedshort*address_list;structlist_headclients;};#defineto_i2c_driver(d)container_of(d,structi2c_driver,driver)//一般编写驱动过程中对象常是driver类型,可以通过to_i2c_driver找到其父类型i2c_driver如同普通设备的驱动能够驱动多个设备一样,一个I2Cdriver也可以对应多个I2Cclient。以重力传感器AXLL34X为例,其实现的I2C驱动为:staticconststructi2c_device_idadxl34x_id[]={{adxl34x,0},//匹配i2cclient名为adxl34x的设备{}};MODULE_DEVICE_TABLE(i2c,adxl34x_id);staticstructi2c_driveradxl34x_driver={.driver={.name=adxl34x,.owner=THIS_MODULE,.pm=&adxl34x_i2c_pm,//指定设备驱动的电源管理接口,包含suspend、resume},.probe=adxl34x_i2c_probe,//组装设备匹配时候的匹配动作.remove=adxl34x_i2c_remove,//组装设备移除接口.id_table=adxl34x_id,//制定匹配设备列表};module_i2c_driver(adxl34x_driver);这里要说明一下module_i2c_driver宏定义(i2c.h):#definemodule_i2c_driver(__i2c_driver)\module_driver(__i2c_driver,i2c_add_driver,\i2c_del_driver)#definei2c_add_driver(driver)\i2c_register_driver(THIS_MODULE,driver)module_driver():#definemodule_driver(__driver,__register,__unregister,...)\staticint__init__driver##_init(void)\{\return__register(&(__driver),##__VA_ARGS__);\}\module_init(__driver##_init);\staticvoid__exit__driver##_exit(void)\{\__unregister(&(__driver),##__VA_ARGS__);\}\module_exit(__driver##_exit);理解上述宏定义后,将module_i2c_driver(adxl34x_driver)展开就可以得到:staticint__intadxl34x_driver_init(void){returni2c_register_driver(&adxl34x_driver);}module_init(adxl34x_driver_init);staticvoid__exitadxl34x_driver_exit(void){returni2c_del_driver(&adxl34x_driver);}module_exit(adxl34x_driver_exit);这一句宏就解决了模块module安装卸载的复杂代码。这样驱动开发者在实现I2C驱动时只要将i2c_driver结构体填充进来就可以了,无需关心设备的注册与反注册过程。I2Cclient即I2C设备。I2C设备的注册一般在板级代码中,在解析实例前还是先熟悉几个定义:structi2c_client{unsignedshortflags;//I2C_CLIENT_TEN表示设备使用10bit从地址,I2C_CLIENT_PEC表示设备使用SMBus检错unsignedshortaddr;//设备从地址,7bit。这里说一下为什么是7位,因为最后以为0表示写,1表示读,通过对这个7bit地址移位处理即可。addr1&0x0即写,addr1|0x01即读。charname[I2C_NAME_SIZE];//从设备名称structi2c_adapter*adapter;//此从设备依附于哪个adapter上structi2c_driver*driver;//此设备对应的I2C驱动指针structdevicedev;//设备模型intirq;//设备使用的中断号structlist_headdetected;//用于链表操作};#defineto_i2c_client(d)container_of(d,structi2c_client,dev)//通常使用device设备模型进行操作,可以通过to_i2c_client找到对应client指针structi2c_board_info{chartype[I2C_NAME_SIZE];//设备名,最长20个字符,最终安装到client的name上unsignedshortflags;//最终安装到client.flagsunsignedshortaddr;//设备从地址slaveaddress,最终安装到client.addr上void*platform_data;//设备数据,最终存储到i2c_client.dev.platform_data上structdev_archdata*archdata;structdevice_node*of_node;//OpenFirmware设备节点指针structacpi_dev_nodeacpi_node;intirq;//设备采用的中断号,最终存储到i2c_client.irq上};//可以看到,i2c_board_info基本是与i2c_client对应的。#defineI2C_BOARD_INFO(dev_type,dev_addr)\.type=dev_type,.addr=(dev_addr)//通过这个宏定义可以方便的定义I2C设备的名称和从地址(别忘了是7bit的)下面还是以adxl34x为例:staticstructi2c_board_infoi2c0_devices[]={{I2C_BOARD_INFO(ak4648,0x12),},{I2C_BOARD_INFO(r2025sd,0x32),},{I2C_BOARD_INFO(ak8975,0x0c),.irq=
本文标题:LinuxI2C设备驱动编写
链接地址:https://www.777doc.com/doc-2885119 .html