I2C驅動

I2C總線

  • I2C(又稱IIC)總線是由PHILIPS公司開發(fā)的串行總線,用于連接微控制器與外圍設備残拐,特點如下
    總線只有兩條線:數(shù)據(jù)線(SDA),時鐘線(SCL)
    每個連接到總線上的設備用唯一的地址來識別
    主/從關系碟嘴,主設備是數(shù)據(jù)的傳輸?shù)陌l(fā)起者并提供時鐘信號溪食。
    位速率:100kbit/s,400kbit/s,3.4Mbit/s。


  • 總線信號(3種)
    開始信號(S):SCL為高電平時娜扇,SDA由高變低電平
    結束信號(P):SCL為高電平時错沃,SDA由低變高電平
    響應信號(ACK):接收器在收到8位數(shù)據(jù)后,在第9位


  • 位數(shù)據(jù)傳輸



    SDA上傳輸?shù)臄?shù)據(jù)必須在SCL為高電平期間保持穩(wěn)定雀瓢,SDA上數(shù)據(jù)的變化只能在SCL為低電平期間枢析。

  • 幾種I2C數(shù)據(jù)傳輸格式


Linux I2C體系結構

  • Linux的I2C體系結構分為3個組成部分
    • I2C核心ceng
      提供了I2C總線驅動和設備驅動的注冊、注銷方法刃麸,I2C通信方法(即Algorithm)上層的與具體適配器無關的代碼以及探測設備醒叁、檢測設備地址的上層代碼等
    • I2C適配器層
      對I2C硬件體系結構中適配器端的實現(xiàn),適配器可由CPU控制泊业,甚至可以直接集成在CPU內部
      適配器i2c_adapter
      算法結構Algorithm把沼。
    • I2C設備驅動層
      I2C設備驅動(也稱為客戶驅動)是對設備端的實現(xiàn),設備一般掛接在受CPU控制的I2C適配器上脱吱,通過I2C適配器與CPU交換數(shù)據(jù)智政。
      使用了I2C總線-設備-驅動模型
      i2c_driver和i2c_client


4個數(shù)據(jù)結構的的關系

  • i2c_adapter與i2c_algorithm
    i2c_adapter對應于物理上的一個適配器(CPU上的I2C控制器)
    而i2c_algorithm對應一套通信方法
    一個I2C適配器需要i2c_algorithm提供的通信函數(shù)來控制適配器產生特定的訪問周期。
    缺少i2c_algorithm的i2c_adapter什么也做不了
    因此i2c_adapter中包含所使用的i2c_algorithm的指針箱蝠。
  • i2c_adpater與i2c_client
    i2c_adpater與i2c_client的關系與I2C硬件體系中適配器和設備的關系一致续捂。
    i2c_adapter對應于物理上的一個適配器(CPU上的I2C控制器)
    i2c_client代表連接到適配器中的設備,主要提供地址信息
    一個i2c_adpater也可以被多個i2c_client連接
    i2c_client必要依附在i2c_adpater上宦搬,才能使用牙瓢。
    一個CPU可以有多個適配器硬件,對應對個i2c_adpater
  • i2c_driver與i2c_client
    i2c_client代表總線上的設備间校,主要提供地址信息
    i2c_driver代表對應設備的驅動程序
    i2c_driver與i2c_client依附在I2C總線上矾克,適合device-bus-driver模型。

