• OpenMV与Arduino通信——IIC


    OpenMV-Code

    # Arduino 作为I2C主设备, OpenMV作为I2C从设备。
    #
    # 请把OpenMV和Arduino按照下面连线:
    #
    # OpenMV Cam Master I2C Data  (P5) - Arduino Uno Data  (A4)     SDA数据线
    # OpenMV Cam Master I2C Clock (P4) - Arduino Uno Clock (A5)     SCL时钟线
    # OpenMV Cam Ground                - Arduino Ground             GND地线
    
    import pyb, ustruct # ustruct 用于打包和解压原始数据类型 的 "类"
    
    text = "Hello World!\n"
    data = ustruct.pack("<%ds" % len(text), text)
    # 使用 "ustruct" 来生成需要发送的数据包 
            #ustruct.pack(fmt, v1, v2, ...):根据格式字符串fmt,打包 v1, v2, … 值。返回值为一个解码该值的字节对象
            # "<" 把数据以小端序放进struct中
            # "%ds" 把字符串放进数据流,比如:"13s" 对应的 "Hello World!\n" (13 chars).
    # 详见 https://docs.python.org/3/library/struct.html
    
    
    # READ ME!!!
    #
    # 请理解,当您的OpenMV摄像头不是I2C主设备,所以不管是使用中断回调,
    # 还是下方的轮循,都可能会错过响应发送数据给主机。当这种情况发生时,
    # Arduino会获得NAK,并且不得不从OpenMV再次读数据。请注意,
    # OpenMV和Arduino都不擅长解决I2C的错误。在OpenMV和Arduino中,
    # 你可以通过释放I2C外设,再重新初始化外设,来恢复功能。
    
    
    # 构造I2C对象
    # OpenMV上的硬件I2C总线都是2
    bus = pyb.I2C(2, pyb.I2C.SLAVE, addr=0x12)
    bus.deinit() # 完全关闭设备
    bus = pyb.I2C(2, pyb.I2C.SLAVE, addr=0x12)
    print("Waiting for Arduino...")
    
    
    
    # 请注意,为了正常同步工作,OpenMV Cam必须 在Arduino轮询数据之前运行此脚本。
    # 否则,I2C字节帧会变得乱七八糟。所以,保持Arduino在reset状态,
    # 直到OpenMV显示“Waiting for Arduino...”。
    
    
    while(True):
        try:
            bus.send(ustruct.pack("<h", len(data)), timeout=10000) # 首先发送长度 (16-bits).
            try:
                bus.send(data, timeout=10000) # 然后发送数据
                print("Sent Data!") # 没有遇到错误时,会显示
            except OSError as err: # 如果遇到错误 触发异常
                pass # 不用担心遇到错误,会跳过
                # 请注意,有3个可能的错误。 超时错误(timeout error),
                # 通用错误(general purpose error)或繁忙错误
                #(busy error)。 “err.arg[0]”的错误代码分别
                # 为116,5,16。
        except OSError as err:
            pass # 不用担心遇到错误,会跳过
            # 请注意,有3个可能的错误。 超时错误(timeout error),
            # 通用错误(general purpose error)或繁忙错误
            #(busy error)。 “err.arg[0]”的错误代码分别
            # 为116,5,16。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    Arduino-Code

    // Arduino Code
    
    #include <Wire.h>
    #define BAUD_RATE 19200 // 设置波特率为19200
    #define CHAR_BUF 128
    
    void setup() 
    {
      Serial.begin(BAUD_RATE); //设置波特率
      Wire.begin(); // begin():无输入参数,表示以 Master 形式加入总线。 有输入参数,表示以从机形式加入总线,设备地址为address(7-bit)
      delay(1000); // 给OpenMV一个启动的时间
    }
    
    void loop() 
    {
      int32_t temp = 0; //int32_t:32位int型数据 不同平台下,使用以下名称可以保证固定长度
      char buff[CHAR_BUF] = {0};
    
    // requestFrom函数用于实现 Master Read From Slave 操作:主设备请求从设备一个字节,这个字节可以被主设备用 read()或available()接受
      Wire.requestFrom(0x12, 2);  // requestFrom(address, quantity):从 address 设备读取 quantity 个字节,结束后,产生 STOP 信号
    
    //available 函数用于统计 Master Read From Slave 操作后, read 缓存区剩余的字节数 每当缓存区的数据被读走 1 个字节,available 函数的返回值减一
      if (Wire.available() == 2) // 如果成功执行了上面的操作(从0x12设备读取2个字节)
      { 
    //read 函数用于在 Master Read From Slave 操作后,读取缓存区的数据。
        temp = Wire.read() | (Wire.read() << 8);
        
        delay(1); // Give some setup time...
    
        Wire.requestFrom(0x12, temp);
        
        if (Wire.available() == temp) 
        { // got full message?
          temp = 0;
          while (Wire.available())
            buff[temp++] = Wire.read();
        } 
        else 
        {
          while (Wire.available()) 
            Wire.read(); // Toss garbage bytes. 
        }
      } 
      else 
      {
        while (Wire.available()) 
          Wire.read(); // Toss garbage bytes.
      }
    
      Serial.print(buff);
      delay(1); // Don't loop to quickly.
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    Arduino通信

    用到了Arduino上的wire.h

    Wire.begin()

    功能:初始化IIC连接,并作为主机或者从机设备加入IIC总线

    当没有填写参数时Wire.begin(),设备会以主机模式加入IIC总线;

    当填写了参数时Wire.begin(adress),设备以从机模式加入IIC总线,address 可以设置为0~127 中任意地址

    例子


    Wire.requesFrom()

    功能:主设备请求从设备一个字节,这个字节可以被主设备用 read()或available()接受

    使用Wire.requesFrom()后,从机端可以使用 onReceive () 注册一个事件以响应主机的请求;主机可以通过Wire.available()Wire.read() 函数读取这些数据

    Wire.requrstFrom(addtess,quantity,stop)
    
    • 1

    参数

    • addtess : 7位的器件地址
    • quantity : 请求得到的数量
    • stop :布尔形,‘1’ 则在请求结束后发送一个停止命令,并释放总线。‘0’则继续发送请求保持连接

    Wire.beginTransmission()

    功能:发出开始传输数据的信号,设定传输数据到指定的从机设备 (开始一次传输数据,发送一个I2C开始字符)

    wire.beginTransmission(address)
    
    • 1

    beginTransmission() 函数调用后,(再调用 write() 函数进行数据写入), 最后再调用 endTransmission() 函数方能产生 Start 信号发送 Slave Address通讯时序


    Wire.endTransmission()

    功能:发出结束数据传输的信号(结束一个由beginTransmission()开始的并且由write()排列的从机的传输)

    默认参数为stop:Wire.endTransmission(stop):如果为1endTransmission()发送一个停止信息;如果为0 则发送开始信息

    endTransmission(0):当输入参数为 0 时,将在通讯结束后,不产生 STOP 信号
    endTransmission(!0):当输入参数为 !0 时,在通讯结束后,生成 STOP 信号。(释放总线)
    endTransmission():当无输入参数时,在通讯结束后,产生 STOP 信号。(释放总线)


    Wire.write()

    功能:write() 函数用于向 从机 写入数据。共有 3 种调用形式:

    write(value) :写入单字节
    write(string) :写入字符串
    write(data, length) :写入 length 个字节

    例子

    #include <Wire.h>  
      
    byte val = 0;  
      
    void setup()  
    {  
       Wire.begin(); // join i2c bus  
    }  
      
    void loop()  
    {  
       Wire.beginTransmission(44); // transmit to device #44 (0x2c)  
                                   // device address is specified in datasheet  
       Wire.write(val);             // sends value byte    
       Wire.endTransmission();     // stop transmitting  
      
       val++;        // increment value  
       if(val == 64) // if reached 64th position (max)  
       {  
         val = 0;    // start over from lowest value  
       }  
       delay(500);  
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    Wire.available()

    功能:Wire.requestFrom()请求从机数据后,可以使用available()接收


    Wire.read()

    功能:Wire.requestFrom()请求从机数据后,可以使用read()接收


    Wire.onReceive()

    功能:该函数可以在从机端注册一个事件,当从机收到主机发送的数据时即触发该函数


    Wire.onRequest()

    功能:该函数可以在注册一个事件,当主机收到从机发送数据请求时触发


    两块Arduino UNO通信

    例子1:主设备读取 从设备发送

    在这个示例中,将使用两块开发板通过I2C通讯协议主设备读取/从设备发送的方式进行通信。

    Arduino UNO主设备通过使用Wire库, 可以请求并读取从唯一寻址的从设备 Arduino UNO发送来的6字节数据。 收到该消息后,便可以在Arduino IDE 串口监视器窗中进行查看。

    电路连接

    主设备开发板引脚4(或SDA数据引脚)引脚5(或SCL时钟引脚)连接到从设备开发板的对应引脚上。图示电路图以两块UNO为例进行连接

    为了保证串口通信的运行,必须通过USB将开发板连接到计算机上并保证各个开发板的供电。

    在这里插入图片描述

    在这里插入图片描述

    Code

    主设备读取

    // 引入Wire库文件
    #include <Wire.h>
     
    void setup()
    {
        // Wire初始化, 加入i2c总线
        // 如果未指定,则以主机身份加入总线。
        Wire.begin();
        // 初始化串口并设置波特率为9600
        Serial.begin(9600);
    }
     
    void loop()
    {
        // 向从设备#8请求6个字节
        Wire.requestFrom(8, 6);
        // 当从从设备接收到信息时值为true
        while (Wire.available())
        {
            // 接受并读取从设备发来的一个字节的数据
            char c = Wire.read();
            // 向串口打印该字节
            Serial.print(c);
        }
        // 延时500毫秒
        delay(500);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    从设备发送

    // 引入Wire库文件
    #include <Wire.h>
     
    void setup()
    {
        // Wire初始化, 并以从设备地址#8的身份加入i2c总线
        Wire.begin(8);
        // 注册请求响应事件
        Wire.onRequest(requestEvent);
    }
     
    void loop()
    {
        delay(100);
    }
     
    // 每当接收到来自主机的数据时执行的事件函数
    // 此函数被注册为事件,调用请见setup()
    void requestEvent()
    {
        // 用6个字节的消息进行响应
        Wire.write("hello ");
        // 以此回应主设备的请求
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    效果演示
    在这里插入图片描述
    通过主设备UNO硬件串口打开的串口监视器窗口视图


    例子2:主设备读取 从设备发送

    在这个示例中,将使用两个开发板通过I2C同步串行协议主机写入从机接受的方式相互通信。

    Arduino UNO(主设备) 经过编程,每半秒向唯一寻址的从设备发送6个字节的数据从设备收到该消息后,可以在Arduino IDE 的串口监视器窗口中查看该数据。

    电路连接

    将主设备开发板的引脚4(或SDA数据引脚)引脚5(或SCL时钟引脚)连接到从设备开发板的对应引脚上。图示电路图以两块UNO为例进行连接

    为了保证串口通信的运行,必须通过USB将开发板连接到计算机上并保证各个开发板的供电。

    在这里插入图片描述

    在这里插入图片描述

    Code

    主设备发送

    // 引入Wire库文件
    #include <Wire.h>
     
    void setup()
    {
        // Wire初始化, 加入i2c总线
        // 如果未指定,则以主机身份加入总线。
        Wire.begin();
    }
     
    // 定义一个byte变量以便串口调试
    byte x = 0;
     
    void loop()
    {
        // 将数据传送到从设备#8
        Wire.beginTransmission(8);
        // 发送5个字节
        Wire.write("x is ");
        // 发送一个字节
        Wire.write(x);
        // 停止传送
        Wire.endTransmission();
     
        x++;
        delay(500);
    }
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    从设备接受

    // 引入Wire库文件
    #include <Wire.h>
     
    void setup()
    {
        // Wire初始化, 并以从设备地址#8的身份加入i2c总线
        Wire.begin(8);
        // 注册接受事件函数
        Wire.onReceive(receiveEvent);
        // 初始化串口并设置波特率为9600
        Serial.begin(9600);
    }
     
    void loop()
    {
        delay(100);
    }
     
    // 每当接收到来自主机的数据时执行的事件函数
    // 此函数被注册为事件,调用请见setup()
    void receiveEvent(int howMany)
    {
        // 循环读取数据(除了最后一个字符,因为最后一个字符是数字,我们要用int来接收)
        while (Wire.available() > 1)
        {
            // 接收字节数据并赋值给变量c(char)
            char c = Wire.read();
            
            // 打印该字节
            Serial.print(c);
        }
        // 以int整数的形式接受字节数据并赋值给x(int)
        int x = Wire.read();
        
        // 打印该int变量x
        Serial.println(x);
    }
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    效果演示
    在这里插入图片描述

    文章参考:太极创客

  • 相关阅读:
    用于生物分子修饰的Alkyne NHS ester,906564-59-8
    C# Winform编程(3)对话框
    基于 51 的点阵屏显示 8*8 点阵仿真实验
    计算机图形学(八):三维对象的表示(常用的三维几何建模方法)
    Python基础——函数(二)
    org.jetbrains.idea.maven.server.RemoteMavenServer36
    java.lang.OutOfMemoryError - 解决方法 (-Xmx -Xms)
    【C++11】函数的可变参数模板
    scratch学习相关资料汇总
    XSS Payload 学习浏览器解码
  • 原文地址:https://blog.csdn.net/m0_59466249/article/details/125412809