【驱动】06.IIC子系统

【嵌入式八股】一、语言篇https://www.nowcoder.com/creation/manager/columnDetail/mwQPeM

【嵌入式八股】二、计算机基础篇https://www.nowcoder.com/creation/manager/columnDetail/Mg5Lym

【嵌入式八股】三、硬件篇https://www.nowcoder.com/creation/manager/columnDetail/MRVDlM

【嵌入式八股】四、嵌入式Linux篇(本专栏)https://www.nowcoder.com/creation/manager/columnDetail/MQ2bb0

IIC子系统

硬件原理

IIC协议

看【硬件篇-接口协议】部分IIC相关

SMBUS协议

SMBus: System Management Bus,系统管理总线。 SMBus是基于I2C协议的,SMBus要求更严格,SMBus是I2C协议的子集。

应用编程

I2C-Tools的访问I2C设备的2种方式

官方留的应用层直接控制的接口

alt

I2C-Tools可以通过SMBus来访问I2C设备,也可以使用一般的I2C协议来访问I2C设备。 使用一句话概括I2C传输:APP通过I2C Controller与I2C Device传输数据。 在APP里,有这几个问题:

  • 怎么指定I2C控制器?
    • i2c-dev.c提供为每个I2C控制器(I2C Bus、I2C Adapter)都生成一个设备节点:/dev/i2c-0、/dev/i2c-1等待
    • open某个/dev/i2c-X节点,就是去访问该I2C控制器下的设备
  • 怎么指定I2C设备?
    • 通过ioctl指定I2C设备的地址
    • ioctl(file, I2C_SLAVE, address)
      • 如果该设备已经有了对应的设备驱动程序,则返回失败
    • ioctl(file, I2C_SLAVE_FORCE, address)
      • 如果该设备已经有了对应的设备驱动程序
      • 但是还是想通过i2c-dev驱动来访问它
      • 则使用这个ioctl来指定I2C设备地址
  • 怎么传输数据?
    • 两种方式
    • 一般的I2C方式:ioctl(file, I2C_RDWR, &rdwr)
    • SMBus方式:ioctl(file, I2C_SMBUS, &args)

alt alt

使用GPIO模拟I2C的驱动程序

内核驱动

通用驱动i2c-dev分析

看看官方留的应用层直接控制的接口对应的驱动

i2c-dev.c注册过程分析

就是字符设备那一套

alt

alt

file_operations函数分析

alt

alt

i2cdev_ioctl: I2C_SLAVE/I2C_SLAVE_FORCE

用于传地址

alt

i2cdev_ioctl: I2C_RDWR

读写方式1

alt

i2cdev_ioctl: I2C_SMBUS

读写方式2

alt

编写设备驱动之i2c_driver

alt

编写设备驱动之i2c_client

问题:Linux IIC子系统设备树中没有写那个IIC adapter,驱动怎么对应上呢:question:

四种方法

1.在用户态生成

示例:

// 在I2C BUS0下创建i2c_client
# echo ap3216c 0x1e > /sys/bus/i2c/devices/i2c-0/new_device

// 删除i2c_client
# echo 0x1e > /sys/bus/i2c/devices/i2c-0/delete_device

2.编写代码

  • i2c_new_device:会创建i2c_client,即使该设备并不存在

      static struct i2c_board_info sfe4001_hwmon_info = {
    	  I2C_BOARD_INFO("max6647", 0x4e),
      };
    
      int sfe4001_init(struct efx_nic *efx)
      {
          (...)
          efx->board_info.hwmon_client =i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);
          (...)
      }
    
  • i2c_new_probed_device

    • 它成功的话,会创建i2c_client,并且表示这个设备肯定存在

    • I2C设备的地址可能发生变化,比如AT24C02的引脚A2A1A0电平不一样时,设备地址就不一样

    • 可以罗列出可能的地址

    • i2c_new_probed_device使用这些地址判断设备是否存在

      static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
    
      static int usb_hcd_nxp_probe(struct platform_device *pdev)
      {
    	(...)
    	struct i2c_adapter *i2c_adap;
    	struct i2c_board_info i2c_info;
    
    	(...)
    	i2c_adap = i2c_get_adapter(2);
    	memset(&i2c_info, 0, sizeof(struct i2c_board_info));
    	strscpy(i2c_info.type, "isp1301_nxp", sizeof(i2c_info.type));
    	isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,
    						   normal_i2c, NULL);
    	i2c_put_adapter(i2c_adap);
    	(...)
      }
    
  • i2c_register_board_info

    • 内核没有EXPORT_SYMBOL(i2c_register_board_info)
    • 使用这个函数的驱动必须编进内核里去

