C

Qt Quick Ultralite camera Example

// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial

#include "camerainterface.h"
#include "camera_support.h"
#include "board.h"
#include "pin_mux.h"
#include "fsl_debug_console.h"
#include <cstdarg>

#define CAMERA_BUFFER_COUNT 3
#define CAMERA_FRAME_WIDTH 480
#define CAMERA_FRAME_HEIGHT 272
#define CAMERA_BPP 2

#define FAIL_IF(cond, msg) \
    do { \
        if (cond) { \
            DbgConsole_Printf(msg); \
            DbgConsole_Printf("\r\n"); \
            while (true) \
                ; \
        } \
    } while (false)

#define RETURN_FALSE_IF(cond, msg) \
    do { \
        if (cond) { \
            DbgConsole_Printf(msg); \
            DbgConsole_Printf("\r\n"); \
            return false; \
        } \
    } while (false)

#define RETURN_IF(cond, msg) \
    do { \
        if (cond) { \
            DbgConsole_Printf(msg); \
            DbgConsole_Printf("\r\n"); \
            return; \
        } \
    } while (false)

AT_NONCACHEABLE_SECTION_ALIGN(
    static uint16_t s_cameraBuffers[CAMERA_BUFFER_COUNT][CAMERA_FRAME_WIDTH][CAMERA_FRAME_HEIGHT], 64);

static void newCameraFrame(camera_receiver_handle_t *handle, status_t status, void *userData)
{
    uint32_t cameraFrameAddr;
    CameraInterface *me = static_cast<CameraInterface *>(userData);

    RETURN_IF(status != kStatus_Success, "Camera driver reported new frame with fail status!");

    status = CAMERA_RECEIVER_GetFullBuffer(handle, &cameraFrameAddr);
    RETURN_IF(status != kStatus_Success, "Failed to get camera buffer!");

    me->postEventFromInterrupt(FrameEvent{(uint8_t *) cameraFrameAddr});
}

bool CameraInterface::initCamera()
{
    status_t err;

    /* Set CSI interrupt priority to match configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY from our FreeRTOSConfig.h*/
    NVIC_SetPriority(CSI_IRQn, 2);

    BOARD_InitCSIPins();

    const camera_config_t cameraConfig = {
        .pixelFormat = kVIDEO_PixelFormatRGB565,
        .bytesPerPixel = CAMERA_BPP,
        .resolution = FSL_VIDEO_RESOLUTION(CAMERA_FRAME_WIDTH, CAMERA_FRAME_HEIGHT),
        .frameBufferLinePitch_Bytes = CAMERA_FRAME_WIDTH * CAMERA_BPP,
        .interface = kCAMERA_InterfaceGatedClock,
        .controlFlags = (kCAMERA_HrefActiveHigh | kCAMERA_DataLatchOnRisingEdge),
        .framePerSec = 30,
        .mipiChannel = 0,
        .csiLanes = 0,
        .fwaddress = 0,
    };

    BOARD_InitCameraResource();

    err = CAMERA_RECEIVER_Init(&cameraReceiver, &cameraConfig, newCameraFrame, this);
    RETURN_FALSE_IF(err != kStatus_Success, "Failed to initialize camera receiver!");

    err = CAMERA_DEVICE_Init(&cameraDevice, &cameraConfig);
    RETURN_FALSE_IF(err != kStatus_Success, "Failed to initialize camera device!");

    for (uint32_t i = 0; i < CAMERA_BUFFER_COUNT; i++) {
        err = CAMERA_RECEIVER_SubmitEmptyBuffer(&cameraReceiver, (uint32_t) (s_cameraBuffers[i]));
        RETURN_FALSE_IF(err != kStatus_Success, "Failed to submit camera buffer!");
    }

    return true;
}

void CameraInterface::startCamera()
{
    status_t err = CAMERA_RECEIVER_Start(&cameraReceiver);
    FAIL_IF(err != kStatus_Success, "Failed to start camera receiver!");
}

void CameraInterface::stopCamera()
{
    status_t err = CAMERA_RECEIVER_Stop(&cameraReceiver);
    FAIL_IF(err != kStatus_Success, "Failed to stop camera receiver!");
}

static void cleanup(uint8_t *memory)
{
    status_t err = CAMERA_RECEIVER_SubmitEmptyBuffer(&cameraReceiver, (uint32_t) (memory));
    FAIL_IF(err != kStatus_Success, "Failed to submit camera buffer");
}

void CameraInterface::onEvent(const FrameEvent &frameEvent)
{
    Qul::Image newFrame(frameEvent.newFrame,
                        CAMERA_FRAME_WIDTH,
                        CAMERA_FRAME_HEIGHT,
                        Qul::PixelFormat_RGB16,
                        -1,
                        cleanup);
    image.setValue(Qul::SharedImage(newFrame));
}

/* We use the platform function instead of SDK VIDEO_DelayMs because the DelayLoop() function in
 * NXP SDK code (fsl_common_arm.c) uses a non-local label in inline assembly, which is
 * incompatible with GCC LTO when that function gets inlined */
extern "C" void qul_delayMs(uint32_t ms);
void VIDEO_DelayMs(uint32_t ms)
{
    qul_delayMs(ms);
}