• 树莓派 -- i2c学习


    硬件平台

    • RaspberryPi-3B+
    • Pioneer600外扩版

    i2c芯片为DS3231,adddress 0x68

    首先来看一下i2ctool的使用

    i2ctool 使用

    https://i2c.wiki.kernel.org/index.php/I2C_Tools
    https://git.kernel.org/pub/scm/utils/i2c-tools/i2c-tools.git/tree/

    i2cdetect

    总线扫描

    pi@raspberrypi:~ $ i2cdetect -l
    i2c-1   i2c         bcm2835 I2C adapter                 I2C adapter

    设备扫描

    pi@raspberrypi:~ $ i2cdetect -y 1
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
    10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- 
    50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- -- 
    70: -- -- -- -- -- -- 76 --

    i2cdump

    Usage: i2cdump [-f] [-y] [-r first-last] I2CBUS ADDRESS [MODE [BANK [BANKREG]]]
      I2CBUS is an integer or an I2C bus name
      ADDRESS is an integer (0x03 - 0x77)
      MODE is one of:
        b (byte, default)
        w (word)
        W (word on even register addresses)
        s (SMBus block)
        i (I2C block)
        c (consecutive byte)
        Append p for SMBus PEC
    pi@raspberrypi:/dev $ i2cdump -y -r 0x00-0x0f 1 0x68 
    No size specified (using byte-data access)
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
    00: 27 48 14 05 19 06 15 00 00 00 00 00 00 00 1c 88    'H?????.......??

    i2c控制器

    processor 外设中一般会集成i2c控制器。 i2c控制器在驱动模型中又称为i2c_adapter.
    在raspberryPi中查看到i2c-adapter如下

    pi@raspberrypi:/sys/class/i2c-adapter/i2c-1 $ ls
    delete_device  device  i2c-dev  name  new_device  of_node  power  subsystem  uevent
    pi@raspberrypi:/sys/class/i2c-adapter/i2c-1 $ cat name 
    bcm2835 I2C adapter

    i2c-adapter的驱动是基于platform bus的
    总线驱动

    pi@raspberrypi:/sys/bus/platform/drivers/i2c-bcm2835 $ ls
    3f804000.i2c  bind  module  uevent  unbind

    总线设备

    pi@raspberrypi:/sys/bus/platform/devices/3f804000.i2c $ ls
    driver  driver_override  i2c-1  modalias  of_node  power  subsystem  uevent
    pi@raspberrypi:/dev $ ls | grep i2c
    i2c-1

    driver

    相关源码driversi2cussesi2c-bcm2835.c
    下面就只列出probe函数

    /*
     * BCM2835 master mode driver
     */
    
    static int bcm2835_i2c_probe(struct platform_device *pdev)
    {
        struct bcm2835_i2c_dev *i2c_dev;
        struct resource *mem, *irq;
        int ret;
        struct i2c_adapter *adap;
    
        i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
        if (!i2c_dev)
            return -ENOMEM;
        platform_set_drvdata(pdev, i2c_dev);
        i2c_dev->dev = &pdev->dev;
        init_completion(&i2c_dev->completion);
    
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        i2c_dev->regs = devm_ioremap_resource(&pdev->dev, mem);
        if (IS_ERR(i2c_dev->regs))
            return PTR_ERR(i2c_dev->regs);
    
        i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(i2c_dev->clk)) {
            if (PTR_ERR(i2c_dev->clk) != -EPROBE_DEFER)
                dev_err(&pdev->dev, "Could not get clock
    ");
            return PTR_ERR(i2c_dev->clk);
        }
    
        ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
                       &i2c_dev->bus_clk_rate);
        if (ret < 0) {
            dev_warn(&pdev->dev,
                 "Could not read clock-frequency property
    ");
            i2c_dev->bus_clk_rate = 100000;
        }
    
        irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (!irq) {
            dev_err(&pdev->dev, "No IRQ resource
    ");
            return -ENODEV;
        }
        i2c_dev->irq = irq->start;
    
        ret = request_irq(i2c_dev->irq, bcm2835_i2c_isr, IRQF_SHARED,
                  dev_name(&pdev->dev), i2c_dev);
        if (ret) {
            dev_err(&pdev->dev, "Could not request IRQ
    ");
            return -ENODEV;
        }
    
        adap = &i2c_dev->adapter;
        i2c_set_adapdata(adap, i2c_dev);
        adap->owner = THIS_MODULE;
        adap->class = I2C_CLASS_DEPRECATED;
        strlcpy(adap->name, "bcm2835 I2C adapter", sizeof(adap->name));
        adap->algo = &bcm2835_i2c_algo;
        adap->dev.parent = &pdev->dev;
        adap->dev.of_node = pdev->dev.of_node;
        adap->quirks = &bcm2835_i2c_quirks;
    
        bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0);
    
        ret = i2c_add_adapter(adap);
        if (ret)
            free_irq(i2c_dev->irq, i2c_dev);
    
        return ret;
    }
    
    
    
    
    static const struct of_device_id bcm2835_i2c_of_match[] = {
        { .compatible = "brcm,bcm2835-i2c" },
        {},
    };
    MODULE_DEVICE_TABLE(of, bcm2835_i2c_of_match);
    
    static struct platform_driver bcm2835_i2c_driver = {
        .probe      = bcm2835_i2c_probe,
        .remove     = bcm2835_i2c_remove,
        .driver     = {
            .name   = "i2c-bcm2835",
            .of_match_table = bcm2835_i2c_of_match,
        },
    };
    module_platform_driver(bcm2835_i2c_driver);
    
    MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>");
    MODULE_DESCRIPTION("BCM2835 I2C bus adapter");
    MODULE_LICENSE("GPL v2");
    MODULE_ALIAS("platform:i2c-bcm2835");
    

    可以看到在probe函数中调用了i2c_add_adapter来注册i2c-adapter到i2ccore.

    设备树中的i2c-adapter设备

    查看源码archarmootdtscm283x.dtsi,

            i2c1: i2c@7e804000 {
                compatible = "brcm,bcm2835-i2c";
                reg = <0x7e804000 0x1000>;
                interrupts = <2 21>;
                clocks = <&clocks BCM2835_CLOCK_VPU>;
                #address-cells = <1>;
                #size-cells = <0>;
                status = "disabled";
            };

    i2c外设

    添加rtc设备

    pi@raspberrypi:/sys/class/i2c-adapter/i2c-1 $ echo ds3231 0x68 | sudo tee new_device 
    ds3231 0x68

    在目录下多了一个1-0068

    pi@raspberrypi:/sys/class/i2c-adapter/i2c-1 $ ls
    1-0068         device   name        of_node  subsystem
    delete_device  i2c-dev  new_device  power    uevent
    pi@raspberrypi:/sys/class/i2c-adapter/i2c-1 $ cd 1-0068/
    pi@raspberrypi:/sys/class/i2c-adapter/i2c-1/1-0068 $ ls
    driver  hwmon  modalias  name  power  rtc  subsystem  uevent
    pi@raspberrypi:/sys/class/i2c-adapter/i2c-1/1-0068 $ cat name 
    ds3231

    查看1-0068

    pi@raspberrypi:/sys/class/i2c-adapter/i2c-1/1-0068 $ cd rtc/
    pi@raspberrypi:/sys/class/i2c-adapter/i2c-1/1-0068/rtc $ ls
    rtc0
    pi@raspberrypi:/sys/class/i2c-adapter/i2c-1/1-0068/rtc $ cd rtc0/
    pi@raspberrypi:/sys/class/i2c-adapter/i2c-1/1-0068/rtc/rtc0 $ ls
    date  device   max_user_freq  power        subsystem  uevent
    dev   hctosys  name           since_epoch  time
    pi@raspberrypi:/sys/class/i2c-adapter/i2c-1/1-0068/rtc/rtc0 $ cat name 
    rtc-ds1307 1-0068
    pi@raspberrypi:/sys/class/i2c-adapter/i2c-1/1-0068/rtc/rtc0 $

    查看/dev/rtc0

    pi@raspberrypi:/dev $ ls -la |grep rtc
    lrwxrwxrwx  1 root root           4 Jul 14 13:43 rtc -> rtc0
    crw-------  1 root root    253,   0 Jul 14 13:43 rtc0

    测试

    pi@raspberrypi:~ $ sudo hwclock 
    2000-01-01 00:07:24.525370+0000
    pi@raspberrypi:~ $ sudo hwclock --debug
    hwclock from util-linux 2.29.2
    Using the /dev interface to the clock.
    Assuming hardware clock is kept in UTC time.
    Waiting for clock tick...
    /dev/rtc does not have interrupt functions. Waiting in loop for time from /dev/rtc to change
    ...got clock tick
    Time read from Hardware Clock: 2000/01/01 00:07:40
    Hw clock time : 2000/01/01 00:07:40 = 946685260 seconds since 1969
    Time since last adjustment is 946685260 seconds
    Calculated Hardware Clock drift is 0.000000 seconds
    2000-01-01 00:07:39.551217+0000

    添加rtc设备分析

    i2c设备的实例化有多种方式,在上文中,采用的通过sysfs的方式来实例化。

    通过sysfs实例化i2c设备

    源码driversi2ci2c-core-base.c,中

    /*
     * Let users instantiate I2C devices through sysfs. This can be used when
     * platform initialization code doesn't contain the proper data for
     * whatever reason. Also useful for drivers that do device detection and
     * detection fails, either because the device uses an unexpected address,
     * or this is a compatible device with different ID register values.
     *
     * Parameter checking may look overzealous, but we really don't want
     * the user to provide incorrect parameters.
     */
    static ssize_t
    i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,
                 const char *buf, size_t count)
    {
        struct i2c_adapter *adap = to_i2c_adapter(dev);
        struct i2c_board_info info;
        struct i2c_client *client;
        char *blank, end;
        int res;
    
        memset(&info, 0, sizeof(struct i2c_board_info));
    
        blank = strchr(buf, ' ');
        if (!blank) {
            dev_err(dev, "%s: Missing parameters
    ", "new_device");
            return -EINVAL;
        }
        if (blank - buf > I2C_NAME_SIZE - 1) {
            dev_err(dev, "%s: Invalid device name
    ", "new_device");
            return -EINVAL;
        }
        memcpy(info.type, buf, blank - buf);
    
        /* Parse remaining parameters, reject extra parameters */
        res = sscanf(++blank, "%hi%c", &info.addr, &end);
        if (res < 1) {
            dev_err(dev, "%s: Can't parse I2C address
    ", "new_device");
            return -EINVAL;
        }
        if (res > 1  && end != '
    ') {
            dev_err(dev, "%s: Extra parameters
    ", "new_device");
            return -EINVAL;
        }
    
        if ((info.addr & I2C_ADDR_OFFSET_TEN_BIT) == I2C_ADDR_OFFSET_TEN_BIT) {
            info.addr &= ~I2C_ADDR_OFFSET_TEN_BIT;
            info.flags |= I2C_CLIENT_TEN;
        }
    
        if (info.addr & I2C_ADDR_OFFSET_SLAVE) {
            info.addr &= ~I2C_ADDR_OFFSET_SLAVE;
            info.flags |= I2C_CLIENT_SLAVE;
        }
    
        client = i2c_new_device(adap, &info);
        if (!client)
            return -EINVAL;
    
        /* Keep track of the added device */
        mutex_lock(&adap->userspace_clients_lock);
        list_add_tail(&client->detected, &adap->userspace_clients);
        mutex_unlock(&adap->userspace_clients_lock);
        dev_info(dev, "%s: Instantiated device %s at 0x%02hx
    ", "new_device",
             info.type, info.addr);
    
        return count;
    }

    通过上面的函数可以看出,在命令

    echo ds3231 0x68 | sudo tee new_device

    中,ds3231被传递到i2c_board_info 的type中, 然后调用i2c_new_device

    struct i2c_board_info {
        char        type[I2C_NAME_SIZE];
        unsigned short  flags;
        unsigned short  addr;
        const char  *dev_name;
        void        *platform_data;
        struct dev_archdata *archdata;
        struct device_node *of_node;
        struct fwnode_handle *fwnode;
        const struct property_entry *properties;
        const struct resource *resources;
        unsigned int    num_resources;
        int     irq;
    };

    源码drivers tc tc-ds1307.c中

    static const struct i2c_device_id ds1307_id[] = {
        { "ds1307", ds_1307 },
        { "ds1308", ds_1308 },
        { "ds1337", ds_1337 },
        { "ds1338", ds_1338 },
        { "ds1339", ds_1339 },
        { "ds1388", ds_1388 },
        { "ds1340", ds_1340 },
        { "ds1341", ds_1341 },
        { "ds3231", ds_3231 },
        { "m41t0", m41t0 },
        { "m41t00", m41t00 },
        { "mcp7940x", mcp794xx },
        { "mcp7941x", mcp794xx },
        { "pt7c4338", ds_1307 },
        { "rx8025", rx_8025 },
        { "isl12057", ds_1337 },
        { "rx8130", rx_8130 },
        { }
    };
    enum ds_type {
        ds_1307,
        ds_1308,
        ds_1337,
        ds_1338,
        ds_1339,
        ds_1340,
        ds_1341,
        ds_1388,
        ds_3231,
        m41t0,
        m41t00,
        mcp794xx,
        rx_8025,
        rx_8130,
        last_ds_type /* always last */
        /* rs5c372 too?  different address... */
    };

    Reference

    https://blog.csdn.net/lizuobin2/article/details/51694574
    https://www.linuxidc.com/Linux/2014-05/101649.htm
    https://blog.csdn.net/qq_33160790/article/details/69048520

  • 相关阅读:
    exceljs的使用
    解决ios下的微信打开的页面背景音乐无法自动播放
    js 给定日期转为星期几
    获取地址栏参数
    解决浏览器缓存 或 刷新URL地址
    计算两个日期之间的天数
    获取当前日期的前后N天日期的方法
    将一下 prototype 是什么东西,原型链的理解,什么时候用 prototype
    apply和 call 什么含义,什么区别?什么时候用
    高效Web开发的10个jQuery代码片段
  • 原文地址:https://www.cnblogs.com/feiwatson/p/9478206.html
Copyright © 2020-2023  润新知