经典i2c 数据交互 总线.docx

上传人:太** 文档编号:35033752 上传时间:2022-08-20 格式:DOCX 页数:14 大小:48.88KB
返回 下载 相关 举报
经典i2c 数据交互 总线.docx_第1页
第1页 / 共14页
经典i2c 数据交互 总线.docx_第2页
第2页 / 共14页
点击查看更多>>
资源描述

《经典i2c 数据交互 总线.docx》由会员分享,可在线阅读,更多相关《经典i2c 数据交互 总线.docx(14页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。

1、1简介I2C总线仅仅使用SCL、SDA两根信号线就实现了设备之间的数据交互,极大地简 化对硬件资源和PCB板布线空间的占用。因此,I2C总线被非常广泛地应用在 EEPROM、实时钟、小型LCD等设备与CPU的接口中。Linux I2C GPIO驱动是在没有专用I2C芯片的情况下,用GPIO 口来模拟12c总线时序, 完成Linux与I2C设备的通信过程。用两根GPIO,分别模拟SDA和SCLo它与使用i2c芯 片的驱动有所不同的是传输算法的实现,GPIO模拟i2c驱动中有自己的一套传输算法。GPIO 模拟12c是要占用CPU资源的,而用12c芯片是不占CPU资源的。使用i2c子系统,而不 使用

2、普通字符设备,有以下好处:1) 使用Linux I2C子系统,不需要去过于详细了解12c操作。2) 编写驱动可移植性强。3) 可以使用内核资源,当面对复杂12c器件,工作量相对少得多。12c工作原理:I2C总线标准的两根传输线,SDA是数据线,Scl是时钟线,当SCL 为高,SDA由高谕低时,发送启动信息,发送9个脉冲,1-7是地址,8是读写控制位,9是 ACK应答位,所以挂在12c上的被控设备都接受所发送的信息,并把接收到的7位地址与自 己的地址进行比拟,如果相同ACK就会反应应答.当SCL为低,SDA由低4高,那么发送 停止信号。2架构Linux的I2C构架分为三个局部:1) I2C co

3、re 框架提供了核心数据结构的定义和相关接口函数,用来实现I2C适配器驱动和设备驱动的注册、注销管理,以及12c通信方法上层的、与具体适配器无关的代码, 为系统中每个12c总线增加相应的读写方法。I2C core 框架具体实现在/drivers/i2c 目录下的 i2c-core.c 和 i2c-dev.c12c总线驱动定义描述具体I2C总线适配器的i2c_ad叩ter数据结构、实现在具体I2C适配器上 的I2C总线通信方法,并由i2c_algorithm数据结构进行描述。经过I2C总线驱动的的代码, 可以为我们控制12c产生开始位、停止位、读写周期以及从设备的读写、产生ACK等。12c总线驱

4、动具体实现在/drivers/i2c目录下busses文件夹。例如:Linux I2C GPIO 总线驱动为i2c_gpio.c. 12c总线算法在/drivers/i2c目录卜algos文件夹。例如:Linux I2C GPIO 总线驱动算法实现在i2c_algo_bit.c.3)I2C设备驱动是对具体I2C硬件驱动的实现。I2C设备驱动通过I2C适配器与CPU通信。其中主 要包含i2c_driver和i2c_client数据结构,i2c_driver结构对应一套具体的驱动方法,例如: probex remove、suspend等,需要自己申明。i2c_clieni数据结构由内核根据具体的设

5、备注册 信息自动生成,设备驱动根据硬件具体情况填充。具体使用下面介绍。I2C设备驱动具体实现放在在/drivers/i2c目录下chips文件夹。ISJINIT_LIST_HEAD(&adap-clients);mutexock(&coreock);/* Add the adapter to the driver core.* If the parent pointer is not set up, * we add this adapter to the host bus.*/adap-dev.parent = &pwiiiT.bus;if (ad叩*dev.parent = NULL) p