I2C設備驅動模型

  • I2C設備驅動層采用bus-dev-drv模型設計
    I2C總線定義如下圖:



    I2C總線上的device可以包含兩種數(shù)據(jù)(i2c_client和i2c_adapter)
    I2C總線提供了match憔足、probe方法

  • I2C總線的match方法i2c_device_match()



  • I2C總線的match方法i2c_device_match()返回真胁附,表示匹配成功酒繁,會調用I2C總線的probe函數(shù)i2c_device_probe():


    • 上面的函數(shù)是簡化的示意代碼:
      client表示匹配成功的dev
      driver表示匹配成功的drv
    • 函數(shù)的作用
      設置client->driver = driver
      調用driver->probe函數(shù),參數(shù)是配置成功的client和id
  • I2C總線模型



    I2C總線上的device端有兩種dev(client和adapter)控妻,而driver只有一種州袒。
    向總線注冊一個i2c_client的函數(shù)是i2c_new_device()
    向總線注冊一個i2c_driver的函數(shù)是i2c_register_driver()

  • 分析i2c_new_device()函數(shù)簡化流程如下:


    • 函數(shù)流程如下:
      分配一個client內存
      用參數(shù)adap、info初始化client
      注冊client
  • 分析i2c_register_driver()函數(shù)簡化流程如下:


    • 函數(shù)流程如下:
      • 注冊driver
      • 對bus上的每個adapter調用__process_new_driver()函數(shù)
        bus上的dev出來client外弓候,還有adapter郎哭,一個CPU上可以有多個adapter;
        所有CPU上的adapter都掛在bus的dev端菇存。



i2c讀寫流程


驅動中的讀夸研、寫方法,統(tǒng)一通過i2c_smbus_xxx族函數(shù)來進行讀寫

  • i2c_smbus函數(shù)族


EEPROOM

  • TINY4412的eeprom設備at24c08連接到CPU的適配器0上


  • 讀寫時序:



讀寫EEPROOM實戰(zhàn)

at24_drv.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <asm/uaccess.h>


static int major;
static struct class *class;
static struct i2c_client *at24_client;

/* 傳入: buf[0] : addr               輸出: buf[0] : data*/
static ssize_t at24_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(at24_client, addr);
    copy_to_user(buf, &data, 1);
    return sizeof(unsigned char);
}

/* buf[0] : addr    buf[1] : data*/
static ssize_t at24_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];

    if (!i2c_smbus_write_byte_data(at24_client, addr, data))
        return 2*sizeof(unsigned char);
    else
        return -EIO;    
}

static struct file_operations at24_fops = {
    .owner = THIS_MODULE,
    .read  = at24_read,
    .write = at24_write,
};

static int __devinit at24_probe(struct i2c_client *client,
                  const struct i2c_device_id *id)
{
    printk("probe\n");
    at24_client = client;
    major = register_chrdev(0, "at24", &at24_fops);
    class = class_create(THIS_MODULE, "at24");
    device_create(class, NULL, MKDEV(major, 0), NULL, "at24"); 
    
    return 0;
}

static int __devexit at24_remove(struct i2c_client *client)
{
    device_destroy(class, MKDEV(major, 0));
    class_destroy(class);
    unregister_chrdev(major, "at24");
        
    return 0;
}

static const struct i2c_device_id at24_id_table[] = {
    { "at24c0x", 0 },{}
};
int (*detect)(struct i2c_client *, struct i2c_board_info *);
unsigned short tiny4412_address_list[] = I2C_ADDRS(0x50);
static struct i2c_driver at24_driver = {/*  分配/設置i2c_driver */
    .driver = {
        .name   = "at24c0x",
        .owner  = THIS_MODULE,
    },
    .probe      = at24_probe,
    .remove     = __devexit_p(at24_remove),
    .id_table   = at24_id_table,
    .address_list = ,
    .detect =,
};

static int at24_drv_init(void)
{
    i2c_add_driver(&at24_driver);   /*  注冊i2c_driver */
    return 0;
}

static void at24_drv_exit(void)
{
    i2c_del_driver(&at24_driver);
}

module_init(at24_drv_init);
module_exit(at24_drv_exit);
MODULE_LICENSE("GPL");

at24_dev.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

static struct i2c_board_info at24_info = {  
    I2C_BOARD_INFO("at24c0x", 0x50),
};

static struct i2c_client *at24_client;

static int at24_dev_init(void)
{
    struct i2c_adapter *i2c_adap;

    i2c_adap = i2c_get_adapter(0);
    at24_client = i2c_new_device(i2c_adap, &at24_info);
    i2c_put_adapter(i2c_adap);
    
    return 0;
}

static void at24_dev_exit(void)
{
    i2c_unregister_device(at24_client);
}


module_init(at24_dev_init);
module_exit(at24_dev_exit);
MODULE_LICENSE("GPL");

app_test.c


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


/* i2c_test r addr
 * i2c_test w addr val
 */

