User Tools

Site Tools


code:sub2r-lib

Windows

You can download the library from this location. What you get in this package is:

  1. A header file FX3.h - the only include you need besides the standard headers
  2. Both Release and Debug libraries with full symbol files (.pdb) and support for edit and continue (.idb - currently only for Debug mode). These include the libraries from Cypress that this package depends on.
  3. A sample code that demonstrates some basics on how to use the library: sample1.cpp

The library is built using MSVC 2017 version 15.9.11 with Windows SDK 10.0.17763.0 for use with UNICODE and multi-threaded CRT in 64-bit mode only. The library requires a C++17 toolchain to be built.

byte_span

Beginning with version 2018-09-05 the code relies on Guidelines Support Library (GSL). Part of that change is to use gsl::span<std::byte> to represent raw byte buffers.

Quite often a need arises to represent “simple” data types (like uint32_t) as a byte buffer when using hardware API. To reduce the potential for stupid bugs and make code more readable it is recommended to use the provided

template<class T> gsl::span<std::byte> byte_span(T& _x) noexcept;

function when such a buffer's address is needed.

All the library's methods were updated to conform to this calling convention.

Sample usage:

using S2R;
using S2R::FX3;
FX3 fx3; // by default opens device #0
if(fx3){
    uint32_t vi{};
    const auto rc{m_fx3.vrCmd(Fx3Cmd::fx3_version // vendor request command
                              , VrCmdOpType::read // read from device
                              , 0                 // "value"
                              , 0                 // "index"
                              , byte_span(vi))};  // buffer to read into
    return vi;                                    // 32 bits of the "version info" (4 bytes)
}

Class diagram

SUB2r-lib class diagram overview

ApiCmdLog

Class diagram - ApiCmdLog

A simple logging mechanism for all the API calls to the camera. This is a singleton object that pre-allocates 10240 log entries (320KB memory) and grows beyond that if necessary.

