2020년 6월 29일 월요일

Pi Camera sensor 그리고 Camera Device Driver에 관하여...

이번 blog post에서는 (머리도 식힐겸 ??) Raspberry Pi Camera와 이를 위한 linux camera device driver(w/ device tree)에 관한 이야기를 게재해 볼까 한다.



목차
1. Raspberry Pi 3 B and Pi Camera
   - motion, mjpg-streamer 시험
2. Pi Camera와 MIPI CSI-2의 이해
3. Pi Camera S/W Architecture
   - V4L2 framework
   - VideoCore IV & MMAL Interface
   - Camera device tree & device driver
4. Raspberry Pi 4 and Pi Camera [나중에 정리한 것임]
5. References


Raspberry Pi는 최초 등장 이후로 전세계적으로 많은 인기를 누리고 있다. 최근에는 Quad core ARM Cortex-A72(ARM v8) 64-bit SoC(Broadcom BCM2711)를 탑재한 Pi 4가 출시되기도 했는데, 본 blog post에서는 아쉽게도 Pi 4가 아니라 (전에 구매해 둔) Raspberry Pi 3 B를 대상으로 글을 기고해 보고자 한다.

1. Raspberry Pi 3 B and Pi Camera
먼저 1장에서는 Raspberry Pi 3 B(이하 RPI3로 칭하겠음)에  Pi Camera 장착 및 Pi OS를 설치하고, motion, mjpg-streamer 등의 application으로 camera가 capture한 영상을 화면에 출력하는 과정을 소개해 보도록 하겠다.

a) Raspberry Pi 3 B 설치
Raspberry Pi OS 설치와 관련해서는 인터넷에서 관련 글을 쉽게 찾을 수 있기 때문에 본 blog post에서는 이와 관련한 자세한 내용은 설명하지 않도록 한다.


[그림 1.1] RPi OS(2020-05-27-raspios-buster-armhf.img) 설치 - Ether 사용

최근(2022년 1월 현재) RPi OS에도 많은 변화가 있는 듯 하다. 오랜만에 Pi camera를 구동할 일이 있어 최신 버젼을 내려 받아 설치 후, motion & mjpg-streamer를 돌려  보려고 했더니, 동작하지 않는다. 이를 해결하는 방법은 다양하게 존재하겠으나, 여기서는 raspbian stretch version을 이용하는 방법을 소개하고자 한다.



위의 그림 처럼 Etcher를 이용해서 image writing을 할 수도 있겠으나, dd를 사용하는 방법도 생각해 볼 수 있다.
<Ubuntu desktop>
$ sudo dd if=./2019-04-08-raspbian-stretch.img of=/dev/sdc bs=4M conv=fsync status=progress
3472883712 bytes (3.5 GB, 3.2 GiB) copied, 130 s, 26.7 MB/s
830+0 레코드 들어옴
830+0 레코드 나감
3481272320 bytes (3.5 GB, 3.2 GiB) copied, 261.673 s, 13.3 MB/s
===========

Pi OS image writing을 하려면, 아래와 같은 준비물은 필수이다.

[그림 1.2] microSD와 microSD to USB converter

<Serial console enable하기>
Serial console을 enable하기 위해서는 OS 설치 후, 먼저 아래 파일을 수정해 주어야 한다.
<Ubuntu Desktop>
$ sudo vi /boot/config.txt
   => 위의 그림 1.2의 converter를 Ubuntu desktop에 꽂은 후, 실제 mount되는 위치를 찾아 boot/config.txt 파일을 편집해 주어야 한다.
...
dtoverlay=pi3-disable-bt
sudo systemctl disable hciuart

파일 편집을 끝낸 후에는 아래와 같이 Serial console cable 연결하여, 부팅하는 모습을 살펴 볼 수가 있겠다.

[그림 1.3] Serial console  연결

[그림 1.4] serial console - minicom(/dev/ttyUSB0, 115200, 8N1)

$ ssh pi@192.168.9.250
password: raspberry
[Tip] RPi 3 ip 192.168.9.250은 Pi OS 설치 후, /etc/network/interfaces 파일을 통해 수동 설정해 주었다. Console에서 아래 raspi-config 명령을 실행하면, 화면이 깨지는 문제가 있으니, 반드시 ssh를 이용하도록 하자.

pi@raspberrypi:~$ sudo raspi-config
  =>  ssh로 접속하여  raspi-config 명령으로 주요 시스템 설정(특히 Camera enable)변경을 해주자.

[그림 1.5] raspi-config 설정 변경 - Camera enable 시키기

다음으로 camera app을 돌려 보기 전에 system을 전체적으로 upgrade해 주도록 하자.

pi@raspberrypi:~$ sudo apt-get update -y
pi@raspberrypi:~$ sudo apt-get upgrade -y
pi@raspberrypi:~$ sudo apt-get dist-upgrade
pi@raspberrypi:~$ sync; sync; sudo reboot



b) motion daemon 돌려 보기
이번 절에서는 Pi camera가 capture한 영상을 원격 PC에서 모니터링할 수 있는 motion project를 소개해 보고자 한다.

[그림 1.6] motion project home page

pi@raspberrypi:~$ sudo apt-get install motion

pi@raspberrypi:~$ sudo vi /etc/motion/motion.conf
먼저 desktop PC에서 web browser로 영상을 보고자 한다면 반드시 아래와 같이 설정 변경을 해 주어야 한다.
...
#daemon off
daemon on
...
#stream_localhost on
stream_localhost off
...
~

[그림 1.7] motion.conf 파일 설정 바꾸기

pi@raspberrypi:~$ sudo service motion start
  => RPi3 ip 192.168.9.250의 8081번 port로 web 접속을 시도하면 motion daemon이 capture한 영상을 화면에서 확인할 수 있다.

Desktop PC(web browser, 192.168.9.156) ==> RPi3(motion daemon, 192.168.9.250:8081)

[그림 1.8] motion daemon이 capture한 영상을 web browser로 확인하기

이때, motion daemon의 CPU 점유율을 확인해 보면 다음과 같다(대략 10% 내외에서 동작하고 있다).

[그림 1.9] motion daemon의 CPU 점유율(width x height: 320 x 240)

이 상태에서 화면 해상도(width x height)를 320 x 240에서 720 x 480으로 늘려 보도록 하자.

[그림 1.10] 720 x 480으로 화면 해상도 변경 모습

해상도를 증가시키니, CPU 점유율도 30% 정도까지 육박하는 것을 알 수 있다.

[그림 1.11] 720 x 480으로 화면 해상도 변경 시 CPU 점유율
[Tip] framerate(default 값: 2)를 15 정도로 올리기만 해도 CPU 점유율이 90% 대까지도 육박한다.

pi@raspberrypi:~$ sudo vi /etc/default/motion
  => 부팅 시에 자동으로 motion daemon을 시작하도록 하려면 아래와 같이 설정을 변경해 주어야 한다.
#start_motion_daemon=no
start_motion_daemon=yes
~

motion daemon은 이 밖에도 아주 다양한 기능을 제공한다. 자세한 사항은 아래 site를 참조하기 바란다.



c) mjpg-streamer 돌려 보기
mjpg-streamer는 camera로 부터 읽어 들인 JPEG frame을 ip network을 통해 다양한 viewer 즉, Chrome, Firefox, Cambozola, VLC, mplayer에게 전달(stream)해 주는 역할을 하는 command line tool이다.

아래  jacksonliam의 github의 코드original mjpg-streamer를 fork하여 RPi 용으로 개조(정확하게는 RPi 용 input plugin을 새로 추가)한 version으로 CPU 점유율을 획기적으로 개선(사용량을 대폭 줄임)하였다.

[그림 1.12] mjpg-streamer github - input_rapicam plugin을 지원

RPi 3에서 직접 source code를 내려 받아 build 후 실행해 보도록 하자.

pi@raspberrypi:~$ sudo apt-get install cmake libjpeg8-dev

pi@raspberrypi:~$ mkdir workspace; cd workspace
pi@raspberrypi:~$ cd mjpg-streamer/mpjg-streamer-experimental

pi@raspberrypi:~$ make
pi@raspberrypi:~$ sudo make install

pi@raspberrypi:~$ ./mjpg_streamer --help
  => mjpg_streadmer 사용법을 확인해 본다.

pi@raspberrypi:~$ ./mjpg_streamer -i 'input_raspicam.so --help'
  => input_raspicam.so plugin 관련 사용법을 확인해 본다.

[그림 1.13] mjpeg-streamer input_raspicam.so plugin  관련 도움말 살펴 보기

pi@raspberrypi:~$ ./mjpg_streamer -o 'output_http.so --help'
=> output_http.so plugin 관련 사용법을 확인해 본다.

[그림 1.14] mjpeg-streamer output_http.so plugin  관련 도움말 살펴 보기

pi@raspberrypi:~$ ./mjpg_streamer -o "outout_http.so -w ./www" -i "input_raspicam.so -fps 10 -x 1280 -y 720 -vf"

[그림 1.15] mjpg-streamer 실행
[Tip] -vf option(Set vertical flip)을 사용하면 화면이 뒤짚여서 출력된다. RPi 3에 연결된 Pi camera가 뒤짚여 연결되어 있기 때문에 이 option을 사용해야 화면이 똑 바로 보이게 된다.

http://192.168.9.250:8080

[그림 1.16] Web browser로 RPi3에 접속한 모습 - mjpg streamer가 streaming하는 영상 확인

[그림 1.17] mjpg streamer의 CPU 점유율(4 ~ 5% 대)

아래 그림은 Ubuntu 18.04에서 mplayer를 사용하여 mjpg-streamer가 전송하는 영상을 출력하는 모습을 보여준다.

mplayer -fps 30 -demuxer lavf "http://192.168.9.250:8080/?action=stream&ignored.mjpg"


[그림 1.18] mplayer로 mjpg streamer가 streaming하는 영상 확인

mjpg-streamer는 어떻게 해서 CPU 점유율을 현저히 떨어뜨릴 수 있었을까 ? 그 해답은 BCM2835 SoC 내에 존재하는 VideoCore IV GPU를 적극 활용했기 때문인데, 이와 관련해서는 3장에서 자세히 설명하도록 할 것이다.

mjpg-streamer의 raspicam input plugin은 raspistill mmal source code를 기초로 하여 작성된 것이다. 따라서 아래 code도 함께 참조해 보기 바란다.



이상으로 RPi 3에 Pi Camera를 장착하고, motion & mjpg-streamer application을 이용해서 camera가 capture한 영상을 desktop PC에서 확인해 보았다. 다음 장에서는 지금까지 테스트에 사용한 Pi Camera의 주요 특징과 MIPI CSI-2 interface에 대해서 알아 볼 것이다.


2. Pi Camera와 MIPI CSI-2의 이해
이 장에서는 Raspberry Pi 재단에서 개발/판매하는 Pi Camera V2(2016년 release)에 대해 살펴 보고자 한다. 이어서 Pi Camera를 Camera controller와 어떻게 연결(MIPI CSI-2 interface)하는 지에 대해서도 파악해 보고자 한다.

a) Pi Camera V2

[그림 2.1] Pi Camera Module v2
[Tip] 최근(2020년)에는 이 보다 성능이 더 우수한 12-megapixel High Quality Camera도 release된 바가 있다.

<Pi Camera V2 주요 spec>
 항목 상세 내용
 Still resolution8 Megapixels 
 Video modes1080p30, 720p60, 640 x 480p60/90 
 Sensor 모델 Sony IMX219
 Sensor resolution 3280 x 2464 pixels
 Linux integration V4L2 driver available


보다 자세한 스펙 사항은 아래 site에 잘 정리되어 있다.

다음으로 Sony IMX219 sensor의 구조를 대략적으로 살펴 보기로 하자.


[그림 2.2] Sony IMX219 camera sensor block diagram


IMX219 camera 센서는 MIPI CSI-2 interface(D-PHY)를 사용하여 camera controller(SoC 쪽)와 연결되어 있다.

[RAM] <--- Controller(AP) <--- MIPI CSI-2 <--- IMX219
[그림 2.3] Camera sensor와 Controller와의 관계  [참고 문헌 3]


 
[그림 2.4] Sony IMX219 camera sensor의 MIPI output block


IMX219 camera 센서의 각종 레지스터 제어를 위해서는 i2c interface가 사용된다.

