硬件平台

  • 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

树莓派 -- i2c学习的更多相关文章

  1. 树莓派 -- i2c学习 续(1) DeviceTree Overlay实例化rtc

    上文中讨论了通过sysfs来实例化i2c设备 (rtc ds3231) https://blog.csdn.net/feiwatson/article/details/81048616 本文继续看看如 ...

  2. 树莓派4B学习札记

    防静电 树莓派比较容易被静电损坏,要做好以下预防措施 使用的时候不要用手去触摸PCB和针脚!特别是上电之后! 拿板卡的时候,要习惯性拿板卡的边缘 勤洗手,勤摸墙壁,释放身上的静电 系统安装 8GB以上 ...

  3. qnx i2c 学习 二

    File still Updating.... many errors have been FOUND , need big change  qnx i2c structure  --written ...

  4. 树莓派3B+学习笔记:1、安装官方系统

    1.登录树莓派官方网站www.raspberrypi.org,点击Downloads: 2.点击NOOBS: 3.选择下载方式,可以选择下载BT种子或直接下载,这里我用迅雷直接下载,下载速度还是很快的 ...

  5. MSP430F5438 I2C学习笔记——AT24C02

    0.前言 对于大多数单片机来说,I2C成了一个老大难问题.从51时代开始,软件模拟I2C成了主流,甚至到ARMCortex M3大行其道的今天,软件模拟I2C依然是使用最广的方法.虽然软件模拟可以解决 ...

  6. 树莓派3B+学习笔记:13、不间断会话服务screen

    screen是一款能够实现多窗口远程控制的开源服务程序,简单来说就是为了解决网络异常中断或为了同时控制多个远程终端窗口而设计的程序.用户还可以使用screen服务程序同时在多个远程会话中自由切换,能够 ...

  7. 树莓派3B+学习笔记:11、查看硬件信息

    1.查看CPU信息 cat /proc/cpuinfo 查看最后三行 如果只想查看最后三行,也可使用这个命令 /proc/cpuinfo lscpu 2.查看树莓派型号 cat /proc/devic ...

  8. 树莓派3B+学习笔记:10、使用SSH连接树莓派

    SSH(Secure Shell)是一种能够以安全的方式提供远程登录的协议,也是目前远程管理Linux系统的首选方式. 1.开启树莓派3B+的SSH远程管理功能,在终端中输入以下命令: sudo ra ...

  9. 树莓派3B+学习笔记:9、更改软件源

    树莓派系统安装完成后,由于默认软件源服务器访问速度慢,安装软件耗时会很长,可以通过更改软件源来加快软件的安装速度. 系统安装完成后默认软件源如下: 更改镜像源前需要自行查找镜像源,并记下网址: 1.阿 ...

随机推荐

  1. python 面向对象二 类和实例

    一.类和实例 面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法, ...

  2. bzoj 1597: [Usaco2008 Mar]土地购买【斜率优化】

    按xy降序排序,把能被完全包含的去掉 然后就得到了x升序y降序的一个数组 然后方程就显然了:f[i]=min(f[j]+y[j+1]x[i]) 斜率优化转移 说起来我还不会斜率优化呢是不是该学一下了 ...

  3. [App Store Connect帮助]八、维护您的 App(1)App 维护概述

    您在 App Store 上发行 App 后,需要执行一些任务来回复顾客反馈并在整个产品周期内维护您的 App.您可以按任何顺序执行这些任务. 监控顾客评论.销售情况和分析 您可以在 App 页面的“ ...

  4. Ubuntu 18 开机后直接进入命令行界面,没法进入桌面

    应该是之前不知道干啥,删了gnome的一个东西,导致没法正常进入 暴力解决,直接重装桌面环境 sudo apt install ubuntu-desktop

  5. PhpStorm Xdebug远程调试环境搭建原理分析及问题排查

    2017年05月26日  经验心得 目录   一. 环境介绍 二. 远程环境配置 2.2 Xdebug安装 2.3 配置 三. 本地phpstorm配置 3.1 下载远程代码 3.2 添加php解释器 ...

  6. 环境变量解释以及在Linux下的环境变量设置

    一.环境变量解释 环境变量是什么? 引用百度百科里面的解释:环境变量是操作系统中一个具有特定名字的对象,它包含了一个或者多个应用程序所将使用到的信息.例如Windows系统中的path环境变量,当要求 ...

  7. [BZOJ1382]Mars Maps

    Description In the year 2051, several Mars expeditions have explored different areas of the red plan ...

  8. Linux环境下Apache反向代理金蝶中间件Apusic集群

    操作系统:RedHat Enterprise Linux 5.6 文档参考:<金蝶Apusic应用服务器 帮助手册| IX. Apusic Http Server使用指南> 一.金蝶中间件 ...

  9. $.each遍历json对象(java将对象转化为json格式以及将json解析为普通对象)

    查看一个简单的jQuery的例子来遍历一个JavaScript数组对象. var json = [ {"id":"1","tagName": ...

  10. 导出数据库报错 EXP-00002: 写入导出文件时出错 EXP-00000: 导出终止失败

    解决方法: 1.检查磁盘所在空间是否够用. 2.磁盘修复下 排除故障考虑的地方要全面啊.