I2C驅動詳解


I2C講解:

在JZ2440開發板上,I2C是由兩條數據線構成的SCL,SDA;SCL作為時鍾總線,SDA作為數據總線;兩條線上可掛載I2C設備,如:AT24C08

兩條線連接ARM9 I2C控制器,通過控制來控制I2C設備的識別設備地址、讀、寫操作;如圖所示

從中所知:I2C線上可以掛載很多個I2C設備;掛載簡單,只需要一根數據線和一根時鍾線就可以掛載上去,通過地址來去別每個設備的區別;

 

I2C操作:

對I2C操作主要思想為:1、找到設備  2、進行讀寫操作 主要原理為:

1、發送開始信號S 然后緊接着發生設備8位地址如:0x50,然后等待設備發出一個應答ACK信號

2、當控制器接收到ACK信號后,表面找到這條I2C總線上確實有這個設備,然后發出數據,是進行讀還是進行寫,由第8位來決定

 原理如下圖:

JZ2440對I2c驅動框架

正常的設備驅動程序,大體框架為:

1、通過應用程序open、read、write、ioctl函數去對硬件操作

2、在內核里,接收到應用程序發送過來的open、read、write、ioctl命令,出發中斷,進而會跳到一個字符描述符的數組(open時返回值)當中,查找驅動程序ID(驅動程序進行注冊);通過該ID找到相對應的drv_open、drv_read、drv_wrte等操作;

3、drv_open、drv_read、drv_wrte等操作跟字符設備的硬件驅動程序緊密相關,會最終會跳到字符設備驅動程序file_operation結構體進行硬件的操作過程。

4、在此之前,需要編寫I2C驅動程序,驅動程序要有主入口、出口、file_openration結構體、進行注冊等操作;

具體字符設備驅動程序框架為:

而I2C設備驅動采用的是總線—設備-驅動模型

總線-設備-驅動模型:

    總線-設備-驅動模型:其原理為在一條總線上分為兩部分,一部分作為設備鏈表對象的,另一部分作為driver鏈表;設備鏈表對象就是

注冊一個設備client對象掛載到這條鏈表上,但注冊的設備名需要與右邊driver鏈表注冊的驅動名相比配才有意義,才能對進入driver鏈表

上與設備名相匹配的驅動程序里面的probe函數進行i2c驅動操作;舉個例子:將驅動程序比作學生,驅動程序的名字比作學生的宿舍號碼

管理員相當於總線,總線知道兩個信息:1、學生 2、宿舍號 ;當有一個學生進行入住,學生想住A10-208,好,經過管理員同意好,就

住進A10-208(注冊一個i2c_driver)了,當管理員想找到那個學生,則需要通過A10-208這個名字去查找到學生所在住處,通過管理員

管理的學生信息本(注冊一個設備)找到名字,進而去到宿舍找到這個學生,讓他掃地、打掃衛生什么的;主要原理圖如下:

 

i2c-總線-驅動模型

 

應用程序是如何調用的呢?如圖所示

 

i2c進行open、read、write、ioctl等操作會進入內核太,找到相關設備驅動函數,然后就進入了總線控制層,I2c總線層兩部分組成,核心層

里面已經定義了對i2c提供的操作函數,不需要用戶自己定義,適配器就是具體的對2440的i2c硬件操作或者定義其它的芯片的硬件操作;

 

總結:總得來說,我們需要進行的操作為

 

1、注冊一個設備,里面要有設備名,以及該設備id,並掛載到這條設備鏈表上

2、注冊一個驅動,該設備驅動需要有設備名,probe函數、id_table設備地址,然后掛載到驅動鏈表上

3、比較兩條鏈表上的設備名字是否相同,如果有相同的話,就去到相關的驅動設備上的probe函數,進行

    操作

4、probe函數里面就是我們正常的驅動程序的編程程序了,主入口、出口、file_operation結構體的構造等等操作;最終應用程序會進入這里操作硬件對象

 

-----------------------------------------------------------------------------------------------------------------------------------------------

如圖代碼所示:新建一個設備有多種方法,該方法為第二種i2c_new_probed_device

at24cxx_dev:注冊一個新設備

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>

static struct i2c_client *at24cxx_client;

static const unsigned short addr_list[] = { 0x60, 0x50, I2C_CLIENT_END };

static int at24cxx_dev_init(void)
{
    struct i2c_adapter *i2c_adap;
    struct i2c_board_info at24cxx_info;

    memset(&at24cxx_info, 0, sizeof(struct i2c_board_info));    
    strlcpy(at24cxx_info.type, "at24c08", I2C_NAME_SIZE);

    i2c_adap = i2c_get_adapter(0);
    at24cxx_client = i2c_new_probed_device(i2c_adap, &at24cxx_info, addr_list, NULL);
    i2c_put_adapter(i2c_adap);

    if (at24cxx_client)
        return 0;
    else
        return -ENODEV;
}

static void at24cxx_dev_exit(void)
{
    i2c_unregister_device(at24cxx_client);
}


