【ARM&Linux】基于c3c2440 Linux IIC驱动程序移植与测试

【ARM9 S3C2440 IIC驱动程序】

环境:
linux kernel: 3.0
tq2440
ubuntu14.04
linux3.0内核移植笔记点此


配置内核

  1. 配置内核支持IIC
    Device Drivers —>
    <*> I2C support —>
    这里写图片描述
    [*] Misc devices —>EEPROM support —>
    这里写图片描述

  2. 修改arch/arm/mach-s3c2440/mach-smdk2440.c
    2.1 添加如下:
    这里写图片描述

    2.2 向内核注册
    在函数smdk2440_machine_init中添加如下红框中内容:
    这里写图片描述


//-------------------- eeprom --------------------------------------//
static struct at24_platform_data at24c02 = {  
    .byte_len   = SZ_2K / 8,        //256字节  
    .page_size  = 8, 
    .flags      = 0,  
};
static struct i2c_board_info __initdata smdk2440_i2c_devs[] = {  
    {   
         I2C_BOARD_INFO("24c02", 0x50),  
         .platform_data = &at24c02,  
    },  
    /*   more devices can be added using expansion connectors  */  
};  

驱动编写

                2440,6410,210三款芯片都共用i2c-s3c2410.c,这个文件的代码,通用驱动文件为:i2c-dev.c
 

IIC设备驱动的设计方式有两种:

  1. 用户态驱动程序: 本质就是通过ioctl函数传入命令,以此操作EEPROM的读写等操作.
  2. 自编驱动程序

[方式1-用户态驱动程序]

                用户态的驱动程序,其实就是一个应用程序,用ioctl传入命令,操作硬件.
 
这里写图片描述
如上图所示函数,是对底层设备的读写函数,copy_from_user函数从用户空间拷贝了一个i2c_rdwr_ioctl_data的结构,这个结构如下图:
这里写图片描述
第一个参数是一条消息, 成员如下:
这里写图片描述
一个消息就表示一次操作,例如对EEPROM读数据, 这样的一次操作就是一条消息.
 

1. 写EEPROM
这里写图片描述
                由AT24C02的datasheet写一个字节的流程时序可知,先写入目标地址,在写入数据,所以msg的len就是2, buf[0]就是要写入的地址,buf[1]就是要写入的数据.
               
               
2.自由读
这里写图片描述
                由时序可知, 对eeprom的读操作,分成了两部分,第一部分是先写入目标地址,第二部分是读取数据, 所以要发送两条消息:
 
                2.1 第一条消是写消息,len就是一个字节,buf[0]就是要写入目标地址,flag为0表示写
                2.2 第二条消息就是读消息了,len还是一个字节,buf[0]就是读到的数据,flag=1表示读。
 

测试程序
/**
 * filename : eeprom_app.c
 * desc : s3c2440 iic eeprom 用户态驱动程序
 */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <linux/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <malloc.h>
#include <fcntl.h>

#define DEVICE_NAME "/dev/i2c-0"    //设备描述文件目录
#define AT24C02_SIZE 256            //AT24C02 的大小是256字节
#define AT24C02_ADDR 0x50           //根据datasheet可知AT24C02的地址是0xA0,但是在Linux系统中,最高位是1,所以就是0x50

/**
 * [e2prom_read description]: 读eeprom
 * @param buf    存放读取的数据
 * @param addr   目标地址
 * @param length 读取长度
 */
void e2prom_read(unsigned char *buf, unsigned int addr, unsigned int length)
{
    int i = 0;
    int fd;
    unsigned int curaddr = addr;
    unsigned char rbuf = 0;
    struct i2c_rdwr_ioctl_data e2prom_data;
    e2prom_data.nmsgs = 2;

    if(length > (AT24C02_SIZE - addr))
    {
        printf("data is too long. \n");
        return;
    }

    if((fd = open(DEVICE_NAME, O_RDWR)) < 0)
    {
        printf("open failed. \n");
        return;
    }

    e2prom_data.msgs = (struct i2c_msg *)malloc((sizeof(struct i2c_msg) * e2prom_data.nmsgs));
    e2prom_data.msgs[0].addr = AT24C02_ADDR;
    e2prom_data.msgs[1].addr = AT24C02_ADDR;
    e2prom_data.msgs[0].flags = 0;                  //0表示写
    e2prom_data.msgs[1].flags = 1;                  //1表示读
    e2prom_data.msgs[0].len = 1;
    e2prom_data.msgs[1].len = 1;

    e2prom_data.msgs[0].buf = (unsigned char *)&curaddr;
    e2prom_data.msgs[1].buf = &rbuf;


    while(i < length)
    {
        if(ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data) < 0)
        {
            printf("int e2prom_read(),  ioctl() err. i = %d\n", i);
            goto exit;
        }
        *(buf + i) = rbuf;
        curaddr++;
        i++;
    }

    goto exit;

exit:
    close(fd);
    free(e2prom_data.msgs);
    return;
}

/**
 * [delay description]:简单延时函数
 * @param n 延时时间
 */
void delay(unsigned int n)
{
    unsigned int k;
    while(n--)
    {
        for(k = 10000; k > 0; k--)
        {
            ;
        }
    }
}

/**
 * [e2prom_write description]:写eeprom
 * @param buf    要写入的数据
 * @param addr   目标地址
 * @param length 要写入的数据长度
 */
