LinuxI2C总线驱动深入分析

                LinuxI2C总线驱动深入分析

Kernel版本:2.6.32

平台: mips

 

 

本文目的: 在工作闲暇之余,写点东西,留个念想,也证明一下自己曾经年轻过,主要的内容以I2C总线注册,设备注册,驱动注册为主线,介绍了详细的注册过程及I2C传输数据的过程。欢迎大家来探讨这些技术细节,mall:http://blog.csdn.net/dyron

 

I2C 总线及device注册过程

首先,I2C注册过程为创建board_info 结构体。

 

static struct i2c_board_info i2c1_devs[]__initdata = {
         {
                   I2C_BOARD_INFO("lm75",0x48),
         },
};
 


通过i2c_register_board_info, 将i2c_board_info设备结构注册进系统中去,

 

         i2c_register_board_info(1, i2c1_devs, ARRAY_SIZE(i2c1_devs));

 

         i2c_register_board_info 传入的参数,busnum为总线地址,info为总线上设备描述,len为总线描述结构的大小。

int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info,unsigned len)
 
                  if (busnum >=__i2c_first_dynamic_bus_num)
                   __i2c_first_dynamic_bus_num= busnum + 1;
 
         for(status = 0; len; len--, info++) {
                   structi2c_devinfo    *devinfo;
 
                   devinfo= kzalloc(sizeof(*devinfo), GFP_KERNEL);
                   if(!devinfo) {
                            pr_debug("i2c-core:can't register boardinfo!\n");
                            status= -ENOMEM;
                            break;
                   }
 
                   devinfo->busnum= busnum;
                   devinfo->board_info= *info;
                   list_add_tail(&devinfo->list,&__i2c_board_list);
         }

 

为每一个I2C设备分配i2c_devinfo结构体, 并注册到__i2c_board_list链表上,这里需要注意的一点是,设备列表上并没有出现按总线区分的现象,而是将所有总线的设备注册到一个统一的列表上。

 

经过以上这几步,I2C的board_info设备就注册好了,现在分析设备的adapter注册过程,一般设备的总线注册是由SOC厂商提供的,在SOC厂商提供的busses注册时,会遍历设备结点,下面具体分析。

         一般在设备注册的probe中,会调用i2c_add_numbered_adapter或者i2c_add_adapter的接口,注册I2C的adapter, 其中就初始化了I2C总线中的设备, 以分析i2c_add_numbered_adapter为例。

i2c_add_numbered_adapter使用静态的总线ID, i2c_add_adapter使用动态的总线ID。

 

inti2c_add_numbered_adapter(struct i2c_adapter *adap)
{
………
         if (status == 0)
                   status =i2c_register_adapter(adap);
         return status;
}

调用i2c_register_adapter来注册传入的adapter.

 

static inti2c_register_adapter(struct i2c_adapter *adap)
{
         ………
         dev_set_name(&adap->dev,"i2c-%d", adap->nr);
         adap->dev.bus = &i2c_bus_type;
         adap->dev.type =&i2c_adapter_type;
         res = device_register(&adap->dev);
         if (res)
                   goto out_list;
         ……………..
         res =class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
                                            adap->dev.parent);
         if (res)
                   dev_warn(&adap->dev,
                             "Failed to create compatibility classlink\n");
         if (adap->nr <__i2c_first_dynamic_bus_num)
                   i2c_scan_static_board_info(adap);
}
 
static voidi2c_scan_static_board_info(struct i2c_adapter *adapter)
{
         struct i2c_devinfo    *devinfo;
 
         down_read(&__i2c_board_lock);
         list_for_each_entry(devinfo,&__i2c_board_list, list) {
                   if (devinfo->busnum ==adapter->nr
                                     &&!i2c_new_device(adapter,
                                                        &devinfo->board_info))
                            dev_err(&adapter->dev,
                                     "Can'tcreate device at 0x%02x\n",
                                     devinfo->board_info.addr);
         }
         up_read(&__i2c_board_lock);
}

 

首先,生成bus-device, 设备device的总线ID与adapter类型, 并接着创建class接口, 如果是静态创建的总线设备,就在已注册的I2C设备链表(__i2c_board_list)中进行查找匹配,并通过查找相同的总线ID,找到相匹配的设备ID,生成client端并注册device到系统中去。

 

以上就是整个I2C总线及总线上的device注册过程。

 

I2C driver注册过程

I2C驱动注册是通过i2c_add_driver接口进行的, i2c_add_driver调用i2c_register_driver, 传入i2c_driver结构。

 

int i2c_register_driver(struct module *owner, struct i2c_driver*driver)
{
         int res;
 
         /* Can't registeruntil after driver model init */
         if(unlikely(WARN_ON(!i2c_bus_type.p)))
                   return-EAGAIN;
 
         /* add the driver tothe list of i2c drivers in the driver core */
         driver->driver.owner= owner;
         driver->driver.bus= &i2c_bus_type;
 
         /* When registrationreturns, the driver core
          * will have called probe() for allmatching-but-unbound devices.
          */
         res =driver_register(&driver->driver);
         if (res)
                   return res;
 
         pr_debug("i2c-core:driver [%s] registered\n", driver->driver.name);
 
         INIT_LIST_HEAD(&driver->clients);
         /* Walk the adaptersthat are already present */
         mutex_lock(&core_lock);
         bus_for_each_dev(&i2c_bus_type,NULL, driver, __attach_adapter);
         mutex_unlock(&core_lock);
 
         return 0;
}

 

由分析i2c_register_driver中得知, driver的注册相对简单, 在函数中填充总线的类型,然后通过driver_register注册进了系统。

 

通过bus_for_each_dev(&i2c_bus_type,NULL, driver, __attach_adapter);来attach adapter, 相对的,如果先注册驱动程序,也会有attach driver的过程。在这里分析device attach driver的过程,相对attach adapter比较复杂。

 

接着回到i2c_register_adapter中的bus_for_each_drv中去,查找机应的driver。

 

intbus_for_each_drv(struct bus_type *bus, struct device_driver *start,
                        void *data, int (*fn)(struct device_driver*, void *))
{
         struct klist_iter i;
         struct device_driver *drv;
         int error = 0;
 
         if (!bus)
                   return -EINVAL;
 
         klist_iter_init_node(&bus->p->klist_drivers,&i,
                                 start ? &start->p->knode_bus :NULL);
         while ((drv = next_driver(&i))&& !error)
                   error = fn(drv, data);
         klist_iter_exit(&i);
         return error;
}
 

在bus_for_each_drv中,通过回调fn,进行drv和data的匹配, data就是我们的adapter, fn是传入的i2c_do_add_adapter。

static int i2c_do_add_adapter(structdevice_driver *d, void *data)
{
         structi2c_driver *driver = to_i2c_driver(d);
         structi2c_adapter *adap = data;
 
         /*Detect supported devices on that bus, and instantiate them */
         i2c_detect(adap,driver);
 
         /*Let legacy drivers scan this bus for matching devices */
         if(driver->attach_adapter) {
                   /*We ignore the return code; if it fails, too bad */
                   driver->attach_adapter(adap);
         }
         return0;
}

 

在i2c_detect中,首先通过i2c_adapter_id(adapter);获得当前adapter的总线id,

在I2C_DETECT中, structi2c_client *temp_client 首先,检测driver 是否存在address_data数据,如果不存在直接返回。存在的现象欢迎来一起讨论,http://blog.csdn.net/dyron, 主要是一些i2c设备的自动探测功能,目前kernel都采用静态注册在i2c_board_info中了.

 

         address_data= driver->address_data;
         if(!driver->detect || !address_data)
                   return0;
 
 
staticint 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)
                   returni2c_match_id(driver->id_table, client) != NULL;
 
         return 0;
}

         至此i2c_driver就与i2c_adapter匹配成功。 I2c 的bus match就会调用驱动程序中的probe函数。

 

      I2C的数据传输分析

         I2c 的数据传输接口主要有 i2c_smbus_read_byte_data, i2c_smbus_write_byte, i2c_smbus_read_word_data, i2c_master_send, i2c_master_recv等等接口,各传入的参数不同,但最终调用的只有两种接口,一种是i2c_smbus_xfer, 一种是i2c_transfer.

 

         这主要的区分在于smbus与i2c, SMBus与I2C的比较.

SMBus 

I2C 


最大传输速度 100kHz

最大传输速度400kHz 


最小传输速度 10kHz 

无最小传输速度 


35ms时钟低超时 

无时钟超时 


固定的逻辑电平 

逻辑电平由VDD决定 


不同的地址类型(保留、动态等) 7位、 
10位和广播呼叫从地址类型 


