65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 中的 I2C 协议仿真

starIconstarIconstarIconstarIconstarIcon

5.00/5 (15投票s)

2013年2月26日

CPOL

10分钟阅读

viewsIcon

68955

本文介绍了在 C# 中仿真 I2C 协议的代码,这在无需微控制器的应用(如数据采集)中可能很有用。

引言

在本文中,我们描述了如何在 Visual Studio C# 应用程序中模拟 I2C 协议。I2C 是一种广泛使用的低端设备通信协议。各种计算机外设都符合 I2C 协议,例如:存储器、PDA、手机等。通常,要访问这些外设,计算机需要额外的微控制器/微处理器。这显著增加了设备产品的成本。本文提出了一种可能的解决方案;它描述了运行在 Windows® 操作系统上的 C# 应用程序如何访问 I2C 外设。

背景

I2C 外设接口由飞利浦电子公司发明。该协议广泛用于处理器与存储设备、PDA、手机等的通信。

而 C# 是由 Microsoft Visual Studio 支持的强大语言;C# 是一种从 Java 和 C++ 继承了通用功能的语言。从 C# 的演变来看,它是否直接是对 Java 的拙劣模仿一直存在争议,但像 LINQ 和安全指针切换这样的功能证明 C# 比其同类产品更先进。用 C# 构建的应用程序在数据采集过程中的效率与 MATLAB 和 Labview 一样高。在 C# 中,我们不需要像 VB6 或 VC++ 6.0 中的 ActiveX 或 mscomm 对象等任何额外的东西。C# 中的 `SerialPort` 类足以支持各种波特率下的简单硬件。

通常,C# 应用程序通过桥接微控制器与 I2C 通信。这种通信方案如图 1 所示。

图 1:通过 C# 应用程序与 I2C 设备通信的常用方案。

该方案存在一些缺点,我们将在下一节讨论这些缺点。

间接通信的缺点

该方案(如图 1 所示)存在以下缺点:

Dm = d1 + d2                                  …………………….(1)

此延迟在需要快速数据采集的应用中会很显著(通常希望延迟越小越好)。d1 的常规值为 20ms(在 9600 波特率下)。

  • 正如我们所观察到的,存在两个通信阶段;第一个应用程序与微控制器通信,然后与 I2C 设备通信。令 d1 和 d2 分别为这些阶段涉及的延迟。通信过程的总延迟将是
  • 其次,微控制器的成本也是外设制造中一个不期望的因素。微控制器(具有 RS232 和 I2C)有各种价格(通常在 9 美元到 30 美元之间)。而昂贵的微控制器会显著增加原型机的最终成本。

毫无疑问,从处理时间短和最终设备成本降低的角度来看,从(图 1 所示)方案中消除微控制器将是有益的。

仿真作为可能的解决方案

由于 C# 中的程序员拥有 `SerialPort` 类(该类包含许多用于发送和接收的方法),因此串行端口是 C# 应用程序与外部世界通信的简便接口。应用程序可以直接访问串行端口上的这些引脚。这些引脚是:RTS、DTR 和 CTS(请参阅图 2)。RTS 和 DTR 是输出引脚,即 C# 软件应用程序可以将其写入布尔值。而 CTS 是输入引脚,软件应用程序可以从该引脚读取布尔值。

有了这些引脚,我们能否模拟 I2C 协议?当然可以。实际上,I2C 设备通过两个引脚 SCL 和 SDA 与微控制器通信。图 3 显示了一个示例 I2C 设备。如果我们参考地电平,PC 的 RS232 端口和 I2C 设备是相同的,并且我们遵循表 1 中给出的连接,则可以轻松模拟 I2C,从而使(图 1 所示)方案中的微控制器变得多余。这可能看起来很奇怪,但应用程序可以为 SDA 引脚写入一个位;同时,应用程序可以通过 CTS 从 SDA 读取一个位值。唯一的复杂性是将整个 I2C 协议在 C# 代码中实现。一旦所需的代码库准备好,C# 程序员就可以轻松地执行数据采集(I2C 传感器 IC,如 DS1621)、内存访问、与手机通信等操作。此外,微控制器中将没有固件编程的开销。

      

图 2:RS 232 DB9 引脚排列 

图 3:DS1621(一个 I2C 设备)引脚排列

I2C 协议概览   

图 4:I2C 总线时序图(请放大以正确查看)

I2C 是一个双向 2 线总线。发送数据的设备称为发送器,接收数据的设备称为接收器。主设备负责生成 SCL(时钟信号)。通常,这些设备之间的连接(SDA 和 SCL 线)通过开漏双向线进行。已定义了以下总线协议(参见图 4):

  • 当总线空闲时,数据开始传输。
  • 当 SCL 线为高电平时,数据线 (SDA) 必须保持稳定。如果 SDA 在 SCL 为高电平时发生变化,则会被解释为控制信号。