void e2prom_write(unsigned char *buf, unsigned int addr, unsigned int length)
{
    int i = 0;
    unsigned char wbuf[2] = {0};
    int fd;
    struct i2c_rdwr_ioctl_data e2prom_data;
    e2prom_data.nmsgs = 1;

    if(length > (AT24C02_SIZE - addr))
    {
        printf("data is too long. \n");
        return;
    }

    if((fd = open(DEVICE_NAME, O_RDWR)) < 0)
    {
        printf("in e2prom_write() open failed. \n");
        return;
    }


    e2prom_data.msgs = (struct i2c_msg *)malloc((sizeof(struct i2c_msg) * e2prom_data.nmsgs));
    e2prom_data.msgs[0].addr = AT24C02_ADDR;
    e2prom_data.msgs[0].flags = 0;
    e2prom_data.msgs[0].len = 2;
    e2prom_data.msgs[0].buf = wbuf;

    while(i < length)
    {
        wbuf[0] = addr;
        wbuf[1] = *(buf + i);

        if( (ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data)) < 0)
        {
            printf("in e2prom_write(), ioctl() failed. i = %d\n", i);
            goto failed_exit;
        }

        addr++;
        i++;
        delay(10);
    }

    goto failed_exit;

failed_exit:
    close(fd);
    free(e2prom_data.msgs);
    return ;
}

int main(int argc, char **argv)
{
    unsigned char rbuf[AT24C02_SIZE] = {0x00};
    unsigned char wbuf[AT24C02_SIZE] = "abcdefghijkl";

    e2prom_write(wbuf, 0, strlen(wbuf));
    e2prom_read(rbuf, 0, strlen(wbuf));
    printf("read date is [%s]\n", rbuf);

    return 0;
}


测试结果

这里写图片描述


[方式2-自编驱动程序]

自编的设备驱动程序就是上面的配置内核,配置好之后,会在文件系统"/sys/devices/platform/s3c2440-i2c/i2c-0/0-0050/eeprom"下生成这个eeprom文件,通过读写eeprom这个文件,就可以操作at24c02。下面编写应用程序测试。
在文件at24.c中,at24_probe函数中创建设备文件eeprom,自己可以更改这个名字。
这里写图片描述

测试用应用程序
/**
 * filename : i2c-app.c
 * desc : at24c2应用程序
 */

#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>

#define AT24C02_NAME "/sys/devices/platform/s3c2440-i2c/i2c-0/0-0050/eeprom"
#define AT24C02_SIZE 256


/**
 * [at24cxx_read description]:读at24c02
 * @param  buf    :存放读出的数据
 * @param  addr   要读的数据地址
 * @param  length 要读数据的长度
 * @return        成功非负数,失败-1
 */
int at24cxx_read(unsigned char *buf, unsigned int addr, unsigned int length)
{
    int fd;
    int ret;

    if(length > (AT24C02_SIZE - addr))
    {
        printf("data is too long. \n");
        return -EINVAL;
    }

    if((fd = open(AT24C02_NAME, O_RDONLY)) < 0)
    {
        printf("in at24cxx_read : open failed. \n");
        return fd;
    }

    lseek(fd, addr, SEEK_SET);

    if( (ret = read(fd, buf, length)) < 0 )
    {
        printf("at24cxx read failed. \n");
    }

    close(fd);
    return ret;
}

/**
 * [at24cxx_write description]:写at24c02
 * @param  buf    要写入的数据
 * @param  addr   目标地址
 * @param  length 数据韩都
 * @return        成功返回写入的长度,失败-1
 */
int at24cxx_write(unsigned char *buf, unsigned int addr, unsigned int length)
{
    int fd;
    int ret;

    if(length > (AT24C02_SIZE - addr))
    {
        printf("data is too long. \n");
        return -EINVAL;
    }

    if((fd = open(AT24C02_NAME, O_WRONLY)) < 0)
    {
        printf("in at24cxx_write : open failed. \n");
        return fd;
    }

    lseek(fd, addr, SEEK_SET);

    if( (ret = write(fd, buf, length)) < 0 )
    {
        printf("at24cxx write failed. \n");
    }

    close(fd);
    return ret;
}

/**
 * [at24cxx_format description]:格式化at24c02
 * @return  成功返回非负,失败-1
 */
int at24cxx_format(void)
{
    int fd;
    int ret;
    unsigned char buf[AT24C02_SIZE];

    memset(buf, 0, AT24C02_SIZE);
    fd = open(AT24C02_NAME, O_WRONLY);
    if(fd < 0)
        return fd;
    lseek(fd, SEEK_SET, 0);
    ret = write(fd, buf, AT24C02_SIZE);
    close(fd);;

    return ret;
}


int main(int argc, char const *argv[])
{
    unsigned char wbuf[AT24C02_SIZE] = "abcdefg";
    unsigned char rbuf[AT24C02_SIZE];

    at24cxx_format();

    at24cxx_write(wbuf, 0, strlen(wbuf));
  果果果

at24cxx_read(rbuf, 0, sizeof(rbuf));

    printf("read data is : [%s] \n", rbuf);
    return 0;
}


测试结果

这里写图片描述


end…

猜你喜欢

转载自blog.csdn.net/qq153471503/article/details/79403732