void print_usage(char *file)
{
    printf("%s r addr\n", file);
    printf("%s w addr val\n", file);
}

int main(int argc, char **argv)
{
    if ((argc != 3) && (argc != 4))
    {
        print_usage(argv[0]);
        return -1;
    }
    
    int fd;
    unsigned char buf[2];

    fd = open("/dev/at24", O_RDWR);
    if (strcmp(argv[1], "r") == 0)
    {
        buf[0] = strtoul(argv[2], NULL, 0);
        read(fd, buf, 1);
        printf("data: %c, %d, 0x%2x\n", 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 = 0x%02x, data = 0x%02x\n", buf[0], buf[1]);
    }
    return 0;
}

makefile

obj-m += at24_dev.o  at24_drv.o
all:
    make -C /home/sice/linux-3.5 M=`pwd` modules 
    arm-linux-gcc app_test.c -o app_test
install:
    make -C /home/sice/linux-3.5 M=`pwd` INSTALL_MOD_PATH=/opt/rootfs modules_install
    cp app_test /opt/rootfs
clean:
    make -C /home/sice/linux-3.5 M=`pwd` clean
    rm app_test
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末依鸥,一起剝皮案震驚了整個濱河市亥至,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贱迟,老刑警劉巖抬闯,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異关筒,居然都是意外死亡,警方通過查閱死者的電腦和手機杯缺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門蒸播,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人萍肆,你說我怎么就攤上這事袍榆。” “怎么了塘揣?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵包雀,是天一觀的道長。 經常有香客問我亲铡,道長才写,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任奖蔓,我火速辦了婚禮赞草,結果婚禮上,老公的妹妹穿的比我還像新娘吆鹤。我一直安慰自己厨疙,他們只是感情好,可當我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布疑务。 她就那樣靜靜地躺著沾凄,像睡著了一般梗醇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上撒蟀,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天叙谨,我揣著相機與錄音,去河邊找鬼牙肝。 笑死唉俗,一個胖子當著我的面吹牛,可吹牛的內容都是我干的配椭。 我是一名探鬼主播虫溜,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼股缸!你這毒婦竟也來了衡楞?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤敦姻,失蹤者是張志新(化名)和其女友劉穎瘾境,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體镰惦,經...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡迷守,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了旺入。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兑凿。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖茵瘾,靈堂內的尸體忽然破棺而出礼华,到底是詐尸還是另有隱情,我是刑警寧澤拗秘,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布圣絮,位于F島的核電站,受9級特大地震影響雕旨,放射性物質發(fā)生泄漏扮匠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一凡涩、第九天 我趴在偏房一處隱蔽的房頂上張望餐禁。 院中可真熱鬧,春花似錦突照、人聲如沸帮非。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽末盔。三九已至筑舅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間陨舱,已是汗流浹背翠拣。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留游盲,地道東北人误墓。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像益缎,于是被迫代替她去往敵國和親谜慌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內容

  • 簡介 I2C驅動由I2C核心,I2C總線驅動和I2C設備驅動組成.I2C核心是I2C總線驅動和I2C設備驅動的中間...
    傀儡世界閱讀 1,108評論 0 1
  • 一莺奔、前言 I2C總線 是一種常用的總線協(xié)議欣范,在設備中經常看到令哟,比如 sensor恼琼、陀螺儀等都是使用 I2C總線。而...
    wipping的技術小棧閱讀 2,994評論 0 3
  • 引言 單片機的IIC編程中屏富,如果我們直接一點晴竞,只需要控制IIC硬件GPIO腳,然后根據(jù)IIC協(xié)議模擬各種電平時序實...
    開源519閱讀 386評論 0 0
  • 一狠半,前言 爽11選東東花費了我一周的業(yè)余時間颓鲜,趁著周末又調整回了常規(guī)的學習狀態(tài)。之前做的applepaper是虛擬...
    applecai閱讀 1,346評論 0 1
  • I2C總線僅僅使用 SCL 典予、 SDA 兩根信號線就實現(xiàn)了設備之間的數(shù)據(jù)交互。 由于各種SOC都有自己的I2C總線...
    gbmaotai閱讀 3,834評論 0 3