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。


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.
}



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)

参数

扫描二维码关注公众号,回复: 15815981 查看本文章
  • addtess : 7位的器件地址
  • quantity : 请求得到的数量
  • stop :布尔形,‘1’ 则在请求结束后发送一个停止命令,并释放总线。‘0’则继续发送请求保持连接

Wire.beginTransmission()

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

wire.beginTransmission(address)

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);  
}  

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);
}

从设备发送

// 引入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 ");
    // 以此回应主设备的请求
}

效果演示
在这里插入图片描述
通过主设备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);
}
 

从设备接受

// 引入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);
}
 

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

文章参考:太极创客

猜你喜欢

转载自blog.csdn.net/m0_59466249/article/details/125412809