Linux I2C核心、总线与设备驱动(二)

从上面的分析可知,虽然I2C硬件体系结构比较简单,但是I2C体系结构在Linux中的实现却相当复杂。当工程师拿到实际的电路板,面对复杂的 Linux I2C子系统,应该如何下手写驱动呢?究竟有哪些是需要亲自做的,哪些是内核已经提供的呢?理清这个问题非常有意义,可以使我们面对具体问题时迅速地抓住重点。
   
一方面,适配器驱动可能是Linux内核本身还不包含的。另一方面,挂接在适配器上的具体设备驱动可能也是Linux不存在的。即便上述设备驱动都存在于Linux内核中,其基于的平台也可能与我们的电路板不一样。因此,工程师要实现的主要工作将包括:
提供I2C适配器的硬件驱动,探测、初始化I2C适配器(如申请I2CI/O地址和中断号)、驱动CPU控制的I2C适配器从硬件上产生各种信号以及处理I2C中断等。
提供I2C适配器的algorithm,用具体适配器的xxx_xfer()函数填充i2c_algorithmmaster_xfer指针,并把i2c_algorithm指针赋值给i2c_adapteralgo指针。
实现I2C设备驱动与i2c_driver接口,用具体设备yyyyyy_attach_adapter()函数指针、 yyy_detach_client()函数指针和yyy_command()函数指针的赋值给i2c_driverattach_adapter detach_adapterdetach_client指针。
实现I2C设备驱动的文件操作接口,即实现具体设备yyyyyy_read()yyy_write()yyy_ioctl()函数等。
上述工作中12属于I2C总线驱动,34属于I2C设备驱动,做完这些工作,系统会增加两个内核模块。本章第34节将详细分析这些工作的实施方法,给出设计模板,而5~6节将给出两个具体的实例。
15.2 Linux I2C
核心
I2C
核心(drivers/i2c/i2c-core.c)中提供了一组不依赖于硬件平台的接口函数,这个文件一般不需要被工程师修改,但是理解其中的主要函数非常关键,因为I2C总线驱动和设备驱动之间依赖于I2C核心作为纽带。I2C核心中的主要函数包括:
增加/删除i2c_adapter
int i2c_add_adapter(struct i2c_adapter *adap);
int i2c_del_adapter(struct i2c_adapter *adap);
增加/删除i2c_driver
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
int i2c_del_driver(struct i2c_driver *driver);
inline int i2c_add_driver(struct i2c_driver *driver);
?  i2c_client
依附/脱离
int i2c_attach_client(struct i2c_client *client);
int i2c_detach_client(struct i2c_client *client);
当一个具体的client被侦测到并被关联的时候,设备和sysfs文件将被注册。相反地,在client被取消关联的时候,sysfs文件和设备也被注销,如代码清单15.6
代码清单15.6 I2C核心client attach/detach函数
1  int i2c_attach_client(struct i2c_client *client)
2  {
3    ...
4   device_register(&client->dev);
5   device_create_file(&client->dev, &dev_attr_client_name);
6   
7   return 0;
8  }

10 int i2c_detach_client(struct i2c_client *client)
11 {
12   ...
13  device_remove_file(&client->dev, &dev_attr_client_name);
14  device_unregister(&client->dev);
15   ...
16 }
4i2c传输、发送和接收
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num);
int i2c_master_send(struct i2c_client *client,const char *buf ,int count);
int i2c_master_recv(struct i2c_client *client, char *buf ,int count);
i2c_transfer ()
函数用于进行I2C适配器和I2C设备之间的一组消息交互,i2c_master_send()函数和i2c_master_recv()函数内部会调用i2c_transfer()函数分别完成一条写消息和一条读消息,如代码清单15.715.8
代码清单15.7 I2C核心i2c_master_send函数
1  int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
2  {
3   int ret;
4   struct i2c_adapter *adap=client->adapter;
5   struct i2c_msg msg;
6    /*
构造一个写消息*/
7   msg.addr = client->addr;
8   msg.flags = client->flags & I2C_M_TEN;
9   msg.len = count;
10  msg.buf = (char *)buf;
11  /*
传输消息*/
12  ret = i2c_transfer(adap, &msg, 1);
13
14  return (ret == 1) ? count : ret;
15 }
代码清单15.8 I2C核心i 2c_master_recv函数
1  int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
2  {
3   struct i2c_adapter *adap=client->adapter;
4   struct i2c_msg msg;
5   int ret;
6   /*
构造一个读消息*/
7   msg.addr = client->addr;
8   msg.flags = client->flags & I2C_M_TEN;
9   msg.flags |= I2C_M_RD;
10  msg.len = count;
11  msg.buf = buf;
12  /*
传输消息*/
13  ret = i2c_transfer(adap, &msg, 1);
14
15  /*
成功(1条消息被处理), 返回读的字节数 */
16  return (ret == 1) ? count : ret;
17 }
i2c_transfer()
函数本身不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithmmaster_xfer()函数真正驱动硬件流程,如代码清单15.9
代码清单15.9 I2C核心i 2c_transfer函数
1  int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
2  {
3   int ret;

5   if (adap->algo->master_xfer) {
6    down(&adap->bus_lock);
7    ret = adap->algo->master_xfer(adap,msgs,num); /*
消息传输 */
8    up(&adap->bus_lock);
9    return ret;
10  } else {
11   dev_dbg(&adap->dev, "I2C level transfers not supported\n");
12   return -ENOSYS;
13  }
14 }
5I2C控制命令分派
下面函数有助于将发给I2C适配器设备文件ioctl的命令分派给对应适配器的algorithmalgo_control()函数或i2c_drivercommand()函数:
int i2c_control(struct i2c_client *client, unsigned int cmd, unsigned long arg);
void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg);
15.3 Linux I2C
总线驱动
15.3.1 I2C
适配器驱动加载与卸载
I2C
总线驱动模块的加载函数要完成两个工作:
初始化I2C适配器所使用的硬件资源,申请I/O地址、中断号等。
通过i2c_add_adapter()添加i2c_adapter的数据结构,当然这个i2c_adapter数据结构的成员已经被xxx适配器的相应函数指针所初始化。
I2C
总线驱动模块的卸载函数要完成的工作与加载函数的相反:
释放I2C适配器所使用的硬件资源,释放I/O地址、中断号等。
通过i2c_del_adapter()删除i2c_adapter的数据结构。
代码清单15.10给出了I2C适配器驱动模块加载和卸载函数的模板。
代码清单15.10 I2C总线驱动模块加载和卸载函数模板
1  static int __init i2c_adapter_xxx_init(void)
2  {
3    xxx_adpater_hw_init();
4    i2c_add_adapter(&xxx_adapter);
5  }

7  static void __exit i2c_adapter_xxx_exit(void)
8  {
9    xxx_adpater_hw_free();
10   i2c_del_adapter(&xxx_adapter);
11 }
上述代码中xxx_adpater_hw_init()xxx_adpater_hw_free()函数的实现都与具体的CPUI2C设备硬件直接相关。
15.3.2 I2C
总线通信方法
我们需要为特定的I2C适配器实现其通信方法,主要实现i2c_algorithmmaster_xfer()函数和functionality()函数。
functionality ()
函数非常简单,用于返回algorithm所支持的通信协议,如I2C_FUNC_I2CI2C_FUNC_10BIT_ADDR I2C_FUNC_SMBUS_READ_BYTEI2C_FUNC_SMBUS_WRITE_BYTE等。
master_xfer()
函数在I2C适配器上完成传递给它的i2c_msg数组中的每个I2C消息,代码清单15.11给出了xxx设备的master_xfer()函数模板。
代码清单15.11 I2C总线驱动master_xfer函数模板
1  static int i2c_adapter_xxx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
2    int num)
3  {
4    ...
5    for (i = 0; i < num; i++)
6    {
7      i2c_adapter_xxx_start(); /*
产生开始位*/
8      /*
是读消息*/
9      if (msgs[i]->flags &I2C_M_RD)
10     {
11       i2c_adapter_xxx_setaddr((msg->addr << 1) | 1); /*
发送从设备读地址*/
12       i2c_adapter_xxx_wait_ack(); /*
获得从设备的ack*/
13       i2c_adapter_xxx_readbytes(msgs[i]->buf, msgs[i]->len); /*
读取msgs[i]
14         ->len
长的数据到msgs[i]->buf*/
15     }
16     else
17      /*
是写消息*/
18     {
19       i2c_adapter_xxx_setaddr(msg->addr << 1); /*
发送从设备写地址*/
20       i2c_adapter_xxx_wait_ack(); /*
获得从设备的ack*/
21       i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len); /*
读取msgs[i]
22         ->len
长的数据到msgs[i]->buf*/
23     }
24   }
25   i2c_adapter_xxx_stop(); /*
产生停止位*/
26 }
述代码实际上给出了一个master_xfer()函数处理I2C消息数组的流程,对于数组中的每个消息,判断消息类型,若为读消息,则赋从设备地址为 (msg->addr << 1) | 1,否则为msg->addr << 1。对每个消息产生1个开始位,紧接着传送从设备地址,然后开始数据的发送或接收,对最后的消息还需产生1个停止位。图15.3描述了整个 master_xfer()完成的时序。


