Phân tích Driver Camera IMX297/296

Bài viết này tập trung vào việc phân tích driver cho các cảm biến hình ảnh IMX297 và IMX296, bao gồm so sánh hai model, phân tích mã nguồn driver Linux và cấu hình thanh ghi.

1. Giới thiệu

Các điểm chính của bài viết bao gồm:

  • So sánh chi tiết giữa IMX297 và IMX296.
  • Phân tích cấu trúc và hoạt động của driver Linux cho các cảm biến này.
  • Khám phá cách cấu hình các thanh ghi của cảm biến.

1.1 Giải thích các khái niệm

Cảm biến hình ảnh Global Shutter

Cảm biến hình ảnh CMOS có hai loại chính:

  • Rolling Shutter: Quét từng dòng để thu nhận hình ảnh. Loại này có thể gây ra hiện tượng méo hình, "hiệu ứng jelly" hoặc đường kẻ dọc bị nghiêng khi chụp các vật thể chuyển động nhanh.
  • Global Shutter: Chụp toàn bộ hình ảnh cùng một lúc trên mặt phẳng tiêu cự. Loại này đảm bảo tất cả các pixel được phơi sáng đồng thời, loại bỏ hiện tượng méo hình khi chụp vật thể chuyển động nhanh.

Cảm biến Global Shutter lý tưởng cho các ứng dụng yêu cầu độ chính xác cao và hình ảnh không bị biến dạng, như kiểm tra công nghiệp, ghi lại chuyển động và các cảnh quay có độ động cao.

Dòng 3.4 Series

Thuật ngữ "3.4 Series" đề cập đến các cảm biến có kích thước pixel là 3.4μm. Đây là tiêu chuẩn cho các cảm biến hình ảnh công nghiệp, phù hợp với nhiều ứng dụng.

So sánh chi tiết IMX297 và IMX296

Cả IMX297 và IMX296 đều thuộc dòng 3.4 Series với cùng kích thước cảm biến quang học. Tuy nhiên, chúng khác nhau về độ phân giải tối đa và tốc độ khung hình.

  • Đều thuộc 3.4 Series.
  • Đều hướng tới ứng dụng nhúng với giao diện MIPI.
  • Kích thước vật lý giống nhau.
  • Tốc độ khung hình và độ phân giải tối đa khác nhau.
Model H x V Kích thước Tốc độ khung hình (fps) Tỷ lệ khung hình
IMX296 1456 x 1088 1/2.9" (6.3mm) 60 4:3
IMX297 728 x 544 1/2.9" (6.3mm) 120 4:3

1.2 Pregius vs Pregius S

Pregius là công nghệ pixel Global Shutter của Sony, kết hợp cấu trúc CCD giảm nhiễu để cho ra hình ảnh chất lượng cao. Tên gọi "Pregius" bắt nguồn từ "Precision GS", thể hiện sự kết hợp giữa hiệu suất giảm nhiễu của CCD và tốc độ, độ chính xác của Global Shutter (GS).

Pregius S là sự nâng cấp của Pregius, sử dụng cấu trúc BSI (Back-Side Illuminated) giúp tăng tốc độ khung hình và cấu trúc xếp chồng (stacked structure) cho phép thu nhỏ kích thước cảm biến, lắp đặt linh hoạt và mở rộng chức năng.

Tóm tắt đơn giản:

  1. Pregius = Cấu trúc CCD + CMOS Active Pixel + Global Shutter
  2. Pregius S = Pregius + Cấu trúc BSI + Cấu trúc xếp chồng

Cấu trúc BSI cải thiện góc chiếu sáng tới, nhưng thường đối mặt với các vấn đề về triệt tiêu nhiễu và dòng tối. Trong công nghệ Pregius S, Sony đã phát triển cấu trúc diode quang và cấu trúc che chắn ánh sáng tối ưu hóa cho BSI, giúp tăng độ nhạy, đặc tính bão hòa và giảm nhiễu.

2. Phân tích mã nguồn

2.1 Cấu trúc dữ liệu imx296

Cấu trúc này đại diện cho thiết bị cảm biến, chứa các con trỏ tới thiết bị, clock, bộ điều chỉnh nguồn, mô tả GPIO, ánh xạ thanh ghi, sub-device V4L2, bộ xử lý điều khiển, v.v.

struct imx296 {
    struct device *dev;
    struct clk *clk;
    struct regulator_bulk_data supplies[ARRAY_SIZE(imx296_supply_names)];
    struct gpio_desc *reset;
    struct regmap *regmap;

