历史上的今天

今天是:2024年08月30日(星期五)

正在发生

2019年08月30日 | S5PV210开发 -- I2C 你知道多少?(二)

发布者:快乐兔子 来源: eefocus关键字:S5PV210  I2C  设备驱动 手机看文章 扫描二维码
随时随地手机看文章

上一篇主要是介绍了下芯片手册 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(argv[1], "w") == 0) && (argc == 4))

{

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

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

if (write(fd, buf, 2) != 2)

printf("write err, addr = 0xx, data = 0xxn", buf[0], buf[1]);

}

else

{

print_usage(argv[0]);

return -1;

}

return 0;

}

4)Makefile


ifneq ($(KERNELRELEASE),)

    obj-m += at24cxx_drv.o

else

    KERNELDIR=/opt/kernel

all:

PWD=$(shell pwd)  

$(MAKE) -C $(KERNELDIR) M=$(PWD)

clean:

rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions module* Module*

endif

5)开发板测试


执行 make 生成驱动 at24cxx_drv.ko


# ls

at24cxx_dev.c  at24cxx_drv.ko     at24cxx_drv.mod.o  built-in.o  Makefile   modules.order

at24cxx_drv.c  at24cxx_drv.mod.c  at24cxx_drv.o      i2c_test.c  Makefile~  Module.symvers

在开发板新建一个 driver 文件夹,使用 tftp 工具将其拷贝到开发板 /driver 目录下


tftp -g -r at24cxx_drv.ko 192.168.x.xx

加载驱动:


insmod /driver/at24c02_drv.ko

 


然后编译测试文件 i2c_test.c 生成可执行文件 i2c_test


# arm-none-linux-gnueabi-gcc i2c_test.c -o i2c_test 

其中这里如果对于交叉编译不熟悉的人会出现问题,line 1: syntax error: unexpected word (expecting ")") 

解决,参看:S5PV210开发 -- 交叉编译器


最后,拷贝到开发板执行 ./i2c_test


 


因为编译kernel 和 运行的内核版本不一致,加载驱动时会出现问题:


# insmod at24cxx_drv.ko 

[  188.062254] at24cxx_drv: version magic '2.6.35.7-Concenwit preempt mod_unload ARMv7 ' 

should be '2.6.35.7 preempt mod_unload ARMv7 '

insmod: can't insert 'at24cxx_drv.ko': invalid module format

解决方法:

参看:内核模块编译怎样绕过insmod时的版本检查


(1)执行 modinfo 命令,先看一下 at24cxx_drv.ko 的信息。


# modinfo at24cxx_drv.ko 

filename:       at24cxx_drv.ko

license:        GPL

depends:        

vermagic:       2.6.35.7-Concenwit preempt mod_unload ARMv7 

(2)修改 kernel 的 vermagic,再重新编译设备驱动

vermagic 的第一个值 2.6.35.7-Concenwit 是由这 kernel/include/generated/utsrelease.h 里的 UTS_RELEASE 所定义。(可能utsrelease.h头文件位置不一样,使用find指令自己搜一下)。


之后再由 kernel/include/linux/vermagic.h 去组合出 VERMAGIC_STRING,也就是 kernel 的vermagic。

[1] [2] [3]
关键字:S5PV210  I2C  设备驱动 引用地址:S5PV210开发 -- I2C 你知道多少?(二)

上一篇:S5PV210开发 -- I2C 你知道多少?(一)
下一篇:S5PV210开发 -- I2C 你知道多少?(三)

推荐阅读

