准备工作:
(1)内核层:查看drivers/i2c目录下的Makefile文件。()是否有编译如下文件:i2c-core.c, i2c-dev.c, i2c-drive.c
(2)总线驱动层:在driver/i2c/busses目录下:看Makefile文件是否有i2c-s3c2410.0
(3)在linux目录下make menuconfig ,查看又没有把配置选进去,这个和Makefile的宏是等同的,比如
obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o ,那么可以在menuconfig搜索CONFIG_I2C_MUX_GPIO这个宏定义。
Device_Deivers--->I2c_support //i2c-core.c
---->i2c device interface //i2c-dev.c
---->i2c hardware bus support---->s3c2410 i2C Driver // i2c-s3c2410.c
(4)然后在linux根目录下:make uImage 编译生成内核烧录
注册设备
cat /proc/device //可以查看设备是否被注册成功
注册驱动------平台设备驱动
卸载设备
卸载驱动
i2cdetect -y 1 //此命令可以监测到IIC总线上所有的iic设备,用于确认iic子设备的设备地址
#include <linux/i2c-dev.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <unistd.h>
int main(int argc, char**argv)
{
int fd;
fd = open("/dev/i2c-1", O_RDWR);
if (fd < 0) {
perror("open failed\n");
exit(1);
}
int device_addr = 0x40; //硬件设备地址
if (ioctl(fd, I2C_SLAVE, device_addr) < 0) {
perror("ioctl failed\n");
exit(1);
}
char wbuf[10];
char register_addr = 0x3;
/* Using I2C Write*/
wbuf[0] = register_addr;
wbuf[1] = 0x5;
if (write(fd ,wbuf, 2) != 2) {
perror("write failed\n");
exit(1);
}
if (write(fd ,®ister_addr, 1) != 1) {
perror("write failed\n");
exit(1);
}
/* Using I2C Read*/
char rbuf[10];
if (read(fd, &rbuf[0], 1) != 1) {
perror("read failed\n");
exit(1);
} else {
printf("rbuf value = [%d]\n", rbuf[0]);
}
}
2.用户态驱动设计
我们先分析一下程序大概的流程:
(1)打开通用的字符设备文件
依然是使用open打开设备文件,在开发板的/dev/下面我们可以找到一个叫做i2c-0的设备文件,我们以读写的方式打开这个设备文件。
// 1.打开通用设备文件
fd = open("/dev/i2c-0", O_RDWR);
(2)构造需要写入到EEPROM中的消息
我们首先需要赋值消息的定义到我们的程序中。即i2c_msg和i2c_rdwr_ioctl_data,可以把一些不需要的数据删掉。
struct i2c_msg
{
unsigned short addr;
unsigned short flags;
unsigned short len;
unsigned char *buf;
};
struct i2c_rdwr_ioctl_data
{
struct i2c_msg *msgs; // 消息指针
unsigned int nmsgs; // 消息数量
};
然后定义一个消息结构,i2c_rdwr_ioctl_data eeprom_data,然后初始化这个结构(别忘了给指针分配空间)。特别要注意的是对应消息的数量读和写肯定是不一样的,因为对于写只需要一个消息,而对于读只需要2个消息,因为先做了一次写,然后在做了一次读。因此我们按最大的长度2,来给i2c_msg分配空间。
接下来可以初始化写的消息,写的信息有2个字节,所以len=2,第一个是偏移地址,第二个是需要写入的数据。初始化后如下:
// 因为对于写只需要一个消息
// 对于读只需要2个消息,因为先做了一次写,然后在做了一次读
// 因此我们按最大的长度2,来给i2c_msg分配空间
e2prom_data.msgs = (struct i2c_msg *)malloc(2 * sizeof(struct i2c_msg));
// 2.构造写数据到eeprom的消息
e2prom_data.nmsgs = 1; // 写只有一条消息
(e2prom_data.msgs[0]).len = 2; // 偏移地址+数据
(e2prom_data.msgs[0]).addr = 0x50; // eeprom设备地址
(e2prom_data.msgs[0]).flags = 0; // 0为写,1为读
(e2prom_data.msgs[0]).buf = (unsigned char *)malloc(2);
(e2prom_data.msgs[0]).buf[0] = 0x10; // 写入到EEPROM的偏移地址
(e2prom_data.msgs[0]).buf[1] = 0x60; // 写入到偏移地址的数据
(3)使用ioctl写入数据
ioctl的第一个参数是fd,第二个参数是操作类型,这里是I2C_RDWR,我们需要拷贝I2C_RDWR到自己的程序中,第三个是参数就是eeprom_data了,我们在取地址之后需要进行类型转换,因为i2cdev_ioctl_rdrw的参数是unsigned long.
#define I2C_RDWR 0x0707
// 3.使用ioctl写入数据
ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data);
(4)构造从EEPROM读数据的消息
读消息的构造也类似,不过这里需要2个消息,第一个实现写,第二个实现读:
// 4.构造从EEPROM读数据的消息
eeprom_data.nmsgs = 2; // 读有二条消息
(eeprom_data.msgs[0]).addr = 0x50;// 先写入需要开始读取的偏移地址,然后开始读
(eeprom_data.msgs[0]).flags = 0;
(eeprom_data.msgs[0]).len = 1;
(eeprom_data.msgs[0]).buf[0] = 0x10;
(eeprom_data.msgs[1]).addr = 0x50;
(eeprom_data.msgs[1]).flags = 1;
(eeprom_data.msgs[1]).len = 1;
(eeprom_data.msgs[1]).buf = (unsigned char *)malloc(2);
(eeprom_data.msgs[1]).buf[0] = 0; // 先把读取缓冲清0
(5)使用ioctl读出消息
ioctl(fd, I2C_RDWR, (unsigned long)&eeprom_data);
// 读取到的消息会保存在以buf[0]为起始地址的存储空间中。
(6)关闭字符设备
close(fd)
完整代码:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#define I2C_RDWR 0x0707
struct i2c_msg
{
unsigned short addr;
unsigned short flags;
unsigned short len;
unsigned char *buf;
};
struct i2c_rdwr_ioctl_data
{
struct i2c_msg *msgs; // 消息指针
unsigned int nmsgs; // 消息数量
};
int main()
{
int fd;
struct i2c_rdwr_ioctl_data e2prom_data;
// 1.打开通用设备文件
fd = open("/dev/i2c-0", O_RDWR);
// 因为对于写只需要一个消息
// 对于读只需要2个消息,因为先做了一次写,然后在做了一次读
// 因此我们按最大的长度2,来给i2c_msg分配空间
e2prom_data.msgs = (struct i2c_msg *)malloc(2 * sizeof(struct i2c_msg));
// 2.构造写数据到eeprom的消息
e2prom_data.nmsgs = 1; // 写只有一条消息
(e2prom_data.msgs[0]).len = 2; // 偏移地址+数据
(e2prom_data.msgs[0]).addr = 0x50; // eeprom设备地址
(e2prom_data.msgs[0]).flags = 0; // //0为写,1为读
(e2prom_data.msgs[0]).buf = (unsigned char *)malloc(2);
(e2prom_data.msgs[0]).buf[0] = 0x10; // 写入到EEPROM的偏移地址
(e2prom_data.msgs[0]).buf[1] = 0x60; // 写入到偏移地址的数据
// 3.使用ioctl写入数据
ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data);
// 4.构造从EEPROM读数据的消息
eeprom_data.nmsgs = 2; // 读有二条消息
(eeprom_data.msgs[0]).addr = 0x50;// 先写入需要开始读取的偏移地址,然后开始读
(eeprom_data.msgs[0]).flags = 0;
(eeprom_data.msgs[0]).len = 1;
(eeprom_data.msgs[0]).buf[0] = 0x10;
(eeprom_data.msgs[1]).addr = 0x50;
(eeprom_data.msgs[1]).flags = 1;
(eeprom_data.msgs[1]).len = 1;
(eeprom_data.msgs[1]).buf = (unsigned char *)malloc(2);
(eeprom_data.msgs[1]).buf[0] = 0; // 先把读取缓冲清0
// 5.使用ioctl读出数据
ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data);
printf("buffer[0] = %x\n", (e2prom_data.msgs[1]).buf[0]);
// 6.关闭设备
close(fd);
}
3.编译
arm-linux-gcc -static i2c-app-drv.c -o i2c-app-drv
拷贝到开发板上,执行./i2c-app-drv: