OpenMV与I2C二线串行协议


在OpenMV的底层逻辑中,I2C被定义为一个类


I2C类—二线串行协议

I2C是一个设备间传输的二线串行协议。其物理层包括两条线:SCL和SDA,分别为时钟线数据线OpenMV不再为SDA和SCL提供上拉,因此SDA和SCL需要外部上拉才能使I2C工作

I2C对象创建在特定总线上。可在创建时或创建后初始化。

例子:

bus:总线	master:主机		slaves:从机
from pyb import I2C

i2c = I2C(2)                         # 在总线2上创建

i2c = I2C(2, I2C.MASTER)             # 初始化为主机
	或
i2c.init(I2C.MASTER, baudrate=20000) # 初始化为master,设置时钟频率为20000hz

i2c.init(I2C.SLAVE, addr=0x42)       # 初始化为具有给定地址的从机

i2c.deinit()                         # 关闭外设

其中的两个常量

  • I2C.MASTER将总线初始化为**"主机"模式**
  • I2C.SLAVE将总线初始化为**"从机"模式**

打印i2c对象可以提供关于其配置的信息

发送与接受

发送:

i2c.send(‘abc’) # 发送3个字节
 i2c.send(0x42) #发送由括号内给定的单个字节

接收:

i2c.recv(3) # 接收3个字节

接收数据,先创建一个缓冲区bytearray:

data = bytearray(3)  # create a buffer 创建一个缓冲区
i2c.recv(data)       # receive 3 bytes, writing them into data 接收3个字节,并将其写入数据

你可以指定一个超时时长(单位:毫秒):

i2c.send(b'123', timeout=2000)   # timeout after 2 seconds 2秒后超时

主机必须指定接收方地址

i2c.init(I2C.MASTER)
i2c.send('123', 0x42)        # send 3 bytes to slave with address 0x42 使用0x42地址向从机发送3个字节
i2c.send(b'456', addr=0x42)  # keyword for address 地址关键字

主机也有其他方法

i2c.is_ready(0x42)           # check if slave 0x42 is ready 检查从机0x42是否准备充分
i2c.scan()                   # scan for slaves on the bus, returning
                             #   a list of valid addresses 在总线上浏览从机,返回一个有效地址列表
                             
i2c.mem_read(3, 0x42, 2)     # read 3 bytes from memory of slave 0x42, 从从机0x42的内存中读取3个字节
                             #   starting at address 2 in the slave  从从机的地址2开始
                             
i2c.mem_write('abc', 0x42, 2, timeout=1000) # write 'abc' (3 bytes) to memory of slave 0x42 将“abc”写入从机内存
                                            # starting at address 2 in the slave, timeout after 1 second 从从机的地址2开始,1秒后超时

构造函数

class pyb.I2C(bus, ...)

在给定总线上构建一个I2C对象。 bus 可为24在没有其他参数的情况下,可创建I2C对象,但未进行初始化( 该对象的设置来自总线的最后一次初始化,若存在的话)。
若给定额外参数则总线进行初始化。初始化参数详见 init 。

OpenMV CamI2C总线的物理引脚为:

  • I2C(2) is on the Y position: (SCL, SDA) = (P4, P5) = (PB10, PB11)

OpenMV Cam M7中:

  • I2C(2) is on the Y position: (SCL, SDA) = (P4, P5) = (PB10, PB11)
  • I2C(4) is on the Y position: (SCL, SDA) = (P7, P8) = (PD12, PD13)

方法

关闭I2C总线

I2C.deinit()

初始化I2C总线

使用给定参数初始化I2C总线:

I2C.init(mode, \*, addr=0x12, baudrate=400000, gencall=False, dma=False)
  • mode 须为 主机I2C.MASTER 或 从机I2C.SLAVE
  • addr 是一个7位地址 (只对从机有效)
  • baudrateSCL时钟线的时钟频率 (只对主机有效)
  • gencall 是指是否支持一般调用模式。
  • dma 是否允许将DMA用于I2C传输(请注意,DMA传输具有更精确的时序,但目前不能正确处理总线错误)

检测从机是否准备好

I2C.is_ready(addr)

检查I2C设备是否响应给定地址。只在主模式有效(只能由主机调用)


主机读从机的数据

I2C.mem_read(data, addr, memaddr, \*, timeout=5000, addr_size=8

从I2C设备(从机)内存中读取

  • data 可为一个要读入的整数(要读取的字节数量)或缓冲区
  • addrI2C设备地址
  • memaddrI2C设备中内存的位置
  • timeout 是以毫秒计的等待读取的超时时长
  • addr_size 选择memaddr宽度:8或16位
    返回读取数据。只在主模式下有效(只能由主机调用)。

主机向从机中写入数据

I2C.mem_write(data, addr, memaddr, \*, timeout=5000, addr_size=8)

写入I2C设备的内存:

  • data 可为一个用来写入的整数或缓冲区。
  • addrI2C设备的地址。
  • memaddrI2C设备中存储写入数据的内存的位置
  • timeout 是以毫秒计的等待写入的超时时长
  • addr_size 选择memaddr宽度:8或16位
    返回值为 None. 只在主模式下有(该方法只能由主机调用)

在总线上接受数据

I2C.recv(recv, addr=0x00, \*, timeout=5000)

总线上接受数据

  • recv 可为一个整数,即接收的字节的数量;或为一个使用收到的字节来填充的可变缓冲区。
  • addr 是用以接收的地址(只在主模式下需要)
  • timeout 是以毫秒计的等待接收的超时时长
    返回值:若 recv 为一个整数,则有一个新的字节缓冲区来接收,否则这一缓冲区将传递给recv

在总线上发送数据

I2C.send(send, addr=0x00, \*, timeout=5000)

总线上发送数据:

  • send 是要发送的数据(一个整数或一个缓冲区对象)
  • addr 是要发送到的地址(只在主模式下需要
  • timeout 是以毫秒计的等待发送的超时时长
    返回值: None.

主机浏览从机

I2C.scan()

浏览从0x010x7f的所有I2C地址,并返回一个响应的地址的列表只在主模式下有效。


例子

# 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。

猜你喜欢

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