重要的总线条件

  • 当 SCL 和 SDA 都为高电平时,总线空闲。
  • SDA 从高电平到低电平的过渡,当 SCL 为高电平时,定义为 START。
  • SDA 从低电平到高电平的过渡,当 SCL 为高电平时,定义为 STOP。
  • 在 START 之后,SDA 的状态定义了 SCL=1(高)的数据位(1 或 0)。每个数据位都通过一个时钟脉冲传输。数据传输以 STOP 或另一个 START 结束(放弃前一次传输并开始一次新的传输)。在 START 和 STOP 条件之间可以传输任意数量的字节。信息以字节为单位传输,接收器必须用第 9 位进行应答。主设备负责生成与该应答位相关的额外时钟脉冲。进行应答的设备将 SDA 线置低,并且在 SCL 为高电平时,该线必须保持低电平(用于应答时钟脉冲)。
  • 为了结束数据传输,从设备将数据线置高,以便主设备可以生成 STOP。

如果 R/W 位(图 4 中)为 1,则数据从主设备传输到从设备。在第一个字节中,主设备首先发送从设备的地址,然后从设备对此进行应答。

另一方面,如果 R/W 位为 0,则数据流从从设备到主设备。在此,主设备首先发送从设备的地址,从设备返回一个应答位。之后,从设备将数据字节发送给主设备。主设备在收到除最后一个字节以外的所有字节后返回一个应答。当收到最后一个字节时,主设备返回一个“非应答”。

在下一节中,我们将看到 C# 对此协议的实现。应注意,本节仅提及了 I2C 协议的一些外围细节。

C# I2C 仿真

在本文中,DS1621(一种温度传感器 IC,有关其规格请参考 [9])被用作 I2C 设备的示例。仿真分为以下两部分。

图 4:仿真所用硬件的原理图
  1. 硬件:图 5 显示了仿真所用硬件的原理图。从原理图可以看出,没有使用微控制器来连接 RS232 和 DS1621。原理图包含一对 5.1V 齐纳二极管和一对 470 欧姆电阻。这些电阻和齐纳二极管用于保护电路免受高 RS232 电压的侵害。提供了一个带电阻的 LED,用于指示系统中的电源。一个流行的电压转换器 LM7805 用于从 9V 直流电源插孔提供 5V 稳定的直流输出。请参阅图 2,了解 RS232 到原理图中给出的引脚号对应的引脚名称(DTR、CTS 和 RTS)。我们可以看到连接符合本文第三节的描述。(硬件 PCB 文件包含在本文提供的补充材料中 点击此处)。
  2. 软件:这是用于仿真 I2C 协议的 C# 应用程序。为了保持代码的可理解性。我将此应用程序制作成控制台(无 GUI)。代码中使用的函数在表 1 中进行了详细介绍。
表 1:代码中的函数及其简要描述。

方法

描述

spinit

此函数初始化一个“串行端口”对象。它还设置初始的 SCL 和 SDA 状态。

示例

此函数在 SCL = 1(高)时采样 SDA 状态。

startcmd

这提供了 I2C 协议中的 START。

stopcmd

这提供了 I2C 协议中的 STOP。

tx_1

此函数向 I2C 设备传输 1。

tx_0

此函数向 I2C 设备传输 1。

tx_byte

此方法将一个字节传输到 I2C 设备。

rx_bit

通过此方法,应用程序读取来自 I2C 设备的一个位。

rx_byte

通过此方法,应用程序从 I2C 设备读取一个字节。

one_shot_mode

此方法特定于 DS1621;它启动一次性模式温度转换。

Start_convert_temperature

此方法特定于 DS1621;它启动温度转换序列。

read_temperature

此方法以 2 字节格式读取温度。

issue_read_temp

此方法通过适当的初始化实现了 `read_temperature` 方法。

Main

此方法是代码入口点方法。

