适配新的传感器
Hololink 软件提供了控制连接到 Hololink 设备的传感器所需的工具。支持新的传感器通常是创建一个对象,该对象具有您需要的设备功能的方法,然后在应用程序的操作器中使用这些 API。让我们用一个例子 MyCamera
对象来做到这一点,它有一个寄存器 (0x100),我们用它来读取设备版本。
import logging
import hololink as hololink_module
class MyCamera:
CAMERA_I2C_BUS_ADDRESS = 0x34
def __init__(self, hololink_channel, hololink_i2c_controller_address):
# Get handles to these controllers but don't actually talk to them yet
self._hololink = hololink_channel.hololink()
self._i2c = self._hololink.get_i2c(hololink_i2c_controller_address)
def get_version(self):
VERSION = 0x100
return self.get_register(VERSION)
def get_register(self, register):
# write_buffer will contain the big-endian 2-byte address
# of the register we're reading.
write_buffer = bytearray(10) # must be at least 2
serializer = hololink_module.Serializer(write_buffer)
serializer.append_uint16_be(register)
# send write_buffer to the peripheral device,
# and return data read back from it. reply will
# be a 4 byte buffer, or None if there's a problem
read_byte_count = 4
reply = self._i2c.i2c_transaction(
self.CAMERA_I2C_BUS_ADDRESS,
serializer.data(), # same as write_buffer[:serializer.length()]
read_byte_count
)
# deserializer fetches data from reply; this
# raises an exception if reply is None
deserializer = hololink_module.Deserializer(reply)
# Fetch an unsigned 32-bit value stored in big-endian format
r = deserializer.next_u32_be()
return r
有了这个,我们可以创建一个简单的程序来读取这个版本寄存器
def main():
# Get a handle to the Hololink port we're connected to.
channel_metadata = hololink_module.Enumerator.find_channel(channel_ip="192.168.0.2")
hololink_channel = hololink_module.DataChannel(channel_metadata)
# Instantiate the camera itself; CAM_I2C_CTRL is the base address of the I2C
# controller our camera is attached to
camera = MyCamera(hololink_channel, hololink_module.CAM_I2C_CTRL)
# Establish a connection to the hololink device
hololink = hololink_channel.hololink()
hololink.start()
# Fetch the device version.
version = camera.get_version()
logging.info(f"{version=}")
在调用 hololink.start
之后,网络控制平面可用于通信。传感器对象应遵循此模式
一个
configure
方法,它使用控制平面来设置传感器产生的数据。此方法由应用程序代码调用。一个
start
方法,数据接收器操作器在其启动时调用该方法,该方法配置传感器开始生成数据。一个
stop
方法,在数据接收器关闭时调用,用于停止传感器数据流。一个
configure_converter
方法,它允许传感器对象配置应用程序管道中的下一个元素。此方法在设置管道时由应用程序层调用。
class MyCamera:
...
def configure(self, mode, ...):
# Configure the camera for our useful mode by putting appropriate values
# in the camera registers.
self.set_register(...)
self.set_register(...)
...
def start(self):
# Tell the camera to start streaming data out.
self.set_register(...)
def stop(self):
# Tell the camera to stop streaming data.
self.set_register(...)
def configure_converter(self, converter):
converter.configure(self._width * self._height ...)
...
configure_converter
方法允许传感器与管道的下一层协调,在下一层处理来自传感器的原始数据。例如,在 CSI-2 视频应用中,原始视频数据由 CSI-2 元数据帧构成,并以编码格式存储(例如 RAW10)。由于转换器是 GPU 加速的过程,因此在为接收到的数据分配内存时,它可能需要考虑额外的因素(例如,包含 GPU 内存缓存对齐)。在我们的视频应用中,传感器将知道原始图像尺寸,而转换器可以添加 CSI-2 帧数据所需的空间。之后,转换器对象现在知道如何最好地为网络接收器分配内存。
应用程序层在调用 sensor.configure_converter
之后,现在可以向转换器请求帮助分配 GPU 内存。然后,此内存将传递给网络接收器操作器。
现在相机已配置为通过数据平面发送流量,应用程序可以实例化一个 RoceReceiverOp
(或 LinuxReceiverOperator
)来接收到 GPU 内存特定区域的数据平面流量。最后,当 application.run
完成配置并调用我们的接收器操作器的 start
方法时,它将调用 camera.start
,这将更新相机以开始发送视频数据。