硬件平台

  • 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

相关源码drivers\i2c\busses\i2c-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\n");
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\n");
i2c_dev->bus_clk_rate = 100000;
} irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq) {
dev_err(&pdev->dev, "No IRQ resource\n");
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\n");
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设备

查看源码arch\arm\boot\dts\bcm283x.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设备

源码drivers\i2c\i2c-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\n", "new_device");
return -EINVAL;
}
if (blank - buf > I2C_NAME_SIZE - 1) {
dev_err(dev, "%s: Invalid device name\n", "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\n", "new_device");
return -EINVAL;
}
if (res > 1 && end != '\n') {
dev_err(dev, "%s: Extra parameters\n", "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\n", "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\rtc\rtc-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

最新文章

  1. 使用putty与SSHSecureShellClient登录远程服务器完成与本地Git项目的同步
  2. github搭建静态博客
  3. apktool+dex2jar+xjad反编译android程序
  4. Linux mke2fs 硬盘格式化
  5. Div 添加阴影
  6. XPATH 注入的介绍与代码防御
  7. Huffman编码实现电文的转码与译码
  8. Holding Bin-Laden Captive!(母函数)
  9. hdoj 3018 Ant Trip(无向图欧拉路||一笔画+并查集)
  10. CSDN markdown 编辑 三 基本语法
  11. Python 2.7.3的文件编码问题,print在控制台下面中文乱码问题,以及推荐做法
  12. Oracle:FOR循环语句练习
  13. Linux下内存问题检测神器:Valgrind
  14. 数据结构(java版)学习笔记(二)——线性表之顺序表
  15. swipe.js实现支持手拔与自动切换的图片轮播
  16. 判断浏览器大于等于ie9
  17. 【转】javaUDP套接字通信
  18. laravel路由别名
  19. spring 配置 applicationContext.xml
  20. 将VirtualBox里安装的虚拟机在后台运行方法(在状态栏隐藏窗口)

热门文章

  1. 洛谷 P2770 航空路线问题【最大费用最大流】
  2. 动态规划基础复习 By cellur925
  3. Hdu 5361 In Touch (dijkatrs+优先队列)
  4. ACM输入外挂
  5. 题解报告:hdu 2057 A + B Again
  6. android:process用法
  7. 421 Maximum XOR of Two Numbers in an Array 数组中两个数的最大异或值
  8. git免密码
  9. HTML5 File API的应用
  10. H.264学习笔记1——相关概念