加载中…
个人资料
phoenix
phoenix
  • 博客等级:
  • 博客积分:0
  • 博客访问:4,652
  • 关注人气:16
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

i2c驱动之调用ioctl函数进行读写at24c08

(2017-11-07 21:48:32)
from:http://blog.chinaunix.net/uid-20641464-id-1595706.html

型号               容量               器件寻址字节(8位)       一次装载字节数 
AT24C01        128×8        1010A2A1A0 R/W                 8
AT24C02       256×8         1010A2A1A0 R/W                
AT24C04       512×8          1010A2A1P0 R/W               16 
AT24C08       1024×8        1010A2P1P0 R/W              16 
AT24C16      2048×8         1010P2P1P0 R/W              16 
AT24C32      4096*8            1010A2A1A0 R/W               32
AT24C64      8192*8            1010A2A1A0 R/W               32
AT24C128    16384*8           1010A2A1A0 R/W             64
AT24C256  16384*8           1010A2A1A0 R/W             64

AT24C系列 E2PROM接口及地址选择
  设备地址:24xx系列的可以通过修改外的地址引脚来设置不同的地址.
      IIC地址的确定,AT24C系列的,24c01,02 1K/2K EEPROM 在一条IIC总线上可以挂8个,地址由A2,A1,A0确定;24C04 4k EEPROM 只有A2,A1的做地址位,这样一条IIC总线上能挂4个设备,A0是用来确定内部页地址的,A0在芯片上没有线连接的(NA); 24C08, 8k EEPROM 使用A2来确定地址线,A1,A0位是在确定内部页地址的,一条IIC总线能扩展2片; 24C16 16k,A2A1A0都是确定内部页地址的;一条总线上只能挂1个一个这样的设备.
    但在AT24C32 ,24C64中又有改变,32,64中 发送的内部地址都是发2次,高地址和低地址,这样有16位地址位可以确定内部地址,就不需要用A2A1A0来确定地址了.
 
控制器的读写时序
AT24C01---AT24C16
 读:  发设备地址---> 送8位地址---->发设备地址--->读取--->NOACK--->停止;
 写:发设备地址--->送8位地址----->写数据--->停止;
AT24C32.AT24C64
 读:发设备地址--->送高8位地址--->送低8位地址--->发设备地址--->读取----->NOACK--->停止
 写:发设备地址--->送高8位地址----->送低8位地址---->写数据--->停止; 

from:http://blog.sina.com.cn/s/blog_14f58a1920102w4y9.html

(一)、AT24C512简介

  AT24C512Atmel公司生产的64KB串行电可擦的可编程存储器,内部有512页,每一页为128字节,任一单元的地址为16位,地址范围为00000FFFFH。它采用8引脚封装,具有结构紧凑、存储容量大等特点,可以在2线总线上并接4片芯片,特别适用于具有大容量数据存储要求的数据采集系统,因此在测控系统中被大量采用。该芯片的主要特性如下:存储容量为 65536byte;与100kHz400kHz1MHzI2C总线兼容;100000次编程/擦写周期;单电源、读写电压为 1.8V5.5VESD保护电压>4kV;数据可保存40年;写保护功能,当WP为高电平时,进入写保护状态;CMOS低功耗技术,最大写入电流为3mA128byte页写入缓存器;自动定时的写周期;具有8引脚DIP20引脚SOIC封装等多种封装形式。

1AT24C512的引脚和功能如下图:

http://s11/bmiddle/0068KRmqzy6XyRk7V6aca&690

 ①A0A1——地址选择输入端。在串行总线结构中,如需连接4AT24C512芯片,则可用A0A1来区分各芯片。A0A1悬空时为0

  ②SDA——双向串行数据输入输出口。用于存储器与单片机之间的数据交换。

  ③SCL——串行时钟输入。通常在其上升沿将SDA上的数据写入存储器,而在下降沿从存储器读出数据并送往SDA

  ④WP——写保护输入。此引脚与地相连时,允许写操作;与VCC相连时,所有的写存储器操作被禁止。如果不连,该脚将在芯片内部下拉到地。

  ⑤VCC——电源。

  GND接地。NC悬空。

2)起停信号

时钟线保持高电平期间,数据线电平从高到低的跳变代为I2C总线的起始信号。时钟线保持高电平期间,数据线电平从低到高的跳变代为I2C总线的停止信号。

3)器件寻址

   主器件通过发送1个起始信号启动发送过程,然后发送它所需要寻址的从器件地址,8位从器件地址的高5位固定为10100,接下来的2位(A0A1)为器件的地址位,因此最多可以将4AT24C512连接到同一总线上使存储容量扩展至256Kbyte。注意,这两位必须与A0A1两引脚的输入状态相对应,从器件地址的最低位为读写控制位,“1”表示对从器件进行读操作,“0”表示对从器件进行写操作,在主器件发送起始信号和从器件发送地址字节后,AT24C512监测总线并当其地址与发送的从地址相符时发出1个应答信号(通过SDA线),AT24C512再根据读写控制位(R/W)的状态进行读写操作,从器件地址字节内容如下图所示。

