主题 : Mini2440之i2c驱动的应用程序祥解【希望对都是初学者的我们都有用】 复制链接 | 浏览器收藏 | 打印
大家互相帮忙一下可以吗?请大家互相帮忙一下啊!
级别: 新手上路
UID: 27546
精华: 0
发帖: 40
金钱: 210 两
威望: 42 点
贡献值: 0 点
综合积分: 80 分
注册时间: 2010-08-27
最后登录: 2013-09-17
楼主  发表于: 2011-03-18 23:02

 Mini2440之i2c驱动的应用程序祥解【希望对都是初学者的我们都有用】

Mini2440之i2c驱动(1)

首先,要明白的是,mini2440-128M上面用的eeprom的型号为AT24C08B,下载手册可知这个eeprom的大小为1024*8(8k),这是什么意思呢?先补充一下存储器相关方面的知识,存储器的容量是以存储一位二进制数(bit)为单位的,因此,存储器的容量即指每个存储器芯片所能存储的二进制数的位数,因此,在标定存储器容量时,同时标出存储器存储单元的数目和位数,即:
    存储器芯片容量=存储单元数*数据位数
用AT24C08B来具体解释就是,AT24C08B共有1024个存储单元,每个存储单元的大小事8位(bits),而我们知道8位为一个字节,所以mini2440开发板的eeprom的大小为1024个字节,即1K大小,那么括弧里面的8K是什么意思的,8K的意思是总共有8*1024个位,不要混淆了哦。所以mini2440用户手册上说eeprom的大小是256字节是错误的,256字节那是AT24C0A的大小。
下面来看下AT24C08B地址的确定:
看手册知:

从上面知,一条I2C总线上最多只能存在两个8K的eeprom,他们用A2这根线来区别。
而A0,A1对于AT24C08B是用不到的,要么不连,要么全接地,看下mini2440的原理图:

可以看到A2接到了地,而A0,A1选择了接到地,而不是不连。顺便提一句,wp接地表示可以正常读写,没有进行写保护,可以自己看手册,很容易的。那么总么确定AT24C08B的地址,接着看手册:



在看下写时序:

看见了,device address 的地址是不包含R/W的,看对应关系,呵呵,所以地址为1010000,即0x50,而eeprom里面的范围通过写时序图也可以看出只能是0x00000000---0xFFFFFFFF,这个从后面的程序测试中可以看出在这个范围以外的是没法读写的。

好,下面开始写驱动程序,这里可以参考下刘洪涛老师的实例解析linux内核I2C体系结构(1),本篇先利用I2C_DEV来实现,在内核层的i2c设备驱动程序等我弄出来再写哈。
这里利用ioctl()方法,不用read(),write方法。因为AT24C08B的读时序中要有重复开始信号。从手册可以看出,如下图:

好了,利用i2c-dev.c操纵适配器,进而操作i2c设备的旅程开始了:
首先熟悉:struct i2c_rdwr_ioctl_data,struct i2c_msg结构体:



上面的解释很清楚,我就不解释了,接下来,开始写程序,在写程序之前,首先要保证内核包括对s3c2410适配器的支持,即你下到开发板上的zImage,不是用make zImage生成的嘛,那么在make zImage不是要make menuconfig吗,在这里面的devices driver support 中要将s3c2410适配器驱动选成*,编译进内核,可以看开发板有没有/dev/i2c/x这个目录,有就没问题了。

程序如下,附解释:
#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 <assert.h>
#include <string.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>

int main(int argc, char **argv)
{
    struct i2c_rdwr_ioctl_data e2prom_data;
    unsigned int fd;
    unsigned int slave_address, reg_address,value; //slave_address为eeprom的地址,reg_address为eeprom中存储单元的地址,范围0x0--0xFFFFFFFF,value为你要写进eeprom的值
    int ret;
    
    if (argc < 5){
        printf("Usage:\n%s /dev/i2c/x start_addr reg_addr value\n",argv[0]);
        return 0;
    }
    
    fd = open(argv[1], O_RDWR);
    //如果flag参数里有O_CREAT表示,该文件如果不存在,系统则会创建该文件,该文件的权限由第三个参数决定,此处为0755
//如果flah参数里没有O_CREAT参数,则第三个参数不起作用.此时,如果要打开的文件不存在,则会报错.
//所以fd=open(argv[1],O_RDWR),仅仅只是打开指定文件


    if (!fd){
        printf("Error on opening the device file\n");
        return 0;
    }

    sscanf(argv[2], "%x", &slave_address);
    sscanf(argv[3], "%x", &reg_address);
    sscanf(argv[4], "%x", &value);
    
    e2prom_data .nmsgs = 2;//因为都时序要两次,所以设为2
    e2prom_data .msgs = (struct i2c_msg *)malloc(e2prom_data.nmsgs * sizeof(struct i2c_msg));

void *malloc(int size);
  说明:malloc 向系统申请分配指定size个字节的内存空间。返回类型是 void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针。

    if (!e2prom_data.msgs){
        printf("Memory alloc error\n");
        close(fd);
        return 0;
    }
    
    ioctl(fd, I2C_TIMEOUT, 2);//设置超时时间
    ioctl(fd, I2C_RETRIES, 1);//设置重发次数

    /* write data to e2prom*/
    e2prom_data.nmsgs = 1;  //写只有进行一次的起动总线、就是说只发送I2C消息的次数只有一次、相反读的话我们看时序可以发现我们进行了两次的起动总线、所以我们要发两次的I2C消息的次数。
    e2prom_data.msgs[0].len = 2;//信息长度为2,看写时序,eeprom的地址不算的,因为付给了addr,而len是指buf中的值的个数
BUF中值的个数要是写时序的话就是两个、包括写入的单元地址和要写入的信息  分别对应下面的BUF【0】和BUF【1】
    e2prom_data.msgs[0].addr = slave_address;
    e2prom_data.msgs[0].flags = 0;//写命令
    e2prom_data.msgs[0].buf = (unsigned char*)malloc(2);
    e2prom_data.msgs[0].buf[0] = reg_address;//信息值1 eeprom中存储单元的地址,即你要往哪写
    e2prom_data.msgs[0].buf[1] = value;//信息值2,即你要写什么
    
    ret = ioctl (fd, I2C_RDWR, (unsigned long)&e2prom_data);//好了 ,写进去吧
    if (ret < 0){
        printf ("ioctl write error\n");
    }

    printf("you have write %02x into e2prom at %02x address\n",value,reg_address);
    
    sleep(1);
    /*read data from e2prom*/
    e2prom_data.nmsgs = 2;//读时序要两次过程,要发两次I2C消息
//写只有进行一次的起动总线、就是说只发送I2C消息的次数只有一次、相反读的话我们看时序可以发现我们进行了两次的起动总线、所以我们要发两次的I2C消息的次数。

    e2prom_data.msgs[0].len = 1;//信息长度为1,第一次只写要读的eeprom中存储单元的地址
    e2prom_data.msgs[0].addr = slave_address; //器件地址
    e2prom_data.msgs[0].flags = 0;//写命令,看读时序理解
    e2prom_data.msgs[0].buf[0] = reg_address;//要写入数据的单元地址
        
    e2prom_data.msgs[1].len = 1;
    e2prom_data.msgs[1].addr = slave_address;   //器件地址
    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);//好了,读吧
    if (ret < 0){
        printf ("ioctl read error\n");
    }
    
    printf("read %02x from e2prom address %02x\n",e2prom_data.msgs[1].buf[0], reg_address);
    
    close(fd);
    return 0;    
}

