4.14. Kiến thức cơ bản về CSI

Module csi là cách mã Python điều khiển cảm biến camera. Mọi tập lệnh chụp một khung hình đều theo cùng một cấu trúc ba phần: nhập module ở đầu, cấu hình một lần ở giữa, và vòng lặp while True ở cuối liên tục lấy khung hình từ camera từng cái một.

4.14.1. Vòng lặp điển hình

import csi, image, time

csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)

clock = time.clock()
while True:
    clock.tick()
    img = csi0.snapshot()
    # process img here
    print(clock.fps())

4.14.2. Mỗi lệnh gọi làm gì

import csi, image, time

Nhập ba module. csi điều khiển cảm biến, image định nghĩa lớp Imagesnapshot() trả về, và time cung cấp helper time.clock() dùng để đo số khung hình mỗi giây.

csi.CSI()

Khởi tạo một thực thể CSI bao gồm một cảm biến camera vật lý. Constructor này chiếm ngoại vi camera và ghi lại cấu hình theo từng cảm biến. Camera có một cảm biến duy nhất có một thực thể CSI; camera có hai cảm biến (màu cộng nhiệt, màu cộng sự kiện) có hai thực thể, mỗi thực thể được chọn bằng đối số cid cho constructor.

csi0.reset()

Cấp nguồn và cấu hình cảm biến. Theo mặc định, nó xung chân (pin) reset của cảm biến, sau đó ghi các thanh ghi I2C của cảm biến về trạng thái khởi đầu đã biết. Các lệnh gọi cấu hình tiếp theo -- pixformat, framesize, các núm điều khiển tự động -- đẩy thêm các lần ghi thanh ghi qua cùng bus điều khiển I2C.

csi0.pixformat(csi.RGB565)

Ghi các thanh ghi cảm biến để chọn định dạng điểm ảnh đầu ra. Các lựa chọn có sẵn là các định dạng mà trang định dạng điểm ảnh đã giới thiệu: RGB565, GRAYSCALE, BAYER, YUV422, và JPEG trên các cảm biến hỗ trợ nó.

csi0.framesize(csi.QVGA)

Ghi các thanh ghi chọn độ phân giải đầu ra. QVGA là 320 × 240; các kích thước có tên chạy lên đến WQXGA2 (2592 × 1944, khoảng 5 MP) trên các cảm biến hỗ trợ chúng. Tuple (width, height) tùy chỉnh cũng hoạt động, miễn là nó phù hợp với khả năng đầu ra của cảm biến.

clock = time.clock()

Tạo một helper clock. Mỗi lệnh gọi clock.tick() bên trong vòng lặp ghi lại thời gian bắt đầu của vòng lặp đó; time.clock.fps() báo cáo tốc độ vòng lặp gần đây theo số khung hình mỗi giây.

img = csi0.snapshot()

Chụp một khung hình từ cảm biến và trả về nó dưới dạng Image. Cơ chế cách khung hình đó đi vào bộ nhớ đáng được xem xét kỹ hơn.

4.14.3. Cách snapshot lấp đầy bộ nhớ

Cảm biến truyền các điểm ảnh qua bus dữ liệu điểm ảnh được mô tả trong bus cảm biến với tốc độ hàng trăm megabyte mỗi giây -- quá nhanh để CPU sao chép từng điểm ảnh một bằng phần mềm.

Thay vào đó, MCU chuyển giao việc truyền dữ liệu cho Truy cập Bộ nhớ Trực tiếp (DMA) -- một engine phần cứng độc lập với CPU, sao chép các byte từ nơi này sang nơi khác bên trong MCU mà không cần CPU tham gia. Ngoại vi đầu vào camera bắt mỗi byte điểm ảnh vào một FIFO nhỏ trên chip; các giai đoạn ISP chạy ở phía MCU xử lý dữ liệu khi đi qua; và engine DMA ghi các điểm ảnh hoàn chỉnh vào một bộ đệm khung hình trong RAM tại vị trí điểm ảnh tương ứng. Không có gì trong chuỗi đó cần CPU sau khi kênh DMA đã được lập trình.

Khi snapshot() được gọi:

  1. Driver CSI lập trình engine DMA với địa chỉ của bộ đệm khung hình, độ dài truyền (một khung hình đáng điểm ảnh), và một hàm gọi lại cho ngắt DMA-done.

  2. Driver kích hoạt ngoại vi đầu vào camera và chờ cảm biến báo hiệu bắt đầu khung hình tiếp theo.

  3. Khi cảm biến truyền khung hình ra, ngoại vi đưa mỗi byte điểm ảnh qua ISP và đến engine DMA, engine này ghi kết quả vào RAM tại vị trí bộ đệm khung hình tiếp theo. CPU được tự do chạy mã khác trong quá trình truyền.

  4. Khi điểm ảnh cuối cùng của khung hình đến, DMA kích hoạt ngắt done, driver bọc bộ đệm khung hình trong Image, và snapshot() trả nó về cho mã người dùng.

Image được trả về không sở hữu bản sao dữ liệu điểm ảnh -- nó trỏ vào một trong các bộ đệm khung hình của camera trong RAM. Số lượng bộ đệm khung hình mà camera giữ, và cách chúng được chuyển giữa DMA và mã người dùng trong mỗi lần gọi snapshot(), phụ thuộc vào chế độ đệm mà ứng dụng đã chọn qua framebuffers().