0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux內(nèi)核中描述I2C的四個(gè)核心結(jié)構(gòu)體

嵌入式開發(fā)愛好者 ? 來源:嵌入式開發(fā)愛好者 ? 2023-09-04 09:35 ? 次閱讀

第一:LinuxI2C驅(qū)動(dòng)框架分析

45107e60-4a73-11ee-97a6-92fbcf53809c.png

I2C核心(i2c_core)

I2C核心維護(hù)了i2c_bus結(jié)構(gòu)體,提供了I2C總線驅(qū)動(dòng)設(shè)備驅(qū)動(dòng)的注冊、注銷方法,維護(hù)了I2C總線的驅(qū)動(dòng)、設(shè)備鏈表,實(shí)現(xiàn)了設(shè)備、驅(qū)動(dòng)的匹配探測。此部分代碼由Linux內(nèi)核提供。

I2C總線驅(qū)動(dòng)

I2C總線驅(qū)動(dòng)維護(hù)了I2C適配器數(shù)據(jù)結(jié)構(gòu)(i2c_adapter)和適配器的通信方法數(shù)據(jù)結(jié)構(gòu)(i2c_algorithm)。所以I2C總線驅(qū)動(dòng)可控制I2C適配器產(chǎn)生start、stop、ACK等。此部分代碼由具體的芯片廠商提供,比如Samsung、高通

I2C設(shè)備驅(qū)動(dòng)

I2C設(shè)備驅(qū)動(dòng)主要維護(hù)兩個(gè)結(jié)構(gòu)體:i2c_driver和i2c_client,實(shí)現(xiàn)和用戶交互的文件操作集合fops、cdev等。此部分代碼就是驅(qū)動(dòng)開發(fā)者需要完成的。

第二:Linux內(nèi)核中描述I2C的四個(gè)核心結(jié)構(gòu)體

1)i2c_client—掛在I2C總線上的I2C從設(shè)備

每一個(gè)i2c從設(shè)備都需要用一個(gè)i2c_client結(jié)構(gòu)體來描述,i2c_client對應(yīng)真實(shí)的i2c物理設(shè)備device。

struct i2c_client { 
    unsigned short flags;     //標(biāo)志位 (讀寫) 
  unsigned short addr;      //7位的設(shè)備地址(低7位) 
  char name[I2C_NAME_SIZE]; //設(shè)備的名字,用來和i2c_driver匹配 
  struct i2c_adapter *adapter; //依附的適配器(adapter),適配器指明所屬的總線(i2c0/1/2_bus) 
  struct device dev;           //繼承的設(shè)備結(jié)構(gòu)體 
  int irq;                     //設(shè)備申請的中斷號
  struct list_head detected;   //已經(jīng)被發(fā)現(xiàn)的設(shè)備鏈表 
};

但是i2c_client不是我們自己寫程序去創(chuàng)建的,而是通過以下常用的方式自動(dòng)創(chuàng)建的:

方法一: 分配、設(shè)置、注冊i2c_board_info

方法二: 獲取adapter調(diào)用i2c_new_device

方法三: 通過設(shè)備樹(devicetree)創(chuàng)建

方法1和方法2通過platform創(chuàng)建,這兩種方法在內(nèi)核3.0版本以前使用所以在這不詳細(xì)介紹;**方法3是最新的方法,**3.0版本之后的內(nèi)核都是通過這種方式創(chuàng)建的,文章后面的案例就按方法3。

2)i2c_adapter

I2C總線適配器,即soc中的I2C總線控制器,硬件上每一對I2C總線都對應(yīng)一個(gè)適配器來控制它。在Linux內(nèi)核代碼中,每一個(gè)adapter提供了一個(gè)描述它的結(jié)構(gòu)(struct i2c_adapter),再通過i2c core層將i2c設(shè)備與i2c adapter關(guān)聯(lián)起來。主要用來完成i2c總線控制器相關(guān)的數(shù)據(jù)通信,此結(jié)構(gòu)體在芯片廠商提供的代碼中維護(hù)。

struct i2c_adapter {
    struct module *owner;
    unsigned int class;               //允許匹配的設(shè)備的類型
    const struct i2c_algorithm *algo; //指向適配器的驅(qū)動(dòng)程序,實(shí)現(xiàn)發(fā)送數(shù)據(jù)的算法
    struct device dev;                //指向適配器的設(shè)備結(jié)構(gòu)體
    char name[48];                    //適配器的名字
};

