12C 概述
I2C 是 philips 提出的外设总线.
I2C 只有两条线,一条串行数据线:SDA,一条是时钟线 SCL ,使用 SCL,SDA 这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线。
因此,I2C 总线被非常广泛地应用在 EEPROM,实时钟,小型 LCD 等设备与 CPU 的接口中。
linux下的驱动思路
在 linux 系统下编写 12C 驱动,目前主要有两种方法,一种是把 12C 设备当作一个普通的字符设备来处理,另一种是利用 linux 下I2C 驱动体系结构来完成。下面比较下这两种方法:
第一种方法:优点:思路比较直接,不需要花很多时间去了解 linux 中复杂的 I2C 子系统的操作方法。缺点:要求工程师不仅要对 I2C 设备的操作熟悉,而且要熟悉 2C 的适配器(2C 控制器)操作。要求工程师对 12C 的设备器及 12C 的设备操作方法都比较熟悉,最重要的是写出的程序可以移植性差。对内核的资源无法直接使用,因为内核提供的所有 12C 设备器以及设备驱动都是基于 2C 子系统的格式。
第一种方法的优点就是第二种方法的缺点。
第一种方法的缺点就是第二种方法的优点。
三、I2C 架构概述
Linux 的 I2C 体系结构分为3 个组成部分:
I2C 核心: I2C 核心提供了 I2C 总线驱动和设备驱动的注册,注销方法,12C 通信方法(”algorithm”)上层的,与具体适配器无关的代码以及探测设备,检测设备地址的上层代码等。
I2C 总线驱动:I2C 总线驱动是对 I2C 硬件体系结构中适配器端的实现,适配器可由 CPU 控制,甚至可以直接集成在 CPU 内部。
I2C 设备驱动: I2C 设备驱动(也称为客户驱动)是对 I2C 硬件体系结构中设备端的实现,设备一般挂接在受 CPU控制的 I2C 适配器上,通过 I2C 适配器与 CPU 交换数据。

上图完整的描述了 linux i2c 动架构,虽然 12C 硬件体系结构比较简单,但是 12C 体系结构在 linux 中的实现却相当复杂。
那么我们如何编写特定 i2c 接口器件的驱动程序? 就是说上述架构中的那些部分需要我们完成,而哪些是linux 内核已经完善的或者是芯片提供商已经提供的?
四、架构层次分类
第一层:提供 i2c adapter 的硬件驱动,探测、初始化 i2c adapter (如申请 i2c 的 io 地址和中断号),驱动soc 控制的 i2c adapter 在硬件上产生信号 (start、stop、ack) 以及处理 i2c 中断。覆盖图中的硬件实现层。
第二层:提供 i2c adapter 的algorithm,用具体适配器的 xxx xferf0函数来填充 i2c algorithm 的master_xfer 函数指针,并把赋值后的i2c algorithm 再赋值给i2c adapter 的algo 指针。覆盖图中的访问抽象层.i2c 核心层。
第三层:实现 i2c 设备驱动中的 i2c driver 接口,用具体的i2c device 设备的attach adapter0)detach adapter0)方法赋值给 i2c driver 的成员函数指针。实现设备 device 与总线(或者 adapter) 的挂接。覆盖图中的 driver 驱动层。
第四层:实现 i2c 设备所对应的具体 device 的驱动,i2c driver 只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别的,所以要实现具体设备 device 的 write()、read0)、ict10 等方法,赋值给file_operations,然后注册字符设备 (多数是字符设备)。覆盖图中的 driver 驱动层。
第一层和第二层又叫 i2c 总线驱动(bus),第三第四属于 i2c 设备驱动(device driver)。
在linux 驱动架构中,几乎不需要驱动开发人员再添加 bus,因为 linux 内核几乎集成所有总线 bus,如usb.pci、i2c 等等。并且总线 bus 中的(与特定硬件相关的代码)已由芯片提供商编写完成,例如三星的 s3c-2440 平台i2c总线 bus 为/drivers/i2c/buses/i2c-s3c2410.c。

