前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >张高兴的 .NET IoT 入门指南:(三)使用 I2C 进行通信

张高兴的 .NET IoT 入门指南:(三)使用 I2C 进行通信

作者头像
张高兴
发布于 2025-05-21 05:47:08
发布于 2025-05-21 05:47:08
11100
代码可运行
举报
文章被收录于专栏:张高兴的博客张高兴的博客
运行总次数:0
代码可运行

什么是 I2C 总线

I2C 总线(Inter-Integrated Circuit Bus)是设备与设备间通信方式的一种。它是一种串行通信总线,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展[1]。I2C 总线包含两根信号线,一根为信号线 SDA ,另一根为时钟线 SCL 。总线上可以挂载多个设备,以 7 位 I2C 地址为例,总线上最多可以挂载 27 - 1 个设备,即 127 个,地址 0x00 不用(类似于网络中的广播地址)。I2C 还包括一个子集叫 SMBus (System Management Bus),是 1995 年由 Intel 提出的[2]。为什么说是子集,是因为 SMBus 是 I2C 的简化版,电气特性和传输速率等方面上略有不同。下图展示了一个 I2C 主设备和三个 I2C 从设备的示意图,总线上只能有一个主设备,而通常情况下你的主机(如 Raspberry Pi,Arduino)就是主设备,传感器为从设备。

图1:I2C 设备

I2C 总线也并不是那么完美。因为 I2C 只有两根信号线,与 SPI 的四根信号线相比,传输速率上并不占优,而且数据在同一时间内只能向一个方向传输。但反过来看,I2C 总线的最大优点是只需要占用两个 IO 接口,在单片机等 IO 接口数量较少的设备上也算是一种优势吧。

在 Raspberry Pi 的引脚中,引出了一组 I2C 接口,其内部总线 ID 为 1,引脚中的 GPIO 2 为 SDA,GPIO 3 为 SCL(如下图所示)。至于 I2C-0,它用于 Raspberry Pi 内部的 GPIO 扩展器、相机、显示器等其他设备。Raspberry Pi 的 I2C 引脚中内置了一个 1.8 kΩ 的上拉电阻,这意味着在一般情况下使用 I2C 总线时不必再连接一个额外的上拉电阻。

Raspberry Pi B+/2B/3B/3B+/Zero 引脚图

相关类

I2C 操作的相关类位于 System.Device.I2c 命名空间下。

I2cConnectionSettings

I2cConnectionSettings 类位于 System.Device.I2c 命名空间下,表示 I2C 设备的连接设置。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public sealed class I2cConnectionSettings
{
    // 构造函数
    // busId 是 I2C 总线的内部 ID,在 Raspberry Pi 上只能填 1
    // deviceAddress 是要连接设备的 I2C 地址
    public I2cConnectionSettings(int busId, int deviceAddress);
}

I2cDevice

I2cDevice 是一个抽象类,通过单例模式创建具体的对象。具体实现是通过两个内部类 UnixI2cDeviceWindows10I2cDevice ,分别代表 Unix 和 Windows10 下的 I2C 控制器。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract partial class I2cDevice : IDisposable
{
    // 创建 I2cDevice 对象
    public static I2cDevice Create(I2cConnectionSettings settings);

    // 从从设备中读取一段数据,数据长度由 Span 的长度决定
    public abstract void Read(Span<byte> buffer);
    // 从从设备中读取一个字节的数据
    public abstract byte ReadByte();

    // 向从设备中写入一段数据,通常 Span 中的第一个数据为要写入数据的寄存器的地址
    public abstract void Write(ReadOnlySpan<byte> data);
    // 向从设备中写入一个字节的数据,通常这个字节为寄存器的地址
    public abstract void WriteByte(byte data);

    // 向从设备中写入一段数据,然后从该设备中读取一段数据
    // 该方法多用于 SMBus 协议
    public abstract void WriteRead(ReadOnlySpan<byte> writeBuffer, Span<byte> readBuffer);
}

I2C 总线的通信步骤

在开始实验之前,首先说明一下 I2C 总线的读取和写入的步骤。因为 .NET 帮我们封装好了一些操作方法,这大大简化了 I2C 的操作难度,即使你没有丰富的硬件知识也可以顺利的操作硬件,所以我们不必像开发单片机一样去研究设备之间通信的时序图(当然,如果通信出现错误的话还是需要用时序图帮助判断)。

