1 IIC硬件设备基本概念

1.1 IIC概述

I2C是飞利浦公司推出的两线式串行扩展总线,用于连接微控制器及其外围设备。

1.2 I2C使用两根双向信号线来传递数据

Serial clock Line(SCL)

Serial Data Address(SDA)

1.3 总线速度

标准速度100kbps,快速模式400kbps,高速模式3.4Mbps

1.4 I2C特点

半双工,仅需要两根线

2 IIC硬件设备时序(AT24C08为例)

image-20220927010339872

IIC详解之AT24C08

2.1 起始信号

当时钟线SCL为高电平时 , 数据线SDA有高电平到低电平的下降沿

20201204092513199

2.2 停止信号

当时钟线SCL为高电平时 , 数据线SDA由低电平到高电平的上升沿。

20201204092537512

2.3 应答信号

应答信号表明IIC的数据传输结束 , IIC在传输一个字节的数据后著期间在第九个时钟周期释放总线SDA , 使其处于高电平 , 此时从器件输出低电平拉蒂数据SDA为应答信号。如果为高电平的话则时传送异常 , 结束发送。

20201204092557281

2.4 非应答信号

当我们在进行读操作的时候 , 器件在读取8位数据之后 ,如果不在继续读取了就发送一个非应答信号 , 就是在第九个时钟周期时释放总线SDA , 将其拉高 持续整个时钟周期。

2.5 读写时序

i2c在在读写时 ,表示i2c总线忙 。要求在读或者写的时(SCL=1)数据必须稳定 。

image-20220927211027463

2.6 读写操作分析

2.6.1写操作

image-20220927011158210

  • 1.装载AT24C08设备地址:0xA0
  • 2.设置IIC开始条件
  • 3.使能ACK
  • 4.检测状态
  • 5.装载存储地址
  • 6.使能ACK
  • 7.检测状态
  • 8.写数据
  • 9.使能ACK
  • 10.设置IIC停止条件

2.6.2读操作

image-20220927011221580

  • 1.装载AT24C08设备地址:0xA0
  • 2.设置IIC开始条件
  • 3.使能ACK
  • 4.检测状态
  • 5.装载存储地址
  • 6.使能ACK
  • 7.检测状态
  • 8.装载设备地址:0xA0 | 0x1
  • 9.设置IIc开始条件
  • 10.使能AcK信号
  • 11.检测状态
  • 12.读取数据
  • 13.关闭ACK信号
  • 14.设置停止条件

3 IIC硬件设备相关寄存器分析(S5PV210)

image-20220927114109434

3.1 I2CCON

使能ACK,选择时钟源,使能Tx/Rx,检测状态

image-20220927114340404

3.2 I2CSTAT

主机发送/接收,设置开始/停止条件,数据Tx/Rx使能,ACK能否被接收

image-20220927114517705

3.3 I2CADD

i2c作为从机才使用

image-20220927114815517

3.4 I2CDS

装载设备地址,发送/接收数据

image-20220927114949862


4 IIC设备驱动框架

image-20220927115929989

4.1 i2c设备驱动层组件(i2c-dev.c)

功能:

1.给用户提供调用接口

2.实现策略问题:它知道发什么数据,但不知道怎么发数据

4.2 i2c核心层组件(i2c-core.c)

功能:

1.注册一根i2c总线

2.给驱动编程人员提供编程接口

4.3 i2c总线驱动层组件(i2c-s3c2410.c)

功能:

1.初始化硬件(初始化i2c控制器), 根据i2c操作时序进行控制i2c控制器实现数据接收/发送

2.实现操作方法:它知道怎么去发数据,但不知道发什么数据

5 IIC设备驱动核心层分析

5.1 核心层代码流程分析

i2c_init:
    //注册一根i2c总线
    retval = bus_register(&i2c_bus_type);

    struct bus_type i2c_bus_type = {
        .name        = "i2c",
        .match        = i2c_device_match, //匹配函数
        .probe        = i2c_device_probe,
        .remove        = i2c_device_remove,
        .shutdown    = i2c_device_shutdown,
    };


    static int i2c_device_match(struct device *dev, struct device_driver *drv)
    {
        struct i2c_client    *client = i2c_verify_client(dev);
        struct i2c_driver    *driver;
        
        //驱动和设备进行匹配
        /* Attempt an OF style match */
        if (i2c_of_match_device(drv->of_match_table, client))
            return 1;

        /* Then ACPI style match */
        if (acpi_driver_match_device(dev, drv))
            return 1;

        driver = to_i2c_driver(drv);

        /* Finally an I2C match */
        if (i2c_match_id(driver->id_table, client))
            return 1;

        return 0;
    }

    //注册i2c驱动
    retval = i2c_add_driver(&dummy_driver);