不同的总线协议(快速命令、 
处理呼叫等) 无总线协议 


 

         暂时更新到这里,后续内容以后再做更新。

<think>好的,我现在需要帮助用户了解MAX96711芯片上I2C总线的使用方法。用户要求教程、配置和示例。首先,我得回忆一下MAX96711的I2C相关功能,可能包括寄存器配置、通信协议等。 首先,我应该查阅MAX96711的数据手册,确认其I2C接口的基本参数,比如从机地址、寄存器结构、通信速率等。根据引用内容,用户提到其他I2C设备的配置方法,比如引用1中的i2c_board_info结构和i2c_new_device函数,这可能适用于Linux环境下的驱动开发。而引用2和3则涉及STM32和RT-Thread的I2C实现,这可能为用户提供不同平台的参考。 接下来,我需要整理MAX96711的I2C配置步骤。通常,使用I2C需要初始化总线,设置从机地址,然后读写寄存器。例如,初始化I2C控制器,配置时钟频率;然后通过发送从机地址和寄存器地址来访问特定寄存器,设置参数如视频模式、数据速率等。 还要考虑用户可能的开发环境,比如嵌入式Linux、MCU(如STM32)或其他RTOS(如RT-Thread)。不同的平台有不同的API,如Linuxi2c-dev接口,STM32的HAL库函数,RT-Thread的rt_i2c_master_send/recv等。需要给出通用步骤,并可能提供不同平台的示例代码片段。 此外,用户可能需要了解如何解决常见的I2C通信问题,比如地址错误、时钟速度不匹配、信号完整性问题等。可以建议使用逻辑分析仪或示波器检查信号波形,确认从机地址是否正确,以及上拉电阻是否合适。 最后,生成相关问题,帮助用户深入理解,比如如何调试I2C通信、多设备管理、硬件设计注意事项等。确保回答结构清晰,分步骤说明,并引用提供的参考资料中的相关内容,比如引用1中的实例化I2C设备的方法,引用3中的接收数据函数。</think>### MAX96711芯片I2C总线使用指南 #### 一、硬件配置要点 1. **物理连接** - SCL/SDA线需接4.7kΩ上拉电阻,建议走线长度<30cm - 电源滤波:在VCC与GND之间并联100nF+10μF电容 - 典型从机地址:$0x40$(可通过ADDR引脚调整为$0x42$) 2. **初始化时序** ```c // STM32 HAL库示例 I2C_HandleTypeDef hi2c1; hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 400000; // 400kHz标准模式 hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; HAL_I2C_Init(&hi2c1); ``` 引用STM32初始化方法[^2] #### 二、寄存器配置流程 1. **视频通道设置** ```python # Linux i2c-tools示例 import smbus bus = smbus.SMBus(1) # 使用I2C总线1 DEV_ADDR = 0x40 # 配置视频输出格式为1080P60 bus.write_byte_data(DEV_ADDR, 0x0D, 0x1A) # 寄存器地址0x0D ``` 引用Linux设备实例化方法[^1] 2. **数据包配置** ```c /* RT-Thread示例 */ struct rt_i2c_msg msgs[2]; uint8_t reg_addr = 0x12; // 目标寄存器 uint8_t config_data = 0xB5; msgs[0].addr = 0x40; msgs[0].flags = RT_I2C_WR; msgs[0].buf = &reg_addr; msgs[0].len = 1; msgs[1].addr = 0x40; msgs[1].flags = RT_I2C_WR | RT_I2C_NO_START; msgs[1].buf = &config_data; msgs[1].len = 1; rt_i2c_transfer(bus, msgs, 2); ``` 引用RT-Thread传输方法[^3] #### 三、关键寄存器说明 1. **0x02h 模式控制寄存器** $$ \text{Bit7-6} = \begin{cases} 00 & \text{正常模式} \\ 01 & \text{待机模式} \\ 10 & \text{休眠模式} \end{cases} $$ 2. **0x1Fh 数据速率设置** $$ \text{OutputRate} = \frac{\text{SYS_CLK}}{N+1} \quad (N=0x00\text{-}0xFF) $$ #### 四、调试技巧 1. 使用`i2cdetect -y 1`扫描设备(Linux2. 示波器测量SCL/SDA波形,确保上升时间<300ns 3. 异常处理流程: ``` 检查电源 -> 验证地址 -> 测试ACK信号 -> 检查上拉电阻 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值