Method Signature Functionality
enable
static void enable(bool _b) noexcept;
enable/disable logging, doesn't affect already stored log entries
clear
static void clear() noexcept;
clear the log, removing all the entries (this doesn't necessarily free up memory)
getLog
static const std::vector<Details> & getLog() noexcept;
access the log

This class provides a convenient data type for the “time stamp” values:

using timestamp_t = decltype(std::chrono::high_resolution_clock::now());

ApiCmdLog::Entry

Used to be ApiCmdLog::Details

This internal class represents each individual log entry.

Method Signature Functionality
constructor
Details(bool _dir2Dev
      , uint8_t _cmd
      , uint16_t _val
      , uint16_t _idx) noexcept;
log an FX3 entry
constructor
Details(uint16_t _addr = 0
      , uint16_t _val = 0
      , uint16_t _mask = 0) noexcept;
log an I²C entry
toString
std::string toString() const;
returns a formatted string representation of the log entry
legend
static std::string legend();
description of the log entries' fields
Variable Use
timestamp_t started;
the time when API request was issued
std::chrono::nanoseconds duration{0ns};
duration, in ns
const enum class DebugLogType : uint8_t{fx3, i2c} type;
the type of log entry
bool success{};
result of the API request
<unnamed struct> fx3;
valid if type == DebugLogType::fx3
<unnamed struct> i2c;
valid if type == DebugLogType::i2c
ApiCmdLog::Entry::<unnamed struct> fx3

FX3-related log entry data.

Variable Use
const bool dir2Dev;
to or from device (from or to host)
const uint8_t cmd;
numeric code of the command
const uint16_t val;
“value” provided to the API request
const uint16_t idx;
“index” value of the API request
std::array<uint8_t, 4> ret{};
first 4 bytes of the return buffer
ApiCmdLog::Entry::<unnamed struct> i2c

I²C-related log entry data.

Variable Use
const uint16_t addr;
target register
const uint16_t val;
value to be written
const uint16_t mask;
bits to affect when writing the above value
uint16_t valRead;
value that was read back
uint16_t valWrite;
value that was written out (after combining the write-out value with the read-in value using the mask)

ApiCmdLog::Helper

This following tasks are automated by this internal class: add a new entry on construction and start the clock, log time in destructor. The user of this object is responsible for updating API's status info (status member of the Details struct).

Method Signature Functionality
constructor
Helper(bool _dir2Dev
     , uint8_t _cmd
     , uint16_t _val
     , uint16_t _idx);
log an FX3 entry
constructor
Helper(uint16_t _addr
     , uint16_t _val
     , uint16_t _mask);
log an I²C entry
destructor
~Helper();
just calls
close();
curr
auto & curr() noexcept;
“safe” method to access the current log entry - provides dummy entry if the logging is disabled. Here the auto is ApiCmdLog::Entry
close
void close() noexcept;
log the time and close the entry

FX3

Class diagram - FX3

This is the main class to access the FX3 hardware component - the module responsible for the overall communication with the camera.

Method Signature Functionality
FX3
FX3(std::chrono::milliseconds
      _defaultTimeout = 500ms)
  noexcept;
Construct the object, optionally setting the USB communication timeout, in milliseconds. This automatically opens the device #0 for read/write and queries its properties
isValid
bool isValid() const
  noexcept;
returns true if the device is open and operational
deviceCount
uint8_t deviceCount() const
  noexcept;
return the number of Cypress-based USB devices attached to the system
open
bool open(uint8_t _dev);
open a device by its 0-based index, return true on success
close
virtual void close()
  noexcept; 
close the device, freeing up the associated resources
getDevPath
static std::wstring
  getDevPath(uint8_t _devNo = 0);
device path is unique per device and is valid across reboots, returns empty string if device not found
open
bool open(IMFActivate * _src);
Windows-specific
open FX3 device that corresponds to the given IUnknown (IMFMediaSource or IMFActivate) - useful when you need to access camera's extended features when only having the standard Windows's media object
toInterface
CComPtr<IUnknown>
  toInterface() const;
Windows-specific
find the media interface for this FX3 device - useful when you need to access UVC for the camera device that corresponds to that FX3
operator[]
XXX operator[](YYY _prop) const;
6 overloads to read various types of properties. The return type XXX depends on the argument type YYY. These are used to access properties like device name, USB class, vendor ID, and others
fx3Version fpgaVersion
const VersionInfo &
  fx3Version() const
  noexcept;
const VersionInfo &
  fpga3Version() const
  noexcept;
get detailed version information on both the FX3 and FPGA as reported by the device. This information is read and cached when the device is opened
vrCmd
bool vrCmd(
      Fx3Cmd _cmd /*uint8_t _cmd*/
    , VrCmdOpType _opType
    , WORD _val,
    , WORD _idx
    , gsl::span<std::byte> _buf = {});
2 overloaded versions that differ in the first parameter: whether it is one of the predefined S2R::FX3::Fx3Cmd or a plain uint8_t command code. Runs a “vendor request command”, providing it with the following info:
_opType - either read or write
_val - a WORD value to use
_idx - some commands require a WORD index parameter
_buf - a buffer to read into, only for the read type operations
_bufLen - a length of the _buf buffer in bytes
vrCmd
template<
  typename T = std::enable_if_t<
    std::is_standard_layout<T>::value
  > >
  bool vrCmd(
      Fx3Cmd _cmd /*uint8_t _cmd*/
    , VrCmdOpType _opType
    , WORD _val,
    , WORD _idx
    , gsl::span<std::byte> _buf = {});
similar to non-template version but instead of a byte buffer these accept a pointer to a “standard layout” object
programFwFPGA
bool programFwFPGA(
    const vector<std::byte> & _buf
  , std::chrono::milliseconds
     _timeout = 90s
  ) const;
program the FPGA with the provided buffer that contains the firmware code, fail if timeout is reached
programFwFX3
bool
 programFwFX3(
    const vector<std::byte> & _buf
    ) const;
program the FX3 with the provided buffer that contains the firmware code
destructor
virtual ~FX3();
calls the close()

I2C

Class diagram - I²C

When accessing image sensor's chip via I²C you use an instance of this class.

Method Signature Functionality
I2C
I2C()
default constructor, just calls FX3 with a 750ms timeout parameter
operator bool
operator bool() const;
returns true if the device is opened and ready to accept vendor and I²C commands
setCtrlPort
void
 setCtrlPort(
    uint8_t _port
    );
sets a new value to be used as a control port and if successful read the chipset info and create a corresponding IImgSensor object
getCtrlPort
uint8_t getCtrlPort() const
  noexcept;
get the current control port, which is also set automatically on open()
operator→
const IImgSensor*
 operator->() const
  noexcept;
access the interface object that is created based on the detected image sensor's chipset and encapsulates a subset of commands that provide direct control over the image sensor
sensor
IImgSensor* sensor()
  noexcept;
const IImgSensor* sensor() const
  noexcept;
2 versions, const and non-const, that, respectively, return a pointer to a const or a non-const IImgSensor object. These provide access to the interface object that is created based on the detected image sensor's chipset and encapsulates a subset of commands that provide direct control over the image sensor
operator()
uint16_t operator()(
      uint16_t _addr
    , uint16_t _val = 0
    , uint16_t _mask = 0
    ) const;
primary function to send commands
_addr - an address of a register to read/write
_val - a new value for the write operation, 0 otherwise
_mask - a bitmask to use that specifies which bits to affect when writing a new value, only bits that are set to 1 in the bitmask will be changed, then new value of those bits will be copied over from the _val
groupWrite
void groupWrite(
      const CVecRegValPairs & _group
    , bool _bUseGroupWrite = true
    ) const;
For the image sensors that support the notion of a group write this function allows to update a set of registers as a group at a vertical sync time. No error checking on the results of all those write operations is done in this call.
_group - a vector of address/value pairs to be written
_bUseGroupWrite - if set to false will do an immediate write, not waiting for the VSYNC
readN
int readN(
      const CVecRegAddr & _r
    , int _n
    ) const;
read _n-bit value into a single integer (up to 32 bits on the current 64-bit code - not 64 bits!), the bits are read starting with the first address, that represents the most significant bits (MSB) and continues on into LSB at the end. The first register address contains the “spill-over” MSB while all the others are full 8-bit parts of the final value
writeN
void writeN(
      const CVecRegAddr & _r
    , int _n
    , int _val
    , bool _bUseGroupWrite = true
    ) const;
similarly to the readN() this function is used to write an integer value that is over 8 bits, spread over multiple registers with the first address representing the byte with “spill-over” MSB and the last one storing the LSB portion of the value

VersionInfo

Class diagram - VersoinInfo VersionInfo encapsulates the details about the version info of the code running on FX3 and FPGA and is populated once the device is opened with an open() call (or during the default construction, which implicitly opens the device #0).

This is an abstract base class, the actual implementations are only used by the FX3 class.

Method Signature Functionality
fromBuffer
virtual void
 fromBuffer(
    gsl::span<std::byte> _buf = {}
    ) = 0;
aching to a “virtual constructor” this virtual function fills-in the version info from the 4 bytes' buffer returned by the API, normally only used internally by the library itself
legend
static wstring legend();
a UNICODE string representing field-by-field description of the version info elements when formatting as a one-liner
relType
static wstring
 relType(int _rel);
release cycle number → release cycle text
countRelTypes
static int
 countRelTypes()
  noexcept;
how many steps are there in the release cycle, ranging from 1..N, inclusive. You can iterate through these values with relType() to get textual values for all of them
format
virtual wstring
 format(
    const wstring & _i = L""
    ) const = 0;
format the version info into a UNICODE text block, using the provided _i indent at the beginning of each line
operator string
virtual operator
 string() const = 0;
format the version info as a single line of UNICODE text, call legend() to get the header for this line
listFw
virtual vector<FwInfo>
 listFw(
    int _rel = 4
    ) const = 0;
Get a list of firmware info objects for this device that are for the release cycle _rel and above.
Product ID, hardware config ID, and vendor ID from the version information must strictly match.
Throws an exception of type std::string on error.
isUpgrade
virtual bool
 isUpgrade(int _bldNo) const
  noexcept = 0;
returns true if that build number is an upgrade to the firmware code currently running on the device
isAtLeast
virtual bool
 isAtLeast(unsigned int _bldNo)
  const noexcept
check if the current code on the device is a given build number or newer

FwInfo

This internal class caches details about the current firmware code running on the device.

Variable Use
unsigned int rel{};
release cycle
unsigned int bld{};
build #
string url;
a URL to download the latest version of the firmware code based on the currently set desired “earliest release cycle to consider”
int fsz{-1};
the size (in bytes) of the firmware code to download from the server

SPIConfigStatus

Class diagram - SPIConfigStatus

This struct simplifies the use of FPGA's configuration status encoded as individual fields on the hardware. The individual status bits are accessible either as a single uint16_t member m_ui16, an array of 2 uint8_t bytes in a member m_ui8, or individually as bool through the member bit.

Here's a short sample code on how to create an instance of and use that struct:

S2R::FX3 fx3;
/*const auto rc =*/ (void) fx3.open(0);  // though don't ignore errors in production code!
if(fx3){
  uint16_t buf = 0;
  const auto rc = m_fx3.vrCmd(FX3::Fx3Cmd::fpga_config_status, FX3::VrCmdOpType::read, 0, 0, buf);
  const S2R::SPIConfigStatus cfg(buf);
  // use the cfg object, e.g. test for if the config is busy:
  if(cfg.bit.configNotBusy){
    // ...
  }
}
Method Signature Functionality
constructor
SPIConfigStatus(const gsl::span<std::byte> _buf) noexcept;
constexpr SPIConfigStatus(uint16_t _val = 0) noexcept;
construct an object from a provided 2-byte buffer or a uint16_t value
operator uint16_t
constexpr operator uint16_t() const noexcept;
return a 16-bit WORD containing all the config bits in the order listed in the documentation (e.g. if you just want to run a for() loop over the bits and display their statuses)
hasErrors
constexpr bool hasErrors() const noexcept;
returns true if any of the error bits is set

Individual bits are described in the FPGA I²C Bridge section of the documentation.

IImgSensor

Class diagram - IImgSensor

Abstract interface class to deal with the variety of supported image sensors which all have different sets of commands and incompatible register maps. I2C automatically instantiates a proper subclass once the chipset is detected. All the concrete implementations are in the .cpp file.

Method Signature Functionality
constructor
= delete
an instance of this class can only be created with the create() factory method
create
static unique_ptr<IImgSensor>
 create(const I2C & _fx3);
provided a valid _fx3 instance of an I2C class this returns a corresponding IImgSensor concrete class
chipsetType
Chipset chipsetType() const
  noexcept;
returns a value from Chipset enumeration for this object
chipsetName
string chipsetName() const;
an ASCII string for the detected chipset
chipsetIdStr
string chipsetIdStr() const;
a dot-separated list of numbers that represent an internal chip version followed by a dash and a stepping
compatible
virtual bool
 compatible(
    const string & _cs
    ) const;
Check if this chipset is compatible with the given one. An empty (unknown) chipset is compatible with every chipset. The compatibility is defined as “can use the settings (.fws) file with this chipset model”
getLimit
virtual int
 getLimit(Value _lt) const
  noexcept;
read an upper limit for a given parameter, specific to this particular chipset
cmd
virtual void
 cmd(Command _c) const;
Execute one of the known commands. If you have access to the manufacturer's documentation you can execute your own I²C commands via S2R::I2C::operator() interface
mode
virtual bool
 mode(
      Mode _mt
    , int _val = -1
    ) const;
configure the device to be a specific mode of operation
_mt - mode type, one of the values from S2R::IImgSensor::Mode enum
_val - a value to be used for that mode, if the value is negative causes a read operation in which case the return is the read value\\return is unspecified for write operations
val
virtual bool
 val(
      Value _vt
    , int _val = -1
    ) const;
read/write a specific value attribute of the device, like a red channel gain or a black level
_vt - value type, one of the values from S2R::IImgSensor::Value enum
_val - a value to send to device, if the value is negative causes a read operation in which case the return is the read value\\return is unspecified for write operations
operator[]
int operator[](
    Value _vt
    ) const;
a read access operator that reads a specified value from the device
operator[]
virtual CDevProp
    operator[](
        Value _vt
        ) = 0;
a “device value” access operator that returns an access objects to allow a code like the following:
I2C dev; // also opens the device #0
auto & ov = *dev.sensor();
// set the red gain to 123
ov[IImgSensor::Value::gain_r] = 123;
testPattern
virtual TestPattern
 testPattern(
    TestPattern _tp = {}
    ) const;
read/write a S2R::TestPattern object that represents configuration parameters to use for setting up test-pattern-tun on the image sensor. Not all the chips support all the functionality in that object and some don't even support the test pattern as such (or it is not documented and is thus not implemented)
groupStart
virtual void
 groupStart(
    uint8_t _grpNo
    ) const = 0;
begin a group write operation for a group # _grpNo, refer to the documentation of the image sensor to see which group numbers are valid and what is the size of those groups. Issuing a group-writing command before closing the current group will always overwrite the group #0. Issuing a new groupStart() for the same group will re-start that group's accumulation of commands
groupEnd
virtual void
 groupEnd(
      uint8_t _grpNo
    , GroupWriteMode _mode
        = GroupWriteMode::
            at_vert_sync
    ) const = 0;
Finalize a write to a group. If this call is not made and another group write is started (either explicitly via a call to groupStart() or implicitly by other functions) a write group could be overwritten. There's no need to explicitly call this function if you want to throw away (cancel) the group. Possible values for the _mode:
immediate - write the values immediately, no wait
AT_LINE_SYNC - wait until the end of the current scan line and then write the values
at_vert_sync - wait until the VSYNC and then write out the values. This is the preferred method for making sure the update is not done mid-frame

Internal class

Name Description
struct CDevProp final
returned from the non-const access operator[](Value) this object allows for the access operator to be used to write values into device. It supports the 4 basic assignment arithmetic operations and a conversion to int

Enumerations

Name Description
Chipset
the type of a detected chipset or unknown in case of errors
Value
lists the values you can read/write for the image sensor
Mode
specifies a mode in which the device is to be configured
Command
commands to send to the device
GroupWriteMode
specify a desired group-write execution mode at the end, affects whether the values are to be written out immediately or with a delay at a pre-defined time (like a VSYNC)

TestPattern

Class diagram - TestPattern

Some image sensors support a special test mode in which the output can be either replaced by or alpha-blended with a test pattern. OmniVision image sensors support a variety of those patterns (vertical color bars, checkers, random noise, etc) and to unify the process of working with those this struct was created. Not all the image sensors support the full range of presented test pattern features and furthermore not every image sensor even has this mode (documented). Refer to your manufacturer's documentation for more specifics on the exact image sensor you are using.

Not all the combination of the configuration parameters are valid when combined with others (for instance you cannot combine a black square and a random noise).

Value Usage
m_mode
a test pattern mode to set:
read - used for reading the current state of the test pattern configuration
color_bar - set the test pattern to vertical colored bars
random - generate random noise
square - checkers pattern
black - just a black frame video stream
m_colorBarType
only for the m_mode == color_bar, the values represent the following configurations:
std - a standard solid color bar
tb_dark - top-bottom darker color bar
rl_dark - right-left darker color bar
bt_dark - bottom-top darker color bar
m_squareType
color mode for the checkers' mode when m_mode == square, values are:
clr - use various colors for the checker squares
bw - use alternating black and white square, just like on a checkers board
m_transparent
true for alpha-blending the test pattern with the video stream, false for replacing the video stream with the test pattern
m_rollingbar
enable/disable a rolling bar, only valid when m_mode == color_bar
m_enabled
enable/disable the test pattern mode

UFix_8_8

Class diagram - UFix_8_8

A helper class for working with a 2-byte floating number format used to specify fractional parameters for the FX3. More details are available in UFIX 8.8 documentation.

Method Signature Functionality
constructor
constexpr
  UFix_8_8(
      uint8_t _i
    , uint8_t _f = 0
    ) noexcept
construct a number from provided integer (_i) and fractional (_f) parts, for example:
auto uf88{1, 128}; // uf88 is now 1.5

No validation is performed to make sure the _f is in range [0..255]

constructor
constexpr
  UFix_8_8(double _d)
    noexcept;
construct from a floating-point number, i.e.:
auto uf88{1.2};
constructor
UFix_8_8(
    gsl::span<const std::byte> _buf
    ) noexcept;
use this constructor when you read a 2-byte UFix_8_8 value from FX3
operator double
constexpr
  operator double() const
    noexcept;
a type conversion into a floating-point number
operator uint16_t
constexpr
  operator uint16_t() const
    noexcept;
make 2 bytes that are ready to be written to FX3, for example:
// omitting all the error checks!!!
uint8_t valI{1}, valF{128};
auto val{S2R::UFix_8_8(valI, valF)};
S2R::I2C dev;
// a port to set the image adjustment auto-
// functions' update interval
constexpr uint8_t port{0xDF};
dev.vrCmd(port, S2R::FX3::write, val, 0);

Sample code

The sample code is a Windows console app that just lists some basic properties of the connected cameras, like in the screenshot below: Sample output

The code itself is pretty minimal and basically fits onto a single screen (depending on your font size, duh):

sample1.cpp
#include <iostream>
#include "FX3.h"
 
#pragma comment(lib, "SUB2r-lib.lib")
 
int main()
{
    using namespace std;
    using namespace S2R;
 
    cout << "SUB2r-lib sample #1: basic functionality" << endl << endl;
 
    I2C dev;
    const auto numDevs = dev.deviceCount();
    cout << "Cypress devices attached to this system: " << static_cast<int>(numDevs) << endl << endl;
    for(uint8_t i = 0; i < numDevs; ++i){
        auto GAIN_R = IImgSensor::Value::gain_r;
        cout << "Opening device #" << static_cast<int>(i) << "... ";
        if(dev.open(i)){
            wcout << L"success.\nDevice name is: " << dev[FX3::PropWString::friendly_name].c_str() << endl;
            cout << "Sensor chipset: " << dev->chipsetName() << endl;
            cout << "FX3 version info: " << string(dev.fx3Version()) << endl;
            cout << "FPGA version info: " << string(dev.fpgaVersion()) << endl;
            auto & ov = *dev.sensor();  // make a shortcut to the sensor chip
            if(dev){    // or dev.isValid() works, too
                cout << "current red channel gain is: " << dev->val(GAIN_R) << endl;
                auto gainR = ov[GAIN_R];
                gainR = gainR + 1;  // increment the red gain by one and update the sensor with the new value
                cout << "after increasing red channel gain by 1 it is now: " << gainR << endl;
                ov[GAIN_R] -= 1;
                cout << "and back to the previous value of: " << gainR << endl;
                ov[GAIN_R] *= 1.0;
            }
            cout << "Closing the device... ";
            dev.close();    // or the destructor (or even the next call to open()) will take care of that
            if(dev){
                cout << "something has gone terribly wrong - the device was supposed to be closed by now!" << endl;
            }else{
                cout << "the device is now closed and cannot be accessed without re-opening it" << endl;
                cout << "For example the red channel gain is now: " << ov[GAIN_R] << endl;
                cout << "But some info is still available, like the sensor chipset: " << dev->chipsetName() << endl;
                cout << "Or the information about the limit of red channel gain: " << dev->getLimit(GAIN_R) << endl;
            }
        }else{
            cout << "failed." << endl;
            if(dev.isValid()){
                wcout << L"But the FX3 part is still functional, so we can get info like the device's name: " << dev[FX3::PropWString::friendly_name].c_str() << endl;
            }
        }
        cout << endl;
    }
    return 0;
}

Built binary

If you just want to launch that executable - here's the built binary (for Windows) lib-sample-1.rar

Linux

Planned for later FIXME

MacOS X

Planned for later FIXME

/home/adminsub2r/public_html/dokuwiki/data/pages/code/sub2r-lib.txt · Last modified: 2019/04/29 01:43 by Igor Yefmov