运行结果截图如下:

由图可以看出,eeprom的地址是0x50,写0x60是读不到的
地址范围也只能是0x0—0xffffffff,在这个范围之外也是不能读写的。

        


                                                                                                                                                                                                                        pursuitxh-lwm
                                                                笑寒
                                                      2010-11-15 晚11点39分
附件设置隐藏,需要回复后才能看到
大家互相帮忙一下可以吗?请大家互相帮忙一下啊!
级别: 新手上路
UID: 27546
精华: 0
发帖: 40
金钱: 210 两
威望: 42 点
贡献值: 0 点
综合积分: 80 分
注册时间: 2010-08-27
最后登录: 2013-09-17
1楼  发表于: 2011-03-18 23:06
大家照着读和写的时序图来看程序就很OK啦~~~因为自己遇到的问题~希望有帮助!!!!!!!!!!!!!!
专注于嵌入式&Linux
级别: 骑士
UID: 12802
精华: 3
发帖: 237
金钱: 1355 两
威望: 271 点
贡献值: 3 点
综合积分: 534 分
注册时间: 2010-01-13
最后登录: 2014-03-18
2楼  发表于: 2011-03-18 23:09
二话不说,回了再说
会当凌绝顶,一览众山小!
级别: 总版主
UID: 2
精华: 17
发帖: 1655
金钱: 13860 两
威望: 5369 点
贡献值: 17 点
综合积分: 3650 分
注册时间: 2008-01-01
最后登录: 2024-02-17
3楼  发表于: 2011-03-18 23:36

 回 楼主(weiqifa0) 的帖子

谢谢分享
友善之臂淘宝直销店:http://shop34928758.taobao.com

手机:13560352861(杨工),QQ:10108270
级别: 新手上路
UID: 735
精华: 0
发帖: 20
金钱: 100 两
威望: 20 点
贡献值: 0 点
综合积分: 40 分
注册时间: 2008-05-21
最后登录: 2012-04-20
4楼  发表于: 2011-03-19 08:52
一万年太久,只争朝夕。
级别: 新手上路
UID: 19092
精华: 0
发帖: 11
金钱: 55 两
威望: 11 点
贡献值: 0 点
综合积分: 22 分
注册时间: 2010-04-16
最后登录: 2013-08-21
5楼  发表于: 2011-03-25 13:59
看看瞧瞧。
级别: 新手上路
UID: 41084
精华: 0
发帖: 4
金钱: 20 两
威望: 4 点
贡献值: 0 点
综合积分: 8 分
注册时间: 2011-03-25
最后登录: 2011-06-07
6楼  发表于: 2011-03-25 15:59
受益匪浅!!!!!!!!!!!!!!
级别: 新手上路
UID: 40434
精华: 0
发帖: 30
金钱: 155 两
威望: 31 点
贡献值: 0 点
综合积分: 60 分
注册时间: 2011-03-18
最后登录: 2011-10-15
7楼  发表于: 2011-03-30 09:49
srehgfdhjjfdjh
级别: 侠客
UID: 8904
精华: 0
发帖: 54
金钱: 400 两
威望: 155 点
贡献值: 0 点
综合积分: 108 分
注册时间: 2009-09-10
最后登录: 2017-09-13
8楼  发表于: 2011-03-31 13:57
看看,谢谢楼主
级别: 新手上路
UID: 28066
精华: 0
发帖: 20
金钱: 105 两
威望: 21 点
贡献值: 0 点
综合积分: 40 分
注册时间: 2010-09-05
最后登录: 2011-08-20
9楼  发表于: 2011-03-31 15:06
给力的帖子一定要留名……………………