class="markdown_views prism-atelier-sulphurpool-light">
【ARM9 S3C2440 IIC驱动程序】
环境:
linux kernel: 3.0
tq2440
ubuntu14.04
linux3.0内核移植笔记点此
配置内核
- 配置内核支持IIC
Device Drivers —>
<*> I2C support —>
[*] Misc devices —>EEPROM support —>

- 修改
arch/arm/mach-s3c2440/mach-smdk2440.c
2.1 添加如下:
2.2 向内核注册
在函数smdk2440_machine_init
中添加如下红框中内容:

static struct at24_platform_data at24c02 = {
.byte_len = SZ_2K / 8,
.page_size = 8,
.flags = 0,
};
static struct i2c_board_info __initdata smdk2440_i2c_devs[] = {
{
I2C_BOARD_INFO("24c02", 0x50),
.platform_data = &at24c02,
},
};
驱动编写
2440,6410,210
三款芯片都共用i2c-s3c2410.c
,这个文件的代码,通用驱动文件为:i2c-dev.c
IIC设备驱动的设计方式有两种:
- 用户态驱动程序: 本质就是通过ioctl函数传入命令,以此操作EEPROM的读写等操作.
- 自编驱动程序
[方式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表示读。
测试程序
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "/dev/i2c-0"
#define AT24C02_SIZE 256
#define AT24C02_ADDR 0x50
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;
e2prom_data.msgs[1].flags = 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;
}
void delay(unsigned int n)
{
unsigned int k;
while(n--)
{
for(k = 10000; k > 0; k--)
{
;
}
}
}
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…