igrr / esp32-cam-demo

Demo for working with a camera on ESP32
Apache License 2.0
624 stars 243 forks source link

Skew for first lines of image grabbed from OV7725 camera #11

Closed krzychb closed 7 years ago

krzychb commented 7 years ago

This is to carry over discussion and track issue from already closed pull request - https://github.com/igrr/esp32-cam-demo/pull/8#pullrequestreview-11294872

For the test pattern generated by OV7725 camera, the following skew of first image lines is observed.

D (691560) camera: Waiting for VSYNC
D (691560) camera: Got VSYNC
D (691560) camera: Waiting for frame
D (691560) camera: Frame done
D (691560) camera_demo: Done
 @@@@@@@@@@@@@@@@@@@@%%%%%%%%%%#########@++++++++++==========:::::::::.
  @@@@@@@@@@@@@@@@@@@@%%%%%%%%%%#########@++++++++++==========:::::::::.
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%##########++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%**********++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%**********++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%**********++++++++++==========::::::::::
   @@@@@@@@@@@@@@@@@@@%%%%%%%%%%**********++++++++++==========::::::::::

Notes:

  1. For QVGA one ASCII character (like @ or space) is printed out for every forth pixel and every eight line.
  2. Similar issue is observed for live images but is harder to notice than for regular test pattern.
  3. Test pattern has been captured @40MHz XCLK. At this speed millisecond clock in some cases does not tick and in other ticks 10 milliseconds.
  4. Last 32 image lines have slightly different pattern between about 120th and 150th column. It is not yet verified whether this a feature of the test pattern or issue with image capture process .
Oitzu commented 7 years ago

Does someone actually has a picture how the fb looks like before its converted to ascii?

krzychb commented 7 years ago

Using this snippet:

for (int ih = 0; ih < 40; ih++){
    for (int iw = 0; iw < 40; iw++){
        uint8_t px = (s_fb[iw + (ih * s_fb_w)]);
        printf("%02x", px);
    }
     printf("\n");
}

I have prepared 40x40 bytes output of left upper corner of test pattern:

D (11629) camera: Waiting for VSYNC
D (11629) camera: Got VSYNC
D (11629) camera: Waiting for frame
D (11639) camera: Frame done
D (11639) camera_demo: Done
13131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
80ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
13131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
13131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

Does it justify asking for a logic analyser for Christmas?

igrr commented 7 years ago

This issue is due to a clumsy way of waiting for VSYNC (polling GPIOs). Instead this should be changed to use GPIO interrupts and/or timers.

Oitzu commented 7 years ago

Does it justify asking for a logic analyser for Christmas?

Totally! Also an osci! You will totally need this. Unfortunately i already asked for a new bluetooth enabled multimeter.

krzychb commented 7 years ago

@igrr - yes, you said about using interrupts before. @Oitzu - thanks! I had doubts after reply by @igrr :wink:

krzychb commented 7 years ago
  1. To capture a single frame we are waiting for VSYNC by directly polling GPIO:
    ESP_LOGD(TAG, "Waiting for VSYNC");
    while(gpio_get_level(s_config.pin_vsync) != 0);
    while(gpio_get_level(s_config.pin_vsync) == 0);
    ESP_LOGD(TAG, "Got VSYNC");

    What is then the purpose of VSYNC in i2s routines? I see it configured in function i2s_init():

    gpio_matrix_in(s_config.pin_vsync, I2S0I_V_SYNC_IDX, false);
  2. Function i2s_init() contains:
    gpio_matrix_in(s_config.pin_href,  I2S0I_H_ENABLE_IDX, false);

    How HREF is used by i2s? My assumption was that HREF triggers interrupt to collect a single line by function i2s_fill_buf(cur_buffer).

If so, why first 8 - 9 pixels (16 - 18 bytes) in a line look like invalid data and are not aligned?

131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
Oitzu commented 7 years ago

@krzychb my guess was that the first buffer is directly filled after the vsync is polled and the delay(2). But that doesn't make much sense either, after looking again at the code. Is line 0 actually discarded?

igrr commented 7 years ago

How HREF is used by i2s? My assumption was that HREF triggers interrupt to collect a single line by function i2s_fill_buf(cur_buffer).

I2S peripheral will sample on each PCLK clock, if all three signals are high: HREF (called H_ENABLE in code), HSYNC, VSYNC.

Here's the relevant diagram about these signals from OV7725 datasheet:

screen shot 2016-12-08 at 2 13 49 pm

As you can see, high VSYNC signal marks spaces between frames. So we need to invert it before feeding it into the I2S peripheral (so that VSYNC == 1 while frame is being sent). This can be done in two ways: a) by setting a register in OV7725 (bit 1 in COM10 register, "VSYNC negative"), or b) by inverting the signal using ESP32 GPIO matrix (change third argument of gpio_matrix_in function to true). In the current code, option a) is used.

So once the I2S peripheral is configured and I2S_RX_START_S bit is set in I2S_CONF_REG(0), it will start receiving data and writing to DMA (via FIFO). Sampling happens on each PCLK clock when HREF & VSYNC & HSYNC = 1. So to remove the skew, we need to start I2S after !VSYNC goes high, before the first HSYNC goes high. Also, the I2S_RX_EOF_NUM field has to be set exactly to the number of PCLK cycles found in one line.

Oitzu commented 7 years ago

@igrr great explanation. :) On which condition is the i2s interrupt called? I conclude as soon as a counter reached I2S_RX_EOF_NUM? If yes: Is there a maximum of dma buffer i can set and can i abort the i2s before the buffer is completly full? I'm asking because i'm still looking envy at the onboard jpeg mode of the ov2640, which would allow a full size, color picture to load into ram. As far as i understand a jpeg frame behaves like one line of pixels without a certain length (due to compression), and i would need to abort the i2s at the end, before the dma buffer is completly full, based on pin signal behaviour.

igrr commented 7 years ago

There is a bunch of interrupt sources within I2S, they can be selected by setting bits in I2S_INT_ENA_REG(0). Right now it uses I2S_IN_DONE_INT_ENA_S, so the interrupt will be generated when the correct number of samples is received. I know that the I2S can time out (there is an I2S_RX_HUNG interrupt for that case), but i don't know how to set the timeout yet. I'll find out and post back. I also need to see the waveforms for JPEG case and check with hardware folks if this is something that I2S can support (although this is not something directly related to this specific issue).

krzychb commented 7 years ago

Could you verify if figure below correctly reflects operation of camera demo?

EDIT: updated basing on https://github.com/igrr/esp32-cam-demo/issues/11#issuecomment-265873535 by @Oitzu.

picture1

Oitzu commented 7 years ago

@krzychb kind of leaves out the waiting on the falling VSYNC edge between camera run and the actual i2s start.

krzychb commented 7 years ago

@Oitzu, thank you for review and finding this one!

I will correct it. I am really grateful that @igrr shed extra light on I2S internals. This figure is my starting point to review how to fix the skew as I still not get how it happens.

I was wondering why we engage in controlling whole I2S process for each snapshot instead of leaving it running continuously and filling in DNA buffer 0 and 1?

Then to take a snapshot I would wait for failing edge of VSYNC and control only the line_filter_task using the semaphore data_ready.

Also what about using failing edge of HREF to trigger interrupt instead of I2S_IN_DONE_INT_ENA_S?

Oitzu commented 7 years ago

I am really grateful that @igrr shed extra light on I2S internals.

i second that. :+1:

This figure is my starting point to review how to fix the skew as I still not get how it happens.

@igrr mentioned that it may be due to the sloppy vsync catching so the offending code would be:

ESP_LOGD(TAG, "Waiting for VSYNC");
while(gpio_get_level(s_config.pin_vsync) != 0);
while(gpio_get_level(s_config.pin_vsync) == 0);
ESP_LOGD(TAG, "Got VSYNC");

But that also confuses me further, because @igrr said VSYNC is inverted by cam registers so looking at the timing diagram, it would make more sense to me to catch the rising edge. But maybe he decided on the falling edge to catch the VSYNC after the previous frame is done. This would indeed be better if we would attach a gpio interrupt to it. But i don't know if this would in any way offend the already set gpio matrix.

I was wondering why we engage in controlling whole I2S process for each snapshot instead of leaving it running continuously and filling in DNA buffer 0 and 1? Then to take a snapshot I would wait for failing edge of VSYNC and control only the line_filter_task using the semaphore data_ready.

I think there is no special reaon for that, just demo-code stuff and of course you would capture frames in the background that you never could use.

Also what about using failing edge of HREF to trigger interrupt instead of I2S_IN_DONE_INT_ENA_S?

A good reason for that, that i can think of, is that you don't run accidentaly in a dma buffer overflow. This is actually pretty near to the method dcmi does it.

igrr commented 7 years ago

Demo code does a separate transfer for each line just because it was easier to debug and figure out things that didn't work. I think we could use two buffers pointing at each other, so that DMA would automatically advance to the next buffer once one is filled. But in that case we probably need to set different RX_EOF_NUM value (full frame size?), and use different interrupt status bit to check that one buffer has been filled. I'll take a look at this if i get some time between other things next week.