3)i2c_algorithm
I2C總線數(shù)據(jù)通信算法,通過管理I2C總線控制器,實(shí)現(xiàn)對I2C總線上數(shù)據(jù)的發(fā)送和接收等操作。亦可以理解為I2C總線控制器(適配器adapter)對應(yīng)的驅(qū)動(dòng)程序,每一個(gè)適配器對應(yīng)一個(gè)驅(qū)動(dòng)程序,用來描述適配器和設(shè)備之間的通信方法,由芯片廠商去實(shí)現(xiàn)的。

struct i2c_algorithm {
    //傳輸函數(shù)指針,指向?qū)崿F(xiàn)IIC總線通信協(xié)議的函數(shù)
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);        
};

4)i2c_driver
用于管理I2C的驅(qū)動(dòng)程序和i2c設(shè)備(client)的匹配探測,實(shí)現(xiàn)與應(yīng)用層交互的文件操作集合fops、cdev等。

struct i2c_driver {
    int (*probe)(struct i2c_client *, const struct i2c_device_id *); //設(shè)備匹配成功調(diào)用的函數(shù)
    int (*remove)(struct i2c_client *);                              //設(shè)備移除之后調(diào)用的函數(shù)
    struct device_driver driver;                                     //設(shè)備驅(qū)動(dòng)結(jié)構(gòu)體
    const struct i2c_device_id *id_table;   //設(shè)備的ID表,匹配用platform創(chuàng)建的client
};

第三:應(yīng)用實(shí)例,實(shí)現(xiàn)mpu6050驅(qū)動(dòng),讀取溫度

在設(shè)備樹中描述I2C設(shè)備信息

@i2c-0 {//表示這個(gè)i2c_client所依附的adapter是i2c-0
    //對應(yīng)i2c_client的name = "invensense,mpu6050"
    compatible = "invensense,mpu6050";
    //對應(yīng)i2c_client的addr = 0x69  -- 從機(jī)設(shè)備的地址
    reg = <0x69>;
    //對應(yīng)i2c_client的irq
    interrupts = <70>;
};

最終內(nèi)核會(huì)將這個(gè)設(shè)備樹的節(jié)點(diǎn)解析為一個(gè)i2c_client結(jié)構(gòu)體與i2c_driver結(jié)構(gòu)體進(jìn)行匹配。

第四:編寫驅(qū)動(dòng)代碼

分配、設(shè)置、注冊i2c_driver結(jié)構(gòu)體

struct i2c_driver mpu6050_driver = { .
  driver = {
     .name = "mpu6050", 
     .owner = THIS_MODULE, 
     .of_match_table = of_match_ptr(mpu6050_of_match), 
   }, 
   .probe = mpu6050_probe, .remove = mpu6050_remove, 
};


