基于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 —>
    这里写图片描述
  1. 修改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…

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

觉皇嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值