首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

linux驱动学习(八) i2c驱动架构(史上最全) davinc dm368 i2c驱动分析【转】

转自:http://blog.csdn.net/ghostyu/article/details/8094049 版权声明:本文为博主原创文章,未经博主允许不得转载。 目录(?)[-] 预备知识 linux设备驱动到底复杂在什么地方 linux驱动中 i2c驱动架构 架构层次分类 具体分析 i2c_driver i2c_client i2c_adapter i2c_algorithm 梳理图 ov2715设备i2c驱动源码分析 预备知识 在阅读本文最好先熟悉一种i2c设备的驱动程序,并且浏览一下i2c-core.c以及芯片提供商的提供的i2c总线驱动(i2c-davinci.c)。标题党请见谅! 其实i2c接口非常的简单,即使用51单片的gpio来模拟i2c,编写一个e2prom或者其他i2c接口的驱动程序,也不是什么难事,几百行代码就能搞定。 但是Linux的i2c驱动体系结构却有相当的复杂度,不管是叫linux i2c驱动还是单片机i2c驱动,其根本还是操作soc芯片内部的i2c模块(也叫i2c adapter)(读写i2c相关的寄存器)来产生start、stop还有ack信号而已。 linux设备驱动到底复杂在什么地方? 假设soc芯片dm368有两个i2c adapter(368内部真正只有一个i2c模块):i2c_adapter1,i2c_adapter1;然后外部有三个i2c接口的设备i2c_device1,i2c_device2,i2c_device3。 现在要求在裸机下写出他们的驱动函数。那么肯定要写出6个不同的驱动函数: [cpp] view plain copy i2c_adapter1_ReadWrite_i2c_device1(); i2c_adapter1_ReadWrite_i2c_device2() i2c_adapter1_ReadWrite_i2c_device3() i2c_adapter2_ReadWrite_i2c_device1() i2c_adapter2_ReadWrite_i2c_device2() i2c_adapter2_ReadWrite_i2c_device3() 设想一共有m个i2c adapter和n个外设i2c device,那么将需要m*n个驱动。并且这m*n个驱动程序必要会有很大部分重复的代码,而且不利于驱动程序的移植。 如果采用adapter和device分离的思想来写这样的驱动会是怎样呢? 图1 这样分离之后,只需要m+n个驱动,而且Adapter和Device的几乎没有耦合性,增加一个Adapter或者device并不会影响其余的驱动。 这就是分离思想带来的好处。除此之外,linux虽然是C写的,但是大量使用了面向对象的变成方法(可以理解为分层的思想),仅仅分离细想和分层思想的引入,就大大增加了linux设备驱动的复杂度。 linux驱动中 i2c驱动架构 图2 上图完整的描述了linux i2c驱动架构,虽然I2C硬件体系结构比较简单,但是i2c体系结构在linux中的实现却相当复杂。那么我们如何编写特定i2c接口器件(比如,ov2715,需要i2c来配置寄存器)的驱动程序?就是说上述架构中的那些部分需要我们完成,而哪些是linux内核已经完善的或者是芯片提供商(TI davinci平台已经做好的)已经提供的? 架构层次分类 第一层:提供i2c adapter的硬件驱动,探测、初始化i2c adapter(如申请i2c的io地址和中断号),驱动soc控制的i2c adapter在硬件上产生信号(start、stop、ack)以及处理i2c中断。覆盖图中的硬件实现层 第二层:提供i2c adapter的algorithm,用具体适配器的xxx_xferf()函数来填充i2c_algorithm的master_xfer函数指针,并把赋值后的i2c_algorithm再赋值给i2c_adapter的algo指针。覆盖图中的访问抽象层、i2c核心层 第三层:实现i2c设备驱动中的i2c_driver接口,用具体的i2c device设备的attach_adapter()、detach_adapter()方法赋值给i2c_driver的成员函数指针。实现设备device与总线(或者叫adapter)的挂接。覆盖图中的driver驱动层 第四层:实现i2c设备所对应的具体device的驱动,i2c_driver只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别的,eeprom和ov2715显然不是同一类的device,所以要实现具体设备device的write()、read()、ioctl()等方法,赋值给file_operations,然后注册字符设备(多数是字符设备)。覆盖图中的driver驱动层 第一层和第二层又叫i2c总线驱动(bus),第三第四属于i2c设备驱动(device driver)。在linux驱动架构中,几乎不需要驱动开发人员再添加bus,因为linux内核几乎集成所有总线bus,如usb、pci、i2c等等。并且总线bus中的【与特定硬件相关的代码】已由芯片提供商编写完成,例如TI davinci平台i2c总线bus与硬件相关的代码在内核目录/drivers/i2c/buses下的i2c-davinci.c源文件中;而三星的s3c-2440平台i2c总线bus为/drivers/i2c/buses/i2c-s3c2410.c 第三第四层又叫设备驱动层与特定device相干的就需要驱动工程师来实现了。 明确了方向后,再来具体分析。 具体分析 i2c_adapter与i2c_client的关系与i2c硬件体系中设配器与设备的关系一致,即i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个i2c设备device,所以相应的,i2c_adapter也可以被多个i2c_client依附,在i2c_adapter中包含i2c_client的链表。同一类的i2c设备device对应一个驱动driver。driver与device的关系是一对多的关系。 现在,我们就来看一下这几个重要的结构体,分别是i2c_driver i2c_client i2c_adapter,也可以先忽略他们,待会回过头来看会更容易理解 1、i2c_driver [cpp] view plain copy structi2c_driver{ intid; unsignedintclass; int(*attach_adapter)(structi2c_adapter*); int(*detach_adapter)(structi2c_adapter*); int(*detach_client)(structi2c_client*); int(*command)(structi2c_client*client,unsignedintcmd,void*arg); structdevice_driverdriver; structlist_headlist; }; 2、i2c_client [cpp] view plain copy structi2c_client{ unsignedintflags;/*div.,seebelow*/ unsignedshortaddr;/*chipaddress-NOTE:7bit*/ /*addressesarestoredinthe*/ /*_LOWER_7bits*/ structi2c_adapter*adapter;/*theadapterwesiton*/ structi2c_driver*driver;/*andouraccessroutines*/ intusage_count;/*Howmanyaccessescurrently*/ /*totheclient*/ structdevicedev;/*thedevicestructure*/ structlist_headlist; charname[I2C_NAME_SIZE]; structcompletionreleased; }; 3、i2c_adapter [cpp] view plain copy structi2c_adapter{ structmodule*owner; unsignedintid; unsignedintclass; structi2c_algorithm*algo;/*thealgorithmtoaccessthebus*/ void*algo_data; /*---administrationstuff.*/ int(*client_register)(structi2c_client*); int(*client_unregister)(structi2c_client*); /*datafieldsthatarevalidforalldevices*/ structmutexbus_lock; structmutexclist_lock; inttimeout; intretries; structdevicedev;/*theadapterdevice*/ structclass_deviceclass_dev;/*theclassdevice*/ intnr; structlist_headclients; structlist_headlist; charname[I2C_NAME_SIZE]; structcompletiondev_released; structcompletionclass_dev_released; }; 4、i2c_algorithm [cpp] view plain copy structi2c_algorithm{ int(*master_xfer)(structi2c_adapter*adap,structi2c_msg*msgs, intnum); int(*slave_send)(structi2c_adapter*,char*,int); int(*slave_recv)(structi2c_adapter*,char*,int); u32(*functionality)(structi2c_adapter*); }; 【i2c_adapter与i2c_algorithm】 i2c_adapter对应与物理上的一个适配器,而i2c_algorithm对应一套通信方法,一个i2c适配器需要i2c_algorithm中提供的(i2c_algorithm中的又是更下层与硬件相关的代码提供)通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用i2c_algorithm的指针。 i2c_algorithm中的关键函数master_xfer()用于产生i2c访问周期需要的start stop ack信号,以i2c_msg(即i2c消息)为单位发送和接收通信数据。i2c_msg也非常关键,调用驱动中的发送接收函数需要填充该结构体 [cpp] view plain copy /* *I2CMessage-usedforpurei2ctransaction,alsofrom/devinterface */ structi2c_msg{ __u16addr;/*slaveaddress*/ __u16flags; __u16len;/*msglength*/ __u8*buf;/*pointertomsgdata*/ }; 【i2c_driver和i2c_client】 i2c_driver对应一套驱动方法,其主要函数是attach_adapter()和detach_client(),i2c_client对应真实的i2c物理设备device,每个i2c设备都需要一个i2c_client来描述,i2c_driver与i2c_client的关系是一对多。一个i2c_driver上可以支持多个同等类型的i2c_client. 【i2c_adapter和i2c_client】 i2c_adapter和i2c_client的关系与i2c硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个i2c设备,所以i2c_adapter中包含依附于它的i2c_client的链表。 从图1图2中都可以看出,linux内核对i2c架构抽象了一个叫核心层core的中间件,它分离了设备驱动device driver和硬件控制的实现细节(如操作i2c的寄存器),core层不但为上面的设备驱动提供封装后的内核注册函数,而且还为小面的硬件时间提供注册接口(也就是i2c总线注册接口),可以说core层起到了承上启下的作用。 我们先看一下i2c-core为外部提供的核心函数(选取部分),i2c-core对应的源文件为i2c-core.c,位于内核目录/driver/i2c/i2c-core.c [cpp] view plain copy EXPORT_SYMBOL(i2c_add_adapter); EXPORT_SYMBOL(i2c_del_adapter); EXPORT_SYMBOL(i2c_del_driver); EXPORT_SYMBOL(i2c_attach_client); EXPORT_SYMBOL(i2c_detach_client); EXPORT_SYMBOL(i2c_transfer); 如果看过i2c设备驱动程序的人一定对上面几个函数比较熟悉。 i2c_transfer()函数,i2c_transfer()函数本身并不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正的驱动硬件流程,代码清单如下,不重要的已删除。 [cpp] view plain copy inti2c_transfer(structi2c_adapter*adap,structi2c_msg*msgs,intnum) { intret; if(adap->algo->master_xfer){//如果master_xfer函数存在,则调用,否则返回错误 ret=adap->algo->master_xfer(adap,msgs,num);//这个函数在硬件相关的代码中给algorithm赋值 returnret; }else{ return-ENOSYS; } } 当一个具体的client被侦测到并被关联的时候,设备和sysfs文件将被注册。相反的,在client被取消关联的时候,sysfs文件和设备也被注销,驱动开发人员需开发i2c设备驱动时,需要调用下列函数。程序清单如下 [cpp] view plain copy inti2c_attach_client(structi2c_client*client) { ... device_register(&client->dev); device_create_file(&client->dev,&dev_attr_client_name); ... return0; } [cpp] view plain copy inti2c_detach_client(structi2c_client*client) { ... device_remove_file(&client->dev,&dev_attr_client_name); device_unregister(&client->dev); ... returnres; } i2c_add_adapter()函数和i2c_del_adapter()在i2c-davinci.c中有调用,稍后分析 [cpp] view plain copy /*----- *i2c_add_adapteriscalledfromwithinthealgorithmlayer, *whenanewhwadapterregisters.Anewdeviceisregistertobe *availableforclients. */ inti2c_add_adapter(structi2c_adapter*adap) { ... device_register(&adap->dev); device_create_file(&adap->dev,&dev_attr_name); ... /*informdriversofnewadapters*/ list_for_each(item,&drivers){ driver=list_entry(item,structi2c_driver,list); if(driver->attach_adapter) /*Weignorethereturncode;ifitfails,toobad*/ driver->attach_adapter(adap); } ... } [cpp] view plain copy inti2c_del_adapter(structi2c_adapter*adap) { ... list_for_each(item,&drivers){ driver=list_entry(item,structi2c_driver,list); if(driver->detach_adapter) if((res=driver->detach_adapter(adap))){ } } ... list_for_each_safe(item,_n,&adap->clients){ client=list_entry(item,structi2c_client,list); if((res=client->driver->detach_client(client))){ } } ... device_remove_file(&adap->dev,&dev_attr_name); device_unregister(&adap->dev); } i2c-davinci.c是实现与硬件相关功能的代码集合,这部分是与平台相关的,也叫做i2c总线驱动,这部分代码是这样添加到系统中的 [cpp] view plain copy staticstructplatform_driverdavinci_i2c_driver={ .probe=davinci_i2c_probe, .remove=davinci_i2c_remove, .driver={ .name="i2c_davinci", .owner=THIS_MODULE, }, }; /*I2Cmaybeneededtobringupotherdrivers*/ staticint__initdavinci_i2c_init_driver(void) { returnplatform_driver_register(&davinci_i2c_driver); } subsys_initcall(davinci_i2c_init_driver); staticvoid__exitdavinci_i2c_exit_driver(void) { platform_driver_unregister(&davinci_i2c_driver); } module_exit(davinci_i2c_exit_driver); 并且,i2c适配器控制硬件发送接收数据的函数在这里赋值给i2c-algorithm,i2c_davinci_xfer稍加修改就可以在裸机中控制i2c适配器 [cpp] view plain copy staticstructi2c_algorithmi2c_davinci_algo={ .master_xfer=i2c_davinci_xfer, .functionality=i2c_davinci_func, }; 然后在davinci_i2c_probe函数中,将i2c_davinci_algo添加到添加到algorithm系统中 [cpp] view plain copy adap->algo=&i2c_davinci_algo; 梳理图 有时候代码比任何文字描述都来得直接,但是过多的代码展示反而让人觉得枯燥。这个时候,需要一幅图来梳理一下上面的内容,请看图3。 图3 好了,上面这些代码的展示是告诉我们,linux内核和芯片提供商为我们的的驱动程序提供了 i2c驱动的框架,以及框架底层与硬件相关的代码的实现。剩下的就是针对挂载在i2c两线上的i2c设备了device,如at24c02,例如ov2715,而编写的具体设备驱动了,这里的设备就是硬件接口外挂载的设备,而非硬件接口本身(soc硬件接口本身的驱动可以理解为总线驱动)。 在理解了i2c驱动架构后,我们接下来再作两方面的分析工作:一是具体的i2c设备ov2715驱动源码分析,二是davinci平台的i2c总线驱动源码。 ov2715设备i2c驱动源码分析 ov2715为200万的CMOS Sensor,芯片的寄存器控制通过i2c接口完成,i2c设备地址为0x6c,寄存器地址为16位两个字节,寄存器值为8位一个字节,可以理解为一般的字符设备。 该驱动程序并非只能用于ov2715,因此源码中存在支持多个设备地址的机制。 该字符设备的用到的结构体有两个,如下 [cpp] view plain copy typedefstruct{ intdevAddr; structi2c_clientclient;//!<Datastructurecontaininggeneralaccessroutines. structi2c_driverdriver;//!<Datastructurecontaininginformationspecifictoeachclient. charname[20]; intnameSize; intusers; }I2C_Obj; [cpp] view plain copy #defineI2C_DEV_MAX_ADDR(0xFF) #defineI2C_TRANSFER_BUF_SIZE_MAX(256) typedefstruct{ structcdevcdev;/*Chardevicestructure*/ intmajor; structsemaphoresemLock; I2C_Obj*pObj[I2C_DEV_MAX_ADDR]; uint8_treg[I2C_TRANSFER_BUF_SIZE_MAX]; uint16_treg16[I2C_TRANSFER_BUF_SIZE_MAX]; uint8_tbuffer[I2C_TRANSFER_BUF_SIZE_MAX*4]; }I2C_Dev; 一个I2C_Obj描述一个设备,devAddr保存该设备的地址,I2C_Obj内嵌到结构体I2C_Dev,I2C_Dev管理该驱动所支持的所有设备,尽管支持多个设备,但i2c适配器只有一个,因此需要一个信号量semLock来保护该共享资源,同时只能向一个设备读写数据。成员变量cdev是我们所熟知的,每个字符设备驱动中几乎总会有一个结构体包含它,major用于保存该驱动的主设备编号,reg数组为寄存器地址为8位的寄存器地址缓冲区,reg16为寄存器地址为16的寄存器地址缓冲区。同时可以读写多个寄存器地址的值。buffer为读写的寄存器值 使用I2C_Dev构建一个全局变量gI2C_dev,在驱动的多个地方均需要它。 下面先从字符设备的基本框架入手,然后深入该驱动的细节部分。 首先是该字符设备的初始化和退出函数 [cpp] view plain copy intI2C_devInit(void) { intresult,i; dev_tdev=0; result=alloc_chrdev_region(&dev,0,1,I2C_DRV_NAME);//分配字符设备空间 for(i=0;i<I2C_DEV_MAX_ADDR;i++) { gI2C_dev.pObj[i]=NULL; } gI2C_dev.major=MAJOR(dev);//保存设备主编号 sema_init(&gI2C_dev.semLock,1);//信号量初始化 cdev_init(&gI2C_dev.cdev,&gI2C_devFileOps);//使用gI2C_devFileOps初始化该字符设备,gI2C_devFileOps见下文 gI2C_dev.cdev.owner=THIS_MODULE;//常规赋值 gI2C_dev.cdev.ops=&gI2C_devFileOps;//常规赋值result=cdev_add(&gI2C_dev.cdev,dev,1);//添加设备到字符设备中returnresult;}voidI2C_devExit(void){dev_tdevno=MKDEV(gI2C_dev.major,0);cdev_del(&gI2C_dev.cdev);//从字符设备中删除该设备unregister_chrdev_region(devno,1);//回收空间} gI2c_devFileOps全局变量,驱动初始化会用到该结构体变量 structfile_operationsgI2C_devFileOps={ .owner=THIS_MODULE, .open=I2C_devOpen, .release=I2C_devRelease, .ioctl=I2C_devIoctl, }; 该驱动只实现了三个函数,open,release和ioctl,对于i2c设备来说,这已经足够了。 在I2C_devOpen和I2C_devOpen中并没有做实际的工作,重要的工作均在I2C_devIoctl这个ioctl中完成。I2C_devIoctl代码展示(将影响结构条理的代码去掉,稍后在做详细分析) [cpp] view plain copy intI2C_devIoctl(structinode*inode,structfile*filp,unsignedintcmd,unsignedlongarg) { I2C_Obj*pObj; intstatus=0; I2C_TransferPrmtransferPrm; pObj=(I2C_Obj*)filp->private_data; if(!I2C_IOCTL_CMD_IS_VALID(cmd)) return-1; cmd=I2C_IOCTL_CMD_GET(cmd);//cmd命令转换,防止混淆,具体原因参见上一篇文章:ioctl中的cmd down_interruptible(&gI2C_dev.semLock);//信号量down switch(cmd) { caseI2C_CMD_SET_DEV_ADDR://命令1,设置设备地址 filp->private_data=I2C_create(arg); caseI2C_CMD_WRITE://命令2,写寄存器值 status=copy_from_user(&transferPrm,(void*)arg,sizeof(transferPrm)); ... break; caseI2C_CMD_READ://命令3,读寄存器值 status=copy_from_user(&transferPrm,(void*)arg,sizeof(transferPrm)); ... break; default: status=-1; break; } up(&gI2C_dev.semLock);//信号量up returnstatus; } 以上三个命令中最重要最复杂的是第一个I2C_CMD_SET_DEV_ADDR,设置设备地址,之所以重要和复杂,因为在I2C_create()函数中,将通过i2c-core提供的函数把该驱动程序和底层的i2c_adapter联系起来。下面是I2C_create()函数源码 [cpp] view plain copy void*I2C_create(intdevAddr){ intret; structi2c_driver*driver; structi2c_client*client=client; I2C_Obj*pObj; devAddr>>=1; if(devAddr>I2C_DEV_MAX_ADDR)//变量合法性判断 returnNULL; if(gI2C_dev.pObj[devAddr]!=NULL){//变量合法性判断,如果该地址的设备已经创建,则调过,防止上层错误调用 //alreadyallocated,incrementusercount,andreturntheallocatedhandle gI2C_dev.pObj[devAddr]->users++; returngI2C_dev.pObj[devAddr]; } pObj=(void*)kmalloc(sizeof(I2C_Obj),GFP_KERNEL);//为pObj分配空间 gI2C_dev.pObj[devAddr]=pObj;//将分配的空间地址保存在全局变量里 memset(pObj,0,sizeof(I2C_Obj)); pObj->client.adapter=NULL; pObj->users++;//用户基数,初始化为0,当前设为1 pObj->devAddr=devAddr;//保存设备地址 gI2C_curAddr=pObj->devAddr;//gI2C_curAddr为全局的整型变量,用于保存当前的设备地址 driver=&pObj->driver;//将成员变量driver单独抽取出来,因为线面要使用driver来初始化驱动 pObj->nameSize=0;//i2c设备名称,注意,这里不是在/dev下面的设备节点名 pObj->name[pObj->nameSize++]='I'; pObj->name[pObj->nameSize++]='2'; pObj->name[pObj->nameSize++]='C'; pObj->name[pObj->nameSize++]='_'; pObj->name[pObj->nameSize++]='A'+((pObj->devAddr>>0)&0xF); pObj->name[pObj->nameSize++]='B'+((pObj->devAddr>>4)&0xF); pObj->name[pObj->nameSize++]=0; driver->driver.name=pObj->name;//保存刚才设置的name driver->id=I2C_DRIVERID_MISC; driver->attach_adapter=I2C_attachAdapter;//这个很重要,将驱动连接到i2c适配器上,在后面分析 driver->detach_client=I2C_detachClient;//这个很重,在后面分析 if((ret=i2c_add_driver(driver)))//使用i2c-core(i2c_register_driver函数)的接口,注册该驱动,i2c_add_driver实质调用了driver_register() { printk(KERN_ERR"I2C:ERROR:Driverregistrationfailed(address=%x),modulenotinserted.\n",pObj->devAddr); } if(ret<0){ gI2C_dev.pObj[pObj->devAddr]=NULL; kfree(pObj); returnNULL; } returnpObj; } 其他两个命令是I2C_CMD_WRITE和I2C_CMD_READ,这个比较简单,只需设置寄存器地址的大小以及寄存器值的大小,然后通过i2c-core 提供的i2c_transfer()函数发送即可。例如I2C_wirte() [cpp] view plain copy intI2C_write(I2C_Obj*pObj,uint8_t*reg,uint8_t*buffer,uint8_tcount,uint8_tdataSize) { uint8_ti; interr; structi2c_client*client; structi2c_msgmsg[1]; unsignedchardata[8]; if(pObj==NULL) return-ENODEV; client=&pObj->client;//得到client信息 if(!client->adapter) return-ENODEV; if(dataSize<=0||dataSize>4) return-1; for(i=0;i<count;i++){ msg->addr=client->addr;//设置要写的i2c设备地址 msg->flags=0;//一直为0 msg->buf=data;//date为准备i2c通信的缓冲区,这个缓冲区除了不包含设备地址外,要包括要目标寄存器地址,和要写入该寄存器的值 data[0]=reg[i];//寄存器地址赋值 if(dataSize==1){//寄存器值长度为1 data[1]=buffer[i];//寄存器值赋值 msg->len=2;//设置data长度为2 }elseif(dataSize==2){//寄存器值长度为2 data[1]=buffer[2*i+1]; data[2]=buffer[2*i]; msg->len=3; } err=i2c_transfer(client->adapter,msg,1);//调用i2c-core中的i2c_transfer发送i2c数据 if(err<0) returnerr; } return0; } 现在,我们重点分析上一段代码void *I2C_create(int devAddr)函数中的i2c_driver结构体部分的代码,下面的代码是从上面I2C_create抽取出来的 [cpp] view plain copy driver->driver.name=pObj->name; driver->id=I2C_DRIVERID_MISC; driver->attach_adapter=I2C_attachAdapter; driver->detach_client=I2C_detachClient; 在i2c_driver结构体中针对attach_adapter有这样的说明: [cpp] view plain copy /*Notifiesthedriverthatanewbushasappeared.Thisroutine *canbeusedbythedrivertotestifthebusmeetsitsconditions *&seekforthepresenceofthechip(s)itsupports.Iffound,it *registerstheclient(s)thatareonthebustothei2cadmin.via *i2c_attach_client. */ 意思是通知驱动,i2c适配器已经就绪了,这时可以讲device的driver连接到总线bus上。所以I2C_attachAdapter的作用就是检测client,然后将client连接上来。attach_adapter和detach_client由内核驱动自动调用,我们只需在调用的时候实现必要的功能即可,如下代码展示 [cpp] view plain copy intI2C_attachAdapter(structi2c_adapter*adapter) { returnI2C_detectClient(adapter,gI2C_curAddr); } intI2C_detectClient(structi2c_adapter*adapter,intaddress) { I2C_Obj*pObj; structi2c_client*client; interr=0; if(address>I2C_DEV_MAX_ADDR){ printk(KERN_ERR"I2C:ERROR:Invaliddeviceaddress%x\n",address); return-1; } pObj=gI2C_dev.pObj[address]; if(pObj==NULL){ printk(KERN_ERR"I2C:ERROR:Objectnotfoundforaddress%x\n",address); return-1; } client=&pObj->client; if(client->adapter) return-EBUSY;/*ourclientisalreadyattached*/ memset(client,0x00,sizeof(structi2c_client)); client->addr=pObj->devAddr; client->adapter=adapter; client->driver=&pObj->driver; if((err=i2c_attach_client(client))) { printk(KERN_ERR"I2C:ERROR:Couldn'tattach%s(address=%x)\n",pObj->name,pObj->devAddr); client->adapter=NULL; returnerr; } return0; } 最终I2C_detectClient()函数调用了i2c-core中的i2c_attach_client,从名字上就能看出什么意思,连接client设备。 当内核驱动准备删除该驱动时会自动调用i2c_driver的成员函数:detech_client,因此我们需要实现删除client设备的函数然后赋值给改函数指针,detech_client的说明如下: [cpp] view plain copy /*tellsthedriverthataclientisabouttobedeleted&givesit *thechancetoremoveitsprivatedata.Also,iftheclientstruct *hasbeendynamicallyallocatedbythedriverinthefunctionabove, *itmustbefreedhere. */ 下面是detech_client调用的函数代码清单,该函数最终调用了i2c-core提供的i2c_detach_client,用于取消client设备的连接 [cpp] view plain copy intI2C_detachClient(structi2c_client*client) { interr; if(!client->adapter) return-ENODEV;/*ourclientisn'tattached*/ if((err=i2c_detach_client(client))){ printk(KERN_ERR"Clientderegistrationfailed(address=%x),clientnotdetached.\n",client->addr); returnerr; } client->adapter=NULL; return0; } ov2715设备的i2c驱动源码的分析就到这里,至于平台相关的i2c总线驱动分析就放到下一篇文章里分析,因为这部分多数情况下并不需要我们亲自去实现。但是对于理解i2c驱动架构来说,还是有很大帮助的。 【作者】 张昺华 【出处】 http://www.cnblogs.com/sky-heaven/ 【博客园】 http://www.cnblogs.com/sky-heaven/ 【新浪博客】 http://blog.sina.com.cn/u/2049150530 【知乎】 http://www.zhihu.com/people/zhang-bing-hua 【我的作品---旋转倒立摆】 http://v.youku.com/v_show/id_XODM5NDAzNjQw.html?spm=a2hzp.8253869.0.0&from=y1.7-2 【我的作品---自平衡自动循迹车】 http://v.youku.com/v_show/id_XODM5MzYyNTIw.html?spm=a2hzp.8253869.0.0&from=y1.7-2 【新浪微博】 张昺华--sky 【twitter】 @sky2030_ 【facebook】 张昺华 zhangbinghua 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

