In discussing grey scale the tearing that can be seen on the display when showing large blocks of black/white keeps coming up and I keep saying the same thing, so I’ll post it here.
First, this is a problem of the screen in general, not grey scale or 3rd color. Normal screen paints are also subject to vertical sync issues, just it’s easier to see with a consistent pattern being animated quickly (such as 96FPS greyscale - 0101010101). While it’s true there would be less tearing at say 40FPS (just because you’d paint the screen less with fewer chances to miss sync) the issue will still be present.
A whole page of the SSD1306 PDF is devoted to this topic (page 23). The way to remove this issue is of course to write quickly and write in-between the vsync (thanks to writing quickly) or to write slowly - starting your write immediately after ONE refresh begins and then writing during the refresh… Then you simply have to finish writing before the next refresh cycle gets to the end of the display RAM.
I’m sure we’re more than fast enough to be considered a fast writer. We also have a few options to increase our SPI writing speed if need be. The real problem is we don’t know when the display clock rises or falls: we don’t know when the sync is. I think the right/best way to do this would be to have the display clock hooked up to a CPU pin and then we’d have a hardware interrupt fired telling us when the sync was and we could draw the screen on that exact cycle. I’m not sure if that’s possible or if it might be an option for the next revision of the Arduboy.
The second option (if the LCD screen always boots consistently) is to just play around with the timings very carefully (screen draw will still need to be interrupt driven). If we can get the CPU and LCD clocks in close enough sync then we paint the screen exactly every XXXX cycles then we stay be in sync. However if there is any drift between the two clocks or we’ve even slightly off then occasionally over time we’d still drift across the sync boundary - but hopefully not as much as before.
I don’t worry about this a lot because I think either it’s easily solvable (via getting access to the LCD clock sync), or it’s simply impossible to solve. Once I get my actual hardware I’m going to see about painting the screen from an interrupt anyways for consistency. You pick the refresh rate and then the hardware just handles painting the screen from the buffer automatically - in sync (as much as possible) with the refresh rate.
If any of the hardware people can comment on actually getting access to the LCD sync signal that would be great… We’d probably need it on any of the INTx pin or Pin Change pins.
" I think the right/best way to do this would be to have the display clock hooked up to a CPU pin and then we’d have a hardware interrupt fired telling us when the sync was and we
could draw the screen on that exact cycle. I’m not sure if that’s
possible or if it might be an option for the next revision of the
I think to remove the issue properly this is what we will need the most.
@bateske Any chance you can chime in if this is possible or already done for the current arduboy?
[quote=“Dreamer3, post:1, topic:140”]
If any of the hardware people can comment on actually getting access to the LCD sync signal that would be great…[/quote]
There are no timing output signals whatsoever on the OLED display’s connector.
Also, the connections to the individual LEDs themselves, which could possibly be used to obtain some kind of sync timing, are sealed. Even if they weren’t sealed, it would be virtually impossible to connect a wire to one.
Yeah, more docs! Also no timing input signals… so I assume the display clock is being generated internally to the SSD1306 and we have no way to know what it is or what it’s doing. Anyone else have any information to the contrary?
That is correct. You just send commands to write to (or using interfaces other than SPI, read from) the controllers internal display buffer. The controller then refreshes the LEDs using its own internal clocks and timing. It likely that the controller generates a fairly imprecise internal clock, so even if your program was timed to sync to one display it could be out of sync with another. The clock might also vary with temperature, so your program would only be in sync at a given temperature.
I had’t even consider that (temperature). I think we should make some attempt to get it “close” for most people. At the very least we shouldn’t paint the screen more than the max FPS - that’s just encouraging more tearing with absolutely zero benefit.
But yeah I was never thinking you could manage them independently. Even if the timings never varied and the boot up time of the screen was a known quantity after you issued “turn on”… it would still never work unless the LCD clock speed was some exact divisor of the CPU clock speed - and I mean exact… otherwise over time the clocks would still drift. And then you’d have to make sure your hardware interrupts didn’t interfere with each other to affect your perfect timing. Fun fun.
Correction: There is a signal. It’s not an LCD thing, the LCD is low level. It’s on the SSD1306 controller. I just needed to read more carefully.
“FR” - “This pin outputs RAM write synchronization signal. Proper timing between MCU data writing and frame display timing can be achieved to prevent tearing effect.”
@bateske Any chance to get that hooked up to an interruptible pin?
“MCU should start to write new frame of ram data just after rising edge of FR pulse and should be finished well before the rising edge of the next FR pulse.”
So we need to detect the rising edge and trigger a redraw. I’m pretty sure we’re a fast MCU since from what I’ve seen we can repaint the screen multiple times during a single refresh cycle, which is crazy.
Good spot @Dreamer3! I never noticed this signal, mainly because I’ve been mostly concerned with the signals available to the user.
I think the only practical way to gain the use of this signal would be to have a custom version of the display made which brings this signal out to an unused pin on the connector. I doubt this is likely to happen.
Correct. There isn’t actually a board. The chip is bonded and connected to the flexible plastic connector strip. It’s sealed under a layer of silicone. It’s size is 6.76mm x 0.86mm (0.266" x 0.034"). I’ve circled in red the location of the chip in the following photo (its actually on the opposite side).
[quote=“Dreamer3, post:10, topic:140”]
You’re saying you’ve physically inspected the SSD1306 and that pin just isn’t brought out? That seems strange to me as dealing with vsync is a pretty common issue.[/quote]
Yes. Why they didn’t bring out this signal, when there are unused pins on the connector, is beyond me as well.
By the way, you should refer to the display as OLED, not LCD.
If you read all of the posts in this topic, and the first word of the title, you should realise that providing vsync is virtually impossible. It would require a custom version of the display to be built which brings out the FR pin from the SSD1306 controller. I doubt the Arduboy creators would be able to justify ordering the quantities required to have a custom display manufactured for a reasonable cost, and in the required time frame.
It’s impossible to do smooth greyscale without VSYNC. Normal black and white display shouldn’t be too affected by it unless you do a lot of rapid flickering and flashing of screen elements. But it’s a shame that greyscale is almost within our grasp and we can’t quite do it consistently.
I think our best hope would be doing calculations based on millis() or micros() to try to sync with the screen refresh rate, if it’s even possible to approximate that. Just a while loop spinning on millis() or micros() modulo something before doing a display.display();.
I don’t think impossible is quite right. If you are drawing greyscale images instead of large blocks of color the effects should be much more minimal… is the tearing that apparent when showing actual images, not just large blocks? i’d have guessed not.
I think our best hope would be doing calculations based on millis() or micros() to try to sync with the screen refresh rate
If your demo is pausing 7-10ms between frames that’s as good as we’re going to get. We can of course make sure sketches don’t draw to the screen TOO much (making the problem worse), but from what I’ve seen most sketches are going to draw too slowly, not too quickly. So the issue would be making sure the paint happens at /10ms moments rather than completely randomly. We can do that by encouraging some animation practices (like gamebuino) or by moving the screen draw to an interrupt and just doing it every 10ms no matter what.