是否有通用 I2C 命令来查看设备是否仍然存在于总线上?

2024-03-16

是否有通用的 I2C 命令来查看设备在初始化一次后是否仍然存在于总线上?例如 OLED 显示器。我问这个的原因是为了避免主程序由于库代码中存在无限循环而冻结(当设备断开连接时),例如 Wire 库。

在 MCU 启动时,我想检查设备是否可用,并在可用时对其进行初始化。我用这个函数做到了这一点并且工作正常......

bool MyClass::isPnpDeviceAvailable( uint8_t iAddress, bool bIsInitOnce = false )
{
     // Try to start connection
    Wire.beginTransmission( iAddress );

     // End connection without STOP command if already is initialized
    return ( Wire.endTransmission( !bIsInitOnce ) == 0x00 ); // No Error?, return true
}

....但是,当我想检查设备是否仍然存在时,在执行更新之前,当我执行以下操作时:

// 1.
if( isPnpDeviceAvailable( 0x3C, true )) 
 { /* Cause program hang */ }
// 2.
if( isPnpDeviceAvailable( 0x3C )) 
 { /* Cause display to turn off  */ }

是否有可用的通用命令,可以说/发送一个“你好你在听吗”并等待答复而不发送 START 和 STOP 命令且不中断设备/总线状态?


这是我用附加的(可选 PNP I2C)显示器制作的原型设备。


好吧,弄清楚并测试它需要更长的旅程。还制作了一个视频,请参阅此答案底部的链接。所有功劳都归功于@user0042,他为我指明了正确的方向。默认的 Wire 库在稳定性、可靠性方面实际上没有什么用处,因此需要将其“替换”为:


I2C 主库 -http://dss Circuits.com/articles/arduino-i2c-master-library http://dsscircuits.com/articles/arduino-i2c-master-library


使用这个库还有更多好处,它的编译大小更小,请阅读上面的文章以获取更多信息。

我更改了我的软件,检测总线上设备的“密钥”可以简化为:

bool TEnjoyPad::isPnpDeviceAvailable( uint8_t iAddress )
{
  return ( I2c.write( (int)iAddress, (int)0x00 ) == 0x00 ); 
}

注意:需要 (int) 类型转换来避免编译器警告,但没有它也能正常工作。

我发送一个**0x00 command**但是,该设备似乎没有任何反应。我创建的函数在插入时返回 true,如果未插入则返回 false。

I doesn't test it with other i2c devices yet, however, will try later and update this question. For now it seems to working fine. 注意:请参阅下面的更新:


PNP 方法

Step #1

在第一个版本中,我没有使用任何电阻器(懒惰),但稳定总线的读数是个好主意。在数据线上的+5V 输出上添加两个电阻(4.7K)。这样做非常重要,可以避免错误检测并避免您的 Arduino 仍然因此而冻结。

Step #2

您需要跟踪每个 I2C 设备的更改/设备状态。我使用三种状态:

  • 连接的
  • 重新连接(又名已连接)
  • 已断开连接(或之前从未连接过)

Step #3

如果您使用类与设备“对话”,则必须在设备可用时动态创建该类。在我的例子中,它是这样的:

TOLEDdisplay* display; // declaration
......
......
display = new TOLEDdisplay( SDA, SCL ); // To create it
display->begin(); // it's a pointer to an object so you need to use -> instead of . (simple explanation ;-) )
......
// etc 

Step #4

每次更新之前,您需要检查可用性和初始化状态(步骤#3中提到的三种状态)。这对于避免不必要的延迟/代码执行(压力)非常重要。

  • 如果之前没有连接,则需要创建类
  • 如果之前已连接(重新连接),则必须重新初始化(类和设备)

Step #5

您需要在循环或中断中检查更改。最好在循环中执行而不是在中断中执行。

Step #6

检测到更改时执行更新。在真正更新之前使用大约 200ms 秒的一点延迟。


一些示例代码

您不能使用此代码,但是,它可以让您了解如何设计代码。我使用许多宏来简化我的实际代码,因此更容易阅读:

void TEnjoyPad::showAbout() // only showed at initialization
{
  __tepClearDisplay();

  __tepSetDisplayText( "ENJOYPAD v1.0"     , TOD_TEXT_ALIGN_CENTER, TEP_DISPLAY_LINE1 );
  __tepSetDisplayText( "(c) 2017 codebeat" , TOD_TEXT_ALIGN_CENTER, TEP_DISPLAY_LINE2 );


  __tepRefreshDisplay();
  setDelay( 2000 );
  updateDisplay();

}

void TEnjoyPad::updateDisplay()
{
 if( !__tepDisplayIsInit() )
  { return; }


 __tepDrawDisplayBitmap( TEP_DISPLAY,           // bitmap
                         0, TEP_DISPLAY_LINE0,  // x,y
                         TEP_DISPLAY_WIDTH,
                         TEP_DISPLAY_HEIGHT
                        );

  uint8_t i = TEP_MIN_MODE - 1;

  __tepDrawDisplayClearRect( 0, 10, 128, 35 );

  while( ++i <= TEP_MAX_MODE )
  {
    if ( emuMode != i )
    {
      // erase area, delete what's NOT selected
      __tepDrawDisplayClearRect( TEP_DISPLAY_MODE_ICON_X + ((i - 1) * (TEP_DISPLAY_MODE_ICON_WIDTH + TEP_DISPLAY_MODE_ICON_SPACING)),
                                 TEP_DISPLAY_MODE_ICON_Y,
                                 TEP_DISPLAY_MODE_ICON_WIDTH,
                                 TEP_DISPLAY_MODE_ICON_HEIGHT
                               );
    }
    else {
            __tepSetDisplayText( TEP_MODE_GET_NAME(i), TOD_TEXT_ALIGN_CENTER, TEP_DISPLAY_LINE1 );
         }
  }

  __tepRefreshDisplay();
}

void TEnjoyPad::beginDisplay( bool bIsFound = false )
{
  static bool bWasConnected = false;

  bIsFound = bIsFound?true:isPnpDeviceAvailable( TEP_PNP_ADDR_DISPLAY );

  if( bIsFound )
  {
    if( !bWasConnected  )
    {
      if( pnpStates[ TEP_PNP_IDX_DISPLAY ] )
      {
        // Reset
        setDelay( 200 );
        // Reset display
        bIsFound = isPnpDeviceAvailable( TEP_PNP_ADDR_DISPLAY );
        if( bIsFound )
        {
          __tepDisplay->begin(); 
          updateDisplay();
        } 
      }
     else {
            // (re-)connected" );
            __tepCreateDisplay(); // This macro checks also if class is created
            __tepInitDisplay();
            showAbout();

             // Set class is created
            pnpStates[ TEP_PNP_IDX_DISPLAY ] = TEP_PNP_ADDR_DISPLAY;
          }  
    }

    bWasConnected = bIsFound;
  }
  else { 
            // Disconnected           
            bWasConnected = false; 
       }  
} 

 // In a loop I call this function:
uint8_t TEnjoyPad::i2CPnpScan()
{
  uint8_t iAddress = 0x7F; // 127
  bool    bFound   = false;
  uint8_t iFound   = 0;

  //Serial.println( "Scanning PNP devices...." );
  while ( --iAddress )
  {
    //Serial.print( "Scanning address: 0x" );
    //Serial.println( iAddress, HEX );

    if( iAddress == TEP_PNP_ADDR_DISPLAY )
     { beginDisplay( bFound = isPnpDeviceAvailable( iAddress ) ); 
       iFound+=bFound;
     }
  }

  return iFound;
}

演示视频

我还创建了一个演示视频,作为概念证明,向您展示这种方法运行良好。您可以在 YouTube 上观看该视频:https://www.youtube.com/watch?v=ODWqPQJk8Xo https://www.youtube.com/watch?v=ODWqPQJk8Xo


感谢大家的帮助,希望这些信息也能帮助其他人。


UPDATE:

我的方法似乎适用于多个 I2C 设备。我写了这个更新的 I2CScanner:


您可以使用的 I2CScanner 代码:

/*
 ----------------------------------------
 i2c_scanner - I2C Master Library Version

 Version 1 (Wire library version)
    This program (or code that looks like it)
    can be found in many places.
    For example on the Arduino.cc forum.
    The original author is not know.

 Version 2, Juni 2012, Using Arduino 1.0.1
     Adapted to be as simple as possible by Arduino.cc user Krodal

 Version 3, Feb 26  2013
    V3 by louarnold

 Version 4, March 3, 2013, Using Arduino 1.0.3
    by Arduino.cc user Krodal.
    Changes by louarnold removed.
    Scanning addresses changed from 0...127 to 1...119,
    according to the i2c scanner by Nick Gammon
    http:www.gammon.com.au/forum/?id=10896

 Version 5, March 28, 2013
    As version 4, but address scans now to 127.
    A sensor seems to use address 120.

 Version 6, November 27, 2015.
    Added waiting for the Leonardo serial communication.

 Version 7, September 11, 2017 (I2C Master Library version)
    - By codebeat
    - Changed/Optimize code and variable names
    - Add configuration defines
    - Add fallback define to standard Wire library
    - Split functionality into functions so it is easier to integrate 
    - Table like output


 This sketch tests the standard 7-bit addresses between
 range 1 to 126 (0x01 to 0x7E)
 Devices with higher addresses cannot be seen.

 ---------------------
 WHY THIS NEW VERSION?

 The Wire library is not that great when it comes to stability, 
 reliability, it can cause the hardware to freeze because of
 infinite loops inside the library when connection is lost or
 the connection is unstable for some reason. Because of that
 the Wire library is also not suitable for plug and play 
 functionality, unplugging an I2C device will immediately
 lock the hardware (if you want to talk to it) and you 
 need to reset the hardware. I will not recover on itselfs.  

 Another reason is the way to check if a device is plugged-in
 or not. The methods of the Wire library doesn't allow to 
 do this because it resets/stop the I2C device when it is
 already started/available.



 Benefits of the I2C Master Library:
 - More flexible;
 - Faster;
 - Smaller compile size;
 - Idiot proof;
 - Self recovering (no hardware freeze);    
 - Able to check for availability of devices without 
   interrupt bus status and/or device (see the 
   example function isDeviceAvailable() how to achieve 
   this)
   .

 More info at:
 http://dsscircuits.com/articles/arduino-i2c-master-library
 You can also download the library there.

 PRECAUTIONS:
 It is a good idea to stabilize the readouts of the bus. 
 Add two resistors (4.7K) on the +5V output to the data lines. 
 Only one pair is required, don't use more or different resistors.
 It is very important to do this to avoid false detections and to 
 avoid your Arduino can still freeze because of that. 

 NOTICE:
 When selecting the default Wire library, this scanner will probably 
 not show the side effects I am talking about because the code 
 don't talk to the device and the connection to a device is extremely 
 short period of time.
*/

// *** Uncomment this if you want to use the default Wire library.
//#define I2C_LIB_WIRE

 // Some settings you can change if you want but be careful
#define I2C_MIN_ADDRESS     0x01
#define I2C_MAX_ADDRESS     0x7F
#define I2C_UPDATE_TIMEOUT  3000
#define I2C_I2CLIB_TIMEOUT  1000
#define I2C_I2CLIB_FASTBUS  true


 // Errorcodes that are normal errors when I2C device does
 // not exists.
#define I2C_I2CLIB_ERROR_NOT_AVAIL  32
#define I2C_WIRELIB_ERROR_NOT_AVAIL  2


// -------------------------------------------------------------


#ifdef I2C_LIB_WIRE
 #define I2C_ERROR_NOT_AVAIL I2C_WIRELIB_ERROR_NOT_AVAIL
  // Compile size with Wire library: 6014 bytes
 #include <Wire.h>
 #pragma message "Compiled with Wire library"
#else 
 #define I2C_ERROR_NOT_AVAIL I2C_I2CLIB_ERROR_NOT_AVAIL
  // Compile size with I2C Master library: 5098 bytes
 #include <I2C.h>
 #define Wire I2c
 #pragma message "Compiled with I2C Master library"
#endif


// -------------------------------------------------------------


int iLastError = 0;

bool isDeviceAvailable( uint8_t iAddress )
{
 #ifdef I2C_LIB_WIRE
  // Wire:
  // The i2c_scanner uses the return value of the Write.endTransmisstion 
  // to see if a device did acknowledge to the address.
  Wire.beginTransmission( iAddress );
  iLastError = Wire.endTransmission();  
 #else
   // I2C Master Library:
   // Just send/write a meaningless 0x00 command to the address 
   // to figure out the device is there and the device answers.
  iLastError = Wire.write( (int)iAddress, (int)0x00 ); 
  // Notice: The (int) typecasting is required to avoid compiler  
  //         function candidate notice. 
 #endif  

 return ( iLastError == 0x00 ); 
}

byte findI2Cdevices( bool bVerbose = true ) 
{
  byte nDevices = 0;

  if( bVerbose )
   { Serial.println("Scanning..."); }

  for(byte iAddress = I2C_MIN_ADDRESS; iAddress < I2C_MAX_ADDRESS; iAddress++ ) 
  {
    if( bVerbose )
    {
     Serial.print("Address 0x");
     if( iAddress < 16 ) 
      { Serial.print("0"); }

     Serial.print( iAddress, HEX );
     Serial.print(": ");
    }

    if( isDeviceAvailable( iAddress ) )
    {
      if( bVerbose )
       { Serial.println("FOUND  !"); }
      nDevices++;
    }
    else { 
            if( bVerbose )
            {
              Serial.print( "<NO DEVICE FOUND" ); 
              if( iLastError != I2C_ERROR_NOT_AVAIL )
              {
                Serial.print( " - ERRCODE: " );
                Serial.print( iLastError );
              }
              Serial.println( ">" ); 
            }
         }    
  }

  if( bVerbose )
  {
    if( nDevices > 0 )
    { 
      Serial.print( nDevices );
      Serial.println( " device(s) found\n" ); 
    }
    else { Serial.println( "No I2C devices found\n"); }

    Serial.print( "Press CTRL+A, CRTL+C to copy data.\n" );
  }

  return nDevices;
}

void setupI2C()
{
 Wire.begin();

 #ifndef I2C_LIB_WIRE  
  // This is important, don't set too low, never set it zero.
  Wire.timeOut( I2C_I2CLIB_TIMEOUT ); 

  #ifdef I2C_I2CLIB_FASTBUS
   if( I2C_I2CLIB_FASTBUS )
    { Wire.setSpeed(1); }
  #endif
 #endif
}

void setupSerial()
{
 Serial.begin(9600);
 while (!Serial); // Leonardo: wait for serial monitor
 Serial.println("\nI2C Scanner");
}

// -------------------------------------------------------------

void setup()
{
  setupI2C();
  setupSerial();
}

void loop()
{
    // Skip the Arduino slow down housekeeping after the loop() 
    // function, we stay here forever ;-) 
   while(1)
   {
     findI2Cdevices();
     delay( I2C_UPDATE_TIMEOUT ); // wait n seconds for next scan
   }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

是否有通用 I2C 命令来查看设备是否仍然存在于总线上? 的相关文章

  • 在 xaml 中编写嵌套类型时出现设计时错误

    我创建了一个用户控件 它接受枚举类型并将该枚举的值分配给该用户控件中的 ComboBox 控件 很简单 我在数据模板中使用此用户控件 当出现嵌套类型时 问题就来了 我使用这个符号来指定 EnumType x Type myNamespace
  • 机器Epsilon精度差异

    我正在尝试计算 C 中双精度数和浮点数的机器 epsilon 值 作为学校作业的一部分 我在 Windows 7 64 位中使用 Cygwin 代码如下 include
  • -webkit-box-shadow 与 QtWebKit 模糊?

    当时有什么方法可以实现 webkit box shadow 的工作模糊吗 看完这篇评论错误报告 https bugs webkit org show bug cgi id 23291 我认识到这仍然是一个问题 尽管错误报告被标记为RESOL
  • 如何在 C++ 中标记字符串?

    Java有一个方便的分割方法 String str The quick brown fox String results str split 在 C 中是否有一种简单的方法可以做到这一点 The 增强分词器 http www boost o
  • 用于 FTP 的文件系统观察器

    我怎样才能实现FileSystemWatcherFTP 位置 在 C 中 这个想法是 每当 FTP 位置添加任何内容时 我都希望将其复制到我的本地计算机 任何想法都会有所帮助 这是我之前问题的后续使用 NET 进行选择性 FTP 下载 ht
  • C++ 多行字符串原始文字[重复]

    这个问题在这里已经有答案了 我们可以像这样定义一个多行字符串 const char text1 part 1 part 2 part 3 part 4 const char text2 part 1 part 2 part 3 part 4
  • 在 Unity 中实现 Fur with Shells 技术

    我正在尝试在 Unity 中实现皮毛贝壳技术 http developer download nvidia com SDK 10 5 direct3d Source Fur doc FurShellsAndFins pdf Fins 技术被
  • WcfSvcHost 的跨域异常

    对于另一个跨域问题 我深表歉意 我一整天都在与这个问题作斗争 现在已经到了沸腾的地步 我有一个 Silverlight 应用程序项目 SLApp1 一个用于托管 Silverlight SLApp1 Web 的 Web 项目和 WCF 项目
  • 为什么这个字符串用AesCryptoServiceProvider第二次解密时不相等?

    我在 C VS2012 NET 4 5 中的文本加密和解密方面遇到问题 具体来说 当我加密并随后解密字符串时 输出与输入不同 然而 奇怪的是 如果我复制加密的输出并将其硬编码为字符串文字 解密就会起作用 以下代码示例说明了该问题 我究竟做错
  • 复制目录下所有文件

    如何将一个目录中的所有内容复制到另一个目录而不循环遍历每个文件 你不能 两者都不Directory http msdn microsoft com en us library system io directory aspx nor Dir
  • 如何在 Android 中使用 C# 生成的 RSA 公钥?

    我想在无法假定 HTTPS 可用的情况下确保 Android 应用程序和 C ASP NET 服务器之间的消息隐私 我想使用 RSA 来加密 Android 设备首次联系服务器时传输的对称密钥 RSA密钥对已在服务器上生成 私钥保存在服务器
  • 编译时展开 for 循环内的模板参数?

    维基百科 here http en wikipedia org wiki Template metaprogramming Compile time code optimization 给出了 for 循环的编译时展开 我想知道我们是否可以
  • 在 WPF 中使用 ReactiveUI 提供长时间运行命令反馈的正确方法

    我有一个 C WPF NET 4 5 应用程序 用户将用它来打开某些文件 然后 应用程序将经历很多动作 读取文件 通过许多插件和解析器传递它 这些文件可能相当大 gt 100MB 因此这可能需要一段时间 我想让用户了解 UI 中发生的情况
  • 使用特定参数从 SQL 数据库填充组合框

    我在使用参数从 sql server 获取特定值时遇到问题 任何人都可以解释一下为什么它在 winfom 上工作但在 wpf 上不起作用以及我如何修复它 我的代码 private void UpdateItems COMBOBOX1 Ite
  • C++ 中的 include 和 using 命名空间

    用于使用cout 我需要指定两者 include
  • 为什么 std::uint32_t 与 uint32_t 不同?

    我对 C 有点陌生 我有一个编码作业 很多文件已经完成 但我注意到 VS2012 似乎有以下语句的问题 typedef std uint32 t identifier 不过 似乎将其更改为 typedef uint32 t identifi
  • DotNetZip:如何提取文件,但忽略zip文件中的路径?

    尝试将文件提取到给定文件夹 忽略 zip 文件中的路径 但似乎没有办法 考虑到其中实现的所有其他好东西 这似乎是一个相当基本的要求 我缺少什么 代码是 using Ionic Zip ZipFile zf Ionic Zip ZipFile
  • Mono 应用程序在非阻塞套接字发送时冻结

    我在 debian 9 上的 mono 下运行一个服务器应用程序 大约有 1000 2000 个客户端连接 并且应用程序经常冻结 CPU 使用率达到 100 我执行 kill QUIT pid 来获取线程堆栈转储 但它总是卡在这个位置
  • 如何确定 CultureInfo 实例是否支持拉丁字符

    是否可以确定是否CultureInfo http msdn microsoft com en us library system globalization cultureinfo aspx我正在使用的实例是否基于拉丁字符集 我相信你可以使
  • 使用 WGL 创建现代 OpenGL 上下文?

    我正在尝试使用 Windows 函数创建 OpenGL 上下文 现代版本 基本上代码就是 创建窗口类 注册班级 创建一个窗口 choose PIXELFORMATDESCRIPTOR并设置它 创建旧版 OpenGL 上下文 使上下文成为当前

随机推荐