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

2019-07-12 17:37发布

class="markdown_views prism-atelier-sulphurpool-light">

【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 #include #include #include #include #include #include #include #include #include #include #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. "); return; } if((fd = open(DEVICE_NAME, O_RDWR)) < 0) { printf("open failed. "); 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 ", 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. "); return; } if((fd = open(DEVICE_NAME, O_RDWR)) < 0) { printf("in e2prom_write() open failed. "); 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 ", 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] ", 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 #include #include #include #include #include #include #include #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. "); return -EINVAL; } if((fd = open(AT24C02_NAME, O_RDONLY)) < 0) { printf("in at24cxx_read : open failed. "); return fd; } lseek(fd, addr, SEEK_SET); if( (ret = read(fd, buf, length)) < 0 ) { printf("at24cxx read failed. "); } 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. "); return -EINVAL; } if((fd = open(AT24C02_NAME, O_WRONLY)) < 0) { printf("in at24cxx_write : open failed. "); return fd; } lseek(fd, addr, SEEK_SET); if( (ret = write(fd, buf, length)) < 0 ) { printf("at24cxx write failed. "); } 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] ", rbuf); return 0; }
测试结果
这里写图片描述
end…