System designTroubleshooting

CSI camera RGB565 half image flicker: RXFIFO size per pixel

Fix a CSI camera showing only half an image in RGB565 with flicker between halves: set the RX FIFO and DMA lengths to width × height × bytes per pixel.

LumaSpec Vision11 min read Share on LinkedIn

On this page 9 sections
  1. 01The signature: a clean half frame that alternates
  2. 02Bytes per pixel set every length in the pipeline
  3. 03Worked example: VGA RGB565, full arithmetic
  4. 04Causes in order of probability
  5. 05The fix, step by step
  6. 06Confirm it, then make it stay fixed
  7. 07Three ways out when you would rather not patch drivers
  8. 08Checklist for any capture-format change
  9. 09Questions

You moved an embedded camera link from packed YUV to RGB565 so the frames could feed an RGB565 panel without a software conversion, and the stream came up broken: only half the image, flickering between the top and bottom halves about once per frame. The sensor is fine. A length in the capture path — the CSI receiver's RXFIFO count, a DMA buffer size, the driver's sizeimage — is still computed for the previous format. This guide gives the byte arithmetic that sets every one of those lengths, the tests that isolate the stale value in minutes, the confirmed fix, and the alternatives when you would rather not maintain a sensor-module driver at all.

The signature: a clean half frame that alternates

The picture toggles between the top half and the bottom half of the scene, each half internally correct, at roughly the frame rate — sometimes twice per frame, set by where the display refresh lands. No error is raised. That combination is diagnostic, not random.

The receive DMA writes pixels sequentially and moves to the next buffer when the programmed byte count is reached. When that count is half the frame, the chain consumes two buffers per frame: the first holds rows 0 to 239, the second rows 240 to 479. Whichever buffer the display grabs next alternates frame to frame, so you see both halves in succession. No bytes were lost; they were cut at the wrong boundary. This is a different fault from a camera that delivers half the expected frame rate — there the rate halves, here the geometry does.

Measure the boundary row: boundary_row = buffer_bytes ÷ bytesperline. At VGA RGB565 a stale length puts the boundary after row 240, exactly half of 480; the arithmetic is below.

Bytes per pixel set every length in the pipeline

One quantity decides the RXFIFO length, the DMA byte counts and sizeimage: the bytes per pixel (bpp) of the format actually streaming. RGB565 packs a pixel into 16 bits — 5 red, 6 green, 5 blue — and the parallel bus carries it as two 8-bit beats per pixel. The media bus code says so: MEDIA_BUS_FMT_RGB565_2X8_LE, two 8-bit transfers, low byte first. Byte order matters when you assemble those beats, the subject of building an RGB565 image converter for microcontroller TFT displays.

V4L2 fourcc Media bus code bpp Frame bytes at 640 × 480
V4L2_PIX_FMT_YUYV MEDIA_BUS_FMT_YUYV8_2X8 2 614,400
V4L2_PIX_FMT_RGB565 MEDIA_BUS_FMT_RGB565_2X8_LE 2 614,400
V4L2_PIX_FMT_YUV32 — 4 1,228,800
V4L2_PIX_FMT_SBGGR8 MEDIA_BUS_FMT_SBGGR8_1X8 1 307,200

YUYV packs two pixels into 4 bytes — also 2 bytes per pixel — so the YUYV-to-RGB565 switch leaves sizeimage unchanged. What broke in the real case was a separately programmed RXFIFO length that had matched the old set-up and did not follow the format change. The format table's bpp entry is only the first of the places a format appears; after any change, re-derive every byte count. The same trap bites host-side code: why your 16-bit image buffer has half the expected pixels and Mono16 buffers with half the elements are the same arithmetic failing at the consumer.

Worked example: VGA RGB565, full arithmetic

bytesperline = width × bpp sizeimage = bytesperline × height fifo_bytes = width × height × bpp

With width = 640 px, height = 480 px and bpp = 2 B:

  • bytesperline = 640 × 2 = 1,280 B
  • sizeimage = 1,280 × 480 = 614,400 B (600 KiB)
  • RXFIFO length = 640 × 480 × 2 = 614,400 B; if the receiver counts 8-byte words, that is 614,400 ÷ 8 = 76,800 words
  • The stale value, computed at 1 B per pixel: 640 × 480 × 1 = 307,200 B, which is 307,200 ÷ 1,280 = 240 lines — rows 0 to 239 per buffer, the observed top half

Bandwidth check: 614,400 B × 30 fps = 18,432,000 B/s, about 18.4 MB/s. Memory is not the constraint here; the correctness of the boundary is.

Causes in order of probability

Rank Cause Test Fix
1 RXFIFO or DMA frame length stale or hardcoded Compare the programmed length with width × height × bpp Set it to width × height × bpp
2 sizeimage and bytesperline cached from the old format Read the format back; sizeimage must equal your computed frame size Derive both from the format table's bpp
3 Width programmed in the wrong unit for the new format Frame shears rather than halving Add the fourcc to every pixelformat switch
4 Sensor still emitting the old format Colors garbled, frame complete Move the sensor output registers
5 Display overlay still consuming UYVY Full frame, wrong colors Set the overlay to IPU_PIX_FMT_RGB565

Cause 1 carries this exact symptom, and there is a one-minute probe for it: set the capture height to 240. Then 640 × 240 × 2 = 307,200 B — precisely the stale length — and if the full smaller image appears with nothing else touched, the length is your culprit.

Cause 3 deserves a line. The CSI set-up code branches on pixelformat to decide whether the width register takes pixels or bytes, and the new fourcc must sit in the case group with V4L2_PIX_FMT_YUV32 and V4L2_PIX_FMT_SBGGR8 so width is programmed in pixels. A format that falls through counts double bytes per line and shears the image. The short-frame cousins of this fault are covered in CSI camera short frame errors: match the mode to the sensor output and CSI short frames in binning mode.

Cause 4: the sensor side has to move too, writing the output-path registers that switch the module from packed YUV to RGB565 — the exact values and what happens if you skip them are in the fix steps and the notes below.

Cause 5 is the display end. The overlay input format changed from IPU_PIX_FMT_UYVY to IPU_PIX_FMT_RGB565 in the same change; leaving it gives a complete but wrongly decoded picture, the display-side mismatch also seen in fixing grayscale display over composite video.

The fix, step by step

  1. Take bpp from the driver's format table: 2 for RGB565.
  2. Set bytesperline = width × bpp and sizeimage = bytesperline × height, both reading their bpp from the table entry, so the next format change cannot leave them stale.
  3. Set the RXFIFO length — the per-frame byte count the receiver is programmed for — to width × height × bpp. In the integration that motivated this guide, that single edit restored full frames.
  4. Add the new fourcc to every switch that branches on pixelformat, above all the width-programming path.
  5. Move the sensor to RGB565 output: 0x4300 = 0x6f and 0x501f = 0x01, as the working change has them.
  6. Point the display overlay at IPU_PIX_FMT_RGB565.
  7. Restart the stream, read the format back, and dump one frame for the checks below.

Format changes that stall or halve elsewhere usually trace to linked attributes rather than the capture length — when changing pixel format halves your frame rate works that list.

Confirm it, then make it stay fixed

  • Byte count: one captured frame occupies exactly 614,400 B at VGA.
  • Content: checksum the top 240 rows and the bottom 240 rows separately; they must differ. A repeated half fails this test.
  • Stability: run a soak capture. If sporadic timeouts appear days later, suspect the ribbon and signal integrity before the driver — the pattern in CSI camera timeout after a working period.
  • Load: the software conversion task disappears from CPU load once the panel is fed RGB565 directly.

Three ways out when you would rather not patch drivers

Let the sensor do it — the path fixed above. RGB565 straight from the module, panel fed from the capture buffer, zero conversion work. Choose this when the panel is the only consumer. When another consumer wants YUV, keep capture as YUYV and convert in the blit instead.

Convert in hardware during the blit. SoCs of this class carry a pixel-processing block that turns YUV into RGB while copying to the framebuffer. Capture lengths stay as they were and the CPU stays idle. A third capture route — streaming compressed frames from the sensor — is covered in a JPEG-over-CSI workaround reference for a closely related SoC.

Replace the module with a standard camera. A USB3 or GigE area scan camera negotiates its pixel format over the transport and sizes its own buffers, so a format change becomes a setting, not a patch. You also gain C-mount optics, strobe triggering and cable lengths a ribbon cannot reach — the same output path discussed in displaying a camera image on a VGA output from a vision controller.

Match camera pixels to the panel before ordering: a 640 × 480 panel needs at least 640 × 480 pixels, more if you want headroom for zoom or detail:

Camera resolution for the smallest feature
Units
Pixels needed
–
Sensor size
– MP
Smallest sensor that fits
–
Pixel on the part
– mm
Pixels across the feature, that sensor
–

Pixels needed = field / feature × pixels per feature on each axis (field incl. margin). The lens must resolve the pixels too: Nyquist = 1000 / (2 × pixel µm) lp/mm.

Find cameras with enough pixels Open the full calculator

Two cameras cover the ends of that range — the MV-CU020-90GC, a 2.5 MP GigE camera with a 1/1.7" sensor, C-mount and global shutter, and the LS-CMR-0166, a 12 MP USB3 camera with a 1/1.7" sensor:

Area scan camerasHikrobot MV-CU020-90GC 2.5 MP Area Scan Camera, GigE, GMAX4002, ColorMV-CU020-90GC
Sensor format
1/1.7"
Lens mount
C-mount
$309.00View part
Area scan cameras12 MP USB3 area scan camera, IMX226, 1/1.7" rolling shutter, 30 fps, 4000 × 3000 resolution (LS-CMR-0166)LS-CMR-0166
Sensor format
1/1.7"
Interface
USB3
$560.00View part

On the host side, terminate USB3 cameras on a quad-port USB3 adaptor and GigE cameras on a dual-port PoE NIC:

Specification MV-GU2204 MV-GE2002P
Delivery bandwidth 1720 MB/s (max. transmission bandwidth); 1600 MB/s (sustained transmission bandwidth) 860 MB/s (max. transmission bandwidth); 800 MB/s (sustained transmission bandwidth)
Standard USB3.0 IEEE 802.3
Interface USB3.0 type-A, female 2 × RJ45
Camera type supported Industrial cameras with USB interfaces GigE industrial camera
Camera quantity supported 4 at most 2 at most
Camera connection speed Max. 5 Gbps (the actual speed is 3.08 Gbps) 10/100/1000 Mbps
Stream 4 2
Power consumption 5.5 W (without USB power supply); 23.5 W (with USB power supply) 7.5 W (excl. PoE)
Dimension 165.5 mm × 120 mm × 22 mm (6.5" × 4.7" × 0.9") 181 mm × 126.3 mm × 21.6 mm
Weight Approx. 143.6 g (0.3 lb.) 190 g
Temperature Working temperature: 0 °C to 60 °C (32 °F to 140 °F); Storage temperature: –20 °C to 70 °C (–4 °F to 158 °F) Working temperature: 0 °C to 60 °C (32 °F to 140 °F); Storage temperature: –20 °C to 70 °C (–4 °F to 158 °F)
Operating system Windows 7/10/11 on Intel® platforms Windows 7/10/11, Linux
Price $469.00 $249.00

All 2 share: Interconnect PCI-E gen2 × 4; Humidity 10% RH to 95% RH (no condensation). Values as printed in each product's data sheet; open a part for its drawing and full specifications.

Cable it with a drag-chain-rated USB3.0 lead such as the 3 m LS-CBL-0219, or browse the camera cables for GigE screw-lock equivalents:

Cables3 m USB3.0 camera cable, high-flex (drag chain) (LS-CBL-0219)LS-CBL-0219$62.00View partBuild a system with it

The decision, case by case:

  • Staying embedded with one panel within ribbon reach — nothing beats the repaired CSI link for weight and parts count.
  • Other consumers need YUV — keep YUYV capture and convert in the blit block.
  • More than one camera, real optics, strobe sync, or a run past a few meters — a standard camera from the area scan cameras range, picked through the camera finder or the configurator's camera step, on a host card from frame grabbers.

If the embedded side stays and you want the camera end sized for you, contact us with the panel spec.

Checklist for any capture-format change

Before:

  • Compute bytesperline and sizeimage for the new format, arithmetic written down.
  • List every place the format appears: the format table entry (fourcc, media bus code, bpp), each pixelformat switch, the RXFIFO length, the sensor output registers, the display overlay format.
  • Note the old values so you can diff the readbacks.

During:

  • Derive each length from the table's bpp; retire every hardcoded byte count you find.
  • Read the format back; sizeimage must equal your number. The readback trap in color image buffers with no size info teaches the same lesson.

After:

  • One-frame dump: byte count plus separate checksums of the top and bottom halves.
  • Soak run; treat new timeouts as signal integrity first.
  • Confirm the software conversion task is gone from CPU load.

Related capture-pipeline fixes are collected on the systems topic page.

Questions

How many bytes is an RGB565 frame at other panel resolutions?

Apply width × height × 2. An 800 × 480 panel needs 800 × 2 = 1,600 B per line and 1,600 × 480 = 768,000 B per frame; a 320 × 240 panel needs 640 B per line and 153,600 B per frame. Halve the height and you halve the frame; move to a 32-bit format and you double it. Write the number down before touching the driver, because bytesperline, sizeimage and the RX FIFO length must all land on it.

Does the two-beat bus encoding change my pixel clock requirement?

No. MEDIA_BUS_FMT_RGB565_2X8_LE means two 8-bit transfers per pixel, low byte first — exactly the wire load of MEDIA_BUS_FMT_YUYV8_2X8, which also moves 2 bytes per pixel. Bus traffic, pixel clock and FIFO pressure are unchanged by the switch; only the meaning of the bytes and the code that decodes the format change. That is why the symptom is a wrong length, not a bandwidth collapse.

How much CPU does software YUYV to RGB565 conversion really cost?

At VGA 30 fps you convert 640 × 480 × 30 ≈ 9.2 million pixels per second, each needing multiplies, clamps and a framebuffer write. A representative embedded integration measured about 240% CPU — more than two cores fully busy — just mirroring the camera to the panel. Sensor-side RGB565 or a hardware conversion block removes the task entirely, which is the whole case for fixing the capture path.

When is a standard USB3 or GigE camera the better call than the sensor module?

When you need more than one camera, C-mount optics, flash triggering, or a cable run a ribbon cannot do: passive USB3 leads hold a few meters, while GigE runs to 100 m on standard Ethernet cabling. The transport stack also negotiates the pixel format and sizes its own buffers, so a format change is a setting, not a patch. For one fixed panel at short range, the repaired CSI link is the lighter system.

Do I still need the sensor register changes if the FIFO fix restored the picture?

They do different jobs. In the working change, sensor register 0x4300 moved from 0x30 to 0x6f and 0x501f from 0x00 to 0x01 — the output path that switches the module from packed YUV to RGB565. Skip them and the usual result is a garbled or wrongly colored complete frame, not clean halves. If colors and geometry are both right after the FIFO edit, the registers you already set are holding.