http://s15/mw690/0068KRmqzy6XyRnjKua3e&690

4)应答信号

I2C总线传送数据时,每成功传送1个字节,接收器都必须产生1个应答信号,应答的器件在第9个时钟周期将SDA线拉低表示其已收到18位数据。AT24C512在接收到起始信号和从器件地址之后产生应答信号,如果器件已选择了写操作,则在每接收18位字节之后会有1个应答信号ACR

5)写操作

 

AT24C512的写操作有写字节和写页两种方式。写字节时通常在向AT24C512发送设备地址字并接到应答信号后,还需要发送28位地址来选择要写数据的地址。AT24C512接收到这个地址后会应答一个ACR信号,然后接收8位数据进来,并再返回一个应答信号。

http://s12/mw690/0068KRmqzy6XyRpQuZZcb&690
http://s13/mw690/0068KRmqzy6XyRrhCSU6c&690

6)读操作

 

  读操作有当前地址读、随机读、读串三种方式。其初始化过程基本与写操作相同,只是在设备选择字中的最低位要改成读而已。在当前地址读操作方式时,内部数据的地址将保持在最后的读写操作地址加1上,读串操作既可以是当前地址读,也可以是随机地址读。当单片机接收到数据但不送应答信号时,读过程结束。

http://s16/mw690/0068KRmqzy6XyRuvgeP1f&690
http://s8/mw690/0068KRmqzy6XyRvh8yz47&690
http://s10/mw690/0068KRmqzy6XyRvI4Y189&690


from:http://blog.csdn.net/rockrockwu/article/details/7863711

根据前一篇的文章介绍 at24c02的读写方式有很多种,

写有两种1.写一字节数据到word address处2.从指定的word address处开始写一页数据,此word address需要页对齐!

读有三种1.从at24c02当前的word address读一字节数据2.从指定的word address 读数据3.从当前的word address地址开始读一串数据

根据驱动中write() read()的实现方法可以发现,当msg发送完毕时才发送stop信号,而msg之间是是连续发送的不会插入stop信号。

但是,write() read()每次都固定只能发送一则msg!这对at24c02的写以及current read、sequential read来说没问题,可以通过write()和read()函数直接实现这四种操作。

但是at24c02的random read就不能直接通过write() read()来实现了。因为random read需要先写word address,写完之后不能直接发送stop信号,

而是要接着发送start信号开始发送device address。而驱动中的write() read()只能一次发送一则msg,并且发送完毕就发送stop信号,所以这种时序不符合random read的操作。

不过系统通过ioctl操作,可以一次发送多则msg,而在msg之间是不会发送stop信号的。

所以at24c02的random read操作可以通过发送两则msg的方式来实现,第一则msg是写的,并且写的内容是word address,第二则msg为读。


from:http://blog.csdn.net/luckywang1103/article/details/16810833

i2c设备驱动有两种模式:一种是用户模式设备驱动,这种驱动依赖于i2c子系统中i2c-dev驱动,这种驱动对应用程序员的要求很高,要求应用程序员了解硬件的一些东西,了解时序、地址等;另一种是普通的设备驱动,应用程序员在使用的时候就像读写文件一样。

在linux驱动中/drivers/i2c/目录下有i2c-dev.c提供了I2C设备的通用驱动,实现了read(),write(),ioctl等函数,不过这里的read()和write()函数只能对应一条消息,即如下,

http://img.blog.csdn.net/20131118212600421
http://img.blog.csdn.net/20131118212621593

http://img.blog.csdn.net/20131118212626453

http://img.blog.csdn.net/20131118212634406


但是如果碰到下面的情况:

http://img.blog.csdn.net/20131118212639250

先写一次地址,然后再开始读数据,即分为两次消息,这个时候read(),write()函数就不能正常读写了,因为先write()地址之后总线上会有stop,之后read(),就与figure 5中所示(中间没有stop)不符了,所以必须利用ioctl函数来发送两条消息,这样中间就没有stop了,发送完这两条消息才有stop。


下面就是使用ioctl函数去写和读at24c08存储器,先看下两个重要的结构体

1、struct i2c_rdwr_ioctl_data结构体

 

[cpp] view plain copy
  1. This is the structure as used in the I2C_RDWR ioctl call */  
  2. struct i2c_rdwr_ioctl_data  
  3.     struct i2c_msg __user *msgs;      
  4.     __u32 nmsgs;              
  5. };  
msgs使用前必须先分配一下内存,msgs=(struct i2c_msg *)malloc(nmsgs*sizeof(struct i2c_msg));

 

nmsgs是msgs的个数


2、struct i2c_msg结构体

 

