能否同时使用两个I2C特种设备使用登记表

同一I2C总线下,接两个设备,不能识别问题。 - 单片机/MCU论坛 -
中国电子技术论坛 -
最好最受欢迎电子论坛!
后使用快捷导航没有帐号?
同一I2C总线下,接两个设备,不能识别问题。
15:31:28  
Linux系统,同一I2C总线下接了音频与RTC设备,单个测试都能正常工作,但全接上就只有音频能正常工作,RTC设备则不能识别了。 求教大神!
Powered by
供应链服务
版权所有 (C) 深圳华强聚丰电子科技有限公司能指点下linux i2c驱动吗? - 知乎有问题,上知乎。知乎作为中文互联网最大的知识分享平台,以「知识连接一切」为愿景,致力于构建一个人人都可以便捷接入的知识分享网络,让人们便捷地与世界分享知识、经验和见解,发现更大的世界。20被浏览<strong class="NumberBoard-itemValue" title="分享邀请回答11 条评论分享收藏感谢收起添加评论分享收藏感谢收起写回答LINUX驱动中,两个从设备的I2C地址冲突如何解决?
[问题点数:40分,结帖人lh199302]
本版专家分:0
结帖率 100%
CSDN今日推荐
本版专家分:260
本版专家分:0
结帖率 100%
本版专家分:6831
本版专家分:0
结帖率 100%
本版专家分:0
结帖率 100%
匿名用户不能发表回复!|
CSDN今日推荐【图片】【arduino】三种方法让你在I2C通信中同时和多个从机通信_arduino吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:56,378贴子:
【arduino】三种方法让你在I2C通信中同时和多个从机通信收藏
对于不同地址的模块就不用多说了,直接分别对其地址进行通信即可。那么若拿到相同地址的模块,或者直接是相同的多个模块怎么办呢?经过楼主对adxl345的苦苦摸索,这里给大家分享三种方法。
楼主纯手打+原创到游弋吧uer8.com发了一次 不过里面都是大学生转过来给更多人分享一下
方法一:(内置了两种地址的模块)对于内置了两种地址的模块,可以通过对某个引脚置高或置低来选择其中一个地址,现假设置高为A,置低为B。假设你有三个模块要同时通信,首先将模块1置高,模块2、模块3的地址选择口置低,这样仅有模块1在地址A,然后对地址A进行通信即可防止其他模块干扰。接下来将模块1置低,模块2置高,即可对模块2通信。循环下去即可实现同时对三个模块通信。
方法二:(具有使能端的模块)若你手中的模块某个引脚必须拉低或拉高才能正常使用,那么仿照方法一,对其中一个使能,其他均处于非使能状态,如此便可以仅对其中一个模块通信咯。如果以上都不行,那就只能方法三了。
万能方法三:(什么都没有,仅有一种地址的模块)原本买来六个ADXL345来读取不同地方的加速度信息,datasheet里说可以通过对某个引脚置高置低来选择其中一个地址。然而发现并不能改变地址!于是只好想出这样一个邪恶的方法。用到的工具是模拟开关,楼主以CD4053为例,这货非常便宜,几元钱可以买一大把。简单来说模拟开关相当于多个单刀双掷开关,可以通过IO口输出高低电平控制某两路连通。我们知道I2C通信有 SCL和SDA两根线,我们通过调整模拟开关使仅有一个模块完整接入I2C BUS即可实现只对其中一个通信。具体实施方式:假设有模块1 2 3CD4053的 ay by cy悬空
ax bx cx并接到单片机的SCL将模块1的SCL口接CD4053的a口将模块2的SCL口接CD4053的b口将模块3的SCL口接CD4053的c口这样只需将A置高,B、C置低,即可与模块1通信将B置高,A、C置低,即可与模块2通信将C置高,A、B置低,即可与模块3通信
亲测有效,读数稳定,不过注意一定要将CD4053 VEE和VSS都接地,不然读数抖动不稳定,楼主在这里卡死了很久!上代码
以ADXL345为例:boolean x=
//是否调试输出 //CD4053的接入方式//INH口接地
aY-&SCL1 bY-&SCL2 cY-&SCL3 a.b.c-&SCL_Arduino #define CD4053_C 10#define CD4053_B 9#define CD4053_A 8
//----------1
//----------2 //PIN#define PIN_SDA 20#define PIN_SCL 21
//I2C (sparkfun breakout)#define Register_ID 0#define Register_2D 0x2D#define Register_X0 0x32#define Register_X1 0x33#define Register_Y0 0x34#define Register_Y1 0x35#define Register_Z0 0x36#define Register_Z1 0x37 #include &Wire.h&int ADXAddress = 0x53; // the default 7-bit slave addressint reading = 0;int val=0;int X0,X1,X_int Y0,Y1,Y_int Z1,Z0,Z_double Xg,Yg,Zg;int flag=1;
//标志变量 void Wire_Start(){
Wire.beginTransmission(ADXAddress);
Wire.write(Register_2D);
Wire.write(8); //measuring enable
Wire.endTransmission(); // stop transmitting} void Wire_Get(){
//--------------X
Wire.beginTransmission(ADXAddress); // transmit to device
Wire.write(Register_X0);
Wire.write(Register_X1);
Wire.endTransmission();
Wire.requestFrom(ADXAddress,2);
if(Wire.available()&=2)
X0 = Wire.read();
X1 = Wire.read();
X_out=X0+X1;
//------------------Y
Wire.beginTransmission(ADXAddress); // transmit to device
Wire.write(Register_Y0);
Wire.write(Register_Y1);
Wire.endTransmission();
Wire.requestFrom(ADXAddress,2);
if(Wire.available()&=2)
Y0 = Wire.read();
Y1 = Wire.read();
Y_out=Y0+Y1;
//------------------Z
Wire.beginTransmission(ADXAddress); // transmit to device
Wire.write(Register_Z0);
Wire.write(Register_Z1);
Wire.endTransmission();
Wire.requestFrom(ADXAddress,2);
if(Wire.available()&=2)
Z0 = Wire.read();
Z1 = Wire.read();
Z_out=Z0+Z1;
//----------------
Xg=X_out/256.0;
Yg=Y_out/256.0;
Zg=Z_out/256.0;} void setup(){
Wire.begin();
Serial.begin(9600);
delay(100);
pinMode(CD4053_A,OUTPUT);
pinMode(CD4053_B,OUTPUT);
pinMode(CD4053_C,OUTPUT);
// enable to measute g data
digitalWrite(CD4053_A,HIGH);
digitalWrite(CD4053_B,LOW);
digitalWrite(CD4053_C,LOW);
delay(10);
Wire_Start();
//初始化模块1
digitalWrite(CD4053_A,LOW);
digitalWrite(CD4053_B,HIGH);
delay(10);
Wire_Start();
//初始化模块2
digitalWrite(CD4053_B,LOW);
digitalWrite(CD4053_C,HIGH);
delay(10);
Wire_Start();
//初始化模块3 }void loop(){
Serial.print(&----&);
Serial.print((flag+0));
Serial.println(&----&);}
switch(flag){
digitalWrite(CD4053_A,HIGH);
digitalWrite(CD4053_C,LOW);
Wire_Get();
//读取模块1
digitalWrite(CD4053_B,HIGH);
digitalWrite(CD4053_A,LOW);
Wire_Get();
//读取模块2
digitalWrite(CD4053_C,HIGH);
digitalWrite(CD4053_B,LOW);
Wire_Get();
//读取模块3
Serial.print(&X= &);
Serial.print(Xg);
Serial.print(& &);
Serial.print(&Y= &);
Serial.print(Yg);
Serial.print(& &);
Serial.print(&Z= &);
Serial.println(Zg);
delay(33); //改变参数可以改变读取频率}
楼主你好,请教下arduino的主机如何与不是arduino的从机进行IIC通信?谢谢
楼主,九个传感器怎么弄啊
感谢楼主!!!挖坟以致谢!!!我正在做一个课题,正好看到了楼主对于这个的解决方法,不用再重新去对代码做重溯了!!!
登录百度帐号i2c总线,设备,驱动之间的关系 - tongchuhuozhai - 博客园
------ 总线上先添加好所有具体驱动,i2c.c遍历i2c_boardinfo链表,依次建立i2c_client, 并对每一个i2c_client与所有这个线上的驱动匹配,匹配上,就调用这个驱动的i2c_xxx_probe &------
  所有设备驱动在init函数里,一般只做注册平台驱动的动作,注意不是平台设备.以i2c.c为例,这个驱动的平台probe函数里做的事情比较多.因为i2c_boardinfo早已在具体驱动注册到链表,i2c.c的平台驱动就是要把每一个i2c_boardinfo,实例化一个设备出来,并把这个设备添加到总线上来.好了, i2c总线上有了一个设备,然后总线针对这个设备,会遍历总线上的所有驱动,这些驱动也是在具体驱动的平台probe里调用i2c_add_driver添加到总线上来的.所以在设备添加到总线上之前,i2c总线上的所有驱动都已经添加好了.注意这里所说的设备是i2c设备,即由i2c_boardinfo实例出一个i2c设备.添加设备,完整说法应是在总线上添加设备,一个实体只有依附于总线,被总线所管理,才能算是一个设备.所以一般根据设备所在的总线来命名设备,如i2c设备.在代码里由i2c_client结构描述.好了,简单小总结一下.
  按发生时间顺序说:
& & & &第1步: &每个设备的驱动在init时, 先要本设备的i2c信息, 包括地址等, 添加到i2c全局链表中. 使用i2c总线通讯的每个设备的驱动, 都要在模块初始化的一开始, 就要做这件事. 所有设备的驱动初始化都走完之后, 再走i2c总线设备的初始化。所有驱动的init函数里把自己的i2c_boardinfo添加到i2c全局链表里,这个链表会在第4步,i2c.c的probe添加i2c设备时用到.以光感供应商光宝驱动代码为例,在6795平台,路径kernel-3.0/drivers/misc/mediatec/alsps/ltr559/ltr559.c,&
static int __init ltr559_init(void)
 struct alsps_hw *hw = ltr559_get_cust_alsps_hw();
i2c_register_board_info(hw-&i2c_num, &i2c_ltr559, 1);
if(platform_driver_register(&ltr559_alsps_driver))
APS_ERR("failed to register driver");
return -ENODEV;
  其他加速度,地磁,陀螺仪init都清一色和上面一样的调用. &在自己去写这个init函数或看到init函数时,要想到mt_i2c_probe用这个东西做的事情,添加设备啦,匹配驱动啦.
   第2步: 所有具体驱动在i2c总线上都添加好,这个时间发生在各具体驱动的平台probe里调用i2c_add_driver. 还以上面为例:
static struct platform_driver ltr559_alsps_driver =
= ltr559_probe,
= ltr559_remove,
.name = "als_ps",
= THIS_MODULE,
   .of_match_table = alsps_of_match,
static int ltr559_probe(struct platform_device *pdev)
struct alsps_hw *hw = ltr559_get_cust_alsps_hw();
ltr559_power(hw, 1);
if(i2c_add_driver(&ltr559_i2c_driver))
APS_ERR("add driver error\n");
return -1;
  写这个函数时,要知道这是在mt_i2c_probe之前,只有这样才能被mt_i2c_probe添加设备时遍历到.写一处代码,能联想到其关联地方. 
  第3步:&i2c.c的平台probe调用.虽然同是module_init, 但makefile可以按排调用顺序.i2c.c的probe一定是在第一步中所有具体驱动都添加完才调用.以确保i2c.c的probe能够把所有具体驱动都能遍历到.在注册adapter之前,i2c总线也算一个设备, 所以有设备的驱动模块,  以mt6795为例,kernel-3.0/drivers/misc/mediatek/i2c/i2c.c,平台probe函数为:
static struct platform_driver mt_i2c_driver = {
= mt_i2c_probe,
static S32 mt_i2c_probe(struct platform_device *pdev)
.......... 其关键一句为如下
ret = i2c_add_numbered_adapter(&i2c-&adap);
//这就走进了标准i2c-core.c
...........
&&  &第4步:&&i2c.c的probe调用i2c-core.c标准核心的i2c_adapter_register,一条i2c总线只有一个适配器.这个probe运行一次,就初始化一条i2c总线.目前liux 都采用i2c_add_numbered_addapter方式,即静态注册方式,即把adapter注册到指定的i2c总线上。
  i2c_adapter_register建立这条i2c总线的sysfs设备模型,包括的属性文件有:  name, new_device, delete_device.& 还有一个包括这条i2c总线上的所有挂载设备的集合。&  如8850项目的所有sensor都挂到第3路i2c总线上,即i2c-2. 在/sys/devices/platform/mt-i2c.2/i2c-2下就有以各i2c设备地址命名的目录名, 如bma056 这个设备的i2c模型在/sys/devices/platform/mt-i2c.2/i2c-2/2-8, 就是这个目录, 2表示所在的i2c总线的id, 0018表示这个设备的地址。& 看一条总线上所有设备的地址, 就看这里就行。 &代码在:kernel-3.0/drivers/i2c/i2c-core.c
static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)
mutex_lock(&core_lock);
id = idr_alloc(&i2c_adapter_idr, adap, adap-&nr, adap-&nr + 1,
GFP_KERNEL);
mutex_unlock(&core_lock);
if (id & 0)
return id == -ENOSPC ? -EBUSY :
return i2c_register_adapter(adap);
static int i2c_register_adapter(struct i2c_adapter *adap)
    dev_set_name(&adap-&dev, "i2c-%d", adap-&nr);     adap-&dev.bus = &i2c_bus_     adap-&dev.type = &i2c_adapter_     res = device_register(&adap-&dev); // adapter也作为一个设备注册,这里得到的sys路径为:
                         //  /sys/devices/platform/mt-i2c.2/i2c-2
if (adap-&nr & __i2c_first_dynamic_bus_num){
printk("lct1: %s call i2c_scan_staic_board_info for i2c new device. \n", __func__);
i2c_scan_static_board_info(adap);
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
struct i2c_devinfo
down_read(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if (devinfo-&busnum == adapter-&nr
&& !i2c_new_device(adapter, // 将i2c_boardinfo传入,构造一个i2c_client, 将此i2c设备与所有此i2c线上的驱动匹配
&devinfo-&board_info))
dev_err(&adapter-&dev,
"Can't create device at 0x%02x\n",
devinfo-&board_info.addr);
up_read(&__i2c_board_lock);
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{  device_register();
// 这个注册函数调用的,正面不好追硕,看下函数调用栈}[ 4.082399].(5)[1:swapper/0][&ffffffc&] ltr559_i2c_probe+0x410/0x8ac[ 4.083295].(5)[1:swapper/0][&ffffffc&] i2c_device_probe+0xac/0x108[ 4.084201].(5)[1:swapper/0][&ffffffc&] really_probe+0x80/0x2e8[ 4.085040].(5)[1:swapper/0][&ffffffcc&] __device_attach+0x5c/0x6c[ 4.085900].(5)[1:swapper/0][&ffffffc&] bus_for_each_drv+0x50/0x94[ 4.086770].(5)[1:swapper/0][&ffffffc&] device_attach+0xa0/0xc4[ 4.087607].(5)[1:swapper/0][&ffffffc&] bus_probe_device+0x90/0xb8[ 4.088476].(5)[1:swapper/0][&ffffffcc&] device_add+0x4b0/0x564[ 4.089301].(5)[1:swapper/0][&ffffffc&] device_register+0x18/0x28[ 4.090158].(5)[1:swapper/0][&ffffffc00074ebb8&] i2c_new_device+0x154/0x184以上从device_register到i2c_device_probe经历的调用,是一个标准过程,中间必有bus_for_each_drv, 可见不管什么设备注册,要要在此设备注册时,遍历该总线上的所有驱动,那些mipi总线,spi总线,uart总线,无一例外.
static int i2c_device_probe(struct device *dev){
status = driver-&probe(client, i2c_match_id(driver-&id_table, client));&// 调到具体驱动的i2c_xxx_probe
  由此可见,i2c.c的probe最终要把i2c全局链表里的i2c_boardinfo都建立一个i2c设备,即i2c_client,并且要很隆重的对每个设备,和在这个线上的所有具体驱动,一一匹配,只有一个才能匹配上,匹配上了,就调用这个具体驱动的probe对此设备做初始化了.
  这个就是具体驱动大名鼎鼎的i2c_xxx_probe的,是干活最多的.上电,对reset脚高低高置位,读芯片id,id读对了,就说明i2c通了,这是具体芯片驱动调试的第一步.做芯片驱动兼容的,也在这一步,这一步不对,就不用继续下面的了,这些下面的做有建立sys设备模型,初始化work,timer等,注册中断.设置misc的文件函数集,注册misc设备(这样可文件读写,ioctl就可以用了),再有就是注册输入子系统.yamaha的芯片自检节点就在输入子系统的class下. 好像就这些吧.写这个函数时,要意识到i2c.c在添加设备,给这设备做驱动匹配时,全靠设备的名字与驱动的名字是否相同来匹配. 
-----------------------------------&&& 接口函数中的 i2c& 地址不同 -----------------------------------
  一般i2c通讯的接口函数, 参数依次为, 从设备地址, 寄存器地址, 数据指针, 长度。 在feature phone上, 一次i2c通讯为8位数据, 开始部分的地址占8位,其中7位是地址, 最后一位是读/写标志位。&mtk feature phone的i2采用软件模拟方式, 按着原理写通讯函数, 先要把数据线电位拉低.&& 在智能机上, 采用linux内核, i2c的地址不包括读写标志位。 每个设备驱动都有自己实现的i2c通讯函数。mtk公开的i2c读写接口为:
在kernel-3.0/drivers/misc/mediatek/i2c/i2c.c中定义:
int mtk_i2c_master_recv(const struct i2c_client *client,
char *buf, int count, u32 ext_flag,u32 timing)
struct i2c_adapter *adap = client-&
struct mt_i2c_
msg.addr = client-&
msg.flags = client-&flags & I2C_M_TEN;
msg.timing =
msg.flags |= I2C_M_RD;
msg.ext_flag = ext_
ret = mtk_i2c_transfer(adap, &msg, 1);  // transfer函数由mtk平台驱动工程师要实现这个函数  
* If everything went ok (i.e. 1 msg received), return #bytes received,
* else error code.
return (ret == 1) ? count :
int mtk_i2c_master_send(const struct i2c_client *client,
const char *buf, int count, u32 ext_flag,u32 timing)
struct i2c_adapter *adap = client-&
struct mt_i2c_
msg.addr = client-&
msg.flags = client-&flags & I2C_M_TEN;
msg.timing =
msg.buf = (char *)
msg.ext_flag = ext_
ret = mtk_i2c_transfer(adap, &msg, 1);
* If everything went ok (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
return (ret == 1) ? count :
  光感ltr559.c对此调用为:
static int ltr559_i2c_read_reg(u8 regnum)
u8 buffer[1],reg_value[1];
int res = 0;
mutex_lock(&read_lock);
buffer[0]=
res = i2c_master_send(ltr559_obj-&client, buffer, 0x1);//传入i2c_client, 发送的数据,发送的字节个数
if(res &= 0)
mutex_unlock(&read_lock);
APS_ERR("read reg send res = %d\n",res);
res = i2c_master_recv(ltr559_obj-&client, reg_value, 0x1);
if(res &= 0)
mutex_unlock(&read_lock);
APS_ERR("read reg recv res = %d\n",res);
mutex_unlock(&read_lock);
return reg_value[0];
---------------------------& i2c&基本原理 及&i2c gpio 配置 -------------------------------------  i2c 是多主机方式, 同一条总线上,可以有多个主线,硬件上, 每一条i2c线,有一个i2c适配器。&6592基带主芯片都有i2c的实现,如果需要使用,可以把相应的gpio口配置成i2c模式, 这样就是硬件i2c方式。 能接到i2c通讯的器件, 也都有i2c接口, 这在芯片的规格书上都有说明的,都会有支持i2c串行接口的说明,i2c地址也是固定的。 在规格书明确写出的。 有些有可选地址, 某个pin角被拉高, 是一个地址, 被拉低是另一个地址。有i2c接口的器件内部都有一个三极管开关电路, pin脚接三极管的集电极, 当基极通电流时, 开关被打开, 外接引脚被拉低。
  总线上的各种状态:&
总线空闲状态: 数据线和时钟线均拉高, 当数据线有下降沿时, 即是i2c通讯的启动信号当数据线有上升沿时, 即是i2c通讯的结束信号谁发起i2c通讯,就由谁结束i2c通讯。 所以i2c的启动信号与下降信号都由主机产生。所有时钟线控制整个i2c总线的状态, 时钟线如果为高电平, 表示i2c可以是启动或结束, 如果为低电平, 表示不能启动或结束, 只能是传输数据状态。
  i2c 先配好i2c的数据线与时钟线. mtk的feature phone和智能机, 都用dws来配制gpio,&i2c可用gpio来模拟,
  dws对i2c gpio 的配置, 数据线和时钟线都要配置成内部上拉, 方向配置成既可输入,又可输出。 还需要配名字, 驱动代码中有表示i2c pin角的名字变量,这样dws生成代码这个变量就会被赋值成这个pin角。 如果没配,&这个变量就取默认值0xff. &如果没配制 i2c gpio, 那么数据就会读异常,有时也能读出来,但异常, 如一个字节要用两个字节的数组存放。
&  i2c要在dws配置gpio口, 如果gpio配制成i2c模式,就是硬件i2c, 如果没有配置成i2c模式,就是软件i2c, 即软件模拟i2c,就在用代码写i2c的时钟,而硬件i2c的时钟是由晶振的倍频产生的,硬件i2c比软件模拟i2c更稳定。
&  判断i2c是硬件方式, 还是软件模拟方式, 就是看dws配置表上, gpio的模式。 无论是硬件还是软件, 都需要配置gpio配置表, 这样代表i2c的pin脚变量会得到赋值。 这个变量在i2c通讯函数会被用到。 mt6260 feature phone平台, 用的是软件模拟i2c. 供应商驱动有自己的i2c通讯函数的实现, 一般都能找到硬件i2c通讯的实现,和软件i2c通讯的实现。 有软件去设置时钟线,由于目前的mtk智能机平台, 都用硬件i2c方式,&&所以简单看下mt6592 平台上的硬件i2c方式的实现:
&  调试初期要做的是, 看i2c通没通, 一般先读一下寄存器的id,& i2c读函数返回小于0, 表示i2c通讯有错。
&---------------- &i2c总线作为一种平台设备被添加到平台总线  ---------------------
&  主芯片内部有i2c的实现, 相应的gpio可以配置成i2c硬件方式, &这个适配器也在主芯片内部. i2c总线作为一种平台设备,在板级初始化时,被添加到平台总线上.
  mt6592有三条i2c总线(mt6795有四条),i2c总线是作为一种平台设备被注册的。&在板级初始化时,注册了三条i2c总线设备。在/sys/devices/platform/ 会有三个i2c目录,平台设备的类型为platform_device,有两个重要的成员,name, id. 如果平台设备名相同,可以用id区分,如三条i2c总线作为平台设备,名字相同,id不同。
&  现在已经注册了三个i2c总线平台设备。有三个name相同, id不同的平台设备。那么在注册平台驱动时,相应的probe,就会被调用三次。 即mt6592平台的对i2c总线的驱动模块i2c.c 的平台驱动的probe就被调用三次。
代码位置:kernel-3.0/drivers/misc/mach/mt6795/mt_devs.c
static struct platform_device mt_device_i2c[] = {
= "mt-i2c",
.num_resources
= ARRAY_SIZE(mt_resource_i2c0),
= mt_resource_i2c0,
= "mt-i2c",
.num_resources
= ARRAY_SIZE(mt_resource_i2c1),
= mt_resource_i2c1,
= "mt-i2c",
.num_resources
= ARRAY_SIZE(mt_resource_i2c2),
= mt_resource_i2c2,
= "mt-i2c",
.num_resources
= ARRAY_SIZE(mt_resource_i2c3),
= mt_resource_i2c3,
__init int mt_board_init(void)
.......... // 平台设备很多,都在这里注册. 
retval = platform_device_register(&gpio_dev);
for (i = 0; i & ARRAY_SIZE(mt_device_i2c); i++){
retval = platform_device_register(&mt_device_i2c[i]);
printk("[%s]: mt_device_i2c[%d] finished probe, retval=%d\n", __func__, i, retval);
if (retval != 0){
.............
late_initcall(mt_board_init);
  platform_device_register 会调用device_add,这样就会走一个流程,即总线上新加一个设备,总线就会把这个总线上的所有驱动与这个设备匹配,作为i2c总线这种平台设备,与其匹配的是i2c.c中的驱动相匹配,最终调用mt_i2c_probe这个平台函数.这个就是i2c总线的一部分,做出把所有设备添加,并把所有驱动与每个设备匹配,这种惊天动地的事,都是这个函数做出来的.在做这些事之前,mt_i2c_probe做如下事情:
&&&&1. 申请内存资源,& 中断资源, i2c总线作为一个设备, 是应该有对应的地址, 所以分配点内存资源给它.
&& &2. i2c中断响应函数做的事: 清除中断掩码,& 保存中断状态, 清除中断状态.
&&& 3. i2c有很多寄存器, 如控制寄存器, 从地址寄存器, 往这些寄存器写数据, 以完成相应的功能, 都需要i2c设备驱动来完成.& i2c硬件初始化就要对软件复位寄存器写1.&
  4. 设备i2c适配器的数据来源,& 适配器是一个设备, 该设备的数据来源就是i2c总线.
 & 5. 添加i2c适配器,&& 这一步有两个事情, 一个是把adapter添加到基树中, 一个注册i2c适配器.&&&注册i2c适配器.& 这个注册函数会把所有设备驱动初始化时所添加到i2c设备信息链表里的所有节点都遍历一遍,& 对每个节点, 都调用i2c_new_device, 建立一个i2c设备,&随之建立一个i2c设备模型。每个挂到i2c总线上的设备都有自己的i2c设备模型。 &这是注册i2c adapter非常重要的一步,& 经过这一步, 挂在i2c总线上的设备才算是i2c设备, 即可以进行i2c通讯的设备。
static S32 mt_i2c_probe(struct platform_device *pdev)
= platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!request_mem_region(res-&start, resource_size(res), pdev-&name)) {
return -ENOMEM;
if (NULL == (i2c = kzalloc(sizeof(mt_i2c), GFP_KERNEL)))
return -ENOMEM;
#ifdef CONFIG_OF
i2c-&adap.dev.of_node
= pdev-&dev.of_
i2c-&adap.nr
i2c-&adap.owner
= THIS_MODULE;
i2c-&adap.algo
= &mt_i2c_
i2c-&adap.algo_data
i2c-&adap.timeout
= 2 * HZ; /*2s*/
i2c-&adap.retries
= 1; /*DO NOT TRY*/
/*need GFP_DMA32 flag to confirm DMA alloc PA is 32bit range*/
i2c-&dma_buf.vaddr =
dma_alloc_coherent(&pdev-&dev, MAX_DMA_TRANS_NUM,
&i2c-&dma_buf.paddr, GFP_KERNEL|GFP_DMA32);
memset(i2c-&dma_buf.vaddr, 0, MAX_DMA_TRANS_NUM);
snprintf(i2c-&adap.name, sizeof(i2c-&adap.name), I2C_DRV_NAME);
init_waitqueue_head(&i2c-&wait);
ret = request_irq(irq, mt_i2c_irq, IRQF_TRIGGER_LOW, I2C_DRV_NAME, i2c);
mt_i2c_init_hw(i2c);
i2c_set_adapdata(&i2c-&adap, i2c);
printk("lct1: %s, register adapter. \n", __func__);
ret = i2c_add_numbered_adapter(&i2c-&adap);
&------- 先把设备添加好,再添加驱动的例子  ------
  i2c总线这种都是在把所有驱动加载好,再添加设备的.但也有出现反过来的.比如下面的例子,就是把设备添加好,然后再添加驱动.如果一个设备不需要挂在实际真实的总线上,就要把它挂载到平台总线,平台总线没有实际的总线.
像红外控制,就是gpio口,发送编码后的pwm波.所以就直接挂到平台总线上.代码如下:
代码位置: kernel-3.0/drivers/misc/mediatek/consumerir/consumerir.c
static struct platform_driver mt_consumerir_driver =
.name = "consumerir",
.probe = consumerir_probe,
static struct platform_device mt_consumerir_dev =
.name = "consumerir",
static int __init consumerir_init(void)
int ret = 0;
if(platform_device_register(&mt_consumerir_dev) != 0)
return -ENODEV;
ret = platform_driver_register(&mt_consumerir_driver);
return -ENODEV;
  由此明显看出,设备中的name与驱动中name必须一致.这样总线在为这个设备遍历各个驱动时,才能和这个驱动匹配上.如两个玉佩,到时见面,就互相认识了.之前在i2c总线,驱动,设备时说过,设备在所有驱动添加完再添加.device_regisgter会调用bus_for_each_drv来遍历所有驱动.但在driver_register也会调用bus_for_each_div遍历所有设备. 所以遍历是双向的.在这个例子,用了第二种.
---------------------------------&&先在总线上添加设备,之后在总线上注册驱动&&-------------------------
  先说平台设备的添加,与i2c设备的添加。平台设备是平台总线设备的简称,i2c设备是i2c总线设备的简称。这里的设备以其所在的总线命名。平台设备类型为platform_device, i2c设备类型为i2c_client, 两个类型都有device这个成员。
  这里所说的设备并不实际意义的设备,只是站在某一个角度对设备的描述。
  其实对实际的设备说,就是给它下寄存器,它就能工作了,放在总线上,就是为了利用总线,设备向总芯片发命令,设备输出数据传给主芯片,所以调试初期,先看总线通没通,一般是i2c总线。实际的设备也是挂在总线上,从代码上,要有一种统一的管理,这种管理架构就要与实际尽量吻合,所以软件架构中出现的平台设备,i2c设备都是以总线的角度对设备的描述。
  所谓描述就是建立一个模型吧,通过对这个模型的操作来达到对实际设备的操作, 如解决一个物理问题时,要有各种变量,模型反映出各变量之间的联系,利用模型中的信息求出未知变量。
  在驱动注册到总线之前,必须保证总线上的所有设备都添加完。 所以在总线上添加设备的动作都是在驱动注册之前完成。
&  在板级初始化时,在平台总线上注册平台设备,即在平台总线上添加设备。 这个阶段会把所有设备都添加到平台总线上。 这个阶段完成后,在/sys/devices/platform/ 就会很多目录,每个目录描述一个平台设备。
  在具体模块驱动init时,先在i2c总线上添加一个设备,这个设备就是i2c设备,实现是通过一个函数向i2c信息链表中添加一个节点, 在稍后的i2c总线驱动模块的平台驱动的probe, 会调用i2c_register_adapter, 把i2c信息链表中的每个节点都建立对应的i2c设备,即i2c_client对象。
----------------------------&& 在总线上注册驱动时,遍历总线上的所有设备 ------------------------
  注册驱动的机制是,这个驱动会遍历平台总线上的所有设备,有些书本会写, 探测总线上的所有设备,给人感觉好像硬件探测一样,其实这里完全是软件上的一种遍历,就是驱动的名字如果与设备的名字如果相同,那么驱动就和该设备匹配上。 这时具体驱动模块的平台驱动probe被调用。
  这个平台驱动的probe一般只会在i2c总线上注册一个驱动(如果是camera,应该是在mipi总线上注册一个驱动),并没有做其他具体的事情。 具体事情由i2c驱动的probe完成,包括建立一个driver的设备模型, 对设备下寄存器,注册中断,注册输入子系统,打开sensor供电。
  举个例子,gsensor, 板级初始化时, 注册平台设备, 代码:&static struct platform_device sensor_gsensor = {&&&& .name&&&&&&&&&& = "gsensor",&&&& .id&&&&&&&&&&&& = -1,& };注册平台设备  platform_device_register(&sensor_gsensor);
  bosch的加速度驱动文件bma056.c中,init函数中, 在平台总线上注册一个平台驱动,static struct platform_driver gsensor_platform_driver = {.probe = platfrom_probe,};platform_driver_register(&gsensor_platform_driver);  平台驱动的probe会在i2c总线上注册一个i2c驱动, 即  i2c_add_driver(&i2c_driver);
  既然在平台总线上注册驱动之前,平台总线上就已经添加好了设备。 那么在i2c总线上注册驱动之前,i2c总线上是否也已经添加好的设备。   这个问题的答案在, 在添加i2c_board_info时, 设备的名字, 与设备的地址传给i2c的一个链表,i2c初始化时,对链表中的每一个建立一个i2c设备。 这个设备会传给i2c驱动的probe。
  看用了哪个驱动,可以到设备模型/sys/bus/i2c/drivers下有没有这个驱动的名字。
  如果平台设备的名字与平台驱动的名字不一样,就驱动就不会probe.& 之前在做bosch与st兼容时,就遇到这个问题。
------------------------------ i2c 设备参数 -----------------------
------------------设备的参数数据, 以加速度驱动为例.&通过i2c总线从设备读过来的数据都是原始数据, 这些数据在驱动要做一些转换,转换的参数如灵敏度,坐标系映射, 校准参数。 这些参数对这个设备是必须的,这些设备参数就要跟设备相关联,即拿到了设备,这个设备就带着这个设备的参数。有专门的函数设置设备参数,即dev_set_drvdata(). 对于i2c总线上的设备, 即注册到i2c总线上,这个设备就是一个i2c设备,即i2c_client. 有专门的函数设置设备的参数数据, 即i2c_set_clientdata(client, data).&这个函数封装了dev_set_drvdata().&
每个sensor驱动都有设备参数结构体类型,因为是i2c设备,所以类型的名字都是...i2c_& 在从i2c总线读取sensor数据时,一般都要i2c_get_clientdata(client)先获取设备参数。
  在哪创建设备节点,要看传给创建设备节点的kobject是谁,可以是driver, 也可以是device. &
6752的sensor驱动换了传入参数,而我还在原来的地方找设备节点,就当然找不到了。
-------------------------- &i2c_probe 函数栈 --------------------
驱动中有空指针错误, 串口log就有oops。 如果在i2c_probe有空指针, 就有如下函数栈:&
[ 4.082399].(5)[1:swapper/0][&ffffffc&] ltr559_i2c_probe+0x410/0x8ac[
4.083295].(5)[1:swapper/0][&ffffffc&] i2c_device_probe+0xac/0x108[
4.084201].(5)[1:swapper/0][&ffffffc&] really_probe+0x80/0x2e8[
4.085040].(5)[1:swapper/0][&ffffffcc&] __device_attach+0x5c/0x6c[
4.085900].(5)[1:swapper/0][&ffffffc&] bus_for_each_drv+0x50/0x94[
4.086770].(5)[1:swapper/0][&ffffffc&] device_attach+0xa0/0xc4[
4.087607].(5)[1:swapper/0][&ffffffc&] bus_probe_device+0x90/0xb8[
4.088476].(5)[1:swapper/0][&ffffffcc&] device_add+0x4b0/0x564[
4.089301].(5)[1:swapper/0][&ffffffc&] device_register+0x18/0x28[
4.090158].(5)[1:swapper/0][&ffffffc00074ebb8&] i2c_new_device+0x154/0x184[
4.091027].(5)[1:swapper/0][&ffffffc00074ef78&] i2c_register_adapter+0x1fc/0x30c[
4.091961].(5)[1:swapper/0][&ffffffc&] __i2c_add_numbered_adapter+0x58/0x88[
4.092939].(5)[1:swapper/0][&ffffffc&] i2c_add_numbered_adapter+0x14/0x2c[
4.093920].(5)[1:swapper/0][&ffffffc&] mt_i2c_probe+0x348/0x45c[
4.094771].(5)[1:swapper/0][&ffffffc&] platform_drv_probe+0x18/0x24[
4.095663].(5)[1:swapper/0][&ffffffc&] really_probe+0x16c/0x2e8[
4.096512].(5)[1:swapper/0][&ffffffc&] __driver_attach+0xa8/0xb0[
4.097370].(5)[1:swapper/0][&ffffffc&] bus_for_each_dev+0x54/0x98[
4.098240].(5)[1:swapper/0][&ffffffc&] driver_attach+0x1c/0x28[
4.099078].(5)[1:swapper/0][&ffffffcc&] bus_add_driver+0x1bc/0x258[
4.099948].(5)[1:swapper/0][&ffffffc&] driver_register+0x68/0x15c[
4.100821].(5)[1:swapper/0][&ffffffc&] platform_driver_register+0x58/0x64[
4.101782].(5)[1:swapper/0][&ffffffc000e11c84&] mt_i2c_init+0x6c/0x78[
4.102601].(5)[1:swapper/0][&ffffffc000de583c&] do_one_initcall+0xa0/0x1b4[
4.103473].(5)[1:swapper/0][&ffffffc000de5a8c&] kernel_init_freeable+0x13c/0x1e0[
4.104433].(5)[1:swapper/0][&ffffffc&] kernel_init+0x14/0x14c[
4.105257]-(5)[1:swapper/0]Internal error: Oops:
[#1] PREEMPT SMP
------------------ &i2c_xxx_probe在i2c总线init调用的平台probe里被遍历 ------------------
mt的i2c总线驱动也做为一种普通的驱动, 放在/kernel-3.0/dirver/misc/medietek/i2c/mt6795下, 文件名为i2c.c这个驱动的模块初始化也一般具体驱动的初始化, 都是调用平台注册函数, platform_register. 不同是, 一般具体驱动平台probe函数里, 只调用i2c_add_driver. 即设置好本驱
动的i2c的回调函数, 而i2c.c里的平台probe则会完成此i2c总线上各个驱动的i2c_xxX_probe的调用, 如光感的ltr559_i2c_probe, 地磁的yas_i2c_probe, 加速度的
bmi_acc_i2c_probe都会在第2条i2c总线的平台probe调用. 其调用栈为: [ 4.082399].(5)[1:swapper/0][&ffffffc&] ltr559_i2c_probe+0x410/0x8ac[ 4.083295].(5)[1:swapper/0][&ffffffc&] i2c_device_probe+0xac/0x108[ 4.084201].(5)[1:swapper/0][&ffffffc&] really_probe+0x80/0x2e8[ 4.085040].(5)[1:swapper/0][&ffffffcc&] __device_attach+0x5c/0x6c[ 4.085900].(5)[1:swapper/0][&ffffffc&] bus_for_each_drv+0x50/0x94[ 4.086770].(5)[1:swapper/0][&ffffffc&] device_attach+0xa0/0xc4[ 4.087607].(5)[1:swapper/0][&ffffffc&] bus_probe_device+0x90/0xb8[ 4.088476].(5)[1:swapper/0][&ffffffcc&] device_add+0x4b0/0x564[ 4.089301].(5)[1:swapper/0][&ffffffc&] device_register+0x18/0x28[ 4.090158].(5)[1:swapper/0][&ffffffc00074ebb8&] i2c_new_device+0x154/0x184[ 4.091027].(5)[1:swapper/0][&ffffffc00074ef78&] i2c_register_adapter+0x1fc/0x30c[ 4.091961].(5)[1:swapper/0][&ffffffc&] __i2c_add_numbered_adapter+0x58/0x88[ 4.092939].(5)[1:swapper/0][&ffffffc&] i2c_add_numbered_adapter+0x14/0x2c[ 4.093920].(5)[1:swapper/0][&ffffffc&] mt_i2c_probe+0x348/0x45c这个在ltr559_i2c_probe埋个空指针错误, 开机kernel_log就会有这个调用栈. 各驱动的probe调用顺序, 应该是同时的, 因为只有具体驱动的probe在调i2c_add_driver把本驱动的i2c_probe等回调设置好后, i2c.c的平台probe才能把他遍历到.
从上面的函数栈来看, i2c注册适配器时, 会把挂在这条i2c总线上的每个设备都建立一个i2c设备对象, 即i2c_new_device, 就是把这个设备变成一个i2c设备. 具体驱动的init时,
的i2c_register_board_info向i2c链表里添加的这个设备的i2c信息(包括i2c第几条线, i2c地址)就会传给这个i2c_new_device函数.
这个变化需要做两件事, 一是要准备一个i2c
client. 一个是要调用到这个设备的i2c_xxx_probe函数, 并且把准备好的i2c client传给这个函数.
从软件的角度, 完成这些才算是把这个设备纳入到这个总线的管理下.
软件的初始化, 就好像建立一个模型, 里面有各种变量的初值, 以及回调函数的有效设置.
mtklod内含mobile log, modem log. mobilelog又包括kernel log, main log, 这些都有开关设置的, 有一个开关控制开机时自启的log, 这个log在分析重启时是必须的.}

我要回帖

更多关于 升降机设备使用记录表 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信