×
单片机 > 单片机程序设计 > 详情

S5PV210开发 -- I2C 你知道多少?(二)

发布时间:2020-06-09 发布时间:
|

上一篇主要是介绍了下芯片手册 I2C 部分,都应该看些什么,以及上拉电阻取值和传输速率模式选择。


这一篇该来点程序了,首先以 AT24C02 (EEPROM)为基础介绍一下I2C设备驱动编程,然后以 MT9P031 为基础介绍 LINUX 下内核配置。 最后是 MPU6050 为基础的单片机下 I2C 通信程序。


一、I2C设备驱动编程

该部分我会以嵌入式Linux软硬件开发详解第 12 章,和Linux设备驱动开发详解第 15 章为参考来展开。


(1)I2C 设备驱动程序结构

1. I2C设备驱动层次结构

从系统的角度来看,Linux中 I2C 设备程序所处的位置如下图所示。


I2C设备驱动程序包含总线驱动层和设备驱动层两部分。设备驱动层为应用的 open、read、write 等提供相对应的接口函数,但是涉及具体的硬件操作,例如寄存器的操作等,则由总线驱动层来完成。


一般来说,针对具体的硬件平台,生产厂家通常已经写好总线驱动层相关内容,用户只要在内核配置选项中选择就可以了。

 

进行上述操作即为 S5PV210 选择了总线驱动层的代码,而程序设计者只需要编写设备驱动层的代码。


2. I2C 设备驱动程序

在设备驱动层,Linux内核对 I2C 设备驱动代码的组织符合 Linux 的设备驱动模型。如下图所示:


Linux 内核提供了 i2c_bus_type 总线来管理设备和驱动,左侧为多个 I2C 设备组成的设备链表,以 i2c_client 结构体来表示各个设备;右侧为适用于多个具体 I2C 设备驱动程序组成的驱动链表,以 i2c_driver 结构体来表示不同的驱动程序,下面我们对其进行简要的介绍。


这些结构体都是在 linux-2.6.32.17/include/linux/i2c.h 头文件下定义的,可自行查看相关源码。


1)I2C 设备


描述 I2C 设备的结构体为 i2c_client,其代码如下:


struct i2c_client {

unsigned short flags; /* div., see below */

unsigned short addr; /* chip address - NOTE: 7bit */

/* addresses are stored in the */

/* _LOWER_ 7 bits */

char name[I2C_NAME_SIZE];

struct i2c_adapter *adapter; /* the adapter we sit on */

struct i2c_driver *driver; /* and our access routines */

struct device dev; /* the device structure */

int irq; /* irq issued by device */

struct list_head detected;

};

I2C 设备结构体 i2c_client 是描述 I2C 设备的基本模板,驱动程序的设备结构应该包含该结构。


2)I2C 驱动


描述 I2C 驱动的结构体为 i2c_driver,其代码如下。


struct i2c_driver {

unsigned int class;

 

/* Notifies the driver that a new bus has appeared or is about to be

* removed. You should avoid using this if you can, it will probably

* be removed in a near future.

*/

int (*attach_adapter)(struct i2c_adapter *);

int (*detach_adapter)(struct i2c_adapter *);

 

/* Standard driver model interfaces */

int (*probe)(struct i2c_client *, const struct i2c_device_id *);

int (*remove)(struct i2c_client *);

 

/* driver model interfaces that don't relate to enumeration  */

void (*shutdown)(struct i2c_client *);

int (*suspend)(struct i2c_client *, pm_message_t mesg);

int (*resume)(struct i2c_client *);

 

/* a ioctl like command that can be used to perform specific functions

* with the device.

*/

int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

 

struct device_driver driver;

const struct i2c_device_id *id_table;

 

/* Device detection callback for automatic device creation */

int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);

const struct i2c_client_address_data *address_data;

struct list_head clients;

};

i2c_driver 结构体中,probe 成员为加载 I2C 驱动程序时探测 I2C 设备所调用的函数,而 remove 函数实现相反的功能。i2c_device_id 结构体代码如下。


struct i2c_device_id {

char name[I2C_NAME_SIZE];

kernel_ulong_t driver_data /* Data private to the driver */

__attribute__((aligned(sizeof(kernel_ulong_t))));

};

代码中 name 成员保存设备的名称,如“at24c02”等。


i2c_driver 结构体成员中我们只需要初始化 probe 和 remove 就够了,其他的函数都是可选的。


3)I2C 总线


描述I2C总线的结构体为i2c_bus_type,其代码如下。


struct bus_type i2c_bus_type = {

.name = "i2c",

.match = i2c_device_match,

.probe = i2c_device_probe,

.remove = i2c_device_remove,

.shutdown = i2c_device_shutdown,

.suspend = i2c_device_suspend,

.resume = i2c_device_resume,

};

i2c_bus_type 总线进行设备和驱动程序的匹配,依靠的是其 match 成员函数,其代码如下。


static int i2c_device_match(struct device *dev, struct device_driver *drv)

{

struct i2c_client *client = i2c_verify_client(dev);

struct i2c_driver *driver;

 

if (!client)

return 0;

 

driver = to_i2c_driver(drv);

/* match on an id table if there is one */

if (driver->id_table)

return i2c_match_id(driver->id_table, client) != NULL;

 

return 0;

}

该函数调用了 i2c_match_id 函数,i2c_match_id 函数的内容如下。


static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,

const struct i2c_client *client)

{

while (id->name[0]) {

if (strcmp(client->name, id->name) == 0)

return id;

id++;

}

return NULL;

}

我们可以看出,match 函数实质是监测 client 描述的设备名称和驱动程序对应的设备名是否一致,如果一致,即找到了和设备对匹配的驱动程序。


上述为 2.6.35 版本 Linux 内核下的 I2C 设备驱动的框架,这里还涉及一些其他的结构体和函数,我们在示例中进行讲解。


(2)AT24C02 设备驱动程序

1. AT24C02 设备驱动程序

S5PV210 开发板具有一片 AMTEL 公司的 I2C 接口 EEPROM 芯片,型号是 AT24C02,其驱动程序代码如下。


1)AT24Cxx 设备代码


//设备驱动 at24cxx_dev.c

#include

#include

#include

#include

#include

#include

 

//声明i2c_board_info 结构体

static struct i2c_board_info at24cxx_info = {

  //设备名 和 设备地址

I2C_BOARD_INFO ("at24c02", 0x50);

}

 

//初始化 i2c_client

static struct i2c_client *at24cxx_client;

 

static int at24cxx_dev_init (void)

{

  //获取 I2C 总线适配器

struct i2c_adapter *i2c_adap;

//获取0号适配器

i2c_adap = i2c_get_adapter (0);

//将AT24CXX 加入0号适配器对应的总线管理设备链表中

at24cxx_client = i2c_new_device (i2c_adap, &at24cxx_info);

i2c_put_adapter (i2c_adap);

 

return 0;

}

 

static void at24cxx_dev_exit (void)

{

i2c_unregister_device (at24cxx_client);

}

 

module_init (at24cxx_dev_init);

module_exit (at24cxx_dev_exit);

MODULE_LICENSE ("GPL");

2)AT24Cxx驱动代码


//驱动代码 at24cxx_drv.c

#include

#include

#include

#include

#include

#include

#include

#include

 

static int major;

static struct class *class;

static struct i2c_client *at24cxx_client;

static ssize_t at24cxx_read(struct file * file, char __user *buf, size_t count, loff_t *off)

{

unsigned char addr, data;

copy_from_user(&addr, buf, 1);

data = i2c_smbus_read_byte_data(at24cxx_client, addr);

copy_to_user(buf, &data, 1);

return 1;

}

static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t count, loff_t *off)

{

unsigned char ker_buf[2];

unsigned char addr, data;

copy_from_user(ker_buf, buf, 2);

addr = ker_buf[0];

data = ker_buf[1];

printk("addr = 0x%02x, data = 0x%02xn", addr, data);

if (!i2c_smbus_write_byte_data(at24cxx_client, addr, data))

return 2;

else

return -EIO;

}

static struct file_operations at24cxx_fops = {

.owner = THIS_MODULE,

.read = at24cxx_read,

.write = at24cxx_write,

};

static int __devinit at24cxx_probe(struct i2c_client *client,

const struct i2c_device_id *id)

{

at24cxx_client = client;

//printk("%s %s %dn", __FILE__, __FUNCTION__, __LINE__);

major = register_chrdev(0, "at24cxx", &at24cxx_fops);

class = class_create(THIS_MODULE, "at24cxx");

device_create(class, NULL, MKDEV(major, 0), NULL, "at24cxx");

return 0;

}

static int __devexit at24cxx_remove(struct i2c_client *client)

{

//printk("%s %s %dn", __FILE__, __FUNCTION__, __LINE__);

device_destroy(class, MKDEV(major, 0));

class_destroy(class);

unregister_chrdev(major, "at24cxx");

return 0;

}

static const struct i2c_device_id at24cxx_id_table[] = {

{ "at24c08", 0 },

{}

};

/* 分配/设置i2c_driver */

static struct i2c_driver at24cxx_driver = {

.driver = {

.name = "100ask",

.owner = THIS_MODULE,

},

.probe = at24cxx_probe,

.remove = __devexit_p(at24cxx_remove),

.id_table = at24cxx_id_table,

};

static int at24cxx_drv_init(void)

{

/*  注册i2c_driver */

i2c_add_driver(&at24cxx_driver);

return 0;

}

static void at24cxx_drv_exit(void)

{

i2c_del_driver(&at24cxx_driver);

}

module_init(at24cxx_drv_init);

module_exit(at24cxx_drv_exit);

MODULE_LICENSE("GPL");

3)AT24Cxx测试程序


//测试程序 i2c_test.c

#include

#include

#include

#include

#include

#include

/* i2c_test r addr

 i2c_test w addr val */

void print_usage(char *file)

{

printf("%s r addrn", file);

printf("%s w addr valn", file);

}

int main(int argc, char **argv)

{

int fd;

unsigned char buf[2];

if ((argc != 3) && (argc != 4))

{

print_usage(argv[0]);

return -1;

}

fd = open("/dev/at24cxx", O_RDWR);

if (fd < 0)

{

printf("cant open /dev/at24cxxn");

return -1;

}

if (strcmp(argv[1], "r") == 0)

{

buf[0] = strtoul(argv[2], NULL, 0);

read(fd, buf, 1);

printf("data: %c, %d, 0x%2xn", buf[0], buf[0], buf[0]);

}

else if ((strcmp


关键字:S5PV210  I2C  设备驱动 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
STM32中断向量表的位置.重定向