15.3 algorithmmaster_xfer的时序

master_xfer() 数模板中的i2c_adapter_xxx_start()i2c_adapter_xxx_setaddr() i2c_adapter_xxx_wait_ack()i2c_adapter_xxx_readbytes() i2c_adapter_xxx_writebytes()i2c_adapter_xxx_stop()函数用于完成适配器的底层硬件操作,与I2C 适配器和CPU的具体硬件直接相关,需要由工程师根据芯片的数据手册来实现。
i2c_adapter_xxx_readbytes()
用于从从设备上接收一串数据,i2c_adapter_xxx_writebytes()用于向从设备写入一串数据,这两个函数的内部也会涉及到I2C总线协议中的ACK应答。
master_xfer ()
函数的实现在形式上会很多样,即便是Linux内核源代码中已经给出的一些I2C总线驱动的master_xfer()函数,由于由不同的组织或个人 完成,风格上的差别也非常大,不一定能与模板完全对应,如master_xfer()函数模板给出的消息处理是顺序进行的,而有的驱动以中断方式来完成这 个流程(第5节的实例即是如此)。不管具体怎么实施,流程的本质都是不变的。因为这个流程不以驱动工程师的意志为转移,最终由I2C总线硬件上的通信协议 决定。
多数I2C总线驱动会定义一个xxx_i2c结构体,作为i2c_adapteralgo_data(类似私有数据),其中包含 I2C消息数组指针、数组索引及I2C适配器algorithm访问控制用的自旋锁、等待队列等,而master_xfer()函数完成消息数组中消息的 处理也可通过对xxx_i2c结构体相关成员的访问来控制。代码清单15.12给出了xxx_i2c结构体的定义,与图15.2中的xxx_i2c是对应 的。
代码清单15.12 xxx_i2c结构体模板
1  struct xxx_i2c
2  {
3   spinlock_t  lock;
4   wait_queue_head_t wait; 
5   struct i2c_msg  *msg;
6   unsigned int  msg_num;
7   unsigned int  msg_idx;
8   unsigned int  msg_ptr;
9   ...
10  struct i2c_adapter adap;
11 };
15.4 Linux I2C
设备驱动
I2C
设备驱动要使用i2c_driveri2c_client数据结构并填充其中的成员函数。i2c_client一般被包含在设备的私有信息结构体 yyy_data中,而i2c_driver则适宜被定义为全局变量并初始化,代码清单15.13显示了被初始化的i2c_driver
代码清单15.13 初始化的i2c_driver
1  static struct i2c_driver yyy_driver =
2  {
3    .driver =
4    {
5      .name = "yyy",
6    } ,
7    .attach_adapter = yyy_attach_adapter,
8    .detach_client =  yyy_detach_client,
9    .command = yyy_command,
10 };
15.4.1 Linux I2C
设备驱动模块加载与卸载
I2C
设备驱动模块加载函数通用的方法是在I2C设备驱动模块加载函数中完成两件事:
通过register_chrdev()函数将I2C设备注册为一个字符设备。
通过I2C核心的i2c_add_driver()函数添加i2c_driver
在模块卸载函数中需要做相反的两件事:
通过I2C核心的i2c_del_driver()函数删除i2c_driver
通过unregister_chrdev()函数注销字符设备。
代码清单15.14给出了I2C设备驱动加载与卸载函数的模板。
代码清单15.14 I2C设备驱动模块加载与卸载函数模板
1  static int __init yyy_init(void)
2  {
3    int res;
4    /*
注册字符设备*/
5    res = register_chrdev(YYY_MAJOR, "yyy", &yyy_fops); //
老内核接口
6    if (res)
7      goto out;
8    /*
添加i2c_driver*/
9    res = i2c_add_driver(&yyy_driver);
10   if (res)
11     goto out_unreg_class;
12   return 0;
13
14   out_unreg_chrdev: unregister_chrdev(I2C_MAJOR, "i2c");
15   out: printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
16   return res;
17 }
18
19 static void __exit yyy_exit(void)
20 {
21   i2c_del_driver(&i2cdev_driver);
22   unregister_chrdev(YYY_MAJOR, "yyy");
23 }
5行代码说明注册“yyy”这个字符设备时,使用的file_operations结构体为yyy_fops15.4.3节将讲解这个结构体中成员函数的实现。
15.4.2 Linux I2C
设备驱动i2c_driver成员函数
i2c_add_driver (&yyy_driver)
的执行会引发i2c_driver结构体中yyy_attach_adapter()函数的执行,我们可以在 yyy_attach_adapter()函数里探测物理设备。为了实现探测,yyy_attach_adapter()函数里面也只需简单地调用I2C 核心的i2c_probe()函数,如代码清单15.15
代码清单15.15 I2C设备驱动i2c_attach_adapter函数模板
1 static int yyy_attach_adapter(struct i2c_adapter *adapter)
2 {
3  return i2c_probe(adapter, &addr_data, yyy_detect);
4 }
代码第3行传递给i2c_probe()函数的第1个参数是i2c_adapter指针,第2个参数是要探测的地址数据,第3个参数是具体的探测函数。要探测的地址实际列表在一个16位无符号整型数组中,这个数组以I2C_CLIENT_END为最后一个元素。
i2c_probe()
函数会引发yyy_detect()函数的调用,可以在yyy_detect()函数中初始化i2c_client,如代码清单15.16
代码清单15.16 I2C设备驱动detect函数模板
1  static int yyy_detect(struct i2c_adapter *adapter, int address, int kind)
2  {
3   struct i2c_client *new_client;
4   struct yyy_data *data;
5   int err = 0;
6
7   if (!i2c_check_functionality(adapter, I2C_FUNC_XXX)
8     goto exit;
9
10  if (!(data = kzalloc(sizeof(struct yyy_data), GFP_KERNEL)))
11  {
12    err =  - ENOMEM;
13    goto exit;
14  }
15
16  new_client = &data->client;
17  new_client->addr = address;
18  new_client->adapter = adapter;
19  new_client->driver = &yyy_driver;
20  new_client->flags = 0;
21
22  /*
新的client将依附于adapter */
23  if ((err = i2c_attach_client(new_client)))
24    goto exit_kfree;
25 
26  yyy_init_client(new_client);
27  return 0;
28  exit_kfree: kfree(data);
29  exit: return err;
30}
码第10行分配私有信息结构体的内存,i2c_client也被创建。第1620行对新创建的i2c_client进行初始化。第23行调用内核的 i2c_attach_client()知会I2C核心系统中包含了一个新的I2C设备。第26行代码初始化i2c_client对应的I2C设备,这个 函数是硬件相关的。
15.4描述了当I2C设备驱动的模块加载函数被调用的时候引发的连锁反应的流程。


15.4 I2C设备驱动模块加载连锁反应

I2C 设备驱动卸载函数进行i2c_del_driver(&yyy_driver)调用后,会引发与yyy_driver关联的每个 i2c_client与之解除关联,即yyy_detach_client()函数将因此而被调用,代码清单15.17给出了函数 yyy_detach_client()的设计。
代码清单15.17 I2C设备驱动i2c_detach_client函数模板
1  static int yyy_detach_client(struct i2c_client *client)
2  {
3   int err;
4   struct yyy_data *data = i2c_get_clientdata(client);

6   if ((err = i2c_detach_client(client)))
7    return err;

9   kfree(data);
10  return 0;
11 }
述函数中第4行的i2c_get_clientdata()函数用于从yyy_data私有信息结构中的i2c_client的指针获取yyy_data 的指针。第6行调用I2C核心函数i2c_detach_client(),这个函数会引发i2c_adapterclient_unregister ()函数被调用。第9行代码释放yyy_data的内存。
15.5描述了当I2C设备驱动的模块卸载函数被调用的时候引发的连锁反应的流程。


15.5 I2C设备驱动模块卸载连锁反应

下面开始分析i2c_driver中重要函数yyy_command()的实现,它实现了针对设备的控制命令。具体的控制命令是设备相关的,如对于实时钟而言,命令将是设置时间和获取时间,而对于视频AD设备而言,命令会是设置采样方式、选择通道等。
假设yyy设备接受两类命令YYY_CMD1YYY_CMD2,而处理这两个命令的函数分别为yyy_cmd1()yyy_cmd2(),代码清单15.18给出了yyy_command()函数的设计。
代码清单15.18 I2C设备驱动command函数模板
1  static int yyy_command(struct i2c_client *client, unsigned int cmd, void
2    *arg)
3  {
4    switch (cmd)
5    {
6      case YYY_CMD1:
7        return yyy_cmd1(client, arg); 
8      case YYY_CMD2:
9        return yyy_cmd2(client, arg);
10     default:
11       return  - EINVAL;
12   }
13 }
具体命令的实现是通过组件i2c_msg消息数组,并调用I2C核心的传输、发送和接收函数,由I2C核心的传输、发送和接收函数调用I2C适配器对应的algorithm相关函数来完成的。代码清单15.19给出了一个yyy_cmd1()的例子。
代码清单15.19 I2C设备具体命令处理函数模板
1  static int yyy_cmd1(struct i2c_client *client, struct rtc_time *dt)
2  {
3   struct i2c_msg msg[2];
4   /*
第一条消息是写消息*/
5   msg[0].addr = client->addr;
6   msg[0].flags = 0;
7   msg[0].len = 1;
8   msg[0].buf = &offs;
9   /*
第二条消息是读消息*/
10  msg[1].addr = client->addr;
11  msg[1].flags = I2C_M_RD;
12  msg[1].len = sizeof(buf);
13  msg[1].buf = &buf[0];
14  
15  i2c_transfer(client->adapter, msg, 2);
16   ...
17 }
15.4.3 Linux I2C
设备驱动文件操作接口
为一种字符类设备,Linux I2C设备驱动文件操作接口与普通的设备驱动是完全一致的,但是在其中要使用i2c_clienti2c_driveri2c_adapter i2c_algorithm结构体和I2C核心,并且对设备的读写和控制需要借助体系结构中各组成部分的协同合作。代码清单15.20给出一个I2C设备 写函数的例子。
代码清单15.20 I2C设备文件接口写函数范例
1  static ssize_t yyy_write(struct file *file, char *buf, size_t count, loff_t off)
2  {
3    struct i2c_client *client = (struct i2c_client*)file->private_data;
4    i2c_msg msg[1];
5    char *tmp;
6    int ret;

8    tmp = kmalloc(count, GFP_KERNEL);
9    if (tmp == NULL)
10     return  - ENOMEM;
11   if (copy_from_user(tmp, buf, count))
12   {
13     kfree(tmp);
14     return  - EFAULT;
15   }
16
17   msg[0].addr = client->addr;//
地址
18   msg[0].flags = 0;       //0
为写
19   msg[0].len = count;     //
要写的字节数
20   msg[0].buf = tmp;      //
要写的数据
21   ret = i2c_transfer(client->adapter, msg, 1);  //
传输i2c消息
22   return (ret == 1) ? count : ret;
23 }
上述程序给出的仅仅是一个写函数的例子,具体的写操作与设备密切相关。我们通过这个例来仔细分析I2C设备读写过程中数据的流向和函数的调用关系。I2C设备的写操作经历了如下几个步骤:
从用户空间到字符设备驱动写函数接口,写函数构造I2C消息数组。
写函数把构造的I2C消息数组传递给I2C核心的传输函数i2c_transfer()
I2C核心的传输函数i2c_transfer()找到对应适配器algorithm的通信方法函数master_xfer()去最终完成I2C消息的处理。
15.6描述了从用户空间发起读写操作到algorithm进行消息传输的流程。

 
15.6 I2C设备读写完整流程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/434730.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mtu设置失败_Oracle RAC该调整网卡MTU值

在Oracle RAC的环境中&#xff0c;如果我们发现OSW监控数据显示包重组失败率过高&#xff0c;就需要引起足够的重视&#xff0c;因为这很可能会引发member kill/Node kill等重大故障&#xff0c;甚至在有些场景会连带影响到所有RAC节点不可用。一般我们会选择调整ipfrag相关参数…

判断字段长度大于某长度_判断数据库性能只能通过count(*)?No,这些优化方案了解一下!...

大多数用户在体验数据库时&#xff0c;接触到的最早的sql语句就是count(*)&#xff0c;因此用户判断数据库性能时通常也会通过count(*)进行比较。但在执行时通常会出现一个问题&#xff1a;对某个表做count(*)时需对全表数据进行扫描&#xff0c;当表中包含数据量较大的字段时&…

10 款基于 jQuery 的切换效果插件推荐

本文整理了 10 款非常好用的jQuery切换效果插件&#xff0c;包括平滑切换和重叠动画等&#xff0c;这些插件可以实现不同元素之间的动态切换。 1. InnerFade 这是一个基于jQuery的小插件&#xff0c;可以实现页面内的元素淡入淡出效果。 源码/演示 2. HighlightFade 该插件可以…

js矢量图类库:Raphaël—JavaScript Library

官方网址&#xff1a;http://raphaeljs.com/ Raphal is a small JavaScript library that should simplify your work with vector graphics on the web. If you want to create your own specific chart or image crop and rotate widget, for example, you can achieve it si…

gridview databind 会导致页面刷新马_Innodb批量页面刷盘情况下的quot;两次写quot;

//Innodb批量页面刷盘情况下的"两次写"//之前的文章中&#xff0c;我们介绍过innodb的两次写特性&#xff0c;这里给出链接&#xff1a;InnoDB的两次写特性今天我们完善一下这部分的内容。我们知道innodb数据页的默认大小是16kb&#xff0c;磁盘和内存通过数据页进行…

实例解析linux内核I2C体系结构(1)

作者&#xff1a;刘洪涛,华清远见嵌入式学院讲师。 一、概述 谈到在linux系统下编写I2C驱动&#xff0c;目前主要有两种方式&#xff0c;一种是把I2C设备当作一个普通的字符设备来处理&#xff0c;另一种是利用linux I2C驱动体系结构来完成。下面比较下这两种驱动。 第一种方…

★ Flex を使って Scalable Vector Graphics とビットマップを描画する

from: http://www.ibm.com/developerworks/jp/web/library/wa-svgbitmap/Flex を使って Scalable Vector Graphics とビットマップを描画するSandeep Malik, Tech Lead, IBM 概要&#xff1a; SVG (Scalable Vector Graphics) はグラフィックスの領域で最も重要な技術の 1 つで…

g5420 win7集显驱动_台式机装WIN7?雷我已经趟完了

注&#xff1a;本文只用于PC爱好者交流测试&#xff0c;文中所有测试版系统均只用于测试&#xff0c;不得用于个人或商业用途。Windows全面更新至win10版本后&#xff0c;改装Win7系统逐渐变得越来越艰难。厂商BIOS中逐渐舍弃了原始界面改为图形化&#xff0c;传统Legacy模式无…

制作完整的java可执行文件

帮教务处的老师做了一个小软件&#xff0c;所以学习了一下制作java可执行文件&#xff0c;在此分享一下。 说明&#xff1a;因为是做完很长一段时间后再截的图&#xff0c;可能有点纰漏&#xff0c;大体应该没什么问题。 我的eclipse工程文件目录: bin | images&#xff08;放图…

ajax中async_小猿圈web前端之ajax的同步和异步有怎样的区别?

对于ajax我们应该知道ajax是主要用来在前端页面中向服务器后端请求数据&#xff0c;ajax中根据async的值不同分为同步&#xff08;async false&#xff09;和异步&#xff08;async true&#xff09;两种执行方式&#xff0c;那么&#xff0c;ajax的同步和异步请求两种方式有…

mysql存储引擎的区别_Mysql的两种存储引擎以及区别

一、Mysql的两种存储引擎1、MyISAM&#xff1a;①不支持事务&#xff0c;但是整个操作是原子性的(事务具备四种特性&#xff1a;原子性、一致性、隔离性、持久性)②不支持外键&#xff0c;支持表锁&#xff0c;每次所住的是整张表MyISAM的表锁有读锁和写锁(两个锁都是表级别)&a…

带给你灵感的3D街画艺术设计

3D街头艺术画已在16世纪以来意大利文艺复兴时期的Madonnari画家创造了令人惊叹的壁画来装饰豪华别墅的内墙。3D艺术也可以跟踪它的航线&#xff0c;。这里有一些新的图像&#xff0c;这将使你想知道它是如何可能的使东西是如此逼真&#xff0c;3D设计们不要错过 1。 &#xff0…

[原]2011年度生活三层总结

一年了。 想到自己从开始没有目标&#xff0c;误打误撞的来到了提高班到现在的成长。我是多么的幸运&#xff0c;幸运的来到廊坊师范&#xff08;现在都要称之为母校了&#xff09;&#xff0c;幸运的来到了提高班&#xff0c;遇到了米老师&#xff0c;在此感谢。 一年了。改变…

hp laser103 属性没有配置项_哦?在hp打印机面板上就可以更改打印机ip地址

修改打印机IP的方法有很多但都没有直接从打印机控制面板上修改方便过瘾虽然有些机器不支持但是惠普大部分机器还是可以的今天我们就以 LaserJet M227 系列打印机为例hp官方为大家介绍一下具体的设置方法步骤一&#xff1a;打印配置报告查看有效IP地址如果机器是2行控制面板 1.在…

python去掉最高分和最低分_去掉一个最高分,去掉一个最低分求平均值(trimmean)...

如下图&#xff1a;演讲比赛&#xff0c;要求去掉一个最高分&#xff0c;去掉一个对低分后求平均值。当然这个太简单了&#xff0c;我们可以用max求出最大值&#xff0c;用min求出最小值&#xff0c;然后sum求出数据总和&#xff0c;用(总和-最大值-最小值)/(数据总个数-2)。思…

[Oracle整理]CASE-END

说明&#xff1a;本内容是工作用到的知识点整理&#xff0c;来自工作中和网络。 代码于Oracle9上测试。 作用: 1可用来进行数据资料行转列的功能 2可用来对数据进行判断&#xff0c;类似decode&#xff0c;但CASE语句在处理范围条件的时候会显得非常灵活。如果只是需要匹配少量…

C# 线程手册 第三章 使用线程 Monitor.TryEnter()

Monitor 类的TryEnter() 方法在尝试获取一个对象上的显式锁方面和 Enter() 方法类似。然而&#xff0c;它不像Enter()方法那样会阻塞执行。如果线程成功进入关键区域那么TryEnter()方法会返回true. TryEnter()方法的三个重载方法中的两个以一个timeout类型值作为参数&#xff0…

pycharm不同py文件共享参数_PyCharm安装笔记

1. 介绍1.1 介绍今天福哥带着大家学习如何安装非常好用的Python编辑器&#xff0c;也就是jetbrains全家桶的PyCharm编辑器。PyCharm是jetbrans开发的一款专门用来编写Python程序的编辑器&#xff0c;它的自动补全、代码联想、框架支持、插件支持以及高效的反应速度成为了编写Py…

Windows与linux双系统安装

[源] [http://xiaomaimai.blog.51cto.com/1182965/294256] Windows与linux双系统 Linux的安装方式有硬盘安装、网络安装、光驱安装。 双系统的安装最后先安装windows&#xff0c;再安装linux&#xff0c;因为windows每次安装时都会重新修改系统引导文件&#xff0c;如果安装…

第一步:Axure 使用svn多人协作产品开发(提交文件)

注册登陆http://www.svnxiezuo.com站点 注册登陆http://www.svnxiezuo.com站点 从注册http://www.svnxiezuo.com站点获取svn项目地址 编辑axure文件 开始创建axure共享文件 创建axure共享文件 axure提交文件到svn版本库当中 登陆http://www.svnxiezuo.com站点svn版本库 axure创…