硬件平台

  • 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. E20170519-ts

    numeric adj. 数字的; 数值的; nibble   vt. 啃,一点一点地咬(吃); rational adj. 理性的; 合理的; n. 合理的事物; [数] 有理数; numerato ...

  2. ElasticSearch | windows 上安装ES

    Elastatic需要java JAVA8 环境,确保安装好环境 在windows上安装ES还是比较简单的, 1.首先在官网上下载zip,地址 https://www.elastic.co/downl ...

  3. 《Windows核心编程系列》十二谈谈Windows内存体系结构

    Windows内存体系结构 理解Windows内存体系结构是每一个励志成为优秀的Windows程序员所必须的. 进程虚拟地址空间 每个进程都有自己的虚拟地址空间.对于32位操作系统来说,它的地址空间是 ...

  4. Kay and Snowflake CodeForces - 686D

    Kay and Snowflake CodeForces - 686D 题意:给一棵有根树,有很多查询(100000级别的),查询是求以任意一点为根的子树的任意重心. 方法很多,但是我一个都不会 重心 ...

  5. Android 插件技术:动态加载dex技术初探

    1.Android动态加载dex技术初探 http://blog.csdn.net/u013478336/article/details/50734108 Android使用Dalvik虚拟机加载可执 ...

  6. Elasticsearch--集群管理_别名&插件&更新API

    目录 使用索引别名 别名 创建别名 修改别名 合并命令 获取所有别名 移除别名 别名中过滤 别名和路由 Elasticsearch插件 基础知识 安装插件 移除插件 更新设置API 使用索引别名 通过 ...

  7. 浅谈网上的zoomlistview存在的问题

    最近项目主要是做一个类似wps文档阅历的功能,以列表的形式显示文档,并且需要实现缩放平移.而网上关于此类功能的实现主要是通过自定义的listview实现的,类名为ZoomListView. 网上的zo ...

  8. 短视频SDK用于旅游行业

    超级简单易用的短视频SDK来自RDSDK.COM.锐动天地为开发者提供短视频编辑.视频直播.特效.录屏.编解码.视频转换,等多种解决方案,涵盖PC.iOS.Android多平台.以市场为导向,不断打磨 ...

  9. 更改ligerui源码实现分页样式修改

    修改后样式: 第一步:实现功能. 更改源码部分ligerui.all.js文件 读源代码,发现ligerui底部工具条是这样实现的(ps:注释部分为源码) _render: function () { ...

  10. 解决qt提示:qt.network.ssl: QSslSocket: cannot call unresolved function DH_free和qt.network.ssl: QSslSocket: cannot call unresolved function d2i_DHparams

    转载请注明出处:https://i.cnblogs.com/EditPosts.aspx?postid=7127254 运行环境:VS2015&Qt5.8 方法一(未能解决):把C:\Qt\Q ...