static int mpu6050_init(void)
{
    printk("%s called
", __func__);


    i2c_add_driver(&mpu6050_driver);


    return 0;
}

i2c總線驅(qū)動(dòng)模型屬于設(shè)備模型中的一類,同樣struct i2c_driver結(jié)構(gòu)體繼承于struct driver,匹配方法和設(shè)備模型中講的一樣,這里要去匹配設(shè)備樹,所以必須實(shí)現(xiàn)i2c_driver結(jié)構(gòu)體中的driver成員中的of_match_table成員:

/* 用來匹配mpu6050的設(shè)備樹 */
static struct of_device_id mpu6050_of_match[] = {
    {.compatible = "invensense,mpu6050"},
    {},
};

如果和設(shè)備樹匹配成功,那么就好調(diào)用probe函數(shù)

/* 匹配函數(shù),設(shè)備樹中的mpu6050結(jié)點(diǎn)對應(yīng)轉(zhuǎn)換為一個(gè)client結(jié)構(gòu)體 */ 
static int mpu6050_probe(struct i2c_client * client, const struct i2c_device_id * id) 
{ 
  int ret; 
  printk("mpu6050 match ok!
"); 
  
  mpu6050_dev.client = client; /* 注冊設(shè)備號 */ 
  mpu6050_dev.devno = MKDEV(MAJOR, MINOR); 
  ret = register_chrdev_region(mpu6050_dev.devno, 1, "mpu6050"); 
  if (ret < 0) goto err1; 
  
  cdev_init(&mpu6050_dev.cdev, &mpu6050_fops); 
  mpu6050_dev.cdev.owner = THIS_MODULE; 
  ret = cdev_add(&mpu6050_dev.cdev, mpu6050_dev.devno, 1); 
  if (ret < 0) goto err2; 
    return 0; 
err2: 
  unregister_chrdev_region(mpu6050_dev.devno, 1); 
err1: 
  return -1; 
}

實(shí)現(xiàn)文件操作集合

struct file_operations mpu6050_fops = { 
  .owner = THIS_MODULE, 
  .open = mpu6050_open, 
  .release = mpu6050_release, 
  .unlocked_ioctl = mpu6050_ioctl, 
};
static int mpu6050_open(struct inode * inodep, struct file * filep) 
{ 
  printk("%s called
", __func__); 
  mpu6050_write_byte(mpu6050_dev.client, PWR_MGMT_1, 0x00); 
  mpu6050_write_byte(mpu6050_dev.client, SMPLRT_DIV, 0x07); 
  mpu6050_write_byte(mpu6050_dev.client, CONFIG, 0x06); 
  mpu6050_write_byte(mpu6050_dev.client, GYRO_CONFIG, 0xF8); 
  mpu6050_write_byte(mpu6050_dev.client, ACCEL_CONFIG, 0x19); 
  return 0; 
} 
static int mpu6050_release(struct inode * inodep, struct file * filep) 
{ 
  printk("%s called
", __func__); 
  return 0; 
} 
void get_temp(union mpu6050_data * data) 
{ 
  data->temp = mpu6050_read_byte(mpu6050_dev.client, TEMP_OUT_L); 
  data->temp |= mpu6050_read_byte(mpu6050_dev.client, TEMP_OUT_H) << 8; 
} 
static long mpu6050_ioctl(struct file * filep, unsigned int cmd, unsigned long arg) 
{ 
  union mpu6050_data data; 
  switch (cmd) 
  { 
    case GET_TEMP: 
      get_temp(&data); 
      break; 
    default: 
      break; 
  } 
  if (copy_to_user((unsigned int *)arg, &data, sizeof(data))) 
    return -1; 
  return 0; 
}

如何實(shí)現(xiàn)對i2c從設(shè)備的讀寫操作?

/* 讀取mpu6050中一個(gè)字節(jié)的數(shù)據(jù),將讀取的數(shù)據(jù)的地址返回 */ 
static int mpu6050_read_byte(struct i2c_client * client, unsigned char reg_add) 
{ 
  int ret; /* 要讀取的那個(gè)寄存器的地址 */ 
  char txbuf = reg_add; /* 用來接收讀到的數(shù)據(jù) */ 
  char rxbuf[1]; /* i2c_msg指明要操作的從機(jī)地址,方向,緩沖區(qū) */ 
  struct i2c_msg msg[] = { 
    {client->addr, 0, 1, &txbuf}, //0表示寫,向往從機(jī)寫要操作的寄存器的地址 
    {client->addr, I2C_M_RD, 1, rxbuf}, //讀數(shù)據(jù) 
  }; 
  /* 通過i2c_transfer函數(shù)操作msg */ 
  ret = i2c_transfer(client->adapter, msg, 2); //執(zhí)行2條msg 
  if (ret < 0) 
  { 
    printk("i2c_transfer read err
"); 
    return -1; 
  } 
  return rxbuf[0]; 
} 
static int mpu6050_write_byte(struct i2c_client * client, unsigned char reg_addr, unsigned char data) 
{ 
  int ret; /* 要寫的那個(gè)寄存器的地址和要寫的數(shù)據(jù) */ 
  char txbuf[] = {reg_addr, data}; /* 1個(gè)msg,寫兩次 */
  struct i2c_msg msg[] = { 
    {client->addr, 0, 2, txbuf} 
  }; 
  ret = i2c_transfer(client->adapter, msg, 1); if (ret < 0) 
  { 
    printk("i2c_transfer write err
"); 
    return -1; 
  } 
  return 0; 
}

在實(shí)現(xiàn)讀寫操作的時(shí)候,使用了一個(gè)重要的函數(shù)i2c_transfer(),這個(gè)函數(shù)是i2c核心提供給設(shè)備驅(qū)動(dòng)的,通過它發(fā)送的數(shù)據(jù)需要被打包成i2c_msg結(jié)構(gòu),這個(gè)函數(shù)最終會(huì)回調(diào)相應(yīng)i2c_adapter->i2c_algorithm->master_xfer()接口將i2c_msg對象發(fā)送到i2c物理控制器

struct i2c_msg {
    __u16 addr;     /* slave address  */
    __u16 flags;    /* 1 - 讀  0 - 寫 */
    __u16 len;      /* msg length     */
    __u8 *buf;      /* 要發(fā)送的數(shù)據(jù)   */
};

以上是我對Linux中I2C驅(qū)動(dòng)框架的分析及實(shí)際案例分析,如有不足歡迎指出。






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 控制器
    +關(guān)注

    關(guān)注

    112

    文章

    15886

    瀏覽量

    175388
  • 內(nèi)核
    +關(guān)注

    關(guān)注

    3

    文章

    1336

    瀏覽量

    40084
  • 適配器
    +關(guān)注

    關(guān)注

    8

    文章

    1889

    瀏覽量

    67587
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11123

    瀏覽量

    207919
  • I2C
    I2C
    +關(guān)注

    關(guān)注

    28

    文章

    1452

    瀏覽量

    122261
  • I2C總線
    +關(guān)注

    關(guān)注

    8

    文章

    384

    瀏覽量

    60664
  • LINUX內(nèi)核
    +關(guān)注

    關(guān)注

    1

    文章

    315

    瀏覽量

    21557
  • MPU6050
    +關(guān)注

    關(guān)注

    38

    文章

    307

    瀏覽量

    70659

原文標(biāo)題:Linux系統(tǒng)中I2C子系統(tǒng)基本分析

文章出處:【微信號:嵌入式開發(fā)愛好者,微信公眾號:嵌入式開發(fā)愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    Linux內(nèi)核I2C系統(tǒng)的設(shè)計(jì)思路

    [ 導(dǎo)讀] 本文通過閱讀內(nèi)核代碼,來梳理一下I2C子系統(tǒng)的整體視圖。在開發(fā)I2C設(shè)備驅(qū)動(dòng)程序時(shí),往往缺乏對于系統(tǒng)整體的認(rèn)識,沒有一個(gè)清晰的思路。所以從高層級來分析一下
    發(fā)表于 09-06 09:40 ?390次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>中</b><b class='flag-5'>I2C</b>系統(tǒng)的設(shè)計(jì)思路

    如何仿照Linux內(nèi)核去編寫I2C驅(qū)動(dòng)

    仿照Linux內(nèi)核編寫MCU的I2C驅(qū)動(dòng)I2C是很常用的串行通信接口,用于連接各種外設(shè),傳感器等器件。在單片機(jī)開發(fā),
    發(fā)表于 08-23 08:03

    請問imx6dl的第四個(gè)I2C接口該如何配置使能呢

    在飛凌官網(wǎng)購買的一塊開發(fā)板,CPU型號為imx6dl,使用的內(nèi)核版本為Linux3.0.35。芯片手冊上說明了有四個(gè)I2C接口,請問第四個(gè)
    發(fā)表于 01-07 08:17

    基于I2C總線的EEPROM驅(qū)動(dòng)程序

     I2C總線是由Philips公司開發(fā)的用于器件之間連接的2線式雙向同步串行總線。Linux內(nèi)核針對
    發(fā)表于 12-07 13:58 ?55次下載

    Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解》第15章、LinuxI2C核心、總線與設(shè)備驅(qū)動(dòng)

    Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解》第15章、LinuxI2C核心、總線與設(shè)備驅(qū)動(dòng)
    發(fā)表于 10-27 11:19 ?8次下載
    《<b class='flag-5'>Linux</b>設(shè)備驅(qū)動(dòng)開發(fā)詳解》第15章、<b class='flag-5'>Linux</b>的<b class='flag-5'>I2C</b><b class='flag-5'>核心</b>、總線與設(shè)備驅(qū)動(dòng)

    基于嵌入式Linux下的I2C設(shè)備驅(qū)動(dòng)的總體思路與框架設(shè)計(jì)

    由于I2C總線的通用性,Linux作為一款優(yōu)秀的嵌入式操作系統(tǒng),也必須要對其要有很好的支持。在Linux內(nèi)核源碼
    發(fā)表于 08-20 09:04 ?3672次閱讀
    基于嵌入式<b class='flag-5'>Linux</b>下的<b class='flag-5'>I2C</b>設(shè)備驅(qū)動(dòng)的總體思路與框架設(shè)計(jì)

    linux自帶i2c工具使用

    平臺(tái)管理總線),DDC(顯示數(shù)據(jù)通道)以及ATCA(高級電信架構(gòu)).如果沒記錯(cuò)的話,linuxI2C框架是完全支持SMBus的.
    發(fā)表于 05-13 09:23 ?3909次閱讀

    LinuxI2C驅(qū)動(dòng)架構(gòu)

    1.???? LinuxI2C驅(qū)動(dòng)架構(gòu)LinuxI2C總線的驅(qū)動(dòng)分為兩個(gè)部分
    發(fā)表于 04-02 14:38 ?626次閱讀

    Linux內(nèi)核I2C子系統(tǒng)的整體視圖

    本文通過閱讀內(nèi)核代碼,來梳理一下I2C子系統(tǒng)的整體視圖。在開發(fā)I2C設(shè)備驅(qū)動(dòng)程序時(shí),往往缺乏對于系統(tǒng)整體的認(rèn)識,沒有一個(gè)清晰的思路。所以從高層級來分析一下
    的頭像 發(fā)表于 12-31 10:40 ?2086次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>中</b><b class='flag-5'>I2C</b>子系統(tǒng)的整體視圖

    Linux驅(qū)動(dòng):I2C設(shè)備驅(qū)動(dòng)(基于Freescale i.MX6ULL平臺(tái)了解I2C的驅(qū)動(dòng)框架,順便寫個(gè)簡陋的MPU6050驅(qū)動(dòng))

    文章目錄1、簡介2、I2C總線、設(shè)備和驅(qū)動(dòng)的結(jié)構(gòu)定義2.1 結(jié)構(gòu)定義--
    發(fā)表于 12-06 13:51 ?8次下載
    <b class='flag-5'>Linux</b>驅(qū)動(dòng):<b class='flag-5'>I2C</b>設(shè)備驅(qū)動(dòng)(基于Freescale <b class='flag-5'>i</b>.MX6ULL平臺(tái)了解<b class='flag-5'>I2C</b>的驅(qū)動(dòng)框架,順便寫個(gè)簡陋的MPU6050驅(qū)動(dòng))

    I2C控制器驅(qū)動(dòng)介紹

    控制器驅(qū)動(dòng) I2C 總線驅(qū)動(dòng)重點(diǎn)是 I2C 適配器驅(qū)動(dòng),這里要用到兩個(gè)重要的數(shù)據(jù)結(jié)構(gòu)i2c_adapter 和
    的頭像 發(fā)表于 07-22 15:38 ?1246次閱讀
    <b class='flag-5'>I2C</b>控制器驅(qū)動(dòng)介紹

    I2C設(shè)備驅(qū)動(dòng)的兩個(gè)數(shù)據(jù)結(jié)構(gòu)

    i2c_client,每檢測到一個(gè) I2C 設(shè)備就會(huì)給這個(gè) I2C 設(shè)備分配一個(gè) i2c_cl
    的頭像 發(fā)表于 07-22 15:49 ?674次閱讀
    <b class='flag-5'>I2C</b>設(shè)備驅(qū)動(dòng)的兩個(gè)數(shù)據(jù)<b class='flag-5'>結(jié)構(gòu)</b>

    I2C子系統(tǒng)SW Architecture

    適配器(控制器)驅(qū)動(dòng),這里用到兩個(gè)重要的數(shù)據(jù)結(jié)構(gòu)i2c_adapter 和 i2c_algorithm。其中,Linux
    的頭像 發(fā)表于 07-22 16:01 ?796次閱讀
    <b class='flag-5'>I2C</b>子系統(tǒng)SW Architecture

    I2C子系統(tǒng)幾個(gè)主要的結(jié)構(gòu)

    I2C Data Structure 我們要搞懂一個(gè) Linux 子系統(tǒng),必須研究它的數(shù)據(jù)結(jié)構(gòu),搞懂每個(gè)結(jié)構(gòu)
    的頭像 發(fā)表于 07-22 16:04 ?682次閱讀
    <b class='flag-5'>I2C</b>子系統(tǒng)幾個(gè)主要的<b class='flag-5'>結(jié)構(gòu)</b><b class='flag-5'>體</b>

    Linux內(nèi)核網(wǎng)絡(luò)擁塞控制算法的實(shí)現(xiàn)框架(二)

    :[Linux內(nèi)核網(wǎng)絡(luò)基礎(chǔ)-TCP相關(guān)的幾個(gè)關(guān)鍵結(jié)構(gòu)-小記]中進(jìn)行了介紹,如下圖是四個(gè)核心結(jié)構(gòu)
    的頭像 發(fā)表于 07-28 11:34 ?697次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b>網(wǎng)絡(luò)擁塞控制算法的實(shí)現(xiàn)框架(二)