读取

向从设备写入要读取的寄存器的地址

这类似于数组的指针,需要先定位到相应的位置才能读取。通常地址是一位的,只需要调用 WriteByte() 方法即可,但也有特殊情况,比如两个字节的地址或者命令+地址时,就需要调用 Write() 方法。

读取从设备中的数据

定位完成后就可以向从设备请求数据了。如果要读取一个字节的数据,那么就调用 ReadByte() 方法,如果要读取多个字节,首先需要实例化一个 byte 数组,通过调用 Read() 方法来读取多个数据,读取的数据取决于数组的长度。比如要读取 8 个字节的数据,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Span<byte> readBuffer = stackalloc byte[8]; 
sensor.Read(readBuffer);

写入

写入一般用于配置从设备的寄存器。因为你不可能只向从设备写入寄存器的地址吧,所以通常会调用 Write() 方法。比如向地址为 0x01 的寄存器写入一个字节的数据,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Span<byte> writeBuffer = stackalloc byte[] { 0x01, 0xFF }; 
sensor.Write(writeBuffer);

温湿度传感器读取实验

本实验选用的传感器为奥松的 DHT12。主要考虑到这个传感器读取非常简单,不用配置,价格便宜,很适合用来练手。数据手册地址:https://wenku.baidu.com/view/325b7096eff9aef8941e06f9.html 。

💡 提示

数据手册(Datasheet)是电子元件的使用说明书,包括介绍、电气特性、通信协议、性能等方面的内容。拿到数据手册时我们应该关注什么?

1. 关注该元件的通信协议。有些设备支持多种通信协议,如本实验用到的 DHT12 不仅支持 I2C,还支持 1-Wire 协议。选择合适的通信协议进行编程。

2. 关注打算使用的通信协议的细节。比如 I2C 总线,你需要关注元件的地址、各个寄存器的地址、最大传输速率等等。

3. 关注该元件的通信的细节。有些设备的通信很简单,并不需要拐弯抹角,但还有一些设备需要发送一些额外的命令。比如你在发送完寄存器地址后还需要紧接着发送一段命令,用于决定是读还是写该寄存器,返回数据时是按字节(byte)返回还是按字(word)返回等。

4. 关注各个寄存器的作用和配置。数据手册中基本上都会把每个寄存器逐条列出。

传感器图像

硬件需求

名称

数量

DHT12

x1

4.7 kΩ 电阻

x2

杜邦线

若干

电路

  • SDA - SDA (Pin 3)
  • SCL - SCL (Pin 5)
  • VCC - 5V
  • GND - GND

如果你的 DHT12 是裸板的话需要像电路图中一样给 SDA 和 SCL 加上上拉电阻。

使用 Docker 运行示例

示例地址:https://github.com/ZhangGaoxing/dotnet-core-iot-demo/tree/master/src/Dhtxx

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
docker build -t dht-sample -f Dockerfile .
docker run --rm -it --device /dev/i2c-1 dht-sample

代码

打开 Visual Studio ,新建一个 .NET Core 控制台应用程序,项目名称为“Dht12”。

引入 System.Device.Gpio NuGet 包。

新建类 Dht12,替换如下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Dht12 : IDisposable
{
    /// <summary>
    /// DHT12 默认 I2C 地址
    /// </summary>
    public const byte DefaultI2cAddress = 0x5C;    // 若数据手册中给的是8位的I2C地址要记得右移1位

    private I2cDevice _sensor;

    private double _temperature;
    /// <summary>
    /// DHT12 温度
    /// </summary>
    public double Temperature
    {
        get
        {
            ReadData();
            return _temperature;
        }
    }

    private double _humidity;
    /// <summary>
    /// DHT12 湿度
    /// </summary>
    public double Humidity
    {
        get
        {
            ReadData();
            return _humidity;
        }
    }

    /// <summary>
    /// 实例化一个 DHT12 对象
    /// </summary>
    /// <param name="sensor">I2C Device</param>
    public Dht12(I2cDevice sensor)
    {
        _sensor = sensor;
    }

    private void ReadData()
    {
        Span<byte> readBuff = stackalloc byte[5]; 

        // 数据手册第三页提供了寄存器地址表

        // DHT12 湿度寄存器地址
        _sensor.WriteByte(0x00);
        // 连续读取数据
        // 湿度整数位,湿度小数位,温度整数位,温度小数位,校验和
        _sensor.Read(readBuff);

        // 校验数据,校验方法见数据手册第五页
        // 校验位=湿度高位+湿度低位+温度高位+温度低位
        if ((readBuff[4] == ((readBuff[0] + readBuff[1] + readBuff[2] + readBuff[3]) & 0xFF)))
        {
            // 温度小数位的范围在0-9,所以与上0x7F即可
            double temp = readBuff[2] + (readBuff[3] & 0x7F) * 0.1;
            // 温度小数位第8个bit为1则表示采样得出的温度为负温
            temp = (readBuff[3] & 0x80) == 0 ? temp : -temp;

            double humi = readBuff[0] + readBuff[1] * 0.1;

            _temperature = temp;
            _humidity = humi;
        }
        else
        {
            _temperature = double.NaN;
            _humidity = double.NaN;
        }
    }
}