6、hysical dcvicen, adap-namc);pr_debug(I2C adapter driver %sl forgot to specify dev set_name(&adap-dev, i2c-%d, adap-nr);adap-dev.release = & i 2c_adapter_de v_rel ease;adap-dev.class = &i2c_adapter_class; res = device_register(&adap-dev);if(res)goto outjist;dev_dbg(&adap-dev, adapter %s registeredn,

7、adap-name);/* create pre-declared device nodes for new-style drivers */if (adap-nr nr);goto out_unlock;其中的 i2c_scan_static_board_info(adap)i2c_ncw_dcvice:static void i2c_scan_slalic_board_infb(sli ucl i2c_adapter *adapter)struct i2c_devinfo *devinfo;mutex_lock(&-i2c_board_lock);list_fbr_each_entry(d

8、cvinfo, &_i2c_board_list, list) if (devinfb-busnum = adapter-nr& ! i2c_ncw_dcvice(adaptcr,&devinfo-board_info) 就是这里,它根据devinfo来创立个新的i2c_client设备结构printk(KERN_ERR i2c-core: cant create i2c%d-%04xn,i2c_adapter_id(adapter),devinfo-board_info.addr);mutex_unlock(&_i2c_board_lock);Mi2c_new_device:struct i

9、2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)Astruct i2c_client client;intstatus;client = kzalloc(sizeof *clien(, GFP_KERNEL);if (Iclient)return NULL;client-adapter = adap;client-dev.platfonn_data = i n fo-pl atform_data;if (info-archdata)clicnt-dcv.archdata

10、= *info-archdata;clien(-flags = info-flags;client-addr = info-addr;client-irq = info-irq;strlcpy(client-name, infb-type, sizeof(client-name);/* a new style driver may be bound to this device when wereturn from this function, or any later moment (e.g. maybe* hotplugging will load the driver module),

11、and the devicerefcount model is the standard driver model one.status = i2c_attach_client(client);if (status addr, 使用设备驱动的一个i2c设备。到这里就可以通过Linux 12c核心提供的不依赖硬件接口的 函数了,接受/发送函数等。也许你会疑惑在创立i2c_client设备时,是根据devinfo来创立的,那么这个devinfo 是从那来的?我们在dev ices.c中不是注册的是i2c_board_info这个结构吗?没错,在 devices.c中确实是注册的i2c_board_

12、inf。结构,下面我们来看看注册这个结构数组的这个 i 2c_regester_board_i n fo() | 数: int initi2c_register_board_info(int busnum, struct i2c_board_infb const unsigned len) .int status;down_write(&-i2c_board_lock);/* dynamic bus numbers will be assigned after the last static one */ if (busnum = _i2c_first_dynamic_bus_num)_i2c

13、_first_dynamic_bus_num = busnum + 1;for (status = 0; len; len-, info+) struct i2c_devinfo *devinfb; devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); if (!devinfo) pr_debug(i2c-core: cant register boardinfo!n);status = -ENOMEM;break;dcvinfo-busnum = busnum;devinfo-board_info = *info;list_add_tail(&de

14、vinfo-list, &_i2c_board_list);up_writc(&-i2c_board_lock); return status;A看到了吧,在这个注册函数里,它创立一个devinfo结构变量,并用总线号和 i2c_board_info结构来初始化这个devinfo变量,然后加入一个全局的devinfo链表,来看看 这个devinfo结构体的定义: struct i2c_devinfo struct list_hcad list;intbusnum;struct i2c_board_infbboard_info;这个注册函数就是在i2c-boardinfo.c中定义的,它维护一个

15、全局的devinfo链表,在 创立i2c_clien(的时候,就是通过这个devinfo链表来逐一创立的。5使用I2C子系统资源函数操作I2C设备Linux I2C核心提供的函数主要有:1)增加/删除 i2c_adapterInt i2c_add_adapter(struct i2c_adapter *adap);Int i2c_del_adapter(struct i2c_adapter *adap);2)增加/删除i2c_driverInt i2c_register_driver(struct module result = 1; elsereturn 0;char buff2;/ cli

16、ent-addr = LIS35DE_AddR;owner,struct i2c_driver *driver);Int i2c_del_driver(struct i2c_driver *driver);3)i2c_client依附和脱离Int i2c_at(ach_client(struct i2c_client *client);Int i2c_dctach_clicnt(struct i2c_clicnt *clicnt);4)i2c传送发送接收Int i2c_transfer(struct i2c_adaper *adap,struct i2c_insg *msgs,int num)

17、;In( i2c_master_send(struct i2c_clien( *client,const char *buf,in( count);Int i2c_master_recv(struct i2c_client *client,const char *buf.int count);以上三个函数必须首先在设备驱动中的xxx_probe中提供指向i2c_client的指针,注意在提取出i2c_client之前不要使用,否那么出现空指针。具体使用方法:eg:struct i2c_client *lhis_clieni;申明全局变量;static int _init st_lis35de_

18、probe(struct i2c_client client, const struct i2c_device_id * devid)I2c_set_clien(data(client,&lis35de);Client-addr = Oxlc;this_clicnt = client;/提取出client,然后使用,这三句要放到acc_init之前。acc_inil();这个函数最终会调用LIS35DE_init(),把这个函数注释掉,因为这个函数的 gpio的设定己经被放到devices.c gpio-i2c注册之前,这里不用再来,遍。void LIS35DE_IICWrite(u_int8

19、_t RegAdd, u_int8_t Data ,u_int8_t *result)Jchar buffer21;*result = 1;/ buHer0=LIS35DE AddW;使用 i2c masler send 等函数,不再需要传送地址 bufferO=RegAdd;bufferl=Data;if(i2c_master_send(this_client, buffer,2)0)printk(KERN_ERR LIS35DEJICWrite: i2c_master_send errorn);return;result = 0;return;Mint8_t LIS35DE_IICRead

20、(u_int8_t RegAdd,u_int8_t *result)IS8 Data;char buffer;/bufferO=LIS35DE_AddW;使用i2c_master_send等函数,不再需要传送地址bufferO=RegAdd;/buffer2=LIS35DE_AddR; 使用 i2c_master_send 等函数,不再需要传送地址 if(i2c_master_send(this_client. buffer, 1 )0)printk(KERN_ERR LIS35DEJICRead: i2c_master_send errorn); return -1;if( i2c._mas

21、ter_recv(this_client, &Data. I )addr, .flags = 0, Jen = 1, .buf = rxData,.addr = this_client-addr,.flags = I2C_M_RD,Jen = length,.buf = rxData,1#if DEBUGprintk(KERNNFO sn, _FUNCTION_);#endifif (i2c_transfer(this_client-adapter, msgs, 2) addr,.flags = 0,Jen = length,.buf = txData,#if DEBUGprinlk(KERN

22、NFO %sn, _FUNCTION_);#endifif (i2c_transfer(his_clienl-adapter, msg, I) addr = LIS35DE_AddW;buffTOl = RegAdd;buff 1 = Data;if( LIS35DE_TxData(buff, 2) 0) #if DEBUGprintk(KERNJNFO %sn; _FUNCTION_);printk(# LIS35IIC Write Error #rn);#endifreturn;*result = 0;Mint8_t LIS35DE_IICRead(u_int8_t RegAdd,u_in

23、t8_t *result)nS8 Data;*result = 1;buffO = RegAdd;if( LIS35DE_RxData(buff, 1) 0 ) #if DEBUGprintkC# LIS35IIC Read Error #rn);#cndifreturn 1;* result = 0;Data = *buff;return Data;其中有一局部/*刃是由代码模拟时序来模拟i2c的。6 Gpio模拟i2c总线的通用传输算法/drivers/i2c/i2c-algo-bit.cinit i2c_bit_add_nuinhered_bus(struct i2c_adapter *

24、adap)int i2c_bi(_add_numbered_bus(struct i2c_adapter *adap)Jint err;err = i2c_bit_prepare_bus(adap);加入adaoter类之前的一些操作,包括设定超时和重试,以及设定i2c_algorithm的具体 设定方法。|if (err)return err;return i2c_add_numbercd_adapter(adap);static int bit_xfer(struct i2c_aclapter i2c_adap,struct i2cjnsg msgs/J, int num) /*参数:具体

25、的适配器需要传送的数据数据数据/static int bit_xfer(struct i2c_adapter *i2c_adap,struct i2c_msg msgsJ, int num)Ji2c_start(adap);启动总线for (i = 0; i flags & I2C_MGNORE_NAK; 检测是否 忽略响应if(!(pmsg-flags & I2C_M_NOSTART) if(i)i2c_repstart(adap);如果是混合模式,那么重新启动传输ret = bit_doAddress(i2c_adap, pinsg);if (ret !=0) & !nak_ok) 如果出

26、错了,那么出错处理goto bailout;if (pmsg-flags & I2C_M_RD) 收数据 else 发送数据/* write bytes from buffer */ret = sendbytcs(i2c_adap, pmsg);if (ret = 1)if (ret len) if (ret = 0)ret = -EREMOTEIO;goto bailout;ret = i;bailout:bit_dbg(3, &i2c_adap-dev, emitting stop conditionn);i2c_stop(adap);return ret;static intsendby

27、tes(struct i2c_cidapter i2c_adap, struct i2c_nisg 知isg) static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)Dwhile (count 0) retval = i2c_outb(i2c_adap, *temp); 发送个字 节的数据/* OK/ACK; or ignored NAK */ /* 一个字节一个字节的往后移动力if (retval 0) | (nak_ok & (retval = 0) count-;lemp+;wrcount+;7总结7

28、.1理清i2c中的个结构体关系通过上面的讲解,已基本上简单地介绍完i2c驱动的方方面面,或许你还是对这里 面的众多结构体之间的联系很迷惑,下面就来分析一下i2c_driver、 i2c_clicnt . i2c_adapter和i2c_algorilhm这4个数据结构的作用及其盘根错节的关系。(1) i2c_adapter 与 i2c_algorithmi2c_ad叩ter对应于物理上的一个适配器,而i2c_algorilhm对应一套通信方法。一个 I2C适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定的访问周期。缺 少i2c_algorithm的i2c_adapte

29、r什么也做不了,因此i2c_adapter中包含其使用的 i2c_algorithm 的指针。i2c_algorithm中的关键函数mastcr_xfcr()用于产生I2C访问周期需要的信号,以 i2c_msg (即I2C消息)为单位。i2c_msg结构体也非常关键,代码清单给出了它的定义。 1 struct i2c_msg _u16addr; /* 设备地址 */2 u16 flags; /* 标志 */_uI61en; /* 消息长度 */3 _u8 *buf; /* 消息数据 */);(2) i2c_driver 与 i2c_clienti2c_driver对应一套驱动方法,是纯粹的用于

30、辅助作用的数据结构,它不对应于任何的物理 实体。i2c_clienl对应于真实的物理设备,每个12c设备都需要一个i2c_client来描述。 i2c_client 一般被包含在i2c字符设备的私有信息结构体中。i2c_driver与i2c_client发生关联的时刻在i2c_driver的a(tach_adapier()函数被运行时。 attach_adapter()会探测物理设备,当确定一个client存在时,把该client使用的i2c_client 数据结构的adapter指针指向对应的i2c_adap(er。driver指针指向该i2c_driver ,并会调用i2c_adapter

31、的client_register()函数。相反的过程 发生在i2c_driver的dctach_clicnt()函数被调用的时候。(3) i2c_adpater 与 i2c_clienti2c_adpater与i2c_client的关系与I2C硬件体系中适配器和设备的关系一致,即i2c_client 依附于i2c_adpater ,由于一个适配器上可以连接多个I2C设备,所以一个i2c_adpaier也 可以被多个i2c_client依附,i2c_adpater中包括依附于它的i2c_client的链表。7.2 i2c驱动的编写建议那么对于个驱开工程师,如何编写自己的i2c相关的驱动,下面仅提

32、供个参考方案:(1)提供I2C适配器的硬件驱动,探测、初始化I2C适配器(如申请I2C的I/O地址 和中断号)、驱动CPU控制的I2C适配器从硬件上产生各种信号以及处理I2C中断等。(2)提供I2C适配器的algorithm ,用具体适配器的xxx_xfer()函数填充i2c_algoriihm 的 master_xfer 指针,并把 i2c_algorilhm 指针赋值给 i2c_adapter 的 algo 指针。(3)实现I2C设备驱动与i2c_driver接口,用具体设备yyy的yyy_attach_adapter()函 数指针、yyy_detach_client()函数指针和yyy_

33、command()函数指针的赋值给i2c_driver的 attach_adapter、 detach_adapter 和 dctach_client 指针。(4)实现I2C设备驱动的文件操作接口,即实现具体设备yyy的yyy_read()、yyy_write() 和yyy_ioctl()函数等。上述工作中1、2属于I2C总线驱动,3、4属于I2C设备驱动,做完这些工作,系 统会增加两个内核模块。3设备注册下面以GP10模拟i2c总线的驱动为例,来介绍设备注册,对于使用i2c芯片的驱动 都是大同小异,主:要在传输算法上的区别。首先make menuconfig把i2c-gpio选上,让它能 编

34、进内核。设备注册包括两种设备的注册,i2c-gpi。总线和i2c设备驱动。1)i2c-gpi。总线注册/drivers/i2c/busses/i2c_gpio.c是i2c-gpio总线驱动源码。在这里可以看到i2c-gpio的注册: static struct platfbrm_driver i2c_gpio_driver = .driver= .name = Mi2c-gpio;/驱动名字.owner = THIS_MODULE,.probe= i2c_gpio_probe,.remove= _devexit_p(i2c_gpio_remove).static int _init i2c_g

35、pio_init(void)9int ret;ret = platform_driver_register(&i2c_gpio_driver); 注册成平台设备 if (ret)printk(KERN_ERR i2c-gpio: probe failed: %dn; ret);return ret;Jmodule_init(i2c_gpio_init);platform是linux虚拟的总线,称为platform总线,相应的设备称为platform_device, 相应的驱动称为platform_drivero我们知道i2c总线也对应一个设备,在这里就是对应的 i2c_adapte结构,这在后

36、面会有详细介绍。在这里可以看到它将i2c总线驱动注册成平台设 备驱动 platform_driver_register(&i2c_gpio_drivcr)o把i2c_gpio设备注册为平台设备,需要在mach_xxx的板级文件(devices.c)中添 加i2c-gpio需要用到的资源定义,即将i2c总线设备封装成平台设备,下面首先定义总线占 用的系统资源:static struct i2c_gpio_pla(form_data i2c3_data = .sda_pin = CONFIG_SDA_PIN;.scLpin = CONFIG_SCL_PIN; 设置需要用到的 gpio 引脚,ude

37、lay = 0, 设置12c工作频率,如果没有默认值为50.timeout = 0, 设置I2C工作超时,如果没有默认值为10由于i2c_gpio驱动需要注册到platform总线上面,还需要在mach_xxx的板级文件中添加 i2c-gpio 的 platform_device 结构。static struct platform_dcvicc i2c3_dcvicc = .name = i2c-gpio 必须和i2c-gpio驱动的名字相同.id= 2,总线ID号.platform_data = &i2c3_data.注册i2c-gpio驱动前要有一个GPIO的设置过程,设置过程如下: I

38、/SDAPnx_gpio_set_mode(GPIO_F8,GPIO_MODE_MUX 1)Pnx_gpio_set_direction(GPIO_F8,GPIO_DIR_OUTPUT) /SCLPnx_gpio_set_mode(GPIO_F7,GPIO_MODE_MUX 1)Pnx_gpio_set_direction(GPIO_F7,GPIO_DIR_OUTPUT)最后把i2c-gpio设备注册进platform总线。platform_device_rcgister(&i2c3_device);把i2c设备驱动注册到i2c-gpio总线例如:设备驱动源码在/drivers/i2c/chi

39、ps/lis35de.c,其注册到i2c总线需要的做法如下。首先定义设备ID:static const struct i2c_device_id lis35de_id = lis35de”,0 /设备名和设备是有数据长度( )砧然后声明i2c_driver结构:static struct i2c_driver st_lis35de_driver = .probe = st_lis35de_probe,.remove= st_lis35de_remove,.suspend = sl_】is35de_suspend,.resume= stis35de_resume, 上面4个函数根据具体情况取舍.

40、id_table = lis35de_id,.driver = .name = lis35de 驱动名字隔最后调用 static inline ini i2c_add_driver(s(ruct i2c_driver driver)注册 lis35de 驱动到后C 总线,如下:static int _init stis35de_init(void)return i2c add diiver(&st lis35de driver);/iJU st lis35de drivermodule_init(st_lis35de_init);但是到目前还不知道注册到那根I2C总线,现在把Iis35de设备

41、驱动添加到我们想要的 i2c-gpio总线上。使用内核提供的函数i2c_register_board_info,在mach_xxx的板级文件中 把设备信息注册到需要注册的12c总线上面。int_init i2c_register_board_info(int busnum,设备需要注册到的总线 IDstruct i2c_board_info const *info,设备信息包括设备名,地址等 unsigned len)例如:把Iis35de驱动注册到i2c-gpio总线,总线ID为2。static struct i2c_board_info i2c_devices_lis35de = 1I2C

42、_BOARDNFO(”lis35de”, Ox IC), 设备名和地址, i2c_register_board_info(2,i2c_devices_lis35de,ARRAY_SIZE(i2c_devices_lis35de);arch/arm/mach-pnx67xx/board_pnx67xx_wavex.c 中 unsigned intpnx_modem_gpio_reserved下注释掉 GPIO_F7,GPIO_F8,防止内核认为 F8, F7已经使用过 了,至此已经把i2c-gpio总线注册到系统,把设备驱动注册到i2c-gpio总线。前面说了那么多,是不是有点乱了,这里我们在来

43、理一下:(一)i2c总线驱动1)在那个devices文件中,声明平台设备占用的系统资源,然后定义一个平台设备,并注 册这个平台设备到平台总线上2)在i2c-gpio.c文件中,声明该驱动支持的设备列表,然后定义-个平台驱动结构,并注册 这个平台驱动到平台总线上(二)i2c设备驱动I )同样在device文件下,在对应总线的设备列表中声明一个i2c设备结构,然后通过 i2c_register_board_info ()函数,将这个设备列表注册到i2c总线上2)在lis35de.c文件中,声明支持的i2c设备列表和一个i2c设备驱动结构体i2c_driver,然 后将其注册到i2c总线上注意:这里不管是设备还是驱动先注册到总线I.,他们都会自动请求匹配总线上的所有 驱动或设备。4 12c关键数据结构和详细注册流程上面的描述都是i2c系统的框架,具体的数据结构注册流程卜面会详细介绍。4.1 关键数据结构在 i2c.h 头文件中定义了 i2c_ad

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 应用文书 > 工作报告

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

© 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

黑龙江省互联网违法和不良信息举报
举报电话:0468-3380021 邮箱:hgswwxb@163.com