module_init(at24cxx_dev_init);
module_exit(at24cxx_dev_exit);
MODULE_LICENSE("GPL");

at24cxx_drv:注冊一個新驅動  注:probe函數並沒有進行i2c的·操作,只是打印一些信息而已

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>


static int __devinit at24cxx_probe(struct i2c_client *client,
                  const struct i2c_device_id *id)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}

static int __devexit at24cxx_remove(struct i2c_client *client)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}

static const struct i2c_device_id at24cxx_id_table[] = {
    { "at24c08", 0 },
    {}
};


/* 1. 分配/設置i2c_driver */
static struct i2c_driver at24cxx_driver = {
    .driver    = {
        .name    = "100ask",
        .owner    = THIS_MODULE,
    },
    .probe        = at24cxx_probe,
    .remove        = __devexit_p(at24cxx_remove),
    .id_table    = at24cxx_id_table,
};

static int at24cxx_drv_init(void)
{
    /* 2. 注冊i2c_driver */
    i2c_add_driver(&at24cxx_driver);
    
    return 0;
}

static void at24cxx_drv_exit(void)
{
    i2c_del_driver(&at24cxx_driver);
}


module_init(at24cxx_drv_init);
module_exit(at24cxx_drv_exit);
MODULE_LICENSE("GPL");

Makefile:

KERN_DIR = /work/system/linux-3.4.2

all:
    make -C $(KERN_DIR) M=`pwd` modules 

clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order

obj-m    += at24cxx_dev.o
obj-m    += at24cxx_drv.o
#obj-m    += i2c_bus_s3c2440.o

 

-----------------------------------------------------------------------------------------------------------------------------------------------

對I2c驅動的操作

前面已經講過對於I2c的操作可以調用總線上現成的函數來操作i2c_smbus_write_byte_data

at24cxx_dev:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>


static struct i2c_board_info at24cxx_info = {    
    I2C_BOARD_INFO("at24c08", 0x50),
};

static struct i2c_client *at24cxx_client;

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

    i2c_adap = i2c_get_adapter(0);
    at24cxx_client = i2c_new_device(i2c_adap, &at24cxx_info);
    i2c_put_adapter(i2c_adap);
    
    return 0;
}

static void at24cxx_dev_exit(void)
{
    i2c_unregister_device(at24cxx_client);
}


module_init(at24cxx_dev_init);
module_exit(at24cxx_dev_exit);
MODULE_LICENSE("GPL");

at24cxx_drv:注冊驅動程序,進行I2c操作

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


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

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

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

    printk("addr = 0x%02x, data = 0x%02x\n", addr, data);

    if (!i2c_smbus_write_byte_data(at24cxx_client, addr, data))
        return 2;
    else
        return -EIO;    
}

static struct file_operations at24cxx_fops = {
    .owner = THIS_MODULE,
    .read  = at24cxx_read,
    .write = at24cxx_write,
};

static int __devinit at24cxx_probe(struct i2c_client *client,
                  const struct i2c_device_id *id)
{
    at24cxx_client = client;
        
    //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    major = register_chrdev(0, "at24cxx", &at24cxx_fops);
    class = class_create(THIS_MODULE, "at24cxx");
    device_create(class, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */
    
    return 0;
}

static int __devexit at24cxx_remove(struct i2c_client *client)
{
    //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    device_destroy(class, MKDEV(major, 0));
    class_destroy(class);
    unregister_chrdev(major, "at24cxx");
        
    return 0;
}

static const struct i2c_device_id at24cxx_id_table[] = {
    { "at24c08", 0 },
    {}
};


/* 1. 分配/設置i2c_driver */
static struct i2c_driver at24cxx_driver = {
    .driver    = {
        .name    = "100ask",
        .owner    = THIS_MODULE,
    },
    .probe        = at24cxx_probe,
    .remove        = __devexit_p(at24cxx_remove),
    .id_table    = at24cxx_id_table,
};

static int at24cxx_drv_init(void)
{
    /* 2. 注冊i2c_driver */
    i2c_add_driver(&at24cxx_driver);
    
    return 0;
}

static void at24cxx_drv_exit(void)
{
    i2c_del_driver(&at24cxx_driver);
}


module_init(at24cxx_drv_init);
module_exit(at24cxx_drv_exit);
MODULE_LICENSE("GPL");

應用測試程序i2c_test:

#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)
{
    int fd;
    unsigned char buf[2];
    
    if ((argc != 3) && (argc != 4))
    {
        print_usage(argv[0]);
        return -1;
    }

    fd = open("/dev/at24cxx", O_RDWR);
    if (fd < 0)
    {
        printf("can't open /dev/at24cxx\n");
        return -1;
    }

    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]);
    }
    else
    {
        print_usage(argv[0]);
        return -1;
    }
    
    return 0;
}

Makefile:

KERN_DIR = /work/system/linux-3.4.2

all:
    make -C $(KERN_DIR) M=`pwd` modules 

clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order

obj-m    += at24cxx_dev.o
obj-m    += at24cxx_drv.o
#obj-m    += i2c_bus_s3c2440.o

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM