241 lines
6.1 KiB
C
241 lines
6.1 KiB
C
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include "lisa_log.h"
|
|
#include "lisa_device.h"
|
|
#include "lisa_camera.h"
|
|
#include "lisa_gpio.h"
|
|
#include "IOMuxManager.h"
|
|
#include "service_camera.h"
|
|
#include "board.h"
|
|
|
|
#define TAG "service_camera"
|
|
|
|
#define CAMERA_DEVICE "camera"
|
|
#define DVP_DEVICE "dvp0"
|
|
#define I2C_DEVICE "i2c0"
|
|
#define DMA_CHANNEL 4
|
|
|
|
struct camera_context {
|
|
uint32_t inited: 1;
|
|
lisa_device_t *camera_dev;
|
|
lisa_device_t *i2c_dev;
|
|
lisa_device_t *dvp_dev;
|
|
lisa_device_t *gpioa;
|
|
lisa_device_t *gpiob;
|
|
lisa_camera_pixel_format_t pixel_format;
|
|
uint16_t width;
|
|
uint16_t height;
|
|
};
|
|
|
|
static struct camera_context cam_ctx = {
|
|
.inited = 0,
|
|
.camera_dev = NULL,
|
|
.i2c_dev = NULL,
|
|
.dvp_dev = NULL,
|
|
.gpioa = NULL,
|
|
.gpiob = NULL,
|
|
.pixel_format = LISA_CAMERA_PIXFMT_RGB565,
|
|
.width = 0,
|
|
.height = 0,
|
|
};
|
|
|
|
static void service_camera_context_reset(void)
|
|
{
|
|
cam_ctx.inited = 0;
|
|
cam_ctx.camera_dev = NULL;
|
|
cam_ctx.i2c_dev = NULL;
|
|
cam_ctx.dvp_dev = NULL;
|
|
cam_ctx.gpioa = NULL;
|
|
cam_ctx.gpiob = NULL;
|
|
cam_ctx.pixel_format = LISA_CAMERA_PIXFMT_RGB565;
|
|
cam_ctx.width = 0;
|
|
cam_ctx.height = 0;
|
|
}
|
|
|
|
int service_camera_init(void)
|
|
{
|
|
int ret;
|
|
|
|
if (cam_ctx.inited) {
|
|
LISA_LOGW(TAG, "Camera already initialized");
|
|
return 0;
|
|
}
|
|
|
|
service_camera_context_reset();
|
|
|
|
cam_ctx.camera_dev = lisa_device_get(CAMERA_DEVICE);
|
|
if (!cam_ctx.camera_dev || !lisa_device_ready(cam_ctx.camera_dev)) {
|
|
LISA_LOGE(TAG, "Camera device not ready");
|
|
return -1;
|
|
}
|
|
|
|
cam_ctx.i2c_dev = lisa_device_get(I2C_DEVICE);
|
|
if (!cam_ctx.i2c_dev || !lisa_device_ready(cam_ctx.i2c_dev)) {
|
|
LISA_LOGE(TAG, "I2C device not ready");
|
|
return -2;
|
|
}
|
|
|
|
cam_ctx.dvp_dev = lisa_device_get(DVP_DEVICE);
|
|
if (!cam_ctx.dvp_dev || !lisa_device_ready(cam_ctx.dvp_dev)) {
|
|
LISA_LOGE(TAG, "DVP device not ready");
|
|
return -3;
|
|
}
|
|
|
|
cam_ctx.gpioa = lisa_device_get("gpioa");
|
|
cam_ctx.gpiob = lisa_device_get("gpiob");
|
|
|
|
lisa_camera_config_t config = {
|
|
.hw_config =
|
|
{
|
|
.mclk_pad = CSK_IOMUX_PAD_A,
|
|
.mclk_pin = CAM_MCLK_PIN,
|
|
.xclk_delay_us = 0,
|
|
.i2c_dev = cam_ctx.i2c_dev,
|
|
},
|
|
.xclk_freq_hz = 18000000,
|
|
.fb_count = 3,
|
|
.enable_hmirror = false,
|
|
.enable_vflip = false,
|
|
.enable_colorbar = false,
|
|
};
|
|
|
|
ret = lisa_camera_setup(cam_ctx.camera_dev, &config);
|
|
if (ret != LISA_DEVICE_OK) {
|
|
LISA_LOGE(TAG, "Failed to setup camera: %d", ret);
|
|
return -4;
|
|
}
|
|
|
|
lisa_camera_bus_config_t bus_config = {
|
|
.dma_channel = DMA_CHANNEL,
|
|
.bus_type = LISA_CAMERA_BUS_DVP,
|
|
.config.dvp =
|
|
{
|
|
.dvp_dev = cam_ctx.dvp_dev,
|
|
.dvp_freq = config.xclk_freq_hz,
|
|
.data_align = 1,
|
|
.line_offset = 0,
|
|
.pixel_offset = 0,
|
|
.pclk_polarity = 0,
|
|
.vsync_polarity = 1,
|
|
.hsync_polarity = 1,
|
|
},
|
|
};
|
|
|
|
lisa_camera_crop_t crop = {
|
|
.x = 0,
|
|
.y = 0,
|
|
.width = 320,
|
|
.height = 240,
|
|
};
|
|
|
|
ret = lisa_camera_set_crop(cam_ctx.camera_dev, &crop);
|
|
if (ret != LISA_DEVICE_OK) {
|
|
LISA_LOGE(TAG, "Failed to set crop: %d", ret);
|
|
return -5;
|
|
}
|
|
|
|
int flip = 0;
|
|
if (lisa_kv_get_int("user.camera.flip", &flip) != 0) {
|
|
flip = 1;
|
|
}
|
|
|
|
if (flip) {
|
|
lisa_camera_set_hmirror(cam_ctx.camera_dev, true);
|
|
lisa_camera_set_vflip(cam_ctx.camera_dev, true);
|
|
} else {
|
|
lisa_camera_set_hmirror(cam_ctx.camera_dev, false);
|
|
lisa_camera_set_vflip(cam_ctx.camera_dev, false);
|
|
}
|
|
|
|
lisa_camera_get_framesize(cam_ctx.camera_dev, &bus_config.width, &bus_config.height);
|
|
bus_config.pixel_format = lisa_camera_get_pixformat(cam_ctx.camera_dev);
|
|
|
|
cam_ctx.width = bus_config.width;
|
|
cam_ctx.height = bus_config.height;
|
|
cam_ctx.pixel_format = bus_config.pixel_format;
|
|
|
|
ret = lisa_camera_attach_bus(cam_ctx.camera_dev, &bus_config);
|
|
if (ret != LISA_DEVICE_OK) {
|
|
LISA_LOGE(TAG, "Failed to attach bus: %d", ret);
|
|
return -6;
|
|
}
|
|
|
|
cam_ctx.inited = 1;
|
|
LISA_LOGI(TAG, "Camera initialized: %ux%u, format=%d", cam_ctx.width, cam_ctx.height, cam_ctx.pixel_format);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int service_camera_capture(uint8_t *buffer, uint32_t buffer_len)
|
|
{
|
|
lisa_camera_fb_t *fb = NULL;
|
|
int ret;
|
|
|
|
if (!cam_ctx.inited || !cam_ctx.camera_dev) {
|
|
LISA_LOGE(TAG, "Camera not initialized");
|
|
return -1;
|
|
}
|
|
|
|
if (buffer == NULL || buffer_len < cam_ctx.width * cam_ctx.height * 2) {
|
|
LISA_LOGE(TAG, "Invalid parameters");
|
|
return -2;
|
|
}
|
|
|
|
ret = lisa_camera_start(cam_ctx.camera_dev);
|
|
if (ret != LISA_DEVICE_OK) {
|
|
LISA_LOGE(TAG, "Failed to start camera: %d", ret);
|
|
return -3;
|
|
}
|
|
|
|
ret = lisa_camera_capture(cam_ctx.camera_dev, &fb);
|
|
if (ret != LISA_DEVICE_OK || fb == NULL) {
|
|
LISA_LOGE(TAG, "Capture failed: %d", ret);
|
|
lisa_camera_stop(cam_ctx.camera_dev);
|
|
return -4;
|
|
}
|
|
|
|
if (buffer_len < fb->len) {
|
|
LISA_LOGE(TAG, "Buffer len %d is smaller than fb len %d", buffer_len, fb->len);
|
|
lisa_camera_release_fb(cam_ctx.camera_dev, fb);
|
|
lisa_camera_stop(cam_ctx.camera_dev);
|
|
return -5;
|
|
}
|
|
|
|
memcpy(buffer, fb->buf, fb->len);
|
|
|
|
lisa_camera_release_fb(cam_ctx.camera_dev, fb);
|
|
lisa_camera_stop(cam_ctx.camera_dev);
|
|
|
|
uint16_t *pixels = (uint16_t *)buffer;
|
|
for (uint32_t i = 0; i < cam_ctx.width * cam_ctx.height; i++) {
|
|
uint16_t pixel = pixels[i];
|
|
pixels[i] = (pixel >> 8) | (pixel << 8);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int service_camera_get_framesize(uint16_t *width, uint16_t *height)
|
|
{
|
|
if (!cam_ctx.inited || !cam_ctx.camera_dev) {
|
|
LISA_LOGE(TAG, "Camera not initialized");
|
|
return -1;
|
|
}
|
|
|
|
if (!width || !height) {
|
|
LISA_LOGE(TAG, "Invalid parameters");
|
|
return -2;
|
|
}
|
|
|
|
*width = cam_ctx.width;
|
|
*height = cam_ctx.height;
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool service_camera_is_inited(void)
|
|
{
|
|
return cam_ctx.inited;
|
|
}
|