安防行业竞争加剧的前几年,部分安防企业企图用“价格牌”来抢占市场份额,掀起行业一阵阵低价浪潮。价格战,这场没有硝烟的战争,通过不断压缩产品利润空间的营销手段,对行业造成很大损害。同行之间压价、排挤 、挖墙脚,一定程度上扰乱市场规则和秩序的行为。恶性循环下去,造成的是两败俱伤,受伤害的只能是供求双方。一半安防企业转型失败 但赌小概...
8月29日,韦尔股份发布2019年半年报,公司上半年实现营业总收入15.5亿元,同比降低18.25%;归属于上市公司股东的净利润0.25亿元,同比降低83.77%。韦尔股份披露,自2018年第四季度以来,半导体市场需求明显下滑,公司半导体分销业务销售额及产品毛利率水平受市场波动影响较大,导致公司整体营业收入及利润水平较去年同期产生较大幅度的下滑。此外,韦尔股...
在2019年的华为开发者大会上,华为正式对外发布了Harmony OS。时隔一年后,华为开发者大会2020即将拉开帷幕。对于鸿蒙OS何时能应用在手机上,华为消费者业务CEO余承东表示,今年先不发布这个计划,明年会有。2019年华为正式推出了Harmony OS的1.0版本,随后华为荣耀智慧屏搭载该系统,成为首个尝鲜的智能终端产品。此前余承东虽然对外表示,华为手机随时...
  1、使用接地电阻测试仪准备工作  ①熟读接地电阻测量仪的使用说明书,应全面了解仪器的结构、性能及使用方法。  ②备齐测量时所必须的工具及全部仪器附件,并将仪器和接地探针擦拭干净,特别是接地探针,一定要将其表面影响导电能力的污垢及锈渍清理干净。  ③将接地干线与接地体的连接点或接地干线上所有接地支线的连接点断开,使接地体脱离任...

史海拾趣

问答坊 | AI 解惑

发一个multism的链接,可以下载看看

http://www.verycd.com/topics/2754295/ [ 本帖最后由 open82977352 于 2010-2-10 16:33 编辑 ]…

查看全部问答∨

LED驱动必备(变压器相关)

LED驱动和目前都是用开关电源之IC 所以免不了磁性器件变压器的设计 希望对各位LED爱好者使用者有用…

查看全部问答∨

关于中九升级

请教高手,我拆开一山寨版卫星直播解码器只看到HTV903和AVL1108EGA.还有一芯片上写着AV2020,请问是5810单晶振的吗?我怎么也找不到5810或5812的字样.因此也不敢确定该用哪个升级文件.请高手帮忙.万分感谢!…

查看全部问答∨

Local Bus 地址线问题

从mpc8313e的LA[0:25]的LA22 LA23 LA24引出三根线接到DSP的HPI管脚,请问如果DSP的片选基地址设为0xF2000000,那么这个UHPI的基地址是怎么算的? 基地址是0xF2000000 + (1 << (25 - 24))还是0xF200000 + (1 << 22U) 也就是说这个地址该从 ...…

查看全部问答∨

我下了protel转pads工具的文件为什么里面的ddb2.exe文档不能运行

我习惯于用pads但是公司给的却是protel文件。所以我想将prote转换成pads文件,所以我下了工具,但是当我运行里面的ddb2.exe文件时提示:“cannot find the font file romansim.fnt"但是文件夹里明明有romansim.fnt 文件,为此我很急,请大家帮帮 ...…

查看全部问答∨

做过或了解 rfid的高手帮看个问题

最近刚开始在研究rfid,就是想试着做一个简单的应用,用的是复旦的FM1715,对于datasheet里面有些描述看不是很懂,上来向大家请教下:对于Read指令, READ 控制单元 --> 射频卡 command:0x30 Len : 1 Data[0] : _Adr  块地址 (0~ ...…

查看全部问答∨

wince系统下GPRS拨号连接上网!

wince4.2系统下,新建连接->拨号连接->...+cgdcont=1,"ip","cmnet"   *99***1# 其他的都是默认的设置,,但是连接不上,,出错在: 端口不可用:该端口配置不正确,或者其他程序正在使用该端口. 请问GPRS模块上网到底要怎么配置啊 ...…

查看全部问答∨

关于USB管道名的问题 (八百里加急)

小弟现在在开发上位机程序 用到USB 通讯 ,但是驱动资料上只给除了两个管道名 由于种种原因 这两个都不符合要求 我该怎么样得到管道名 (用一个共享软件 人家都可以得到的) 请高手指点!!…

查看全部问答∨

DIY MP3活动发起

读大学的时候就打算自己做个MP3玩的,搞了一堆的资料来看。 后来参加了电子大赛,DIY  MP3的想法就搁置在一边了。 电子大赛完了就基本上开始找工作,直到现在MP3连个影子都木有。 要不大家就一起DIY一个MP3玩玩吧。 虽然现在买一个MP ...…

查看全部问答∨

~~请教I/O口控制直流电机开关~~

我用STC12C5204AD单片机的IO口控制直流电机开关,接法如电路图,用外部中断1,低电平控制; 有个问题是:上电的时候,空载电机会动一下,适配器的指示灯会暗一会。开关按下电机启动,不过需要长按一两秒之后,电机才会常转,不然会动一会就不转了 ...…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

更多每日新闻

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved