文章目录
在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
可为2或4。在没有其他参数的情况下,可创建I2C
对象,但未进行初始化( 该对象的设置来自总线的最后一次初始化,若存在的话)。
若给定额外参数,则总线进行初始化。初始化参数详见 init 。
在OpenMV Cam中I2C
总线的物理引脚为:
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位地址 (只对从机有效)baudrate
是SCL时钟线的时钟频率 (只对主机有效)gencall
是指是否支持一般调用模式。dma
是否允许将DMA用于I2C
传输(请注意,DMA传输具有更精确的时序,但目前不能正确处理总线错误)
检测从机是否准备好
I2C.is_ready(addr)
检查I2C
设备是否响应给定地址。只在主模式有效(只能由主机调用)。
主机读从机的数据
I2C.mem_read(data, addr, memaddr, \*, timeout=5000, addr_size=8
从I2C设备(从机)内存中读取
data
可为一个要读入的整数(要读取的字节数量)或缓冲区addr
是I2C
设备地址memaddr
是I2C
设备中内存的位置timeout
是以毫秒计的等待读取的超时时长addr_size
选择memaddr
宽度:8或16位
返回读取数据。只在主模式下有效(只能由主机调用)。
主机向从机中写入数据
I2C.mem_write(data, addr, memaddr, \*, timeout=5000, addr_size=8)
写入I2C
设备的内存:
data
可为一个用来写入的整数或缓冲区。addr
是I2C
设备的地址。memaddr
是I2C
设备中存储写入数据的内存的位置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
是要发送到的地址(只在主模式下需要- ti
meout
是以毫秒计的等待发送的超时时长
返回值:None
.
主机浏览从机
I2C.scan()
浏览从0x01到0x7f的所有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。