C# 中的 I2C 协议仿真





5.00/5 (15投票s)
本文介绍了在 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 协议概览
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 设备的示例。仿真分为以下两部分。
- 硬件:图 5 显示了仿真所用硬件的原理图。从原理图可以看出,没有使用微控制器来连接 RS232 和 DS1621。原理图包含一对 5.1V 齐纳二极管和一对 470 欧姆电阻。这些电阻和齐纳二极管用于保护电路免受高 RS232 电压的侵害。提供了一个带电阻的 LED,用于指示系统中的电源。一个流行的电压转换器 LM7805 用于从 9V 直流电源插孔提供 5V 稳定的直流输出。请参阅图 2,了解 RS232 到原理图中给出的引脚号对应的引脚名称(DTR、CTS 和 RTS)。我们可以看到连接符合本文第三节的描述。(硬件 PCB 文件包含在本文提供的补充材料中 点击此处)。
- 软件:这是用于仿真 I2C 协议的 C# 应用程序。为了保持代码的可理解性。我将此应用程序制作成控制台(无 GUI)。代码中使用的函数在表 1 中进行了详细介绍。
方法 |
描述 |
|
此函数初始化一个“串行端口”对象。它还设置初始的 SCL 和 SDA 状态。 |
|
此函数在 SCL = 1(高)时采样 SDA 状态。 |
|
这提供了 I2C 协议中的 START。 |
|
这提供了 I2C 协议中的 STOP。 |
|
此函数向 I2C 设备传输 1。 |
|
此函数向 I2C 设备传输 1。 |
|
此方法将一个字节传输到 I2C 设备。 |
|
通过此方法,应用程序读取来自 I2C 设备的一个位。 |
|
通过此方法,应用程序从 I2C 设备读取一个字节。 |
|
此方法特定于 DS1621;它启动一次性模式温度转换。 |
|
此方法特定于 DS1621;它启动温度转换序列。 |
|
此方法以 2 字节格式读取温度。 |
|
此方法通过适当的初始化实现了 `read_temperature` 方法。 |
|
此方法是代码入口点方法。 |
主方法首先调用 `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();
}
}
}
结论
本文提出了一种 C# 应用程序与 I2C 设备通信的新方法。传统上,C# 应用程序首先使用串行通信协议与微控制器通信,然后由微控制器负责与 I2C 设备通信。相比之下,本文提出了一种方案,其中 C# 应用程序无需微控制器即可与 I2C 设备通信。在此方案中,C# 应用程序直接在 RS232 端口(DB9 串行)上仿真 I2C 协议。由于该方案不需要微控制器,因此消除了固件开销以及与原型制作相关的最终成本。此外,还避免了微控制器处理数据不必要的延迟。为了验证所提出的方案,我们成功地通过 C# 控制台应用程序在 DS1621(一个示例 I2C 设备)上进行了 I2C 仿真演示。
参考文献
- [1]I2C总线及其使用方法(含规范),飞利浦半导体,1995年更新
- [2] Wylie Wong (2002)。“为什么微软的 C# 不是”。CNET:CBS Interactive。检索日期 2009 年 11 月 14 日。
- [3] C# 语言规范(第 4 版)。Ecma International。2006 年 6 月。检索日期 2012 年 1 月 26 日。
- [4] Vyas, Nakul;,“一种实用的碳税计费制度”,无线和光通信网络(WOCN),2012 年第九届国际会议,卷,号, pp.1-7,2012 年 9 月 20-22 日
- [5] Visual Basic:MSComm 控件,Microsoft 开发人员网络(MSDN),库
- [6] SerialPort 类,Microsoft 开发人员网络(MSDN),库
- [7] EIA 标准 RS-232-C:数据终端设备和数据通信设备之间采用串行二进制数据交换的接口。华盛顿:电子工业协会。工程部。1969 年。
- [8] D-sub 9 连接器引脚排列
- [9] 数据手册:Atmel ATMega32 L
- [10]数据手册:Maxim DS1621
注意:请使用此处的 PCB 文件(要打开此文件,您需要DIPTRACE 软件的**免费版本**)与本项目一起提供,以快速开发原型。