3.使用设备树生成

在某个I2C控制器的节点下,添加如下代码:

		ap3216c@1e {
			compatible = "lite-on,ap3216c";
			reg = <0x1e>;
		};

**4.(不推荐):由i2c_driver.detect函数来判断是否有对应的I2C设备并生成i2c_client **

I2C_Adapter驱动框架讲解与编写

它知道怎么发数据,不知道要发什么数据。要发的数据通过i2c_driver经ic_core过来

关心第几条IIC总线nr,IIC 算法i2c_algorithm,master_xfer函数

一般来说芯片厂商都写好了

alt

alt

master_xfer:这是最重要的函数,它实现了一般的I2C传输,用来传输一个或多个i2c_msg

编写i2c_adapter驱动程序

设备树

在设备树里构造I2C Bus节点:

	i2c-bus-virtual {
		 compatible = "100ask,i2c-bus-virtual";
	};

platform_driver

分配、设置、注册platform_driver结构体。

核心是probe函数,它要做这几件事:

  • 根据设备树信息设置硬件(引脚、时钟等)
  • 分配、设置、注册i2c_apdater

i2c_apdater

i2c_apdater核心是master_xfer函数,它的实现取决于硬件,大概代码如下:

static int xxx_master_xfer(struct i2c_adapter *adapter,
						struct i2c_msg *msgs, int num)
{
    for (i = 0; i < num; i++) {
        struct i2c_msg *msg = msgs[i];
        {
        	// 1. 发出S信号: 设置寄存器发出S信号
            CTLREG = S;
            
            // 2. 根据Flag发出设备地址和R/W位: 把这8位数据写入某个DATAREG即可发出信号
            //    判断是否有ACK
            
            if (!ACK)
                return ERROR;
            else {
	            // 3. read / write
	            if (read) {
                    STATUS = XXX; // 这决定读到一个数据后是否发出ACK给对方
                    val = DATAREG; // 这会发起I2C读操作
                } else if(write) {
                    DATAREG = val; // 这会发起I2C写操作
                    val = STATUS;  // 判断是否收到ACK
                    if (!ACK)
                        return ERROR;
                }                
            }
            // 4. 发出P信号
            CTLREG = P;
        }
    }    
    return i;
}

具体芯片的I2C_Adapter驱动分析

总体框架

I2C驱动框架

对 IIC 总线的理解、调用函数以及常见面试问题 - AlanTu - 博客园 (cnblogs.com)

06-IIC子系统开发之设备驱动框架层功能分析_哔哩哔哩_bilibili

Linux定义了系统的IIC驱动体系结构,在Linux系统中,IIC驱动由3部分组成,即IIC核心IIC总线驱动IIC设备驱动。这3部分相互协作,形成了非常通用、可适应性很强的IIC框架。

alt

alt

alt

alt

I2C Core就是I2C核心层,它的作用:

  • 提供统一的访问函数,比如i2c_transfer、i2c_smbus_xfer等
  • 实现I2C总线-设备-驱动模型,管理:I2C设备(i2c_client)、I2C设备驱动(i2c_driver)、I2C控制器(i2c_adapter)

IIC子系统的重要结构体

先有个整体框架

APP

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

【嵌入式八股】嵌入式Linux 文章被收录于专栏

查阅整理上千份嵌入式面经,将相关资料汇集于此,主要包括: 0.简历面试 1.语言篇 2.计算机基础 3.硬件篇 4.嵌入式Linux【本专栏】 (建议PC端查看)

全部评论

相关推荐

10-25 00:32
香梨想要offer:感觉考研以后好好学 后面能乱杀,目前这简历有点难
点赞 评论 收藏
分享
评论
点赞
5
分享
牛客网
牛客企业服务