[cpp] view plain copy
  1. struct i2c_msg  
  2.     __u16 addr;   
  3.     __u16 flags;  
  4. #define I2C_M_TEN       0x0010    
  5. #define I2C_M_RD        0x0001    
  6. #define I2C_M_NOSTART       0x4000    
  7. #define I2C_M_REV_DIR_ADDR  0x2000    
  8. #define I2C_M_IGNORE_NAK    0x1000    
  9. #define I2C_M_NO_RD_ACK     0x0800    
  10. #define I2C_M_RECV_LEN      0x0400    
  11.     __u16 len;        
  12.     __u8 *buf;        
  13. };  
len是指buf的长度

 

buf在使用前必须先分配内存,buf=(unsigned char *)malloc(len);

一般如果写,buf[0]是写的地址,buf[1]之后都是写的数据了;如果读,第一遍写地址时buf[0]是地址,第二遍读数据时存放读的数据

 

[cpp] view plain copy
  1.   
  2. #include  
  3. #include  
  4. #include  
  5. #include  
  6. #include  
  7. #include  
  8. #include  
  9. #include  
  10. #include  
  11. #include  
  12. #include  
  13. #include  
  14.   
  15. int main(int argc, char** argv)  
  16.  
  17.     struct i2c_rdwr_ioctl_data work_queue;  
  18.   
  19.     unsigned int slave_address,reg_address,dat;  
  20.     int i,ret;  
  21.     unsigned char val;  
  22.     unsigned int fd;  
  23.   
  24.     if(argc != 3)   
  25.      
  26.         printf("usage:./eeprom_ioctl address data\n");  
  27.         return 0;  
  28.      
  29.     fd=open("/dev/i2c/0",O_RDWR);  
  30.     if(!fd)  
  31.      
  32.         printf("error on opening the device file\n");  
  33.         exit(1);  
  34.      
  35.     ioctl(fd,I2C_TIMEOUT,2);//超时时间  
  36.     ioctl(fd,I2C_RETRIES,1);//重复次数  
  37.   
  38.     slave_address 0x50;//24c08的访问地址是101000b  
  39.     reg_address (argv[1][2]-48)<<4 (argv[1][3]-48);  
  40.     dat (argv[2][2]-48)<<4 (argv[2][3]-48);  
  41. //nmsgs决定了有多少start信号  
  42. //一个msgs对应一个start信号  
  43. //在nmsg个信号结束后总线会产生一个stop  
  44. //下面因为在操作时序中最多用到2个start信号(字节读操作中)  
  45.     work_queue.nmsgs 2;  
  46.     work_queue.msgs (struct i2c_msg *)malloc(work_queue.nmsgs sizeof(work_queue.msgs));  
  47.     if(!work_queue.msgs)  
  48.      
  49.         printf("memory alloc failed");  
  50.         close(fd);  
  51.         exit(1);  
  52.      
  53.     //往i2c里面写数据  
  54.     printf("began to write:\n");  
  55.     work_queue.nmsgs  1;    
  56.     (work_queue.msgs[0]).len 2;//buf的长度  
  57.     (work_queue.msgs[0]).flags 0;//write  
  58.     (work_queue.msgs[0]).addr slave_address;//设备地址  
  59.     (work_queue.msgs[0]).buf (unsigned char *)malloc(2);  
  60.     (work_queue.msgs[0]).buf[0] reg_address;//写的地址  
  61.     (work_queue.msgs[0]).buf[1] dat;//你要写的数据  
  62.   
  63.     ret ioctl(fd, I2C_RDWR, (unsigned long)&work_queue);  
  64.     if(ret 0)  
  65.         printf("error during I2C_RDWR ioctl with error code %d\n"ret);  
  66.   
  67.     //从i2c里面读出数据  
  68.     printf("\nbegan to read:\n");  
  69.     work_queue.nmsgs  2;  
  70.     //先设定一下地址  
  71.     (work_queue.msgs[0]).len 1;  
  72.     (work_queue.msgs[0]).flags 0;//write  
  73.     (work_queue.msgs[0]).addr slave_address;  
  74.     (work_queue.msgs[0]).buf[0] reg_address;//因为上面buf已经分配过了  
  75.     //然后从刚才设定的地址处读  
  76.     (work_queue.msgs[1]).len 1;  
  77.     (work_queue.msgs[1]).flags I2C_M_RD;  
  78.     (work_queue.msgs[1]).addr slave_address;  
  79.     (work_queue.msgs[1]).buf (unsigned char *)malloc(1);  
  80.     (work_queue.msgs[1]).buf[0] 0;//初始化读缓冲  
  81.   
  82.     ret ioctl(fd, I2C_RDWR, (unsigned long)&work_queue);  
  83.     if(ret 0)  
  84.         printf("error during I2C_RDWR ioctl with error code %d\n"ret);  
  85.   
  86.     close(fd);  
  87.     return 0;   
  88.       
  89.  

 

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有