5.2 涉及重要结构体

struct i2c_driver { //表示一个i2c驱动

    int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
    int (*remove)(struct i2c_client *client);

    struct device_driver driver;//表示驱动
    const struct i2c_device_id *id_table;//记录i2c驱动能服务于那些设备
    ...
};
struct i2c_client { //表示i2c设备
    unsigned short flags;        //标号
    unsigned short addr;        //设备地址
    char name[I2C_NAME_SIZE];   //设备名字
    struct i2c_adapter *adapter;    //所属适配器
    struct device dev;        //设备结构体
    int init_irq;            //初始化阶段中断
    int irq;            //中断号
    struct list_head detected; //链表

};
struct i2c_adapter { //表示i2c适配器,控制器
    //i2c操作方法(利用i2c操作协议数据)
    const struct i2c_algorithm *algo; 
        int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
               int num);
    
};
struct i2c_msg { //表示i2c数据包
    __u16 addr;  //设备地址
    __u16 flags; //数据包: 读-1 写-0
    __u16 len;        //有效数据长度
    __u8 *buf;        //有效数据指针
};
struct i2c_board_info { //表示i2c办卡信息
    char        type[I2C_NAME_SIZE]; //i2c设备名字
    unsigned short    flags;
    unsigned short    addr;  //i2c设备地址
    const char    *dev_name;
    void        *platform_data;
    struct device_node *of_node;
    struct fwnode_handle *fwnode;
    const struct property_entry *properties;
    const struct resource *resources;
    unsigned int    num_resources;
    int        irq;
};

#define I2C_BOARD_INFO(dev_type, dev_addr) \
    .type = dev_type, .addr = (dev_addr)

6 IIC设备驱动驱动层分析

6.1 设备驱动层代码流程分析

static int __init i2c_dev_init(void)
{    
    //注册主设备号
    res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");

    //创建设备类
    i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");

    //绑定适配器,搜索i2c总线设备链表,每搜索到一个设备,i2cdev_attach_adapter
    i2c_for_each_dev(NULL, i2cdev_attach_adapter);

}
//创建设备文件 /dev/i2c-0  /dev/i2c-1
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
    struct i2c_adapter *adap;
    struct i2c_dev *i2c_dev;
    int res;

    if (dev->type != &i2c_adapter_type)
        return 0;
    adap = to_i2c_adapter(dev);

    i2c_dev = get_free_i2c_dev(adap);
    if (IS_ERR(i2c_dev))
        return PTR_ERR(i2c_dev);

    //注册硬件操作方法
    cdev_init(&i2c_dev->cdev, &i2cdev_fops);
    i2c_dev->cdev.owner = THIS_MODULE;

    device_initialize(&i2c_dev->dev);
    i2c_dev->dev.devt = MKDEV(I2C_MAJOR, adap->nr);
    i2c_dev->dev.class = i2c_dev_class;
    i2c_dev->dev.parent = &adap->dev;
    i2c_dev->dev.release = i2cdev_dev_release;
    dev_set_name(&i2c_dev->dev, "i2c-%d", adap->nr);

    res = cdev_device_add(&i2c_dev->cdev, &i2c_dev->dev);
    if (res) {
        put_i2c_dev(i2c_dev, false);
        return res;
    }

    pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
         adap->name, adap->nr);
    return 0;
}
//硬件操作方法
static const struct file_operations i2cdev_fops = {
    .owner        = THIS_MODULE,
    .llseek        = no_llseek,
    .read        = i2cdev_read,
    .write        = i2cdev_write,
    .unlocked_ioctl    = i2cdev_ioctl,
    .compat_ioctl    = compat_i2cdev_ioctl,
    .open        = i2cdev_open,
    .release    = i2cdev_release,
};

6.2 涉及重要操作函数

//发送i2c数据
static inline int i2c_master_send(const struct i2c_client *client,const char *buf, int count);

//接收i2c数据
static inline int i2c_master_recv(const struct i2c_client *client,char *buf, int count)

//传输一个i2c数据包
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);

