mirror of
https://gitee.com/Vancouver2017/luban-lite.git
synced 2025-12-16 17:18:56 +00:00
619 lines
15 KiB
C
619 lines
15 KiB
C
/*
|
|
* Copyright (c) 2024-2025, ArtInChip Technology Co., Ltd
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Authors: matteo <duanmt@artinchip.com>
|
|
*/
|
|
|
|
#define LOG_TAG "sc031gs"
|
|
|
|
#include <drivers/i2c.h>
|
|
#include <drivers/pin.h>
|
|
|
|
#include "aic_core.h"
|
|
#include "mpp_types.h"
|
|
#include "mpp_img_size.h"
|
|
#include "mpp_vin.h"
|
|
|
|
#include "drv_camera.h"
|
|
#include "camera_inner.h"
|
|
|
|
/* Default format configuration of SC031GS */
|
|
#define SC031GS_DFT_WIDTH VGA_WIDTH
|
|
#define SC031GS_DFT_HEIGHT VGA_HEIGHT
|
|
#define SC031GS_DFT_BUS_TYPE MEDIA_BUS_RAW8_MONO
|
|
#define SC031GS_DFT_CODE MEDIA_BUS_FMT_Y8_1X8
|
|
// #define SC031GS_TEST_MODE
|
|
|
|
#define SC031GS_I2C_SLAVE_ID 0x30
|
|
#define SC031GS_CHIP_ID 0x3105
|
|
|
|
#define SC031GS_PID_HIGH_REG 0x3108
|
|
#define SC031GS_PID_LOW_REG 0x3109
|
|
#define SC031GS_MAX_FRAME_WIDTH (640)
|
|
#define SC031GS_MAX_FRAME_HIGH (480)
|
|
|
|
#define SC031GS_OUTPUT_WINDOW_WIDTH_H_REG 0x3208
|
|
#define SC031GS_OUTPUT_WINDOW_WIDTH_L_REG 0x3209
|
|
#define SC031GS_OUTPUT_WINDOW_HIGH_H_REG 0x320a
|
|
#define SC031GS_OUTPUT_WINDOW_HIGH_L_REG 0x320b
|
|
#define SC031GS_OUTPUT_WINDOW_START_Y_H_REG 0x3210
|
|
#define SC031GS_OUTPUT_WINDOW_START_Y_L_REG 0x3211
|
|
#define SC031GS_OUTPUT_WINDOW_START_X_H_REG 0x3212
|
|
#define SC031GS_OUTPUT_WINDOW_START_X_L_REG 0x3213
|
|
#define SC031GS_LED_STROBE_ENABLE_REG 0x3361 // When the camera is in exposure, this PAD LEDSTROBE will be high to drive the external LED.
|
|
|
|
#define REG_NULL 0xFFFF
|
|
#define REG_DELAY 0X0000
|
|
|
|
// 640*480, xclk=24M, fps=60fps
|
|
static const struct reg16_info sc031gs_640x480_init_regs[] = {
|
|
{0x0103, 0x01},
|
|
{0x0100, 0x00},
|
|
{0x3018, 0x1f},
|
|
{0x3019, 0xff},
|
|
{0x301c, 0xb4},
|
|
{0x363c, 0x08},
|
|
{0x3630, 0x82},
|
|
{0x3638, 0x0f},
|
|
{0x3639, 0x08},
|
|
{0x335b, 0x80},
|
|
{0x3636, 0x25},
|
|
{0x3640, 0x02},
|
|
{0x3306, 0x38},
|
|
{0x3304, 0x48},
|
|
{0x3389, 0x01},
|
|
{0x3385, 0x31},
|
|
{0x330c, 0x18},
|
|
{0x3315, 0x38},
|
|
{0x3306, 0x28},
|
|
{0x3309, 0x68},
|
|
{0x3387, 0x51},
|
|
{0x3306, 0x48},
|
|
{0x3366, 0x04},
|
|
{0x335f, 0x80},
|
|
{0x363a, 0x00},
|
|
{0x3622, 0x01},
|
|
{0x3633, 0x62},
|
|
{0x36f9, 0x20},
|
|
{0x3637, 0x80},
|
|
{0x363d, 0x04},
|
|
{0x3e06, 0x00},
|
|
{0x363c, 0x48},
|
|
{0x320c, 0x03},
|
|
{0x320e, 0x0e},
|
|
{0x320f, 0xa8},
|
|
{0x3306, 0x38},
|
|
{0x330b, 0xb6},
|
|
{0x36f9, 0x24},
|
|
{0x363b, 0x4a},
|
|
{0x3366, 0x02},
|
|
{0x3316, 0x78},
|
|
{0x3344, 0x74},
|
|
{0x3335, 0x74},
|
|
{0x332f, 0x70},
|
|
{0x332d, 0x6c},
|
|
{0x3329, 0x6c},
|
|
{0x363c, 0x08},
|
|
{0x3630, 0x81},
|
|
{0x3366, 0x06},
|
|
{0x3314, 0x3a},
|
|
{0x3317, 0x28},
|
|
{0x3622, 0x05},
|
|
{0x363d, 0x00},
|
|
{0x3637, 0x86},
|
|
{0x3e01, 0x62},
|
|
{0x3633, 0x52},
|
|
{0x3630, 0x86},
|
|
{0x3306, 0x4c},
|
|
{0x330b, 0xa0},
|
|
{0x3631, 0x48},
|
|
{0x33b1, 0x03},
|
|
{0x33b2, 0x06},
|
|
{0x320c, 0x02},
|
|
{0x320e, 0x02},
|
|
{0x320f, 0x0d},
|
|
{0x3e01, 0x20},
|
|
{0x3e02, 0x20},
|
|
{0x3316, 0x68},
|
|
{0x3344, 0x64},
|
|
{0x3335, 0x64},
|
|
{0x332f, 0x60},
|
|
{0x332d, 0x5c},
|
|
{0x3329, 0x5c},
|
|
{0x3310, 0x10},
|
|
{0x3637, 0x87},
|
|
{0x363e, 0xf8},
|
|
{0x3254, 0x02},
|
|
{0x3255, 0x07},
|
|
{0x3252, 0x02},
|
|
{0x3253, 0xa6},
|
|
{0x3250, 0xf0},
|
|
{0x3251, 0x02},
|
|
{0x330f, 0x50},
|
|
{0x3630, 0x46},
|
|
{0x3621, 0xa2},
|
|
{0x3621, 0xa0},
|
|
{0x4500, 0x59},
|
|
{0x3637, 0x88},
|
|
{0x3908, 0x81},
|
|
{0x3640, 0x00},
|
|
{0x3641, 0x02},
|
|
{0x363c, 0x05},
|
|
{0x363b, 0x4c},
|
|
{0x36e9, 0x40},
|
|
{0x36ea, 0x36},
|
|
{0x36ed, 0x13},
|
|
{0x36f9, 0x04},
|
|
{0x36fa, 0x38},
|
|
{0x330b, 0x80},
|
|
{0x3640, 0x00},
|
|
{0x3641, 0x01},
|
|
{0x3d08, 0x00},
|
|
{0x3306, 0x48},
|
|
{0x3621, 0xa4},
|
|
{0x300f, 0x0f},
|
|
{0x4837, 0x1b},
|
|
{0x4809, 0x01},
|
|
{0x363b, 0x48},
|
|
{0x363c, 0x06},
|
|
{0x36e9, 0x00},
|
|
{0x36ea, 0x3b},
|
|
{0x36eb, 0x1A},
|
|
{0x36ec, 0x0A},
|
|
{0x36ed, 0x33},
|
|
{0x36f9, 0x00},
|
|
{0x36fa, 0x3a},
|
|
{0x36fc, 0x01},
|
|
{0x320c, 0x03},
|
|
{0x320d, 0x6e},
|
|
#if 0
|
|
/* 120fps */
|
|
{0x320e, 0x02},
|
|
{0x320f, 0xab},
|
|
#else
|
|
/* 60fps */
|
|
{0x320e, 0x05},
|
|
{0x320f, 0x56},
|
|
#endif
|
|
{0x330b, 0x80},
|
|
{0x330f, 0x50},
|
|
{0x3637, 0x89},
|
|
{0x3641, 0x01},
|
|
{0x4501, 0xc4},
|
|
{0x5011, 0x01},
|
|
{0x3908, 0x21},
|
|
// AEC
|
|
{0x3e01, 0x18},
|
|
{0x3e02, 0x80},
|
|
|
|
{0x3306, 0x38},
|
|
{0x330b, 0xe0},
|
|
{0x330f, 0x20},
|
|
{0x3d08, 0x01},
|
|
{0x3314, 0x65},
|
|
{0x3317, 0x10},
|
|
{0x5011, 0x00},
|
|
{0x3e06, 0x0c},
|
|
{0x3908, 0x91},
|
|
{0x3624, 0x47},
|
|
{0x3220, 0x10},
|
|
{0x3635, 0x18},
|
|
{0x3223, 0x50},
|
|
{0x301f, 0x01},
|
|
{0x3028, 0x82},
|
|
// AGC
|
|
{0x3e09, 0x1f},
|
|
|
|
{0x0100, 0x01},
|
|
{REG_DELAY, 0x0a},
|
|
{0x4418, 0x08},
|
|
{0x4419, 0x8e},
|
|
{0x4419, 0x80},
|
|
{0x363d, 0x10},
|
|
{0x3630, 0x48},
|
|
|
|
{REG_NULL, 0x00},
|
|
};
|
|
|
|
struct sc03_dev {
|
|
struct rt_device dev;
|
|
struct rt_i2c_bus_device *i2c;
|
|
u32 pwdn_pin;
|
|
struct clk *clk;
|
|
|
|
struct mpp_video_fmt fmt;
|
|
|
|
bool on;
|
|
bool streaming;
|
|
};
|
|
|
|
static struct sc03_dev g_sc03_dev = {0};
|
|
|
|
static int sc031gs_read_reg(struct rt_i2c_bus_device *i2c, u16 reg, u8 *val)
|
|
{
|
|
if (rt_i2c_read_reg16(i2c, SC031GS_I2C_SLAVE_ID, reg, val, 1) != 1) {
|
|
LOG_E("%s: error: reg = 0x%x, val = 0x%x", __func__, reg, *val);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u16 sc031gs_read_u16(struct rt_i2c_bus_device *i2c, u16 reg_h, u16 reg_l)
|
|
{
|
|
u8 val_h = 0, val_l = 0;
|
|
|
|
if (sc031gs_read_reg(i2c, reg_h, &val_h) ||
|
|
sc031gs_read_reg(i2c, reg_l, &val_l))
|
|
return 0;
|
|
|
|
return val_h << 8 | val_l;
|
|
}
|
|
|
|
static int sc031gs_write_reg(struct rt_i2c_bus_device *i2c, u16 reg, u8 val)
|
|
{
|
|
if (rt_i2c_write_reg16(i2c, SC031GS_I2C_SLAVE_ID, reg, &val, 1) != 1) {
|
|
LOG_E("%s: error: reg = 0x%x, val = 0x%x", __func__, reg, val);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sc031gs_write_array(struct rt_i2c_bus_device *i2c,
|
|
const struct reg16_info *regs, u32 size)
|
|
{
|
|
int i, ret = 0;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
if (regs[i].reg == REG_NULL)
|
|
break;
|
|
|
|
if (regs[i].reg == REG_DELAY) {
|
|
aicos_udelay(regs[i].val * 1000);
|
|
continue;
|
|
}
|
|
|
|
ret = sc031gs_write_reg(i2c, regs[i].reg, regs[i].val);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef SC031GS_TEST_MODE
|
|
static void sc031gs_test_mode(struct sc03_dev *sensor)
|
|
{
|
|
sc031gs_write_reg(sensor->i2c, 0x4501, 0xcc);
|
|
sc031gs_write_reg(sensor->i2c, 0x3902, 0x0);
|
|
sc031gs_write_reg(sensor->i2c, 0x3e06, 0x3);
|
|
}
|
|
#endif
|
|
|
|
static int sc031gs_init(struct sc03_dev *sensor)
|
|
{
|
|
if (sc031gs_write_array(sensor->i2c, sc031gs_640x480_init_regs, ARRAY_SIZE(sc031gs_640x480_init_regs)))
|
|
return -1;
|
|
|
|
#ifdef SC031GS_TEST_MODE
|
|
sc031gs_test_mode(sensor);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sc031gs_probe(struct sc03_dev *sensor)
|
|
{
|
|
u8 id_h = 0, id_l = 0;
|
|
|
|
if (sc031gs_read_reg(sensor->i2c, SC031GS_PID_LOW_REG, &id_l) ||
|
|
sc031gs_read_reg(sensor->i2c, SC031GS_PID_HIGH_REG, &id_h))
|
|
return -1;
|
|
|
|
if ((id_h << 8 | id_l) != SC031GS_CHIP_ID) {
|
|
LOG_E("Invalid chip ID: %02x %02x\n", id_h, id_l);
|
|
return -1;
|
|
}
|
|
return sc031gs_init(sensor);
|
|
}
|
|
|
|
static bool sc031gs_is_open(struct sc03_dev *sensor)
|
|
{
|
|
return sensor->on;
|
|
}
|
|
|
|
static void sc031gs_power_on(struct sc03_dev *sensor)
|
|
{
|
|
if (sensor->on)
|
|
return;
|
|
|
|
camera_pin_set_high(sensor->pwdn_pin);
|
|
aicos_udelay(2);
|
|
|
|
LOG_I("Power on");
|
|
sensor->on = true;
|
|
}
|
|
|
|
static void sc031gs_power_off(struct sc03_dev *sensor)
|
|
{
|
|
if (!sensor->on)
|
|
return;
|
|
|
|
camera_pin_set_low(sensor->pwdn_pin);
|
|
|
|
LOG_I("Power off");
|
|
sensor->on = false;
|
|
}
|
|
|
|
static rt_err_t sc03_init(rt_device_t dev)
|
|
{
|
|
struct sc03_dev *sensor = (struct sc03_dev *)dev;
|
|
|
|
sensor->i2c = camera_i2c_get();
|
|
if (!sensor->i2c)
|
|
return -RT_EINVAL;
|
|
|
|
sensor->fmt.code = SC031GS_DFT_CODE;
|
|
sensor->fmt.width = SC031GS_DFT_WIDTH;
|
|
sensor->fmt.height = SC031GS_DFT_HEIGHT;
|
|
sensor->fmt.bus_type = SC031GS_DFT_BUS_TYPE;
|
|
sensor->fmt.flags = MEDIA_SIGNAL_HSYNC_ACTIVE_HIGH |
|
|
MEDIA_SIGNAL_VSYNC_ACTIVE_HIGH |
|
|
MEDIA_SIGNAL_PCLK_SAMPLE_FALLING;
|
|
|
|
sensor->pwdn_pin = camera_pwdn_pin_get();
|
|
if (!sensor->pwdn_pin)
|
|
return -RT_EINVAL;
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t sc03_open(rt_device_t dev, rt_uint16_t oflag)
|
|
{
|
|
struct sc03_dev *sensor = (struct sc03_dev *)dev;
|
|
|
|
if (sc031gs_is_open(sensor))
|
|
return RT_EOK;
|
|
|
|
sc031gs_power_on(sensor);
|
|
|
|
if (sc031gs_probe(sensor)) {
|
|
sc031gs_power_off(sensor);
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
LOG_I("SC031GS inited");
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t sc03_close(rt_device_t dev)
|
|
{
|
|
struct sc03_dev *sensor = (struct sc03_dev *)dev;
|
|
|
|
if (!sc031gs_is_open(sensor))
|
|
return -RT_ERROR;
|
|
|
|
sc031gs_power_off(sensor);
|
|
return RT_EOK;
|
|
}
|
|
|
|
static int sc03_get_fmt(struct sc03_dev *sensor, struct mpp_video_fmt *cfg)
|
|
{
|
|
cfg->code = sensor->fmt.code;
|
|
cfg->width = sensor->fmt.width;
|
|
cfg->height = sensor->fmt.height;
|
|
cfg->flags = sensor->fmt.flags;
|
|
cfg->bus_type = sensor->fmt.bus_type;
|
|
return RT_EOK;
|
|
}
|
|
|
|
static int sc03_start(struct sc03_dev *sensor)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int sc03_stop(struct sc03_dev *sensor)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int sc03_set_fps(struct sc03_dev *sensor, u32 fps)
|
|
{
|
|
u16 cur = 0, base = 0x2ab, val = 0;
|
|
|
|
if (fps < 10)
|
|
fps = 10;
|
|
if (fps > 120)
|
|
fps = 120;
|
|
|
|
cur = sc031gs_read_u16(sensor->i2c, 0x320e, 0x320f);
|
|
if (!cur) {
|
|
LOG_E("Failed to read FPS\n");
|
|
return -1;
|
|
}
|
|
|
|
val = base * 120 / fps;
|
|
|
|
LOG_I("Set FPS %d[0x%03x] -> %d[0x%03x]\n",
|
|
120 * base / cur, cur, 120 * base / val, val);
|
|
|
|
if (cur == val)
|
|
return 0;
|
|
|
|
if (sc031gs_write_reg(sensor->i2c, 0x320e, val >> 8) ||
|
|
sc031gs_write_reg(sensor->i2c, 0x320f, val & 0xFF))
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Adjust AEC preferentially, and then adjust AGC */
|
|
static int sc03_set_agc(struct sc03_dev *sensor, u32 percent)
|
|
{
|
|
u16 cur = 0, val = 0, gain = PERCENT_TO_INT(0x10, 0xFF, percent);
|
|
u8 val_h = 0, val_l = 0;
|
|
|
|
if (sc031gs_read_reg(sensor->i2c, 0x3e08, &val_h)
|
|
|| sc031gs_read_reg(sensor->i2c, 0x3e09, &val_l)) {
|
|
LOG_E("Failed to get current AGC\n");
|
|
return -1;
|
|
}
|
|
cur = ((val_h & 0xC) << 6) | val_l; /* {16'h3e08[4:2],16h3e09} */
|
|
|
|
sc031gs_write_reg(sensor->i2c, 0x3303, 0x0b);
|
|
|
|
if (gain < 0x20) { /* 1x ~ 2x */
|
|
val_h = 0x03;
|
|
val_l = 0x10 | ((gain - 0x10) & 0xF);
|
|
} else if (gain < 0x40) { /* 2x ~ 4x */
|
|
val_h = 0x7;
|
|
val_l = 0x10 | (((gain - 0x20) >> 1) & 0xF);
|
|
} else if (gain < 0x80) { /* 4x ~ 8x */
|
|
val_h = 0xf;
|
|
val_l = 0x10 | (((gain - 0x40) >> 2) & 0xF);
|
|
} else { /* 8x ~ 16x */
|
|
val_h = 0x1f;
|
|
val_l = 0x10 | (((gain - 0x80) >> 3) & 0xF);
|
|
}
|
|
|
|
val = (val_h << 8) | val_l;
|
|
LOG_I("Set AGC %d[0x%03x] -> %d[0x%03x]\n", cur, cur, val, val);
|
|
|
|
if (cur == val)
|
|
return 0;
|
|
|
|
if (sc031gs_write_reg(sensor->i2c, 0x3e08, val_h) ||
|
|
sc031gs_write_reg(sensor->i2c, 0x3e09, val_l))
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* SC031GS doesn't support AEC. Adjust the exposure time to control brightness. */
|
|
static int sc03_set_aec(struct sc03_dev *sensor, u32 percent)
|
|
{
|
|
u16 frame_len = 0, cur = 0, val = 0;
|
|
u8 val_h = 0, val_l = 0;
|
|
|
|
frame_len = sc031gs_read_u16(sensor->i2c, 0x320e, 0x320f);
|
|
if (!frame_len) {
|
|
LOG_E("Failed to get frame length\n");
|
|
return -1;
|
|
}
|
|
if (frame_len - 6 > 0xFFF) {
|
|
LOG_W("Frame length %d is too large\n", frame_len);
|
|
frame_len = 0xFFF + 6;
|
|
}
|
|
|
|
if (sc031gs_read_reg(sensor->i2c, 0x3e01, &val_h)
|
|
|| sc031gs_read_reg(sensor->i2c, 0x3e02, &val_l)) {
|
|
LOG_E("Failed to get current AEC\n");
|
|
return -1;
|
|
}
|
|
cur = (val_h << 4) | (val_l >> 4); // {16'h3e01,16'h3e02[7:4]}
|
|
|
|
val = PERCENT_TO_INT(1, frame_len - 6, percent);
|
|
LOG_I("Set AEC %d[0x%03x] -> %d[0x%03x] (Frame length %d[0x%03x])\n",
|
|
cur, cur, val, val, frame_len, frame_len);
|
|
|
|
if (cur == val)
|
|
return 0;
|
|
|
|
if (sc031gs_write_reg(sensor->i2c, 0x3e01, (val >> 4) & 0xFF) ||
|
|
sc031gs_write_reg(sensor->i2c, 0x3e02, (val & 0xF) << 4))
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int sc03_enable_h_flip(struct sc03_dev *sensor, bool enable)
|
|
{
|
|
u8 cur = 0, mask = 0x6;
|
|
|
|
if (sc031gs_read_reg(sensor->i2c, 0x3221, &cur)) {
|
|
LOG_E("Failed to get the H flip status\n");
|
|
return -1;
|
|
}
|
|
|
|
LOG_I("Set H flip 0x%x -> 0x%x\n", (cur & mask) >> 1, enable ? 3 : 0);
|
|
if (enable)
|
|
return sc031gs_write_reg(sensor->i2c, 0x3221, cur | mask);
|
|
else
|
|
return sc031gs_write_reg(sensor->i2c, 0x3221, cur & ~mask);
|
|
}
|
|
|
|
static int sc03_enable_v_flip(struct sc03_dev *sensor, bool enable)
|
|
{
|
|
u8 cur = 0, mask = 0x60;
|
|
|
|
if (sc031gs_read_reg(sensor->i2c, 0x3221, &cur)) {
|
|
LOG_E("Failed to get the V flip status\n");
|
|
return -1;
|
|
}
|
|
|
|
LOG_I("Set V flip 0x%x -> 0x%x\n", (cur & mask) >> 5, enable ? 3 : 0);
|
|
if (enable)
|
|
return sc031gs_write_reg(sensor->i2c, 0x3221, cur | mask);
|
|
else
|
|
return sc031gs_write_reg(sensor->i2c, 0x3221, cur & ~mask);
|
|
}
|
|
|
|
static rt_err_t sc03_control(rt_device_t dev, int cmd, void *args)
|
|
{
|
|
struct sc03_dev *sensor = (struct sc03_dev *)dev;
|
|
|
|
switch (cmd) {
|
|
case CAMERA_CMD_START:
|
|
return sc03_start(sensor);
|
|
case CAMERA_CMD_STOP:
|
|
return sc03_stop(sensor);
|
|
case CAMERA_CMD_GET_FMT:
|
|
return sc03_get_fmt(sensor, (struct mpp_video_fmt *)args);
|
|
case CAMERA_CMD_SET_FPS:
|
|
return sc03_set_fps(sensor, *(u32 *)args);
|
|
case CAMERA_CMD_SET_AUTOGAIN:
|
|
return sc03_set_agc(sensor, *(u32 *)args);
|
|
case CAMERA_CMD_SET_AEC_VAL:
|
|
return sc03_set_aec(sensor, *(u32 *)args);
|
|
case CAMERA_CMD_SET_H_FLIP:
|
|
return sc03_enable_h_flip(sensor, *(bool *)args);
|
|
case CAMERA_CMD_SET_V_FLIP:
|
|
return sc03_enable_v_flip(sensor, *(bool *)args);
|
|
default:
|
|
LOG_I("Unsupported cmd: 0x%x", cmd);
|
|
return -RT_EINVAL;
|
|
}
|
|
return RT_EOK;
|
|
}
|
|
|
|
#ifdef RT_USING_DEVICE_OPS
|
|
static const struct rt_device_ops sc03_ops =
|
|
{
|
|
.init = sc03_init,
|
|
.open = sc03_open,
|
|
.close = sc03_close,
|
|
.control = sc03_control,
|
|
};
|
|
#endif
|
|
|
|
int rt_hw_sc03_init(void)
|
|
{
|
|
#ifdef RT_USING_DEVICE_OPS
|
|
g_sc03_dev.dev.ops = &sc03_ops;
|
|
#else
|
|
g_sc03_dev.dev.init = sc03_init;
|
|
g_sc03_dev.dev.open = sc03_open;
|
|
g_sc03_dev.dev.close = sc03_close;
|
|
g_sc03_dev.dev.control = sc03_control;
|
|
#endif
|
|
g_sc03_dev.dev.type = RT_Device_Class_CAMERA;
|
|
|
|
rt_device_register(&g_sc03_dev.dev, CAMERA_DEV_NAME, 0);
|
|
|
|
return 0;
|
|
}
|
|
INIT_DEVICE_EXPORT(rt_hw_sc03_init);
|