Program.cs 中,将主函数代码替换如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void Main(string[] args)
{
    I2cConnectionSettings settings = new I2cConnectionSettings(1, Dht12.DefaultI2cAddress);
    I2cDevice device = I2cDevice.Create(settings);

    using (Dht12 dht = new Dht12(device))
    {
        while (true)
        {
            Console.WriteLine($"Temperature: {dht.Temperature.ToString("0.0")} °C, Humidity: {dht.Humidity.ToString("0.0")} %");

            Thread.Sleep(2000);
        }
    }
}

发布、拷贝、更改权限、运行

效果图

参考

  1. I2C - Wikipedia:https://en.wikipedia.org/wiki/I²C
  2. I2C source code:https://github.com/dotnet/iot/tree/master/src/System.Device.Gpio/System/Device/I2c
  3. I2C总线 - 百度百科:https://baike.baidu.com/item/I2C总线/918424
  4. SMBus - 百度百科:https://baike.baidu.com/item/SMBus/1190006
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 土味儿编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
张高兴的 .NET Core IoT 入门指南:(三)使用 I2C 进行通信
I2C 总线(Inter-Integrated Circuit Bus)是设备与设备间通信方式的一种。它是一种串行通信总线,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展[1]。I2C 总线包含两根信号线,一根为信号线 SDA ,另一根为时钟线 SCL 。总线上可以挂载多个设备,以 7 位 I2C 地址为例,总线上最多可以挂载 27 - 1 个设备,即 127 个,地址 0x00 不用(类似于网络中的广播地址)。I2C 还包括一个子集叫 SMBus (System Management Bus),是 1995 年由 Intel 提出的[2]。为什么说是子集,是因为 SMBus 是 I2C 的简化版,电气特性和传输速率等方面上略有不同。下图展示了一个 I2C 主设备和三个 I2C 从设备的示意图,总线上只能有一个主设备,而通常情况下你的主机(如 Raspberry Pi,Arduino)就是主设备,传感器为从设备。
张高兴
2019/05/23
1.7K0
树莓派基础实验30:BMP180气压传感器实验
   BMP180气压计是新型数字气压传感器,具有非常高的性能,可用于高级移动设备,如智能手机,平板电脑和运动设备。它兼容BMP085传感器,但是在该基础上拥有许多改进,如更小的尺寸和更多的数字接口。