[그림 2.5] Sony IMX219 camera sensor 2 wire serial communication - CCI(Camera Control Instance)


b) MIPI CSI-2
Camera sensor의 동작 방식을 제대로 이해하기 위해서는 MIPI CSI-2와 같은 camera interface에 대한 이해가 선행되어야 한다. 
먼저 아래 그림은 mipi alliance에서 배포하고 있는 CSI-2 spec 문서에 포함되어 있는 내용으로 MIPI CSI-2 D-PHY 인터페이스와 C-PHY 인터페이스를 개략적으로 보여준다.
 
[그림 2.6] MIPI CSI-2 : D-PHY, C-PHY [참고 문헌 11]

아래 그림은 위의 그림과 동일한 내용으로 CSI-2 D-PHY, CSI-2 C-PHY 외에도 CSI-3 Interface에 대한 그림도 포함되어 있다.

[그림 2.7] MIPI CSI-2, CSI-3 Interface

D-PHY: clock lane(+/- 2 line으로 구성)과 data lane(2개 or 4개의 data lane)으로 구성
C-PHY: clock을 data에 포함시켜 전달, lane당 3개의 line으로 구성

MIPI CSI-2 D-PHY, C-PHY 및 CSI-2 Packet format 등과 관련해서는 (한눈에 보기 쉽게) 아래 문서에 잘 정리가 되어 있다. 저작권 문제가 있는 듯하여 관련 이미지를 별도로 capture하지는 않았으니, 아래 파일을 직접 참조해 보기 바란다.


MIPI PHY(physical layer) 못지 않게 중요한 부분이 CSI-2 protocol에 관한 부분이다. 아래 그림은 MIPI protocol layer에서 사용되는 MIPI CSI-2 packet format을 간력히 소개한 것이다. 아래 그림에서 Short packet(data type이 0x00 ~ 0x0F에 해당)은 frame start를 알려주는 packet이며, long packet(data type이 0x10 ~ 0x37에 해당)은 실제 data packet에 해당한다.

[그림 2.8] MIPI CSI-2 Packet Format [참고 문헌 12]

[그림 2.9] MIPI CSI-2 Packet Format - long packet [참고 문헌 12]


[그림 2.10] MIPI CSI-2 Packet Format - short packet [참고 문헌 12]

[그림 2.11] MIPI CSI-2 Data Type Table [참고 문헌 12]

이상으로 (정말 간략하게) PI Camera sensor와 MIPI CSI-2 interface에 대해 살펴 보았다. 다음으로 확인할 사항은 지금까지 설명한 내용(H/W 관점)을 S/W 관점에서 재 조명해 보는 것이다.


3. Pi Camera S/W Architecture
이장에서는  V4L2 sub system을 기반으로 하는 camera device driver(platform driver <=> sensor driver)와 Broadcom Videocore IV(VCOS) 및 MMAL(브로드컴 MultiMedia Abstraction Layer)에 관하여 간략히 언급해 보고자 한다.

a) V4L2(Video4Linux 2) subsystem
V4L2는 camera, TV Tuner와 같은 device를 위한 video capture framework(or sub system)이다.

[그림 3.1] Camera system architecture [참고 문헌 13]
[Tip] 위의 그림의 IPU는 Raspberry Pi 보드에서는 GPU로 보면 될 듯하다.


V4L2는 다양한 유형의 장치를 지원하며 생각보다 전체 구조가 매우 복잡하다. 따라서 이를 한마디로 요약하기는 참으로 힘들다. 그럼에도 불구하고 아래 그림 3.2 ~ 3.5에서 V4L2의 개념을 간단히 정리해 보았다.

[그림 3.2] V4L2 관점에서 바라본 Streaming 모드 아키텍쳐 [참고 문헌 3]
[Tip] V4L2 framwork은 Controller driver, Camera(sensor) driver, userspace program 전체에 걸쳐 동작하도록 설계되어 있다.


[그림 3.3] V4L2 subsystem 개요 [참고 문헌 14]


[그림 3.4] V4L2 관점에서 camera controller driver의 기본 흐름도(probe  함수 내용) [참고 문헌 3]


[그림 3.5] V4L2 image capture operation(userspace application) flowchart


V4L2 관련 보다 구체적인 사항은 필히 아래 site 내용을 검토해 보기 바란다.


이 밖에도 v4l-utils 패키지를 설치하면 v4l2-ctl(video4linux device를 제어하는 명령)이라는 명령을 사용할 수 있는데, 이를 통해 현재 v4l2 device가 무엇인지를 확인할 수 있다. 

v4l2-ctl --list-devices 명령을 실행하면 현재 video4linux device의 전체 목록을 확인할 수 있는데, 아래 그림에는 /dev/video0와 연결된 하나의 device 즉,  mmal service 16.1 (platform:bcm2835-v4l2)가 보인다.

[그림 3.6] v4l2-ctl 명령 (1)

다음 내용은 vtl2-ctl를 사용하여 첫번째 device에 대한 driver 정보를 출력해 본 것이다.

[그림 3.7] v4l2-ctl 명령 (2)

그렇다면, RPi 3에 구현되어 있는 V4L2 kernel driver는 어떤  것들이 있을까 ?
아래 명령은 현재 구동 중인 v4l2 관련 kernel module을 보여준다. 이중 특히 눈여겨 볼 부분은 bcm2835_v4l2bcm2835_mmal_vchiq  등이다.

[그림 3.8] v4l2 kernel module

<여기서 잠깐 !>
  => bcm2835_v4l2 및 bcm2835 mmal_vchiq 등 관련 kernel code는 어디에 있을까 ?
  => kernel에서 어디에 있는지를 찾는데, 위의 mmal이 힌트가 될 수 있다.
chyi@mars:~/workspace/Boards/RPi3B/linux/drivers$ grep -rl mmal *
staging/vc04_services/vchiq-mmal/mmal-common.h
staging/vc04_services/vchiq-mmal/mmal-msg-common.h
staging/vc04_services/vchiq-mmal/Makefile
staging/vc04_services/vchiq-mmal/mmal-msg-port.h
staging/vc04_services/vchiq-mmal/mmal-vchiq.h
staging/vc04_services/vchiq-mmal/mmal-vchiq.c
staging/vc04_services/vchiq-mmal/mmal-msg.h
staging/vc04_services/vchiq-mmal/mmal-msg-format.h
staging/vc04_services/vchiq-mmal/mmal-parameters.h
staging/vc04_services/Makefile
staging/vc04_services/bcm2835-camera/bcm2835-camera.c
staging/vc04_services/bcm2835-camera/controls.c
staging/vc04_services/bcm2835-camera/bcm2835-camera.h
staging/vc04_services/bcm2835-camera/Makefile
staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
staging/vc04_services/vc-sm-cma/vc_sm.h
staging/vc04_services/Kconfig
--------------

b) camera device driver
RPi 3는 재밌게도(?) 두가지의 BCM283x 용 camera driver를 제공 한다(단, 동시에 사용은 불가).

1) drivers/media/platform/bcm2835 <=> drivers/media/i2c/imx219.c
  => 일명 Unicam driver(platform driver) <=> imx219 camera sensor driver
  => broadcom VideoCore랑은 상관 없이 동작. 즉 MIPI CSI-2로 부터 직접 data를 읽어와 SDRAM에 저장
  => device tree 내용도 별도로 정리되어 있음.
       --> SoC internal IP block: Documentation/devicetree/bindings/media/bcm2835-unicam.txt
       --> camera sensor: Documentation/devicetree/bindings/media/i2c/imx219.txt 참조 요망
  => GPU의 다양한 기능을 사용하지 않고, CPU에서 바로 처리하는 방식(느림).

2) drivers/staging/vc04_services/bcm2835-camera
  => broadcom VideoCore를 이용하는 방식(빠름).
  => VideoCore firmware(VCOS라는 RTOS)는 부팅시 device tree 내용을 확인하여 csi0 or csi1에 대한 active node 정보가 존재하지 않을 경우에만, 동작하도록 함.
  => imx219 camera sensor에 대한 driver 역시 VCOS에 포함되어 있을 것으로 예상됨.

그렇다면, 이 둘 중 현재 어떤 녀석이 사용되고 있는 것일까 ?

먼저, kernel booting message를 확인해 보면, bcm2835-camera 드라이버(실제 driver 이름은 bcm2835-v4l2) 즉, 두번째 녀석이 사용되는 듯 보인다. 보다 정확한 내용 확인을 위해 device tree가 enable되어 있는지를 확인해 보도록 하자.

[그림 3.9] Kernel dmesg

c) camera device tree 분석
RPi 3에서 현재 동작 중인 device tree 내용을 확인해 보도록 하자.

pi@raspberrypi:~$ sudo apt-get install device-tree-compiler
pi@raspberrypi:~$ dtc -I fs -O dts /sys/firmware/devicetree/base > ~/workspace/rpi3.dts

이 상태에서 rpi3.dts 파일을 열어 보면,  csi0(7e800000), cs1(7e801000) controller의 status 값이 둘다 disabled되어 있음을 알 수 있다. 즉, 현재 linux kernel 상에서는 사용하지 않는다는 뜻이다.

[그림 3.10] csi (controller) device tree 내용 중 csi controller 내용 발췌

다음으로 imx219 camera sensor에 대한 MIPI CSI-2용 device tree node를 살펴 본 결과, 앞서 추출한 device tree 내용에는 관련 내용이 어디에도 보이질 않는다.

arch/arm/boot/dts/overlays/imx219-overlay.dts

[그림 3.11] imx219 camera sensor에 대한 device tree overlay 내용

[Tip] 뒤에서 다시 설명하겠지만, 아래 두 부분이 서로를 연결(MIPI CSI-2)해 주고 있다.

<fragment@0 : i2c controller & device>
                port {
                    imx219_0: endpoint {
                        remote-endpoint = <&csi1_ep>;
                        clock-lanes = <0>;
                        data-lanes = <1 2>;
                        clock-noncontinuous;
                        link-frequencies =
                            /bits/ 64 <297000000>;
                    };
                };

<fragment@1: cs1 controller>
            port {
                csi1_ep: endpoint {
                    remote-endpoint = <&imx219_0>;
                };
            };
-------------------

따라서 이상의 두가지 내용(dmesg, device tree 내용)으로 미루어 볼 때, drivers/staging/vc04_services/bcm2835-camera  드라이버가 사용되는 것이 확실하다고 말할 수 있다.

<여기서 잠깐 !>
  => video receiver and transmitter interface(MIPI CSI-2, parallel interface 등)를 위한 device tree 작성과 관련하여 ...

이 부분은 생각보다 좀 복잡해 보이는데, 우선 아래 두가지 파일을 면밀히 검토할 필요가 있어 보인다.

Documentation/devicetree/bindings/media/video-interfaces.txt
Documentation/devicetree/bindings/media/bcm2835-unicam.txt

먼저, Video receiver & transmitter interface를 위한 device tree(controller node)는 일반적으로 아래와 같은 형식으로 작성해야 한다. 내용을 보니 ports, port@0, port@1, endpoint 등 그 동안 보지 못했던 표현들이 눈에 들어온다.

<video-interfaces.txt>

구체적인 예(두번째 파일)를 하나 들어 보도록 하자. 

<controller: bcm2835-unicam.txt>

cs1 controller(bcm2835-unicam) <== MIPI CSI-2 bus ==> tc358734(HDMI device)
i2c0 controller <== i2c0 bus ==> tc358734(HDMI device)

위의 내용에는 앞서 설명한 imx219 camera sensor 대신 tc358743 hdmi device가 예제로 소개되고 있다.
이 중 눈여겨 볼 부분은 cs1 controller port node의 child endpoint 정보는 tc358743_0 노드(i2c0 controller의 child node 내에 표현되어 있음)를 가리키고 있으며, i2c0 controller의 child node 즉, tc358734 device의 endpoint는 반대로 cs1_ep node를 가리키고 있는 대목이라 말할 수 있다.

<csi1 controller>
csi1_ep: endpoint {
                remote-endpoint = <&tc358743_0>;    /* 아래 tc358743_0 node를 가리킴 */
                data-lanes = <1 2>;
};

<cs1 device, i20 device>
tc358743_0: endpoint {
                    remote-endpoint = <&csi1_ep>;       /* 위의 csi1_ep node를 가리킴 */
                    clock-lanes = <0>;
                    data-lanes = <1 2>;
                    clock-noncontinuous;
                    link-frequencies =
                        /bits/ 64 <297000000>;
};

물론, 그 밖에도 MIPI CSI-2 interface를 표현하기 위해 clock-lanes, data-lanes 등의 property가 추가된 부분도 중요하다고 하겠다.
-------------------

d) VideoCore IV GPU
앞 절에서 이미 언급했던 MMAL은 과연 무엇일까 ? 이절에서는 MIPI CSI-2 interface의 host(application processor) 쪽 endpoint인 VideCore IV GPU와 이를 Linux에서 이용하기 위한 MMAL interface에 관하여 살펴 보기로 하겠다.

아래 두개의 그림은 RPi 3 Camera 동작과 관련한 전체 구조를 아주 쉽게 표현해 주고 있다.

Linux(myscript.py -> picamera -> MMAL) => VCHI => VideoCore IV GPU(VCOS) => MIPI CSI2 bus => Camera sensor

[그림 3.12] BCM2835 SoC 내의 VideoCore IV GPU 동작 원리 1 [참고문헌 6]
[Tip] 위의 그림은 Pi camera python project에서 발췌한 것이다. 그림 내용 중 picamera library는 MMAL API를 사용하여 VideoCore firmware(VCOS)와 통신한다.

<동작 원리>
  => 원문의 내용을 그대로 전달하는 편이 이해하는데 도움이 될 듯하여, 아래 site에서 원문을 그대로 옮겼다.

  1. The camera’s sensor has been configured and is continually streaming frame lines over the CSI-2 interface to the GPU.
  2. The GPU is assembling complete frame buffers from these lines and performing post-processing on these buffers (we’ll go into further detail about this part in the next section).
  3. Meanwhile, over on the CPU, myscript.py makes a capture call using picamera.
  4. The picamera library in turn uses the MMAL API to enact this request (actually there’s quite a lot of MMAL calls that go on here but for the sake of simplicity we represent all this with a single arrow).
  5. The MMAL API sends a message over VCHI requesting a frame capture (again, in reality there’s a lot more activity than a single message).
  6. In response, the GPU initiates a DMA transfer of the next complete frame from its portion of RAM to the CPU’s portion.
  7. Finally, the GPU sends a message back over VCHI that the capture is complete.
  8. This causes an MMAL thread to fire a callback in the picamera library, which in turn retrieves the frame (in reality, this requires more MMAL and VCHI activity).
  9. Finally, picamera calls write on the output object provided by myscript.py.

            
[그림 3.13] BCM2835 SoC 내의 VideoCore IV GPU 동작 원리 2 [참고문헌 6]


e) bcm2835_v4l2 driver
끝으로 bcm2835_v4l2 driver의 초기화 부분을 capture하는 것으로 이장을 정리할까 한다. 드라이버에 대한 상세 코드 분석은 독자 여러분의 몫으로 남긴다. 😙

drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c


[그림 3.14] bcm2835_v4l2 driver의 초기화 부분

-----------

이번 blog post의 처음 취지는 MIPI CSI-2를 알아본 후, 이를 사용하는 linux camera device driver(platform driver <=> sensor driver, device tree)를 분석해 보는데 있었다. 하지만, 내용을 살펴본 결과 뜻하지 않게 linux camera device driver 관련 부분은 사용되지 않고, 모든 기능은 GPU 상의 VCOS에서 처리되고 있었으며, linux kernel에서는 이와의 interface(MMAL interface)용 driver만이 사용되고 있었다. 😂

(좀 아쉽지만) 이상으로 RPi 3 보드 상에서 Pi Camera와 관련 Camera S/W가 어떻게 동작하는지를 대략적으로 살펴 보았다. 늘 느끼는 거지만, 당초 계획했던 것 보다 최종 결과물이 항상 뭔가 2% 부족한 것 같다. 다음 번에는 보다 수준 높은 내용을 선보일 것을 약속드리며, 이번 posting을 마치고자 한다. 끝까지 읽어 주신 독자 여러분께 감사의 마음을 전한다. 😎


4. Raspberry Pi 4와 Pi Camera
[나중에 정리한 내용임]최근에 Raspberry Pi 4 B 보드를 하나 입수했다. Camera 관련 동작 방식에도 많은 변화가 있는 듯하다. (다른 일로 바쁜 관계로) 간략하게나마 Pi4에서의 Camera 영상 출력 시험을 해보기로 한다.

얼마 전에 여러가지 면에서 성능이 향상된 Raspberry Pi 4 B가 출시된 바 있다. 이번 장에서는 Pi 4와 Pi Camera를 사용하는 방법을 간략히 소개해 보고자 한다.

[그림 4.1] Raspberry Pi 4 Model B


Raspberry Pi 4의 h/w적인 변화 못지 않게, Pi OS도 그동안 많은 변화를 거쳐왔다.


<에필로그>
내용을 정리하다 보니, linux media community(핵심 개발자: Laurent Pinchart)를 중심으로 (현재까지) 매우 복잡한 시스템인 camera를 보다 효율적으로 관리/제어하기 위해  libcamera라는 project가 최근(2018)에 결성되었고, Raspberry Pi 진영에서도 이를 기반으로 camera s/w를 porting하는 작업이 진행되었다고 한다. libcamera project가 목표로 하는 대상은 linux, android, chromOS 등이다. 관심있는 분들은 아래 내용을 참고하시기 바란다.


[그림 4.2] libcamera project
---------------

먼저 Raspi OS bullseye를 설치한 후, libcamera app(아래 site 참조)을 이용하여 camera 영상을 출력해 보도록 하자.



<Ubuntu 18.04 PC>
sudo dd if=./2022-01-28-raspios-bullseye-armhf-lite.img of=/dev/sdc bs=4M conv=fsync status=progress
479+0 레코드 들어옴
479+0 레코드 나감
2009071616 bytes (2.0 GB, 1.9 GiB) copied, 136.377 s, 14.7 MB/s

$ cd /media/chyi/boot
$ sudo vi /boot/config.txt
...
enable_uart=1  #Serial console 출력을 위해 옆의 한 줄을 추가해 주도록 한다.
~

microSD 카드를 RPi4 board에 장착 후, 부팅을 시도해 본다.

[그림 4.3] RPi4 부팅 모습(minicom)

$ sudo raspi-config
  => ssh를 enable 하고, update 메뉴를 선택한다.
  => 주의: legacy pi camera 메뉴는 enable해 주어서는 안된다(motion daemon 등을 사용하고자 하는 경우 선택).

마지막으로 아래 명령을 실행하여 camera에서 찍은 영상을 stream 형태로 출력하도록 하자.
$ libcamera-vid -t 0 --width 1280 --height 720 --codec h264 --vflip -n -l -o tcp://0.0.0.0:5001 &
[Tip1] 1장에서 사용했던 motion 및 mjpg-streamer는 RaspiOS bullseye에서는 제대로 동작하지 않는다(실제로는 raspi-config에서 Legacy Camera를 enable하면 동작하기는 한다. 단, 완벽하게 동작하지는 않는다.)
[Tip2] --vflip option을 지정하면 영상이 반대로 뒤집혀 출력된다.

영상이 제대로 출력되고 있는지, VLC를 통해 확인해 보도록 하자.

VLC 미디어 메뉴  => 네트워크 스트림 열기 => tcp/h264://192.168.8.161:5001 입력


[그림 4.4] Ubuntu 18.04에서 VLC 실행 모습(1)


[그림 4.5] Ubuntu 18.04에서 VLC 실행 모습(2)

참고로, 아래 site의 내용을 잘 살펴 보면, RTSP를 이용하여 영상을 송출하고, VLC를 통해 확인하는 방법도 테스트해 볼 수 있다.


<RPi4>
$ sudo apt-get install cvlc
$ libcamera-vid -t 0 --inline -o - | cvlc stream:///dev/stdin --sout '#rtp{sdp=rtsp://:8554/stream1}' :demux=h264

<PC VLC>
rtsp://192.168.8.194:8554/stream1

-------------



5. References
  => V4L2 subsystem
  => Pi Camera
[7] Camera Serial Interface_CSI2_CSI3_Overview.pdf, mipi alliance
  => mipi CSI
  => device tree and overlay
  => RPi kernel build
  => misc.



Slowboot