优秀的个人博客,低调大师

【翻译】Sklearn与TensorFlow机器学习实用指南 ——第12章 设备和服务器上的分布式TensorFlow(上)

本文来自云栖社区官方钉群“Python技术进阶”,了解相关信息可以关注“Python技术进阶”。 在第 11 章,我们讨论了几种可以明显加速训练的技术:更好的权重初始化,批量标准化,复杂的优化器等等。 但是,即使采用了所有这些技术,在具有单个 CPU 的单台机器上训练大型神经网络可能需要几天甚至几周的时间。 在本章中,我们将看到如何使用 TensorFlow 在多个设备(CPU 和 GPU)上分配计算并将它们并行运行(参见图 12-1)。 首先,我们会先在一台机器上的多个设备上分配计算,然后在多台机器上的多个设备上分配计算。 与其他神经网络框架相比,TensorFlow 对分布式计算的支持是其主要亮点之一。 它使您可以完全控制如何跨设备和服务器分布(或复制)您的计算图,并且可以让您以灵活的方式并行和同步操作,以便您可以在各种并行方法

优秀的个人博客,低调大师

【翻译】Sklearn与TensorFlow机器学习实用指南 ——第12章 设备和服务器上的分布式TensorFlow(下)

并行运行 当 TensorFlow 运行图时,它首先找出需要求值的节点列表,然后计算每个节点有多少依赖关系。 然后 TensorFlow 开始求值具有零依赖关系的节点(即源节点)。 如果这些节点被放置在不同的设备上,它们显然会被并行求值。 如果它们放在同一个设备上,它们将在不同的线程中进行求值,因此它们也可以并行运行(在单独的 GPU 线程或 CPU 内核中)。 TensorFlow 管理每个设备上的线程池以并行化操作(参见图 12-5)。 这些被称为 inter-op 线程池。 有些操作具有多线程内核:它们可以使用其他线程池(每个设备一个)称为 intra-op 线程池(下面写成内部线程池)。 <p style="text-align:center">![image](https://yqfile.alicdn.com/d

优秀的个人博客,低调大师

java基础学习_常用类04_正则表达式、Math类、Random类、System类、BigInteger类、BigDecimal类、Dat...

==========================================================================================================================================================涉及到的知识点有:1:正则表达式(理解) (1)正则表达式的概述 (2)常见规则 (3)常见功能:(分别用的是那个类的功能呢?) (4)正则表达式的使用案例2:Math类的概述及其成员方法(掌握) (1)Math类的概述 (2)Math类的成员变量(字段) (3)Math类的成员方法 (4)Math类的案例3:Random类的概述及其构造方法(理解) (1)Random类的概述 (2)Random类的构造方法 (3)Random类的成员方法4:System类的概述及其成员方法(掌握) (1)System类的概述 (2)System类的成员方法(自己补齐)5:BigInteger类的概述及其构造方法和成员方法(理解) (1)BigInteger类的概述 (2)BigInteger类的构造方法(有6个) (3)BigInteger类的成员方法6:BigDecimal类的概述及其构造方法(理解) (1)BigDecimal类的概述 (2)BigDecimal类的构造方法(有16个) (3)BigDecimal类的成员方法7:Date类和DateFormat类的概述及其方法(掌握) (1)Date类的概述(JDK1.1就出现了,之后使用Calendar类) (2)Date类的构造方法、成员方法和小案例 (3)DateFormat类的概述(抽象类) (4)SimpleDateFormat类的构造方法(具体类) (5)DateFormat类的成员方法(抽象类) (6)DateFormat类的案例8:Calendar类的概述及其方法(掌握) (1)Calendar类的概述(抽象类) (2)因为Calendar类是抽象类,那么如何得到一个日历对象呢? (3)Calendar类的成员方法(抽象类) (4)Calendar类的案例==========================================================================================================================================================1:正则表达式(理解) (1)正则表达式的概述 就是符合一定规则的字符串。 (2)常见规则 A:字符 x 字符 x。举例:'a'表示字符a \\ \ 两个反斜杠字符代表一个反斜杠字符 \n 新行(换行)符 ('\u000A') \r 回车符 ('\u000D') B:字符类 [abc] a、b或 c(简单类) 例如:[38] 表示3或者8 [^abc] 任何字符,除了a、b或 c(否定) [a-zA-Z] a到z或 A到Z,两头的字母包括在内(范围) [0-9] 0到9,两头的数字包括在内(范围) C:预定义字符类 . 任何字符。我的就是.字符本身,怎么表示呢? \. \. .字符本身 \\ \ 两个反斜杠字符代表一个反斜杠字符 \d 数字:[0-9] \D 非数字:[^0-9] \w 单词字符:[a-zA-Z_0-9](在正则表达式里面组成单词的东西必须由这些东西组成。) D:边界匹配器 ^ 行的开头 $ 行的结尾 \b 单词边界(不是单词字符的地方。举例:hello world?haha;xixi 例子中:空格、?、;就是单词边界。) E:Greedy 数量词 X X出现一次(X后面什么也没有) X? X出现一次或一次也没有 X* X出现零次或多次 X+ X出现一次或多次 X{n} X出现恰好 n 次 X{n,} X出现至少 n 次 X{n,m} X出现至少 n 次,但是不超过 m 次 例如: [1-9][0-9]{4,14} [1-9]出现一次[0-9]出现至少4次,但是不超过14次。 --------------------------------------- (3)常见功能:(分别用的是那个类的功能呢?) A:判断功能 String类的public boolean matches(String regex) 告知此字符串是否匹配给定的正则表达式 B:分割功能 String类的public String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串 C:替换功能 String类的public String replaceAll(String regex,String replacement) 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串 D:获取功能 Pattern类和Matcher类的使用 模式和匹配器的典型调用顺序如下:(基本使用顺序) // 把正则表达式编译成模式对象 Pattern p = Pattern.compile("a*b"); // 通过模式对象得到匹配器对象,这个时候需要的是被匹配的字符串 Matcher m = p.matcher("aaaaab"); // 调用匹配器对象的匹配功能 boolean b = m.matches(); // 注意:一定要先调find(),然后才能调group() Matcher类的方法:public boolean find() 查找有没有满足条件的子串 Matcher类的方法:public String group() 返回由以前匹配操作所匹配的输入子序列 --------------------------------------- (4)正则表达式的使用案例 A:判断电话号码和邮箱 示例代码如下: 1 package cn.itcast_02; 2 3 import java.util.Scanner; 4 5 /* 6 * 校验邮箱 7 * 8 * 分析: 9 * A:键盘录入邮箱 10 * B:定义邮箱的规则 11 * 1517806580@qq.com 12 * liuyi@163.com 13 * linqingxia@126.com 14 * fengqingyang@sina.com.cn 15 * fqy@itcast.cn 16 * C:调用功能,判断即可 17 * D:输出结果 18 */ 19 public class RegexTest { 20 public static void main(String[] args) { 21 //键盘录入邮箱 22 Scanner sc = new Scanner(System.in); 23 System.out.println("请输入邮箱:"); 24 String email = sc.nextLine(); 25 26 //定义邮箱的规则 27 //String regex = "[a-zA-Z_0-9]+@[a-zA-Z_0-9]{2,6}(\\.[a-zA-Z_0-9]{2,3})+"; 28 String regex = "\\w+@\\w{2,6}(\\.\\w{2,3})+"; 29 30 //调用功能,判断即可 31 boolean flag = email.matches(regex); 32 33 //输出结果 34 System.out.println("flag:"+flag); 35 } 36 } View Code B:按照不同的规则分割数据 示例代码如下: 1 package cn.itcast_03; 2 3 import java.util.Arrays; 4 5 /* 6 * 我有如下一个字符串:"91 27 46 38 50" 7 * 请写代码实现最终输出结果是:"27 38 46 50 91" 8 * 9 * 分析: 10 * A:定义一个字符串 11 * B:把字符串进行分割,得到一个字符串数组 12 * C:把字符串数组变换成int数组 13 * D:对int数组排序 14 * E:把排序后的int数组再组装成一个字符串 15 * F:输出字符串 16 */ 17 public class RegexTest { 18 public static void main(String[] args) { 19 // 定义一个字符串 20 String s = "91 27 46 38 50"; 21 22 // 把字符串进行分割,得到一个字符串数组 23 String[] strArray = s.split(" "); 24 25 // 把字符串数组变换成int数组 26 int[] arr = new int[strArray.length]; 27 28 for (int x = 0; x < arr.length; x++) { 29 arr[x] = Integer.parseInt(strArray[x]); 30 } 31 32 // 对int数组排序 33 Arrays.sort(arr); 34 35 // 把排序后的int数组在组装成一个字符串缓冲区数组 36 // 可以把任意类型数据添加到字符串缓冲区里面,并返回字符串缓冲区对象本身,所以不需要再去创建对象接收了(该点很重要)。 37 StringBuilder sb = new StringBuilder(); 38 for (int x = 0; x < arr.length; x++) { 39 sb.append(arr[x]).append(" "); 40 } 41 // 把字符串缓冲区数组转化为字符串,并去除前后端空格 trim() 42 String result = sb.toString().trim(); 43 44 // 输出字符串 45 System.out.println("result:"+result); 46 } 47 } View Code C:把论坛中的数字替换为* 示例代码如下: 1 package cn.itcast_04; 2 3 /* 4 * 替换功能 5 * String类的public String replaceAll(String regex, String replacement) 6 * 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。 7 */ 8 public class RegexDemo { 9 public static void main(String[] args) { 10 // 定义一个字符串 11 String s = "helloqq12345worldkh622112345678java"; 12 13 // 我要去除所有的数字,用*给替换掉 14 // String regex = "\\d+"; 15 // String regex = "\\d"; 16 // String ss = "*"; 17 18 // 直接把数字干掉 19 String regex = "\\d+"; 20 String ss = ""; 21 22 String result = s.replaceAll(regex, ss); 23 System.out.println(result); // helloqqworldkhjava 24 } 25 } View Code D:获取字符串中由3个字符组成的单词 示例代码如下: 1 package cn.itcast_05; 2 3 import java.util.regex.Matcher; 4 import java.util.regex.Pattern; 5 6 /* 7 * 获取功能: 8 * 获取下面这个字符串中由三个字符组成的单词 9 * da jia ting wo shuo,jin tian yao xia yu,bu shang wan zi xi,gao xing bu? 10 */ 11 public class RegexDemo2 { 12 public static void main(String[] args) { 13 // 定义字符串 14 String s = "da jia ting wo shuo,jin tian yao xia yu,bu shang wan zi xi,gao xing bu?"; 15 // 规则 16 String regex = "\\b\\w{3}\\b"; 17 18 // 把规则编译成模式对象 19 Pattern p = Pattern.compile(regex); 20 // 通过模式对象得到匹配器对象 21 Matcher m = p.matcher(s); 22 // 调用匹配器对象的功能 23 // Matcher类的方法:public boolean find() 查找有没有满足条件的子串 24 // boolean flag = m.find(); 25 // System.out.println(flag); 26 27 // 如何或获取值呢? 28 // Matcher类的方法:public String group() 返回由以前匹配操作所匹配的输入子序列 29 // String ss = m.group(); 30 // System.out.println(ss); 31 32 // 再来一次 33 // 判断有没有 34 // flag = m.find(); 35 // System.out.println(flag); 36 // 获取值 37 // ss = m.group(); 38 // System.out.println(ss); 39 40 while (m.find()) { 41 System.out.println(m.group()); 42 } 43 44 // 注意:一定要先调find(),然后才能调group() 45 // 若直接group(),会抛出异常 46 // IllegalStateException: No match found 47 // String ss = m.group(); 48 // System.out.println(ss); 49 } 50 } View Code ----------------------------------------------------------------------------- 2:Math类的概述及其成员方法(掌握) (1)Math类的概述 Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。 (2)Math类的成员变量(字段) public static final double PI = 3.14159265358979323846; public static final double E = 2.7182818284590452354; (3)Math类的成员方法 public static int abs(int a) 绝对值(形参的数据类型可以为:int、double、float、long) public static double ceil(double a) 向上取整 public static double floor(double a) 向下取整 public static int max(int a, int b) 最大值(min自学) public static double pow(double a, double b) a的b次幂 public static double random() 随机数 [0.0,1.0) public static int round(float a) 四舍五入(参数为double的自学) public static double sqrt(double a) 正平方根 (4)Math类的案例 A:猜数字小游戏 B:获取任意范围的随机数 1 package cn.itcast_02; 2 3 import java.util.Scanner; 4 5 /* 6 * 需求:请设计一个方法,可以实现获取任意范围内的随机数。 7 * 8 * 分析: 9 * A:键盘录入两个数据; 10 * int strat; 11 * int end; 12 * B:想办法获取在start到end之间的随机数; 13 * 我写一个功能实现这个效果,得到一个随机数。(int) 14 * C:输出这个随机数。 15 */ 16 public class MathDemo { 17 public static void main(String[] args) { 18 Scanner sc = new Scanner(System.in); 19 System.out.println("请输入开始数:"); 20 int start = sc.nextInt(); 21 System.out.println("请输入结束数:"); 22 int end = sc.nextInt(); 23 24 for (int x = 0; x < 100; x++) { 25 // 调用功能 26 int num = getRandom(start, end); 27 // 输出结果 28 System.out.println(num); 29 } 30 } 31 32 /* 33 * 写一个功能 两个明确: 34 * 返回值类型:int 35 * 参数列表:int start, int end 36 */ 37 public static int getRandom(int start, int end) { 38 // 回想我们讲过的1-100之间的随机数 39 // int number = (int) (Math.random() * 100) + 1; 40 // int number = (int) (Math.random() * end) + start; 41 // 发现有问题了,怎么办呢? 42 int number = (int) (Math.random() * (end - start + 1)) + start; 43 return number; 44 } 45 } View Code ----------------------------------------------------------------------------- 3:Random类的概述及其构造方法(理解) (1)Random类的概述 用于产生随机数的类。 (2)Random类的构造方法 A:public Random() 没有给种子,用的是默认种子,是当前时间的毫秒值,随机数不相同 B:public Random(long seed) 给出指定的种子,每次种子相同,随机数就相同 (3)Random类的成员方法 A:public int nextInt() 返回的是int范围内的随机数 B:public int nextInt(int n) 返回的是[0,n)范围的内随机数-----------------------------------------------------------------------------4:System类的概述及其成员方法(掌握) (1)System类的概述 System类是系统类,包含一些有用的类字段和方法。它不能被实例化。 (2)System类的成员方法(自己补齐) A:public static void gc() 运行垃圾回收器 B:public static void exit(int status) 终止当前正在运行的 Java 虚拟机。参数用作状态码;根据惯例,非0的状态码表示异常终止。 C:public static long currentTimeMillis() 返回以毫秒为单位的当前时间 D:public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 数组复制 src --> 源数组。 srcPos --> 源数组中的起始位置。 dest --> 目标数组。 destPos --> 目标数据中的起始位置。 length --> 要复制的数组元素的数量。 -----------------------------------------------------------------------------5:BigInteger类的概述及其构造方法和成员方法(理解) (1)BigInteger类的概述 针对大整数的运算,即可以让超过Integer范围内的数据进行运算。 (2)BigInteger类的构造方法(有6个) A:public BigInteger(String s) BigInteger bi = new BigInteger("2147483648"); (3)BigInteger类的成员方法 A:public BigInteger add(BigInteger val) 加 B:public BigInteger subtract(BigInteger val) 减 C:public BigInteger multiply(BigInteger val) 乘 D:public BigInteger divide(BigInteger val) 除 E:public BigInteger[] divideAndRemainder(BigInteger val) 返回商和余数的数组-----------------------------------------------------------------------------6:BigDecimal类的概述及其构造方法(理解) (1)BigDecimal类的概述 是不可变的、任意精度的有符号十进制数,可以解决数据丢失问题。 因为浮点数据做运算,会丢失精度。所以,针对浮点数据的操作建议采用BigDecimal。(金融相关的项目) 由于在运算的时候,float类型和double类型很容易丢失精度,演示案例如下所示。所以,为了能精确的表示、计算浮点数,Java提供了BigDecimal类。 因为float类型的数据存储和整数类型的数据存储是不一样导致的。它们大部分的时候,都是带有有效数字位。 没有使用BigDecimal时的示例代码: 1 package cn.itcast_01; 2 3 /* 4 * 看程序写结果:结果和我们想的有一点点不一样,这是因为float类型的数据存储和整数不一样导致的。它们大部分的时候,都是带有有效数字位。 5 * 6 * 由于在运算的时候,float类型和double很容易丢失精度,演示案例。所以,为了能精确的表示、计算浮点数,Java提供了BigDecimal 7 * 8 * BigDecimal类:不可变的、任意精度的有符号十进制数,可以解决数据丢失问题。 9 */ 10 public class BigDecimalDemo { 11 public static void main(String[] args) { 12 System.out.println(0.09 + 0.01); // 0.09999999999999999 13 System.out.println(1.0 - 0.32); // 0.6799999999999999 14 System.out.println(1.015 * 100); // 101.49999999999999 15 System.out.println(1.301 / 100); // 0.013009999999999999 16 17 System.out.println(1.0 - 0.12); // 0.88 18 } 19 } (2)BigDecimal类的构造方法(有16个) A:public BigDecimal(String val) (3)BigDecimal类的成员方法 A:public BigDecimal add(BigDecimal augend)) 加 B:public BigDecimal subtract(BigDecimal subtrahend) 减 C:public BigDecimal multiply(BigDecimal multiplicand) 乘 D:public BigDecimal divide(BigDecimal divisor) 除 E:public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) 被除数,几位小数,如何舍取(自己保留小数几位) 已过时。 应该优先使用方法divide(BigDecimal, int, RoundingMode) 使用了BigDecimal后的示例代码: 1 package cn.itcast_02; 2 3 import java.math.BigDecimal; 4 5 /* 6 * BigDecimal类的构造方法: 7 * public BigDecimal(String val) 8 * 9 * BigDecimal类的成员方法 10 * public BigDecimal add(BigDecimal augend) 11 * public BigDecimal subtract(BigDecimal subtrahend) 12 * public BigDecimal multiply(BigDecimal multiplicand) 13 * public BigDecimal divide(BigDecimal divisor) 14 * public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) 被除数,几位小数,如何舍取 15 * 已过时。 应该优先使用方法divide(BigDecimal, int, RoundingMode) 。 16 */ 17 public class BigDecimalDemo { 18 public static void main(String[] args) { 19 // System.out.println(0.09 + 0.01); 20 // System.out.println(1.0 - 0.32); 21 // System.out.println(1.015 * 100); 22 // System.out.println(1.301 / 100); 23 24 BigDecimal bd1 = new BigDecimal("0.09"); 25 BigDecimal bd2 = new BigDecimal("0.01"); 26 System.out.println("add:" + bd1.add(bd2)); // 0.10 27 System.out.println("-------------------"); 28 29 BigDecimal bd3 = new BigDecimal("1.0"); 30 BigDecimal bd4 = new BigDecimal("0.32"); 31 System.out.println("subtract:" + bd3.subtract(bd4)); // 0.68 32 System.out.println("-------------------"); 33 34 BigDecimal bd5 = new BigDecimal("1.015"); 35 BigDecimal bd6 = new BigDecimal("100"); 36 System.out.println("multiply:" + bd5.multiply(bd6)); // 101.500 37 System.out.println("-------------------"); 38 39 BigDecimal bd7 = new BigDecimal("1.301"); 40 BigDecimal bd8 = new BigDecimal("100"); 41 System.out.println("divide:" + bd7.divide(bd8)); // 0.01301 42 System.out.println("divide:" + bd7.divide(bd8, 3, BigDecimal.ROUND_HALF_UP)); // 0.013 43 System.out.println("divide:" + bd7.divide(bd8, 8, BigDecimal.ROUND_HALF_UP)); // 0.01301000 44 } 45 } View Code -----------------------------------------------------------------------------7:Date类和DateFormat类的概述及其方法(掌握) (1)Date类的概述(JDK1.1就出现了,之后使用Calendar类) Date是日期类,表示特定的瞬间,精确到毫秒。 (2)Date类的构造方法、成员方法和小案例 A:Date类的构造方法 public Date() 根据当前的默认毫秒值创建日期对象 public Date(long date) 根据给定的毫秒值创建日期对象 B:Date类的成员方法 public long getTime() 获取当前时间,以毫秒为单位 public void setTime(long time) 设置时间,以毫秒为单位 C:Date类的小案例 案例1:日期和毫秒值的相互转换。 把Date(日期)转换为一个毫秒值 把Date(日期)转换为一个毫秒值 1.通过Date类的成员方法:public long getTime() Date d = new Date(); long time = d.getTime(); 把一个毫秒值转换为Date(日期) 1.通过Date类的构造方法:public Date(long date) long time = 1000 * 60 * 60; // 1小时 Date d = new Date(time); 2.通过Date类的成员方法:public void setTime(long time) Date d = new Date(); d.setTime(1000); 案例2:你来到这个世界多少天了? 1 package cn.itcast_05; 2 3 import java.text.ParseException; 4 import java.text.SimpleDateFormat; 5 import java.util.Date; 6 import java.util.Scanner; 7 8 /* 9 * 算一下你来到这个世界多少天? 10 * 11 * 分析: 12 * A:键盘录入你的出生的年月日 13 * B:把该字符串转换为一个日期 14 * C:通过该日期得到一个毫秒值 15 * D:获取当前时间的毫秒值 16 * E:用D-C得到一个毫秒值 17 * F:把E的毫秒值转换为天 18 * /1000/60/60/24 19 */ 20 public class MyYearOldDemo { 21 public static void main(String[] args) throws ParseException { 22 // 键盘录入你的出生的年月日 23 Scanner sc = new Scanner(System.in); 24 System.out.println("请输入你的出生年月日:"); // 1992-11-11 25 String line = sc.nextLine(); 26 27 // 把该字符串解析成一个日期 28 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 29 Date d = sdf.parse(line); 30 31 // 通过该日期得到一个毫秒值 32 long myTime = d.getTime(); 33 34 // 获取当前时间的毫秒值 35 long nowTime = System.currentTimeMillis(); 36 37 // 用D-C得到一个毫秒值 38 long time = nowTime - myTime; 39 40 // 把E的毫秒值转换为天 41 long day = time / 1000 / 60 / 60 / 24; 42 43 System.out.println("你来到这个世界:" + day + "天"); // 9259天 44 } 45 } View Code --------------------------------------- (3)DateFormat类的概述(抽象类) DateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。是抽象类,所以使用其子类SimpleDateFormat。 即:针对日期/时间进行格式化和针对字符串(文本)进行解析的类,但是它是抽象类,所以使用其子类SimpleDateFormat。 (4)SimpleDateFormat类的构造方法(具体类) public SimpleDateFormat() 默认模式 public SimpleDateFormat(String pattern) 给定的模式 例如:常用的给定模式为:yyyy-MM-dd HH:mm:ss (5)DateFormat类的成员方法(抽象类) A:public final String format(Date date) B:public Date parse(String source) throws ParseException 日期和字符串的转换 a:Date --> String(格式化) public final String format(Date date) b:String --> Date(解析) public Date parse(String source) throws ParseException 示例代码如下: 1 package cn.itcast_03; 2 3 import java.text.ParseException; 4 import java.text.SimpleDateFormat; 5 import java.util.Date; 6 7 /* 8 * (格式化) 9 * Date --> String 10 * public final String format(Date date) 11 * 12 * (解析) 13 * String --> Date 14 * public Date parse(String source) 15 * 16 * DateForamt类是针对日期/时间进行格式化和针对字符串(文本)进行解析的类,但是它是抽象类,所以使用其子类SimpleDateFormat。 17 * 18 * SimpleDateFormat类的构造方法: 19 * public SimpleDateFormat() 默认模式 20 * public SimpleDateFormat(String pattern) 给定的模式 21 * 这个给定的模式字符串该如何写呢? 22 * 通过查看API,我们就找到了对应的模式: 23 * 年 y 24 * 月 M 25 * 日 d 26 * 时 H 27 * 分 m 28 * 秒 s 29 * 一般我们要以下这种格式: 30 * 2014年12月12日 12:12:12 31 */ 32 public class DateFormatDemo { 33 public static void main(String[] args) throws ParseException { 34 // Date --> String(格式化) 35 // 创建日期对象 36 Date d = new Date(); 37 38 // 创建格式化对象 39 // 默认模式 40 // SimpleDateFormat sdf = new SimpleDateFormat(); 41 // DateFormat类的方法:public final String format(Date date) 42 // String s = sdf.format(d); 43 // System.out.println(s); // 2018/3/19 下午12:04 44 45 // 给定模式 46 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); 47 SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); 48 String s = sdf.format(d); 49 String s2 = sdf2.format(d); 50 System.out.println(s); // 20180319121200 51 System.out.println(s2); // 2018年03月19日 12:12:00 52 53 54 // String --> Date(解析) 55 String str = "2008-08-08 12:12:12"; 56 // 注意:在把一个字符串解析为日期的时候,解析的格式必须和给定的字符串的格式匹配。 57 SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 58 Date dd = sdf3.parse(str); 59 System.out.println(dd); // Fri Aug 08 12:12:12 CST 2008 60 } 61 } (6)DateFormat类的案例 制作了一个针对日期操作的工具类DateUtil。(很多公司都是这样封装成自己的东西的) 示例代码如下: 1 package cn.itcast_04; 2 3 import java.text.ParseException; 4 import java.text.SimpleDateFormat; 5 import java.util.Date; 6 7 /** 8 * 这是日期和字符串相互转换的工具类 9 * 10 * @author 风清扬 11 */ 12 public class DateUtil { 13 private DateUtil() { 14 } 15 16 /** 17 * 这个方法的作用就是把日期格式化成一个字符串 18 * 19 * @param d 20 * 被格式化的日期对象 21 * @param format 22 * 传递过来的要被转换的格式 23 * @return 格式化后的字符串 24 */ 25 public static String dateToString(Date d, String format) { 26 // SimpleDateFormat sdf = new SimpleDateFormat(format); 27 // return sdf.format(d); 28 return new SimpleDateFormat(format).format(d); 29 } 30 31 /** 32 * 这个方法的作用就是把一个字符串解析成一个日期对象 33 * 34 * @param s 35 * 被解析的字符串 36 * @param format 37 * 传递过来的要被转换的格式 38 * @return 解析后的日期对象 39 * @throws ParseException 40 */ 41 public static Date stringToDate(String s, String format) throws ParseException { 42 return new SimpleDateFormat(format).parse(s); 43 } 44 } View Code 1 package cn.itcast_04; 2 3 import java.text.ParseException; 4 import java.util.Date; 5 6 /* 7 * 工具类的测试 8 */ 9 public class DateUtilDemo { 10 public static void main(String[] args) throws ParseException { 11 // 格式为:yyyy年MM月dd日 HH:mm:ss 12 Date d = new Date(); 13 String s = DateUtil.dateToString(d, "yyyy年MM月dd日 HH:mm:ss"); 14 System.out.println(s); // 2018年03月19日 14:57:19 15 16 // 格式为:yyyy年MM月dd日 17 String s2 = DateUtil.dateToString(d, "yyyy年MM月dd日"); 18 System.out.println(s2); // 2018年03月19日 19 20 // 格式为:HH:mm:ss 21 String s3 = DateUtil.dateToString(d, "HH:mm:ss"); 22 System.out.println(s3); // 14:57:19 23 24 String str = "2014-10-14"; 25 Date dd = DateUtil.stringToDate(str, "yyyy-MM-dd"); 26 System.out.println(dd); // Tue Oct 14 00:00:00 CST 2014 27 } 28 } View Code ----------------------------------------------------------------------------- 8:Calendar类的概述及其方法(掌握) (1)Calendar类的概述(抽象类) Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。 即:日历类,封装了所有的日历字段值(日历类中的每个日历字段都是静态的成员变量,并且是int类型),通过统一的方法根据传入不同的日历字段可以获取值。 (2)因为Calendar类是抽象类,那么如何得到一个日历对象呢? Calendar类是抽象类,不能够直接new对象,但是Calendar类中提供了一个静态方法,该方法内部返回的是其子类对象。 Calendar rightNow = Calendar.getInstance(); // 本质返回的是子类对象 int year = rightNow.get(Calendar.YEAR); // 获取年 ...... (3)Calendar类的成员方法(抽象类) A:public static Calendar getInstance() public int get(int field) 根据日历字段得到对应的值 B:public void add(int field, int amount) 根据日历字段和一个正负数确定是添加还是减去对应日历字段的值 C:public final void set(int year, int month, int date) 设置当前日历对象的年月日 (4)Calendar类的案例 计算任意一年的2月份有多少天?(面试题)(day01_50道编程题中有这题) 示例代码如下: 1 package cn.itcast_03; 2 3 import java.util.Calendar; 4 import java.util.Scanner; 5 6 /* 7 * 获取任意一年的二月有多少天 8 * 9 * 分析: 10 * A:键盘录入任意的年份 11 * B:设置日历对象的年月日 12 * 年就是A输入的数据 13 * 月是2 14 * 日是1 15 * 是3月1日 16 * C:把时间往前推一天,就是2月的最后一天 17 * D:获取这一天输出即可 18 */ 19 public class CalendarTest { 20 public static void main(String[] args) { 21 // 键盘录入任意的年份 22 Scanner sc = new Scanner(System.in); 23 System.out.println("请输入年份:"); 24 int year = sc.nextInt(); 25 26 // 设置日历对象的年月日 27 Calendar c = Calendar.getInstance(); 28 c.set(year, 2, 1); // 其实是这一年的3月1日 29 // 把时间往前推一天,就是2月的最后一天 30 c.add(Calendar.DATE, -1); 31 32 // 获取这一天输出即可 33 System.out.println(c.get(Calendar.DATE)); 34 } 35 } View Code =============================================================================我的GitHub地址: https://github.com/heizemingjun 我的博客园地址: http://www.cnblogs.com/chenmingjun 我的蚂蚁笔记博客地址: http://blog.leanote.com/chenmingjun Copyright ©2018 黑泽明军 【转载文章务必保留出处和署名,谢谢!】

优秀的个人博客,低调大师

《从零开始学Swift》学习笔记(Day 70)——Swift与Objective-C混合编程之Swift与Objective-CAPI映射

Swift与Objective-C API映射 在混合编程过程中Swift与Objective-C调用是双向的,由于不同语言对于相同API的表述是不同的,他们之间是有某种映射规律的,这种API映射规律主要体现在构造函数和方法两个方面。 1、构造函数映射 在Swift与Objective-C语言进行混合编程时,首先涉及到调用构造函数实例化对象问题,不同语言下构造函数表述形式不同,如图是苹果公司官方API文档,描述了NSString类的一个构造函数。 Swift构造函数除了第一个参数外,其它参数的外部名就是选择器对应部分名。规律的其它细节图中已经解释的很清楚了,这个规律反之亦然,这里不再赘述。 2、方法名映射 在Swift与Objective-C语言进行混合编程时,不同语言下方法名表述形式也是不同的,如图是苹果公司官方API文档,描述了NSString类的rangeOfString:options:range:方法。 选择器第一个部分rangeOfString作为方法名,一般情况下Swift方法第一个参数的外部参数名是要省略的,“_”符号表示省略。之后的选择器各部分名(如:options和range),作为外部参数名。除了参数名对应为,参数类型也要对应下来。 Swift 2.0之后方法可以声明抛出错误,这些能抛出错误的方法,不同语言下方法名表述形式如图下图所示,是writeToFile:atomically:encoding:error:苹果公司官方API文档。 比较两种不同语言,我们会发现error参数在Swift语言中不再使用,而是在方法后添加了throws关键字。 这种映射规律不仅仅只适用于苹果公司官方提供的Objective-C类,也适用于自己编写的Objective-C类。 本文转自 tony关东升 51CTO博客,原文链接:http://blog.51cto.com/tonyguan/1749107,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册