张国平
2020/09/27
1.4K0
张高兴的 Windows 10 IoT 开发笔记:使用 ADS1115 读取模拟信号
  考虑到 Raspberry Pi 读取模拟信号是很烦人的事情,更何况是在没人玩的 Windows 10 IoT 下,所以准备正儿八经的写点东西。   需求:使用 Raspberry Pi 读取输出模拟信号的 MQ 系列气体传感器。(GitHub:https://github.com/ZhangGaoxing/windows-iot-demo/tree/master/ADS1115)    由于 Raspberry Pi 3B 内部并没有集成 ADC,因此需要外接 ADC 来进行模数转换。外接 ADC 选
张高兴
2018/05/18
1.5K0
树莓派基础实验27:温湿度传感器DHT11 实验
   数字温湿度传感器DHT11是一种复合传感器,包含温度和湿度的校准数字信号输出。采用专用数字模块采集技术和温湿度传感技术,确保产品具有高可靠性和优异的长期稳定性。    该传感器包含一个电阻湿感元件和一个NTC温度测量设备,并与一个高性能8位微控制器连接。其精度:湿度+-5%RH, 温度+-2℃。量程:湿度20-90%RH, 温度0~50℃。采样周期:大于等于1秒/次。    在我们刚开始练习写传感器的时序时,DHT11非常适合新手入门练习如何写时序。
张国平
2020/09/27
5.5K0
【玩转 RT-Thread】I2C 内核
由飞利浦公司开发,支持设备间的短距离通信。i2c通信需要的引脚少,硬件实现简单、可扩展性强,被广泛应用在系统内多个集成电路(IC)间的通信。
攻城狮杰森
2022/06/03
5600
【玩转 RT-Thread】I2C 内核
张高兴的 Windows 10 IoT 开发笔记:BH1750FVI 光照度传感器
  BH1750FVI 是一款 IIC 接口的数字型光强度传感器集成电路。下面介绍一下其在 Windows 10 IoT Core 环境下的用法。   项目运行在 Raspberry Pi 2/3 上
张高兴
2018/05/18
7380
Python控制物联网设备实战代码指南
物联网(IoT,Internet of Things)是当前技术领域的热门话题,它通过互联网将各种物理设备连接起来,使它们能够进行数据交换和远程控制。Python作为一种高效且易于学习的编程语言,已经成为开发物联网应用的首选语言之一。本文将探讨如何使用Python进行物联网设备的控制与数据收集,并提供相应的代码示例。
一键难忘
2024/08/17
5930
树莓派学习-I2c通信
由于之前参加了学校的飞兆杯的比赛,题目是循迹小车,由于缺乏对于ldc1314芯片使用知识以及个人的能力原因,项目并没有做出来,但是还是学习了很多东西的。
全栈程序员站长
2022/08/25
1.4K0
张高兴的 .NET Core IoT 入门指南:(四)使用 SPI 进行通信
和上一篇文章的 I2C 总线一样,SPI(Serial Peripheral Interface,串行外设接口)也是设备与设备间通信方式的一种。SPI 是一种全双工(数据可以两个方向同时传输)的串行通信总线,由摩托罗拉于上个世纪 80 年代开发[1],用于短距离设备之间的通信。SPI 包含 4 根信号线,一根时钟线 SCK(Serial Clock,串行时钟),两根数据线 MOSI(Master Output Slave Input,主机输出从机输入)和 MISO(Master Input Slave Output,主机输入从机输出),以及一根片选信号 CS(Chip Select,或者叫 SS,Slave Select)。所谓的时钟线就是一种周期,两台设备数据传输不能各发各的,这样就没有意义,因此需要一种周期去对通信进行约束;数据线就是按照 MOSI 和 MISO 的中文翻译理解即可;片选信号用于主设备选择 SPI 上的从设备,I2C 是靠地址选择设备,而 SPI 靠的是片选信号,一般来说要选择哪个从设备只要将相应的 CS 线设置为低电平即可,特殊情况需要看数据手册。下图展示了一个 SPI 主设备和三个 SPI 从设备的示意图。
张高兴
2019/06/03
8190
Linux系统驱动之SMBus协议
SMBus: System Management Bus,系统管理总线。 SMBus最初的目的是为智能电池、充电电池、其他微控制器之间的通信链路而定义的。 SMBus也被用来连接各种设备,包括电源相关设备,系统传感器,EEPROM通讯设备等等。 SMBus 为系统和电源管理这样的任务提供了一条控制总线,使用 SMBus 的系统,设备之间发送和接收消息都是通过 SMBus,而不是使用单独的控制线,这样可以节省设备的管脚数。 SMBus是基于I2C协议的,SMBus要求更严格,SMBus是I2C协议的子集。
韦东山
2021/12/08
2.5K0
Linux系统驱动之SMBus协议
STM32使用硬件I2C读取SHTC3温湿度传感器并显示在0.96寸OLED屏上
STM32使用硬件I2C读取SHTC3温湿度传感器的数据并显示在0.96寸OLED屏上
zeruns
2022/12/17
1.4K0
STM32使用硬件I2C读取SHTC3温湿度传感器并显示在0.96寸OLED屏上
推荐一个好用的i2c调试小工具
在嵌入式开发仲,有时候需要确认硬件是否正常连接,设备是否正常工作,设备的地址是多少等等,这里我们就需要使用一个用于测试I2C总线的工具——i2c-tools。
嵌入式与Linux那些事
2022/04/08
2.3K0
Linux的I2C驱动框架分析
总线代表着同类设备需要共同遵守的工作时序,不同的总线对于物理电平的要求是不一样的,对于每个比特的电平维持宽度也是不一样,而总线上传递的命令也会有自己的格式约束。如I2C总线、USB总线、PCI总线等等。以I2C总线为例,在同一组I2C总线上连接着不同的I2C设备。
bigmagic
2020/03/17
5.2K0
STM32的I2C框图详解及通讯过程
  如果我们直接控制STM32 的两个GPIO 引脚,分别用作SCL 及SDA,按照上述信号的时序要求,直接像控制LED 灯那样控制引脚的输出(若是接收数据时则读取SDA 电平),就可以实现I2C 通讯。同样,假如我们按照USART 的要求去控制引脚,也能实现USART通讯。所以只要遵守协议,就是标准的通讯,不管您如何实现它,不管是ST 生产的控制器还是ATMEL 生产的存储器, 都能按通讯标准交互。
用户6754675
2019/12/20
2K0
STM32通信硬件 I2C
STM32F103系列的I²C控制器,可作为通信主机或从机,因此有四种工作模式可选择:主机发送模式、主机接收模式、从机发送模式、从机接收模式。
韦东山
2022/05/09
1.2K0
STM32通信硬件 I2C
张高兴的 .NET IoT 入门指南:(七)制作一个气象站
距离上一篇《张高兴的 .NET Core IoT 入门指南》系列博客的发布已经过去 2 年的时间了,2 年的时间 .NET 版本发生了巨大的变化,.NET Core 也已不复存在,因此本系列博客更名为 《张高兴的 .NET IoT 入门指南》,我也重新审阅了之前的内容进行了相应的更改以保证内容的时效性。
张高兴
2022/05/09
8440
张高兴的 .NET IoT 入门指南:(七)制作一个气象站
I2C spec 总结
速度由 SCL 决定,不同模式对上升沿的要求不一样,上升沿斜率受上拉电阻和等效电容影响。
Jasonangel
2022/10/25
1.6K0
I2C spec  总结
STM32软件I2C读取AM2320温湿度传感器数据
STM32单片机使用软件IIC读取AM2320温湿度传感器的数据并显示在0.96寸OLED屏上。
zeruns
2022/12/17
8440
STM32软件I2C读取AM2320温湿度传感器数据
i2c总线的通信协议(i2c通信协议原理)
I2C(Inter-integrated Circuit)总线支持设备之间的短距离通信,用于处理器和一些外围设备之间的接口,它只需要两根信号线来完成信息交换。I2C最早是飞利浦在1982年开发设计并用于自己的芯片上,一开始只允许100kHz、7-bit标准地址。1992年,I2C的第一个公共规范发行,增加了400kHz的快速模式以及10-bit扩展地址。在I2C的基础上,1995年Intel提出了“System Management Bus” (SMBus),用于低速设备通信,SMBus 把时钟频率限制在10kHz~100kHz,但I2C可以支持0kHz~5MHz的设备:普通模式(100kHz即100kbps)、快速模式(400kHz)、快速模式+(1MHz)、高速模式(3.4MHz)和超高速模式(5MHz)。
全栈程序员站长
2022/07/29
2K0
i2c总线的通信协议(i2c通信协议原理)
使用 .NET Core 3.0 进行跨平台 IoT 编程
Microsoft Build 2019 为 .NET 开发人员带来了令人激动的消息:.NET Core 3.0 现在支持 C# 8.0、Windows 桌面和 IoT,因此,可以使用现有的 .NET 技能为智能设备开发跨平台应用。在本文中,我将向你演示如何使用 Sense HAT 附加板为 Raspberry Pi 2/3 创建一个 .NET Core 应用。该应用将获得各种传感器读数,并可通过 ASP.NET Core Web API 服务获取最新读数。我将使用 Swagger(图 1)为此服务创建简单的 UI,这样,你可以轻松地与 IoT 设备进行交互。除了从设备获取数据外,还可以远程更改 Sense HAT LED 阵列的颜色(图 2)。可通过我的 GitHub 页面 bit.ly/2WCj0G2 获得随附的代码。
AI.NET 极客圈
2020/05/18
3.1K0
相关推荐
张高兴的 .NET Core IoT 入门指南:(三)使用 I2C 进行通信
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验