主方法首先调用 `spinit` 以完成“serialport”(此类在 C# 中提供了与 RS232 的接口)实例的所需初始化。然后,主方法调用 `issue_read_temp`。

`issue_read_temp` 提供了使用 I2C 协议从 DS1621 读取温度的序列。

该应用程序在控制台窗口中运行。它首先要求用户提供连接了 I2C 设备的端口号。当用户提供所需信息(端口号)后,应用程序会花费一些时间,如果一切顺利(没有运行时问题),应用程序将成功计算温度并将其显示给用户界面。

通过与简单的水银温度计读数进行比较,已验证应用程序给出的温度值是正确的。

因此,我们可以说,该应用程序成功地仿真了 I2C 协议。使用此方案,我们成功地在处理 I2C 设备的 C# 应用程序中消除了微控制器。

/*
 TITLE : I2C EMULATION IN C#
 * Author : NAKUL VYAS
  VERSION : 3.0

 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;

namespace ConsoleApplication8
{
    class Program
    {
        public static SerialPort sp;
        public static void spinit(int sn)  // function for serial port initialization
        {
          sp =new SerialPort("COM"+sn);
          Console.WriteLine("openning the port..........");
         try{
              sp.Open();
            }
         catch(Exception ex){
             Console.WriteLine("Cannot open port following exception occured:" + ex.Message.ToString());
         }
        
            //making Clcok and SDA line stable
          sp.RtsEnable = true;
          sp.DtrEnable = true;
        }
       
        //this samples SDA pin of DS1621 by reading value of ctsHolding of RS232
        public static bool sample() 
        {
         return sp.CtsHolding;
        }
    
        // This is start condition for DS1621 
        public static void startcmd()
        {
            sp.DtrEnable= true;
            sp.DtrEnable= true;
            sp.RtsEnable = true;
            sp.DtrEnable = false;
        }
        //this is stop condition for Ds1621
            public static void stopcmd()
        {
            sp.DtrEnable =true;
            sp.DtrEnable = false;
            sp.RtsEnable = true;
            sp.DtrEnable = true;
        }
   
        
        //This function transmitt 1(high) to DS1621
        public static void tx_1()
       {
        sp.RtsEnable = false;
        sp.DtrEnable=true;
        sp.DtrEnable = true;
        sp.RtsEnable = true;
        sp.RtsEnable= false;
       }
        //This function transmitt 0(low) to DS1621
        public static void tx_0()
       { 
        sp.RtsEnable = false;
        sp.DtrEnable= true;
        sp.DtrEnable = false; 
        sp.RtsEnable = true;
        sp.RtsEnable = false;
       }
     
       //This functions tranmitt a byte value as a binary string
        public static bool tx_byte(string b)
       {
           foreach (char c in b)
           {
               if (c == '0')
                   tx_0();
               else if (c == '1')
                   tx_1();
           }
        return rx_bit();
       }
      
        //This reads a bit value from DS1621
        public static bool rx_bit()
       {
           bool temp;
           sp.DtrEnable = true;
           sp.RtsEnable = false;
           sp.RtsEnable = true;
           temp = sample();
           sp.RtsEnable = false;
           return temp;
       }

        //This reads a byte value from DS1621
        public static int rx_byte(bool ack)
        {
            int i; 
            int retval=0; 
            for(i = 0;i <=7;i++) 
            { 
                retval = retval * 2;
                if(rx_bit()) 
                retval = retval + 1;
            }
        if(ack)
            tx_0();
        else
            tx_1();
        return retval;
        }
        //This Squence is for setting DS1621 for setting it in one shot mode (see data sheet)
        public static void one_shot_mode()
        {
            startcmd(); //start command for DS1621
            tx_byte("10010000");
            tx_byte("10101100");
            tx_byte("00000001");
            //no need of stop
        }


        //This Squence is for setting DS1621 for starting conversion of temperature (see data sheet)
        public static void Start_convert_temperature()
        {
            startcmd();
            tx_byte("10010000"); //Bus Master sends DS1621 address; R/ W= 0.DS1621 generates acknowledge bit.
            tx_byte("11101110");// Bus Master sends Start Convert T command protocol.DS1621 generates acknowledge bit.
            stopcmd();
        }
        //This Squence is for setting DS1621 for reading 2 byte temperature format  MSB and LSB (see data sheet)
        public static double read_temperature()
        {
            int temperature_MSB;
            int temperature_LSB;
            startcmd(); 
            tx_byte("10010000"); 
            tx_byte("10101010"); 
            startcmd();
            tx_byte("10010001"); 
            temperature_MSB = rx_byte(true); //MSB is integral value of temperature
            temperature_LSB = rx_byte(false); //LSB is fraction pf temperature
            stopcmd();
            
            //to convert temperature in readable format
            double temp = ((temperature_MSB * 256 + temperature_LSB))/256;
            if (temperature_MSB >= 128)
                temp = temp - 256;
            return temp;
        }
        
        public static double issue_read_temp()
        {
            //an extra stop to stabalize bus
            stopcmd();
            //issue a one shot mode command
            one_shot_mode();
            return read_temperature();
        }
        
        
        static void Main(string[] args)
        {
            Console.WriteLine("Enter the COM port number where Hardware is connected :");
            //serial port initialization
            spinit(int.Parse(Console.ReadLine()));
            Console.WriteLine("Temperature Value of DS1621 is :");
           //issue a read sequence for DS1621
            int i = 0;
            
            Console.WriteLine(issue_read_temp());
            while (i < 20)
                i++;
            Console.ReadKey();
        }
    }
}

图 5:应用程序运行的屏幕截图。

结论 

本文提出了一种 C# 应用程序与 I2C 设备通信的新方法。传统上,C# 应用程序首先使用串行通信协议与微控制器通信,然后由微控制器负责与 I2C 设备通信。相比之下,本文提出了一种方案,其中 C# 应用程序无需微控制器即可与 I2C 设备通信。在此方案中,C# 应用程序直接在 RS232 端口(DB9 串行)上仿真 I2C 协议。由于该方案不需要微控制器,因此消除了固件开销以及与原型制作相关的最终成本。此外,还避免了微控制器处理数据不必要的延迟。为了验证所提出的方案,我们成功地通过 C# 控制台应用程序在 DS1621(一个示例 I2C 设备)上进行了 I2C 仿真演示。

参考文献

注意:请使用此处的 PCB 文件(要打开此文件,您需要DIPTRACE 软件的**免费版本**)与本项目一起提供,以快速开发原型。

© . All rights reserved.