i2c-core.c 这个文件实现了 I2C 核心的功能以及/proc/bus/i2c*接口。i2c-dev.c 实现了 I2C 适配器设备文件的功能,每一个 2C 适配器都被分配一个设备。通过适配器访设备时的主设备号都为 89,次设备号为 0-255。
i2c-dev.c 并没有针对特定的设备而设计,只是提供了通用的 read0,write0,和 ioct10等接口,应用层可以借用这些接口访问挂接在适配器上的 I2C 设备的存储空间或寄存器,并控制 2C 设备的工作方式。busses 文件夹这个文件中包含了一些 12C 总线的动,如针对 S3C2410,S3C2440,S3C6410 等处理器的 12C控制器驱动为 i2c-s3c2410.c。
algos 文件夹实现了一些 I2C 总线适配器的 algorithm。
函数调用层次
有时候代码比任何文字描述都来得直接,但是过多的代码展示反而让人觉得枯燥。这个时候,需要一幅图来梳理一下上面的内容

i2c board info
该结构体用于描述访问 i2c 从机设备信息,如从机设备名字和地址

上述对应的 I2C 设备驱动的名字为 24c04,设备地址为 0x50。另外 platform data 指定了其他的一些设备数据,如这片 EERPOM 的存储容量和页大小的信息。
最后使用 i2c_register board_info 将at2xx_i2c_bdi 数组中的所有设备都注册到了 2号 I2C 总线上。
注:
#define I2C_BOARD_INFO(dev_type, dev_addr).type = dev_type, .addr = (dev_addr)

1.向从设备写数据

2)如果要向设备先写后读,可以通过类似于下面的代码来实现

msg[o].len=1;
msg[o].addr=0x50:
msg[o].flags=0;
msg[o].buf=txbuf:

msg[1].len=2;
msg[1].addr=0x50;

msg[1].flags=I2C_M_RD;

msg[1].buf=rxbuf ;

msg[1].buf[0]=0x0

msg[1].buf[1]=0x0

上面的代码构造了两条消息,两条消息都是要操作设备地址为 0x50 的 I2C 设备,第一条消息是向设备写 1个字节的数据,第二条消息是读两个字节的数据。i2c transfer 不要求每条消息的地址都是一样的,该函数返回被成功执行的消息条数,为负表示失败。

适合于 SMBUS (System Managment Bus)的更简单的 API
下述函数为单字节或多字节的随机读写函数,command 是 SMBUS 中的命令,通常是 i2c 设备内部的寄存器地址。
返回的是实际读写的字节数,为负表示失败。

 