//注册板块信息
int i2c_register_board_info(int busnum, struct i2c_board_info const *info,unsigned n);

//注册、注销适配器
int i2c_add_adapter(struct i2c_adapter *adap);
void i2c_del_adapter(struct i2c_adapter *adap);
int i2c_add_numbered_adapter(struct i2c_adapter *adap);

//注册、注销驱动
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
void i2c_del_driver(struct i2c_driver *driver);

7 IIC应用开发

7.1 查看驱动是否编入

cat /proc/devices 看是否有89

7.2 驱动编入

make menuconfig 开启相关选项

image-20220927152636235

7.3 应用源码

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

#define I2C_SLAVE    0x0703

int main(int argc, char **argv)
{
    int fd;
    int device_addr = 0x50; /* The I2C address AT24C08*/
    char register_addr = 0x3;

    char wbuf[10];
    char rbuf[10];


    fd = open("/dev/i2c-0", O_RDWR);
    if (fd < 0) {
        perror("open failed!\n");
        exit(1);
    }

    if (ioctl(fd, I2C_SLAVE, device_addr) < 0) {
        perror("ioctl failed!\n");
        exit(1);
    }

    //printf("argv[1] = %s\n", argv[1]);
    if(strcmp(argv[1], "w")==0)
    {
        wbuf[0] = register_addr;
        wbuf[1] = argv[2];

        if (write(fd, wbuf, 2) != 2) {
            perror("write failed!\n");
            exit(1);
        }

    }
    else 
    {
        if (write(fd, &register_addr, 1) != 1) {
            perror("write failed!\n");
            exit(1); 
        }

        if (read(fd, &rbuf[0], 1) != 1) {
            perror("read failed!\n");
            exit(1);
        } else {
            printf("rbuf[0] = %d\n", rbuf[0]);
        }
    }

}

8 IIC设备驱动系统流程分析

8.1 open

APP: fd = open("/dev/i2c-0", O_RDWR);
=====================================
VFS: sys_open

i2c-dev.c
    i2c_dev_init
    //硬件操作方法
    static const struct file_operations i2cdev_fops = {
    .open        = i2cdev_open,
        //获取次设备号
        unsigned int minor = iminor(inode);
        struct i2c_client *client;
        struct i2c_adapter *adap;
        
        //获取适配器
        adap = i2c_get_adapter(minor);

        client->adapter = adap; //制定client属于那个设备器控制
        file->private_data = client;

    return 0;
};
    

8.2 ioctl

APP: ioctl(fd, I2C_SLAVE, device_addr)
=====================================
VFS: sys_ioctl

i2c-dev.c
    i2c_dev_init
    //硬件操作方法
    static const struct file_operations i2cdev_fops = {
    .unlocked_ioctl    = i2cdev_ioctl,
        
        //获取client属性
        struct i2c_client *client = file->private_data;
        switch (cmd) {
            case I2C_SLAVE:
                client->addr = arg;

    return 0;
};

8.3 write

APP: write(fd, wbuf, 2) != 2
=====================================
VFS: sys_ioctl

i2c-dev.c
    i2c_dev_init
    //硬件操作方法
    static const struct file_operations i2cdev_fops = {
    .write        = i2cdev_write,
        
        //获取client属性
        struct i2c_client *client = file->private_data;
        //获取用户空间数据
        tmp = memdup_user(buf, count);
            copy_from_user(p, src, len)
        //发送i2c数据        
        ret = i2c_master_send(client, tmp, count);

i2c-core.c
        //发送i2c数据
        ret = i2c_master_send(client, tmp, count);
        
        //构建i2c数据包
        struct i2c_msg msg = {
            .addr = client->addr,
            .flags = flags | (client->flags & I2C_M_TEN),
            .len = count,
            .buf = buf,
        };

        //发送i2c数据包
        ret = i2c_transfer(client->adapter, &msg, 1);
            adap->algo->master_xfer(adap, msgs, num);
        
i2c-s3c2410.c
        //调用i2c总线驱动操作方法
        adap->algo->master_xfer(adap, msgs, num);
        static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
            .master_xfer        = s3c24xx_i2c_xfer,
            .functionality        = s3c24xx_i2c_func,
        };

9 IIC设备驱动资源层分析

arch/arm/mach-s3c/devs.c

/* I2C */
//构建平台资源
static struct resource s3c_i2c0_resource[] = {
    [0] = DEFINE_RES_MEM(S3C_PA_IIC, SZ_4K),
    [1] = DEFINE_RES_IRQ(IRQ_IIC),
};

//构建i2c结构体
struct platform_device s3c_device_i2c0 = {
    .name        = "s3c2410-i2c",
    .id        = 0,
    .num_resources    = ARRAY_SIZE(s3c_i2c0_resource),
    .resource    = s3c_i2c0_resource,
};

//构建i2c平台数据
struct s3c2410_platform_i2c default_i2c_data __initdata = {
    .flags        = 0,
    .slave_addr    = 0x10,
    .frequency    = 100*1000,
    .sda_delay    = 100, //间隔时间
};

//设置i2c平台数据
void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)
{
    struct s3c2410_platform_i2c *npd;

    if (!pd) {
        pd = &default_i2c_data;
        pd->bus_num = 0;
    }

    npd = s3c_set_platdata(pd, sizeof(*npd), &s3c_device_i2c0);

    if (!npd->cfg_gpio)
        npd->cfg_gpio = s3c_i2c0_cfg_gpio; //配置i2c GPIO 功能
}
//注册平台设备
arch/arm/mach-s3c/smdk6400.c
static struct platform_device *smdk6400_devices[] __initdata = {
    &s3c_device_i2c0,
};

