Writing a Camera Driver
The ImageSensor interface a camera library implements, registration, the format contract, and the CustomSensor example.
Each camera chip supported by the core is an ImageSensor subclass in its own library. The OV5640 and VD66GY libraries are the two implementations. A new sensor is added by writing one class; no core changes are needed unless the sensor outputs a pixel format the core does not handle.
Interface
class MySensor : public ImageSensor {
public:
MySensor() { registerAsActive(1u); } // 1 = physical CSI lanes, 2 = inverted
int init(CameraResolutionN6 res, CameraFpsN6 fps, CameraMirrorFlipN6 flip,
uint16_t &out_w, uint16_t &out_h) override {
// reset, chip ID check, register tables, orientation, stream on, over I2C
out_w = 640; out_h = 480;
return 0; // 0 on success, negative on failure
}
uint32_t chipId() override { return 0x1234u; }
void getFormat(SensorFormat &out) override {
out.pixel_format = N6_PIXFMT_YUV422_YUYV;
out.color_space = N6_COLORSPACE_BT601;
}
void streamOn() override {}
void streamOff() override {}
const char *name() override { return "MySensor"; }
};
MySensor camera;| Method | Function |
|---|---|
| Constructor | Calls registerAsActive(lanes). Runs at static initialisation, before the HAL is ready, and must not touch hardware |
init() | Brings the sensor up at the requested mode, frame rate and orientation. Reports the achieved dimensions. Returns 0 or a negative error |
maxFps(res) | The native maximum frame rate for a mode. Defaults to the shared table; overridden by sensors with different limits |
chipId() | The expected chip ID, used in the console diagnostic when init() fails |
getFormat() | The pixel format and colour space the sensor outputs |
streamOn(), streamOff() | Start and stop pixel output |
name() | The name printed in diagnostics |
Optional methods cover autofocus, strobe, exposure, gain and white balance, with controlCaps() reporting which are implemented.
Registration
registerAsActive() records the object as the sensor Vision drives and publishes its CSI data lane mapping. The lane argument is a property of the carrier: 1 for the standard wiring, 2 for inverted lanes. Vision.begin() later calls init(), getFormat() and streamOn() on the registered object.
A sketch declares exactly one sensor object. Vision.setSensor() exists for choosing between several declared sensors at run time and is rarely needed.
Format contract
getFormat() drives the whole DCMIPP pipeline: the CSI data type, the packer, the colour space conversion and the JPEG encoder configuration. Three formats are defined:
| Format | Description | Used by |
|---|---|---|
N6_PIXFMT_YUV422_YUYV | Processed YUV 4:2:2 | OV5640 |
N6_PIXFMT_RGB565 | Processed RGB | Reserved for future sensors |
N6_PIXFMT_RAW8 | Raw Bayer or monochrome, demosaiced by the DCMIPP ISP. bayer_order selects the pattern | VD66GY |
A sensor with a format not in this list requires a case added to n6_apply_sensor_format() in cores/NeuroN6/NeuroN6.c.
I2C
Sensor control runs over I2C1, shared with the on-board sensors. A driver that calls the HAL directly brackets each transaction with n6_i2c_lock() and n6_i2c_unlock(). See I2C with Wire.
Failure behaviour
If init() returns a negative value, Vision.begin() prints a console line naming the sensor and its expected chip ID, and Vision.run() returns early on every call rather than blocking in inference. A sketch with a missing or miswired camera therefore degrades to a diagnostic.
References
The CustomSensor example, under File > Examples > OV5640, is a compilable skeleton. libraries/OV5640/src/OV5640_Sensor.cpp is a complete implementation with autofocus, strobe and capture controls. libraries/VD66GY/src/VD66GY_Sensor.cpp shows a raw Bayer sensor with an ISP.