#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/platform_device.h"
#include "linux/i2c.h"
#include "linux/err.h"
#include "linux/regmap.h"
#include "linux/slab.h"static struct i2c_board_info gec6818_eeprom_info = { //如果地址是0x50,会跟内核自带的24c04地址冲突,所以内核必须卸载24c04驱动才行;I2C_BOARD_INFO("gec6818_eeprom", 0x50),
};static struct i2c_client *gec6818_eeprom_client;static int gec6818_eeprom_dev_init(void)
{struct i2c_adapter *i2c_adap;// 获取设备号为 2 的adpter ,也就是adapter->nr == 2i2c_adap = i2c_get_adapter(2);//直接使用 i2c_new_device 创建 client 自动注册到i2c_bus_type 中去//client->name == "gec6818_eeprom" //client->addr = 0x50gec6818_eeprom_client = i2c_new_device(i2c_adap, &gec6818_eeprom_info);// 释放掉 adapteri2c_put_adapter(i2c_adap);return 0;
}static void gec6818_eeprom_dev_exit(void)
{i2c_unregister_device(gec6818_eeprom_client);
}module_init(gec6818_eeprom_dev_init);
module_exit(gec6818_eeprom_dev_exit);
//模块描述
MODULE_AUTHOR("stephenwen88@163.com");           //作者信息
MODULE_DESCRIPTION("gec6818 eeprom driver");      //模块功能说明
MODULE_LICENSE("GPL");                            //许可证:驱动遵循GPL协议
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>#include <linux/io.h>
#include <linux/gpio.h>
#include <cfg_type.h>
#include <linux/miscdevice.h>
#include <linux/i2c.h>static struct i2c_client *this_client;static int  gec6818_eeprom_open (struct inode * inode, struct file *file)
{printk("gec6818_eeprom_open \n");return 0;
}static int  gec6818_eeprom_release (struct inode * inode, struct file *file)
{printk("gec6818_eeprom_release \n");return 0;
}// buf[0] : addr
// buf[1] : data
//
static ssize_t gec6818_eeprom_write (struct file * file, const char __user * buf, size_t len, loff_t * off)
{int rt;char wbuf[2]={0};unsigned char addr, data;if(len > sizeof wbuf)return -EINVAL;rt = copy_from_user(wbuf, buf, 2);if(rt != 0)return -EFAULT;//写入的地址addr = wbuf[0];//写入的数据data = wbuf[1];printk("addr = %x, data = %x\n", addr, data);//传输1字节数据rt = i2c_smbus_write_byte_data(this_client, addr, data);if(rt < 0)return rt; return 2;
}// 传入: buf[0] : addr
// 输出: buf[0] : data
//
static ssize_t gec6818_eeprom_read (struct file *file, char __user *buf, size_t len, loff_t * offs)
{int rt=0;unsigned char addr, data;rt=copy_from_user(&addr, buf, 1);if(rt != 0)return -EFAULT;   //读取1字节数据rt = i2c_smbus_read_byte_data(this_client, addr);if(rt < 0)return rt;//若正确,获取读取到的字节data = rt & 0xFF;rt = copy_to_user(buf, &data, 1);if(rt != 0)return -EFAULT;return 1;
}static const struct file_operations gec6818_eeprom_fops = {.owner         = THIS_MODULE,.write       = gec6818_eeprom_write,.open       = gec6818_eeprom_open,.release     = gec6818_eeprom_release,.read         = gec6818_eeprom_read,
};static struct miscdevice gec6818_eeprom_miscdev = {.minor        = MISC_DYNAMIC_MINOR,  //MISC_DYNAMIC_MINOR,动态分配次设备号.name      = "gec6818_eeprom",      //设备名称,/dev/gec6818_eeprom  .fops       = &gec6818_eeprom_fops,    //文件操作集
};static int gec6818_eeprom_probe(struct i2c_client *client, const struct i2c_device_id *id)
{int rt;//检查是否支持I2C功能if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {printk("i2c_check_functionality fail\n");return -ENODEV;}   //混杂设备的注册rt = misc_register(&gec6818_eeprom_miscdev);if (rt) {printk("misc_register fail\n");return rt;}//保存client指针,里面有各种设备信息this_client = client;printk("gec6818_eeprom_probe ok\n");return 0;
}static int __devexit gec6818_eeprom_remove(struct i2c_client *client)
{misc_deregister(&gec6818_eeprom_miscdev);return 0;
}static const struct i2c_device_id gec6818_eeprom_id[] = {{ "gec6818_eeprom", 0 },   //设备类型,ID号{ }                            //标识结束
};//添加到内核i2c设备列表
MODULE_DEVICE_TABLE(i2c, gec6818_eeprom_id);// 分配/设置i2c_driver
static struct i2c_driver gec6818_eeprom_driver = {.driver = {.name = "gec6818_eeprom",                     //用于匹配设备.owner = THIS_MODULE,},.probe      = gec6818_eeprom_probe,                    //驱动初始化.remove  = __devexit_p(gec6818_eeprom_remove),  //驱动删除.id_table = gec6818_eeprom_id,                       //添加设备ID
};//入口函数
static int __init gec6818_eeprom_drv_init(void)
{printk("gec6818_eeprom_init\n");//添加I2C设备return i2c_add_driver(&gec6818_eeprom_driver);
}//出口函数
static void __exit gec6818_eeprom_drv_exit(void)
{//删除I2C设备i2c_del_driver(&gec6818_eeprom_driver);printk("gec6818 eeprom exit\n");
}module_init(gec6818_eeprom_drv_init);
module_exit(gec6818_eeprom_drv_exit)//模块描述
MODULE_AUTHOR("stephenwen88@163.com");           //作者信息
MODULE_DESCRIPTION("gec6818 eeprom driver");  //模块功能说明
MODULE_LICENSE("GPL");                            //许可证:驱动遵循GPL协议
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main(int argc, char **argv)
{int fd=-1;int len;int i=0;char buf[8];//打开gec6818_eeprom设备fd = open("/dev/gec6818_eeprom",O_RDWR);if(fd < 0){perror("open /dev/gec6818_eeprom:");return fd;}while(1){for(i=0; i<255; i++){//地址buf[0]=i;//数据buf[1]=i;len=write(fd,buf,2);usleep(500*1000);if(len > 0 )printf("write ok\n");elseprintf("write fail\n");len=read(fd,buf,1);    if(len > 0 )printf("read data=%02X\n",buf[0]);elseprintf("read fail\n");                usleep(500*1000);}}close(fd);return 0;
}

-------------------------------------------------

------------------------------------------------

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>#include <linux/io.h>
#include <linux/gpio.h>
#include <cfg_type.h>
#include <linux/miscdevice.h>
#include <linux/i2c.h>static struct i2c_client *this_client;static int  gec6818_eeprom_open (struct inode * inode, struct file *file)
{printk("gec6818_eeprom_open \n");return 0;
}static int  gec6818_eeprom_release (struct inode * inode, struct file *file)
{printk("gec6818_eeprom_release \n");return 0;
}static ssize_t gec6818_eeprom_write (struct file * file, const char __user * buf, size_t len, loff_t * off)
{int rt;char wbuf[2]={0};struct i2c_msg msgs[2];if(len > sizeof wbuf)return -EINVAL;rt = copy_from_user(wbuf,buf,len);if(rt != 0)return -EFAULT;msgs[0].addr  = this_client->addr;//从机地址msgs[0].flags = 0;               //写操作msgs[0].len        = 1;               //写长度msgs[0].buf        = &wbuf[0];            //写入要写入数据的地址msgs[1].addr    = this_client->addr;//从机地址msgs[1].flags = 0|I2C_M_NOSTART; //再次执行写,不带起始信号msgs[1].len        = 1;               //写长度msgs[1].buf        = &wbuf[1];            //写内容//发送2组消息rt = i2c_transfer(this_client->adapter, msgs, 2);  if(rt < 0)return rt;return len;
}static ssize_t gec6818_eeprom_read (struct file *file, char __user *buf, size_t len, loff_t * offs)
{int rt=0;char rbuf[2];struct i2c_msg msgs[2];if(len > sizeof rbuf)return -EINVAL;rt = copy_from_user(rbuf,buf,len);if(rt != 0)return -EFAULT;msgs[0].addr    = this_client->addr;    //从机地址msgs[0].flags = 0;                   //写操作msgs[0].len        = 1;                   //写长度msgs[0].buf        = &rbuf[0];                //写入要读取数据的地址msgs[1].addr    = this_client->addr;    //从机地址msgs[1].flags = I2C_M_RD;                //读操作msgs[1].len        = 1;                   //读长度msgs[1].buf        = &rbuf[1];                //保存要读取的数据//发送2组消息rt = i2c_transfer(this_client->adapter, msgs, 2); if(rt < 0)return rt;rt = copy_to_user(buf,rbuf,len);if(rt != 0)return -EFAULT; len = len -rt;return len;
}static const struct file_operations gec6818_eeprom_fops = {.owner         = THIS_MODULE,.write       = gec6818_eeprom_write,.open       = gec6818_eeprom_open,.release     = gec6818_eeprom_release,.read         = gec6818_eeprom_read,
};static struct miscdevice gec6818_eeprom_miscdev = {.minor        = MISC_DYNAMIC_MINOR,  //MISC_DYNAMIC_MINOR,动态分配次设备号.name      = "gec6818_eeprom",      //设备名称,/dev/gec6818_eeprom  .fops       = &gec6818_eeprom_fops,    //文件操作集
};static int gec6818_eeprom_probe(struct i2c_client *client, const struct i2c_device_id *id)
{int rt;//检查是否支持I2C功能if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {printk("i2c_check_functionality fail\n");return -ENODEV;}   //混杂设备的注册rt = misc_register(&gec6818_eeprom_miscdev);if (rt) {printk("misc_register fail\n");return rt;}//保存client指针,里面有各种设备信息this_client = client;return 0;
}static int __devexit gec6818_eeprom_remove(struct i2c_client *client)
{misc_deregister(&gec6818_eeprom_miscdev);return 0;
}static const struct i2c_device_id gec6818_eeprom_id[] = {{ "gec6818_eeprom", 0 },   //设备类型,ID号{ }                            //标识结束
};//添加到内核i2c设备列表
MODULE_DEVICE_TABLE(i2c, gec6818_eeprom_id);static struct i2c_driver gec6818_eeprom_driver = {.driver = {.name = "gec6818_eeprom",                     //用于匹配设备.owner = THIS_MODULE,},.probe      = gec6818_eeprom_probe,                    //驱动初始化.remove  = __devexit_p(gec6818_eeprom_remove),  //驱动删除.id_table     = gec6818_eeprom_id,                   //添加设备ID                    };//入口函数
static int __init gec6818_eeprom_init(void)
{//添加I2C设备return i2c_add_driver(&gec6818_eeprom_driver);
}//出口函数
static void __exit gec6818_eeprom_exit(void)
{//删除I2C设备i2c_del_driver(&gec6818_eeprom_driver);
}module_init(gec6818_eeprom_init);
module_exit(gec6818_eeprom_exit)//模块描述
MODULE_AUTHOR("stephenwen88@163.com");           //作者信息
MODULE_DESCRIPTION("gec6818 eeprom driver");  //模块功能说明
MODULE_LICENSE("GPL");                            //许可证:驱动遵循GPL协议
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main(int argc, char **argv)
{int fd=-1;int len;int i=0;char buf[8];//打开gec6818_eeprom设备fd = open("/dev/gec6818_eeprom",O_RDWR);if(fd < 0){perror("open /dev/gec6818_eeprom:");return fd;}while(1){for(i=0; i<128; i++){//写地址buf[0]=i;  //写数据buf[1]=i;len=write(fd,buf,2);usleep(500*1000);if(len > 0 )printf("write ok\n");elseperror("write fail:");memset(buf,0,sizeof buf);//读地址buf[0]=i;     //读的时候返回:地址 数据len=read(fd,buf,2);   if(len > 0 )printf("read data at %02X data %02X\n",buf[0],buf[1]);elseperror("read fail:");              usleep(500*1000);}}close(fd);return 0;
}

--------------------------

-------------------------------

at24c02

#include <stdio.h>
#include <linux/types.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>int main(int argc,char **argv)
{int fd,ret;struct i2c_rdwr_ioctl_data e2prom_data;//打开eeprom设备文件结点fd=open("/dev/i2c-2",O_RDWR);if(fd<0){perror("open error");}//因为操作时序中,最多是用到2个开始信号(字节读操作中),所以此将e2prom_data.nmsgs配置为2e2prom_data.nmsgs=2; //分配空间e2prom_data.msgs=(struct i2c_msg*)malloc(e2prom_data.nmsgs*sizeof(struct i2c_msg));if(!e2prom_data.msgs){perror("malloc error");exit(1);}ioctl(fd,I2C_TIMEOUT,1);/*超时时间,时间单位为jiffies*/ioctl(fd,I2C_RETRIES,2);/*重复次数,重复2次*//*写eeprom*/e2prom_data.nmsgs=1;                                  //由前面eeprom读写分析可知,写eeprom需要一条消息(e2prom_data.msgs[0]).len=2;                         //此消息的长度为2个字节,第一个字节是要写入数据的地址,第二个字节是要写入的数据(e2prom_data.msgs[0]).addr=0x50;                        //e2prom 设备地址(e2prom_data.msgs[0]).flags=0;                            //写(e2prom_data.msgs[0]).buf=(unsigned char*)malloc(2);(e2prom_data.msgs[0]).buf[0]=0x10;                     //e2prom 写入目标的地址(e2prom_data.msgs[0]).buf[1]=0x58;                     //写入的数据ret=ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);     //通过ioctl进行实际写入操作,后面会详细分析if(ret<0){perror("ioctl error1");}sleep(1);/*读eeprom*/e2prom_data.nmsgs=2;//读eeprom需要两条消息,也就是需要两个启动信号(e2prom_data.msgs[0]).len=1;             //第一条消息实际是写eeprom,需要告诉eeprom需要读数据的地址,因此长度为1个字节(e2prom_data.msgs[0]).addr=0x50;       //e2prom 设备地址(e2prom_data.msgs[0]).flags=0;            //先是写(e2prom_data.msgs[0]).buf[0]=0x10;        //e2prom上需要读的数据的地址(e2prom_data.msgs[1]).len=1;         //第二条消息才是读eeprom,(e2prom_data.msgs[1]).addr=0x50;       //e2prom 设备地址 (e2prom_data.msgs[1]).flags=I2C_M_RD;    //然后是读(e2prom_data.msgs[1]).buf=(unsigned char*)malloc(1);//存放返回值的地址。(e2prom_data.msgs[1]).buf[0]=0;                      //初始化读缓冲,读到的数据放到此缓冲区ret=ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);//通过ioctl进行实际的读操作if(ret<0){perror("ioctl error2");}printf("buff[0]=%x\n",(e2prom_data.msgs[1]).buf[0]);free((e2prom_data.msgs[0]).buf);free((e2prom_data.msgs[1]).buf);/***打印读出的值,没错的话,就应该是前面写的0x58了***/close(fd);return 0;
}

linux下的i2c相关推荐

  1. linux i2c子系统看不懂啊,Linux 下的I2C子系统

    Linux 下的I2C子系统 2013.7.16 本文分为两部分,一.设备模型 二.平台相关 . ================================================ 第一 ...

  2. linux pcf8563开发文档,linux下的i2c与时钟芯片pcf8563通信

    2012/7/19 linux下的i2c与时钟芯片pcf8563通信 by: 韩大卫 @吉林师范大学 1,本程序增加了对星期寄存器(wday)的写操作. 2, 本程序将i2c-test 改为rtdat ...

  3. S3C2440 Linux下的I2C驱动以及I2C体系下对EEPROM进行读写操作。

    这篇文档算上期末复习这段时间其实拖了有好久了,因为从一开始接触linux的i2c驱动体系我就各种凌乱,因为起初脑海中既没有整体框架也不熟悉相关体系下的结构,所以四处乱看,经常性的在看内核代码时看着看着 ...

  4. linux下的I2C驱动记录(RK)

    现在做的是RK平台的I2C驱动,不是说linux每个外设对应一个设备文件吗?可是找了一下一直没找到,今天特别搞了一下.I2C设备是注册为platform平台驱动的. 下面是我用adb命令查看的 lin ...

  5. linux下的I2c 和展锐8310下的I2c

    I2C物理层 它是一个支持设备的总线."总线"指多个设备共用的信号线.在一个I2C通讯总线中,可连接多个I2C通讯设备,支持多个通讯主机及多个通讯从机. ·一个I2C总线只使用两条 ...

  6. linux 下i2c读写命令,S3C2440 Linux下的I2C驱动以及I2C体系下对EEPROM进行读写操作

    成员.我们可以看到消息结构体里面有从设备地址,读写标志,数据长度以及存储数据buf.这些成员我们看完之后会发现它大致符合先给设备地址,然后给写信号以及数据的时序.其实但我们写代码的时候并不一定是add ...

  7. linux pcf8563开发文档,Linux下i2c与时钟芯片PCF8563的通信

    Linux下的i2c驱动以及与时钟芯片PCF8563通信过程. 为更深入的了解linux下的i2c总线驱动以及通信原理,可以用一个用户程序模拟,这个程序,可以使用一个addr, 一个offset,对i ...

  8. linux下I2C驱动

    linux下I2C驱动(-) by good02xaut 最近的一段时间,总结一下linux下开发I2C设备驱动的要点.内容随想,没有多加整理. I2C协议规定了主机和从机的概念,在驱动中采用的多是适 ...

  9. linux下实时时钟芯片(RTC)的读写

    硬件 由于项目的需要在ARM嵌入式板子上挂上了一颗EPSON的RX8010实时时钟芯片,为数据采集提供可靠的时间.RX8010内置了具有温补的晶振,可以简化设计,而且也不贵,所以就选择了它.其实选择其 ...

最新文章

  1. 【留言板 Message Board】
  2. 天正坐标标注显示不全_高效率的天正CAD技巧,其实制图很简单!
  3. HIbernate抽象出通用方法
  4. 人脸识别+检索项目记录
  5. 上计算机绘图的心得,计算机绘图学习心得体会.doc
  6. pythontkinter显示表格_详谈Python 窗体(tkinter)表格数据(Treeview)
  7. 凸优化系列一:什么是最优化算法
  8. 在Android上实现汉字笔顺动画效果
  9. NS3000 电子海图长江导航应用分析
  10. AndroidStudio NDK开发环境配置及示例
  11. mandriva urpm类命令
  12. win10 + ubantu双系统 彻底删除Ubantu
  13. linux红帽子认证费用RHCT,关于RHCE和RHCT认证
  14. 人工智能作业 - A*算法程序求解八数码
  15. 《自动化学报》踩坑心得
  16. LeetCode之玩筹码
  17. php 小程序服务商支付模式
  18. 《WEB安全渗透测试》(21)kali安装w3af指南
  19. dig命令的常见用法详解
  20. 宾西法尼亚大学强制对齐标注软件(P2FA)介绍以及使用说明

热门文章

  1. TVS中双向TVS和单向TVS使用区别---真的是单向只能用在直流?
  2. 直线模组在各行业中的应用
  3. 东南大学跟华中科技大学计算机学院,四大工学院:华科大读研比例最高;东南大学就业最好...
  4. 列表的索引、切片、运算
  5. STM32学习|STM32最小系统介绍
  6. go语言 服务器接入小米支付SDK
  7. Java常用的四大框架有什么
  8. 嵌入式常见接口协议总结
  9. 2022年的职称你通过了吗
  10. Qt QTimer定时器