static struct i2c_board_info i2c_devs[] __initdata = {
    { I2C_BOARD_INFO("wm8753", 0x1A), },
    { I2C_BOARD_INFO("24c08", 0x50), },
};

static void __init smdk6400_machine_init(void)
{
    //注册i2c板块信息
    i2c_register_board_info(0, i2c_devs, ARRAY_SIZE(i2c_devs));
    //注册平台设备
    platform_add_devices(smdk6400_devices, ARRAY_SIZE(smdk6400_devices));
}

10 IIC设备总线驱动层分析

//构建i2c平台设备驱动结构体
//通过id_table匹配对应驱动,如果匹配会调用probe函数
static struct platform_driver s3c24xx_i2c_driver = {
    .probe        = s3c24xx_i2c_probe, //通过name匹配
    .remove        = s3c24xx_i2c_remove,
    .id_table    = s3c24xx_driver_ids,
    .driver        = {
        .name    = "s3c-i2c",
        .pm    = S3C24XX_DEV_PM_OPS,
        .of_match_table = of_match_ptr(s3c24xx_i2c_match),
    },
};

//注册平台设备驱动
//1.将平台驱动结构体加入驱动链表
//2.搜索平台设备链表,每搜索一个会调用平台总线match,按照平台设备名字与平台驱动id_table名字进行匹配,匹配成功,调用probe函数
static int __init i2c_adap_s3c_init(void)
{
    return platform_driver_register(&s3c24xx_i2c_driver);
}
subsys_initcall(i2c_adap_s3c_init);

//注销平台设备驱动
static void __exit i2c_adap_s3c_exit(void)
{
    platform_driver_unregister(&s3c24xx_i2c_driver);
}
module_exit(i2c_adap_s3c_exit);
s3c24xx_i2c_probe:
    //获取平台数据资源
    pdata = dev_get_platdata(&pdev->dev);

    //配置协议
    strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
    i2c->adap.owner = THIS_MODULE;
    //设置adapter,发送数据接口,里面配置了时序
    i2c->adap.algo = &s3c24xx_i2c_algorithm;//i2c时序操作函数
    i2c->adap.retries = 2;
    i2c->adap.class = I2C_CLASS_DEPRECATED;
    i2c->tx_setup = 50;


    //获取平台资源,IO内存地址资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    //物理地址映射为虚拟地址
    i2c->regs = devm_ioremap_resource(&pdev->dev, res);

    //初始化i2c控制器,硬件相关操作
    ret = s3c24xx_i2c_init(i2c);
    
    //获取IRQ资源
    i2c->irq = ret = platform_get_irq(pdev, 0);
    //申请IRQ
    ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq,
                       0, dev_name(&pdev->dev), i2c);
        //利用i2c中断方式发送/接受数据
        s3c24xx_i2c_irq  -->  i2c_s3c_irq_nextbyte

    //设置平台驱动数据
    platform_set_drvdata(pdev, i2c);
            
    //注册i2c适配器
    ret = i2c_add_numbered_adapter(&i2c->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);
最后修改:2022 年 09 月 27 日
如果觉得我的文章对你有用,请随意赞赏