Repository navigation
ov3640: fix the output size chain, crop aspect ratio and sensor clock - #870
Merged
Merged
Conversation
The driver was derived from OV3660 and changed resolution by writing
0x3800..0x3815, but the OV3640 has none of those registers. The DVP output
therefore stayed at the QVGA 320x240 configured by sensor_default_regs while
the host sized its frame buffer from the requested framesize, so every
non-QVGA framesize produced overflowing, smeared frames.
Use the size chain the datasheet documents instead:
crop HS/VS/HW/VH (0x3020..0x3027) -- 4.2, table 4-2
-> zoom out SIZE_IN (0x335F..0x3361) -- 5.7, table 5-7
SIZE_OUT (0x3362..0x3364)
-> ISP output (0x3088..0x308B) -- 7-1, what DVP sends
Scale_En is 0x3302[5] on this sensor (0x5001 does not exist here). The crop is
centred inside the readout window read back from the sensor, so the configured
window is never exceeded, and SIZE_OUT stays slightly above the requested
output while remaining strictly below SIZE_IN as 5.7 requires.
Tested on ESP32-S3 with the Korvo OV3640 module: QVGA and 96x96 both produce
correct frames (96x96 logs crop 410,11 1536x1536 -> zoom 104x100 -> DVP 96x96).
This driver was derived from the OV3660 one, so set_framesize() programmed the OV3660 PLL block. None of those registers exist on the OV3640: 0x303a..0x303d are the AEC metering window (AVS/AHW), so every frame size change silently rewrote the auto exposure window, and 0x3824 / 0x460c are not implemented at all. The frame rate never changed either. Program the OV3640 clock registers instead (0x300e Rx_PLL, 0x300f dividers, 0x3010 DvpDiv) and keep the vendor default PLLDiv = 14. Measured on a board with a 20MHz XCLK the frame rate is ~0.414 x PLLDiv (PLLDiv 14 -> 5.8 fps, PLLDiv 7 -> 2.9 fps) and it does not depend on the output size, because the full QXGA array is always read out - cropping cannot raise it. Also fix the crop window: when the readout window is smaller than the requested aspect ratio in one dimension, set_window() clamped only that dimension and the zoom block stretched the image. Shrink the other dimension too so a 4:3 request stays 4:3 (1787x1340 instead of 1787x1536).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The OV3640 driver was derived from the OV3660 one, so
set_framesize()changes resolution by writing the0x3800..0x3815windowing block and the OV3660 PLL block. The OV3640 has none of those registers, so the DVP output never moved away from the QVGA 320x240 thatsensor_default_regssets up. The host sizes its frame buffer from the requestedframesize, so any other size - 96x96, 240x240, VGA - gets mismatched line widths and comes out smeared, together withcam_hal: FB-OVFspam.QVGA only worked by coincidence: it is exactly the size the init table is tuned for.
Fix 1: output size chain
Program the size chain the datasheet documents (4.2 / 5.7 / table 7-1):
0x3302[5]on this part (0x5001, used by the OV3660 code path, does not exist here).0x3024(0x1800 = 6144) is not what the part actually reports (1787 on my module), hence the read-back.SIZE_OUTstays slightly above the requested output, as the application notes recommend, but strictly belowSIZE_IN, which 5.7 requires.Fix 2: crop aspect ratio
When the readout window is narrower or shorter than the requested aspect ratio,
set_window()clamped only that dimension and the zoom block stretched the image to the output size. A 4:3 request in a 1787-wide window produced a 1787x1536 crop (ratio 1.163) that came out horizontally stretched at 320x240.Keep the ratio by shrinking the other dimension as well, so the 4:3 request becomes 1787x1340:
Square requests (96x96) are unaffected because both dimensions clamp equally.
Fix 3: clock registers
The old non-JPEG path called the inherited
set_pll(), which writes0x303a..0x303d,0x3824and0x460c. On this part0x303A..0x303Dare the AEC metering window (AVS[15:8],AVS[7:0],AHW[11:8],AHW[7:0]), so every frame size change silently rewrote auto exposure (the window width went from 512 to 259 pixels), and0x3824/0x460cdo not exist at all. The frame rate never changed, so the whole block only did harm.The real clock is
0x300e(Rx_PLL,PLLDiv = 64 - Rx_PLL),0x300f(FreqDiv / Bit8Div / bypass / InDiv) and0x3010(DvpDiv / LaneDiv / SensorDiv / ScaleDiv). Program those with the vendor defaults instead.Worth noting for anyone trying to raise the frame rate: the frame time is
HTS (0x3028) x VTS (0x302a) / core clockand does not depend on the output size, because the full QXGA array is always read out. Measured on a 20MHz XCLK board, frame rate ~=0.414 x PLLDiv:Raising
PLLDivalso raises the DVP clock, and above ~25 MHz some hosts start dropping pixels, so the vendor default is kept here.0x3012[6:4](XGA 2x2 subsampling) would halve the readout instead of the clock, but on this part it only changes the readout window and the Bayer phase and needs the full vendor XGA init sequence, so it is pinned to QXGA mode.Testing
ESP32-S3 with a Korvo OV3640 module,
PIXFORMAT_RGB565,fb_location = CAMERA_FB_IN_PSRAM,fb_count = 2.96x96 verified on hardware:
Before fix 1 the same build produced a 2x-magnified corner of the scene; after it the full square field of view is shown, no FB-OVF.
QVGA 320x240 after fix 2:
1340 / 1787 = 0.75, i.e. 4:3, and the preview fills the panel without stretching. Measured 5.8 fps at the default clock, with auto exposure now metering over the correct window.