    const struct imx296_clk_params *clk_params;
    bool mono; // Cờ báo hiệu cảm biến đơn sắc hay màu

    bool streaming; // Cờ báo hiệu trạng thái streaming

    struct v4l2_subdev subdev; // Sub-device V4L2
    struct media_pad pad; // Pad của media controller

    struct v4l2_ctrl_handler ctrls; // Bộ xử lý các điều khiển V4L2
    struct v4l2_ctrl *hblank; // Điều khiển Horizontal Blanking
    struct v4l2_ctrl *vblank; // Điều khiển Vertical Blanking
    struct v4l2_ctrl *vflip; // Điều khiển lật dọc
    struct v4l2_ctrl *hflip; // Điều khiển lật ngang
};
    

2.2 Hàm đọc/ghi I2C

Các hàm này được sử dụng để đọc và ghi dữ liệu vào các thanh ghi của cảm biến thông qua giao diện I2C.

/**
 * imx296_read - Đọc dữ liệu từ thanh ghi I2C của cảm biến
 * @sensor: Con trỏ tới cấu trúc imx296
 * @addr: Địa chỉ thanh ghi cần đọc
 *
 * Trả về giá trị đọc được hoặc mã lỗi âm.
 */
static int imx296_read(struct imx296 *sensor, u32 addr)
{
    u8 data_buffer[3] = { 0, 0, 0 }; // Đệm lưu trữ dữ liệu đọc được
    int ret_code;

    // Sử dụng regmap_raw_read để đọc dữ liệu
    // addr & IMX296_REG_ADDR_MASK: Lấy địa chỉ thanh ghi
    // (addr >> IMX296_REG_SIZE_SHIFT) & 3: Xác định số byte cần đọc
    ret_code = regmap_raw_read(sensor->regmap, addr & IMX296_REG_ADDR_MASK, data_buffer,
                  (addr >> IMX296_REG_SIZE_SHIFT) & 3);
    if (ret_code < 0) {
        // Xử lý lỗi đọc
        return ret_code;
    }

    // Ghép các byte đọc được thành giá trị 32-bit
    return (data_buffer[2] << 16) | (data_buffer[1] << 8) | data_buffer[0];
}

/**
 * imx296_write - Ghi dữ liệu vào thanh ghi I2C của cảm biến
 * @sensor: Con trỏ tới cấu trúc imx296
 * @addr: Địa chỉ thanh ghi cần ghi
 * @value: Giá trị cần ghi
 * @err: Con trỏ để lưu mã lỗi (tùy chọn)
 *
 * Trả về 0 nếu thành công, hoặc mã lỗi âm nếu thất bại.
 */
static int imx296_write(struct imx296 *sensor, u32 addr, u32 value, int *err)
{
    // Chuẩn bị dữ liệu ghi theo định dạng byte
    u8 data_to_write[3] = { value & 0xff, (value >> 8) & 0xff, value >> 16 };
    int ret_code;

    // Kiểm tra lỗi đã được báo hiệu trước đó (nếu có)
    if (err && *err)
        return *err;

    // Sử dụng regmap_raw_write để ghi dữ liệu
    // addr & IMX296_REG_ADDR_MASK: Địa chỉ thanh ghi
    // (addr >> IMX296_REG_SIZE_SHIFT) & 3: Số byte cần ghi
    ret_code = regmap_raw_write(sensor->regmap, addr & IMX296_REG_ADDR_MASK,
                   data_to_write, (addr >> IMX296_REG_SIZE_SHIFT) & 3);
    if (ret_code < 0) {
        // Ghi thông báo lỗi chi tiết
        dev_err(sensor->dev, "%u-bit write to 0x%04x failed: %d\n",
            ((addr >> IMX296_REG_SIZE_SHIFT) & 3) * 8, // Số bit được ghi
            addr & IMX296_REG_ADDR_MASK, // Địa chỉ thanh ghi
            ret_code); // Mã lỗi
        if (err)
            *err = ret_code; // Lưu lỗi nếu có con trỏ err
    }

    return ret_code; // Trả về mã trạng thái
}
    

2.3 Quản lý nguồn điện

Các hàm imx296_power_onimx296_power_off chịu trách nhiệm quản lý trạng thái nguồn của cảm biến, bao gồm cung cấp điện áp, điều khiển tín hiệu reset và bật/tắt clock.

Driver cũng hỗ trợ quản lý nguồn theo thời gian thực (runtime PM) thông qua các hàm imx296_runtime_resumeimx296_runtime_suspend.

/**
 * imx296_power_on - Bật nguồn cho cảm biến IMX296
 * @sensor: Con trỏ tới cấu trúc imx296
 *
 * Cung cấp nguồn, kích hoạt reset và bật clock cho cảm biến.
 * Trả về 0 nếu thành công, mã lỗi âm nếu thất bại.
 */
static int imx296_power_on(struct imx296 *sensor)
{
    int ret_status;

    // Kích hoạt các nguồn cung cấp (regulators)
    ret_status = regulator_bulk_enable(ARRAY_SIZE(sensor->supplies),
                    sensor->supplies);
    if (ret_status < 0) {
        dev_err(sensor->dev, "Failed to enable supplies: %d\n", ret_status);
        return ret_status;
    }

    // Chờ một khoảng thời gian ngắn sau khi cấp nguồn
    udelay(1);

    // Thiết lập chân RESET ở trạng thái đầu ra và kéo xuống (reset)
    ret_status = gpiod_direction_output(sensor->reset, 0);
    if (ret_status < 0) {
        dev_err(sensor->dev, "Failed to set reset pin as output: %d\n", ret_status);
        goto power_off_supplies; // Quay lại tắt nguồn cung cấp nếu lỗi
    }

    // Chờ một khoảng thời gian ngắn sau khi reset
    udelay(1);

    // Chuẩn bị và kích hoạt clock cho cảm biến
    ret_status = clk_prepare_enable(sensor->clk);
    if (ret_status < 0) {
        dev_err(sensor->dev, "Failed to enable clock: %d\n", ret_status);
        goto release_reset_pin; // Quay lại giải phóng chân reset nếu lỗi
    }

    /*
     * Tài liệu không nêu rõ khoảng thời gian cần thiết sau khi cung cấp
     * clock và trước khi bắt đầu giao tiếp I2C. Tài liệu đề cập đến độ trễ
     * 20µs ở chế độ 4-wire, nhưng thử nghiệm cho thấy độ trễ 100µs có thể
     * gây lỗi giao tiếp I2C, trong khi 500µs dường như đủ. Chọn giá trị an toàn.
     */
    usleep_range(1000, 2000); // Độ trễ từ 1ms đến 2ms

    return 0; // Trả về thành công

power_off_supplies:
    regulator_bulk_disable(ARRAY_SIZE(sensor->supplies), sensor->supplies);
release_reset_pin:
    gpiod_direction_output(sensor->reset, 1); // Đặt chân reset về trạng thái không reset (output 1)
    return ret_status;
}

// imx296_power_off sẽ thực hiện các hành động ngược lại
// static int imx296_power_off(struct imx296 *sensor) { ... }
    

2.4 Giao diện điều khiển

Driver quản lý các điều khiển của cảm biến thông qua V4L2 (Video for Linux Two) API. Các điều khiển này bao gồm các tham số như Horizontal Blanking (hblank), Vertical Blanking (vblank), lật ảnh dọc (vflip) và lật ảnh ngang (hflip).

2.5 Thao tác V4L2 Sub-device

Driver triển khai các thao tác (operations) cần thiết cho một V4L2 sub-device, cho phép nó tích hợp vào framework media controller của Linux. Điều này bao gồm các hàm để query, set format, stream on/off, v.v.

2.6 Khởi tạo và Dọn dẹp

Hàm khởi tạo (probe) sẽ thực hiện việc đăng ký sub-device, thiết lập các kết nối media, khởi tạo các điều khiển và cấu hình ban đầu cho cảm biến. Hàm dọn dẹp (remove) sẽ giải phóng tài nguyên, hủy đăng ký sub-device và tắt nguồn cảm biến.

2.7 Hỗ trợ Device Tree

Driver sử dụng Device Tree để mô tả phần cứng, bao gồm các kết nối I2C, GPIO, clock và các thông số cấu hình khác của cảm biến. Điều này giúp driver trở nên linh hoạt và có thể chạy trên nhiều cấu hình phần cứng khác nhau mà không cần sửa đổi mã nguồn.

3. Cấu hình thanh ghi

Phần này sẽ đi sâu vào các thanh ghi cụ thể của IMX297/296, giải thích chức năng của từng thanh ghi và cách chúng được cấu hình để điều khiển các chế độ hoạt động của cảm biến, như độ phân giải, tốc độ khung hình, phơi sáng, gain, v.v. Việc phân tích này thường dựa trên datasheet của nhà sản xuất.

Thẻ: imx297 imx296 camera driver V4L2 Linux kernel

Đăng vào ngày 26 tháng 8 lúc 06:05