liubenyuan commented 7 years ago

@igrr Could we simply config the size of DMA to receive one full frame (nlines x npixels_per_line) data ? I see the timing diagram on mt9v034, it has exactly npixels_per_line PCLK cycles when HREF is high.

igrr commented 7 years ago

That would require 4x current framebuffer size, as line_filter_task currently extracts one byte out of every 4 bytes.

liubenyuan commented 7 years ago

OK, I see that.

I used xilinx zynq before and remember that xilinx has one special type of DMA called VDMA, it spawns nlines copies of DMA and the size of each DMA is npixels, pointing at different locations of framebuffer.

I am new to ARM coding and esp-idf might support this type of DMA for image capturing.

Oitzu commented 7 years ago

Couldn't we just replace the sloppy pull with:

gpio_set_intr_type(s_config.pin_vsync, GPIO_INTR_POSEDGE);
gpio_intr_enable(s_config.pin_vsync);
gpio_isr_register(23, gpioCallback, (void *)TAG);

and do:

void gpioCallback(void* arg)
{
    if(!i2s_running)
    {
        cur_buffer = 0;
        line_count = 0;
        isr_count = 0;
        i2s_running = true;
        i2s_fill_buf(cur_buffer);
    }
    else
    {
        gpio_intr_disable(s_config.pin_vsync)
        i2s_running = false;
        i2s_stop();
    }
}

?

Oitzu commented 7 years ago

To clarify after thinking about it: I mean just the concept point of view. The first intr maybe should just detect just rising edge and reconfigured to falling edge in the interrupt to avoid false detection.

krzychb commented 7 years ago

@Oitzu,

I will get to changing the sloppy pull of GPIO to interrupt. What I do not get is why sloppy VSYNC catching (I assume not at the right time) would cause wavy vertical edge shown in https://github.com/igrr/esp32-cam-demo/issues/11#issuecomment-265521877

I would expect the same horizontal offset for each line without creeping for first lines and then stabilising.

BTW I got the the above pattern for XCLK @40HMz.

Now, following information about bit 1 in COM10 register, "VSYNC negative" by @igrr, I have updated camera setting in COM10 from:

{COM10,         (1 << 1) | (1 << 5)},

to

{COM10,         (COM10_VSYNC_NEG | COM10_PCLK_REV)},

This is to maintain negation of VSYNC and to change PCLK setting - see ov7725_regs.h for clarification.

I have also reduced XCLK to 10Mhz.

EDIT: After such changes the horizontal offset on 40x40 pixels cropped from the upper left corner of frame buffer looks as follows:

14141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
14141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
14141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
14141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

The offset is still creeping for first couple of lines but at least is sharp / without jumping by two pixels every second line.

Oitzu commented 7 years ago

I would expect the same horizontal offset for each line without creeping for first lines and then stabilising.

I was also expecting that. My guess was that it syncs up with href after a few lines.

Why do you want to reverse PCLK?

krzychb commented 7 years ago

Why do you want to reverse PCLK?

I do not want to reverse :smile: There is no documentation for I2S, I am just guessing what may be the issue and poking settings.

Oitzu commented 7 years ago

Ah i see desperate times poking bits. I do not even understand what reversing PCLK do, inverting the PCLK signal? This could help or, it could happen that it trys to read the bus in a undefined state between 2 bytes, as far as the timing diagram says. My bet was on COM10_PCLK_MASK maybe helping.

krzychb commented 7 years ago

...it trys to read the bus in a undefined state between 2 pixels, as far as the timing diagram says.

This is my guess. I do not have documentation to say on what edge of PCLK I2S reads the bus / how it is configured now.

My bet was on COM10_PCLK_MASK maybe helping.

Setting COM10_PCLK_MASK makes PCLK as a logical AND of HREF & XCLK. As I2C that does not sample data when HREF is low, such masking should not change anything.

To double check I have applied:

{COM10,         (COM10_VSYNC_NEG | COM10_PCLK_REV | COM10_PCLK_MASK)},

I can not see any difference.

Oitzu commented 7 years ago

This is my guess. I do not have documentation to say on what edge of PCLK I2S reads the bus / how it is configured now.

Well, at I2S it would sample on the rising edge of CLK while wordselect (configured to be PCLK) is high. But i can't quite figure out how CLK is configured. The new code suggests that somehow "HSYNC/VSYNC/HREF" with a clock divider is used to get the sampling rate. Cryptic register settings are cryptic. :D

such masking should not change anything.

Also we shouldn't have skew ¯ \ (ツ)

krzychb commented 7 years ago

The new code suggests that somehow "HSYNC/VSYNC/HREF" with a clock divider is used to get the sampling rate.

I guess you mean lines 358-361?

This looks like plenty of time, also for a lousy VSYNC detection.

I have noticed that the first two lines of the very first grabbed frame look OK. Second frame is skew from the beginning.

D (1366) camera: Allocating DMA buffer #0, size=1280
D (1366) camera: Allocating DMA buffer #1, size=1280
D (1376) camera: Init done
D (1376) camera: Waiting for VSYNC
D (1406) camera: Got VSYNC
D (1406) camera: Waiting for frame
D (1436) camera: Frame done
D (1436) camera_demo: Done
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
14141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
14141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
D (2716) camera: Waiting for VSYNC
D (2716) camera: Got VSYNC
D (2716) camera: Waiting for frame
D (2746) camera: Frame done
D (2746) camera_demo: Done
14141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
14141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
14141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
14141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
krzychb commented 7 years ago

I have also checked https://github.com/espressif/esp-idf/issues/152

Changing DR_REG_I2S_BASE (and DR_REG_I2S1_BASE just in case) according to errata made the skew two times bigger:

D (1355) camera: Allocating DMA buffer #0, size=1280
D (1355) camera: Allocating DMA buffer #1, size=1280
D (1365) camera: Init done
D (1365) camera: Waiting for VSYNC
D (1395) camera: Got VSYNC
D (1395) camera: Waiting for frame
D (1425) camera: Frame done
D (1425) camera_demo: Done
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
14141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
14141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
Oitzu commented 7 years ago

I guess you mean lines 358-361?

Also the lines inbefore configure a parallel mode (on a serial interface?) and a camera mode which isn't excatly clear what it cause.

Sampling should start 24 lines

Isn't it 22 lines? But yeah, should be plenty of time.

Hm... changing the base-adress maybe has some not wanted side effects? Could you print out the first 3-4 complete lines?

Oitzu commented 7 years ago

Also maybe even a raw output would give maybe new insights what is going wrong.

krzychb commented 7 years ago

@Oitzu,

Thank you for being with me!

Here is the code for first four lines.

for (int ih = 0; ih < 4; ih++){
    for (int iw = 0; iw < s_fb_w; iw++){
        uint8_t px = (s_fb[iw + (ih * s_fb_w)]);
        printf("%02x", px);
    }
     printf("\n");
}

Below are first two captured frames / first four lines each.

D (1092) camera: Allocating DMA buffer #0, size=1280
D (1092) camera: Allocating DMA buffer #1, size=1280
D (1092) camera: Init done
D (1102) camera: Waiting for VSYNC
D (1132) camera: Got VSYNC
D (1132) camera: Waiting for frame
D (1162) camera: Frame done
D (1162) camera_demo: Done
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffae2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2d9c6bdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdff9a12979797979797979797979797979797979797979797979797979797979797979797979797978d7b72727272727272727272727272727272727272727272727272727272727272727272727272727b7550393939393939393939393939393939393939393939393939393939393939393939393939392f1d14141414141414141414141414141414141414141414141414141414141414141414141414141414
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffae2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2d9c6bdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdff9a12979797979797979797979797979797979797979797979797979797979797979797979797978d7b72727272727272727272727272727272727272727272727272727272727272727272727272727b7550393939393939393939393939393939393939393939393939393939393939393939393939392f1d1414141414141414141414141414141414141414141414141414141414141414141414149fc54119
14141414fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffae2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2d9c6bdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdff9a12979797979797979797979797979797979797979797979797979797979797979797979797978d7b72727272727272727272727272727272727272727272727272727272727272727272727272727b7550393939393939393939393939393939393939393939393939393939393939393939393939392f1d141414141414141414141414141414141414141414141414141414141414141414141414
14141414fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffae2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2d9c6bdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdff9a12979797979797979797979797979797979797979797979797979797979797979797979797978d7b72727272727272727272727272727272727272727272727272727272727272727272727272727b7550393939393939393939393939393939393939393939393939393939393939393939393939392f1d14141414141414141414141414141414141414141414141414141414141414149fc54119
D (2382) camera: Waiting for VSYNC
D (2382) camera: Got VSYNC
D (2382) camera: Waiting for frame
D (2412) camera: Frame done
D (2412) camera_demo: Done
14141414fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffae2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2d9c6bdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdff9a12979797979797979797979797979797979797979797979797979797979797979797979797978d7b72727272727272727272727272727272727272727272727272727272727272727272727272727b7550393939393939393939393939393939393939393939393939393939393939393939393939392f1d141414141414141414141414141414141414141414141414141414141414141414141414
14141414fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffae2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2d9c6bdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdff9a12979797979797979797979797979797979797979797979797979797979797979797979797978d7b72727272727272727272727272727272727272727272727272727272727272727272727272727b7550393939393939393939393939393939393939393939393939393939393939393939393939392f1d141414141414141414141414141414141414141414141414141414141414141414141414
14141414fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffae2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2d9c6bdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdff9a12979797979797979797979797979797979797979797979797979797979797979797979797978d7b72727272727272727272727272727272727272727272727272727272727272727272727272727b7550393939393939393939393939393939393939393939393939393939393939393939393939392f1d141414141414141414141414141414141414141414141414141414141414141414141414
14141414fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffae2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2d9c6bdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdff9a12979797979797979797979797979797979797979797979797979797979797979797979797978d7b72727272727272727272727272727272727272727272727272727272727272727272727272727b7550393939393939393939393939393939393939393939393939393939393939393939393939392f1d141414141414141414141414141414141414141414141414141414141414141414141414
Oitzu commented 7 years ago

@krzychb i also want this to work, despite having my camera not yet (but looking every 8 hours into my postbox) :D

The data suggests that the previous line leaks into the next line. That could be a memory corruption, or maybe a speed problem?

Changing DR_REG_I2S_BASE (and DR_REG_I2S1_BASE just in case) according to errata made the skew two times bigger

I actually read in the forum that the "changed addresses" are slower as the "original addresses" so that the "rapidly write" problem don't occur. Maybe this could be an indicator to an speed problem?

For a memory problem speaks: The first 2 lines on your output in the first frame seem to be okay. At this point none of the dma buffers were filled in before. At the third line, both had been used. On the second frame both had been used before obviously.

krzychb commented 7 years ago

Maybe this could be an indicator to an speed problem?

This is a good point! I believe the speed problem shows up by shaky offset and is visible in two cases:

  1. When XCLK is increased to 40MHz - case https://github.com/igrr/esp32-cam-demo/issues/11#issuecomment-265589702
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
131313131313131313ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1313131313131380ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
  1. After changing DR_REG_I2S_BASE because the "changed addresses" are slower as the "original addresses" - case https://github.com/igrr/esp32-cam-demo/issues/11#issuecomment-266210967
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff
1414141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffff
141414141414141414141414141414ffffffffffffffffffffffffffffffffffffffffffffffffff

none of the dma buffers were filled in before. At the third line, both had been used.

Another great observation with two dma buffers! The tail of line somehow winds up to the beginning of next line. I was thinking that maybe I2S FIFO is not fully purged to the dma buffer. The leftover adds up to the next line. But then the offset would creep to the right every two lines.

Good luck with quickly getting your camera. I have ordered mine from the UK. Estimated delivery was 10 days and it arrived exactly after 10 days.

Oitzu commented 7 years ago

The hard question is: How to debug this? A good starting point may be to check the dma buffers. Possible printing out the the first byte of the currently used dma buffer after it filled the fb? Just to see if it still looks okay at this point if its still okay.

liubenyuan commented 7 years ago

save .bmp file remotely at the PC, and analyze its contents using python.

igrr commented 7 years ago

I have just verified that this issue is not caused by VSYNC detection. Seems like i'm not configuring DMA correctly. We are trying to troubleshoot this in simulation, then will test on the real chip.

Edit: works great in simulation... next hypothesis is that we are loosing some samples. Still messing around with this.

igrr commented 7 years ago

@krzychb the operation diagram is really cool and should definitely be added to the readme.

krzychb commented 7 years ago

@igrr it is fantastic you resolved the issue with configuration. Currently I am travelling and I will do my testing this Thursday. Later I will also update the readme.

krzychb commented 7 years ago

Test pattern from OV7725 is now shown nice and square:

@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########## +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%********** +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%********** +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%********** +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%********** +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%********** +++++++++==========-:::::::::
@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%********** +++++++++==========-:::::::::

If anybody is curious where the horizontal space in the middle comes from, here is the whole first line of the test pattern:

fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffae2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2d9c6bdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdff9a12979797979797979797979797979797979797979797979797979797979797979797979797978d7b72727272727272727272727272727272727272727272727272727272727272727272727272727b7550393939393939393939393939393939393939393939393939393939393939393939393939392f1d14141414141414141414141414141414141414141414141414141414141414141414141414141414