Main Site Documentation

Cerb40 and Adafruit 2.8" TFT


I got this screen instead of an SPI one with the intent of expirimenting with more hardware, rather than actually hoping to use the screen in a real project.
It is 16-bit color (5/6/5) and, after you initialize and all that, you basically just set the cursor, then pump colors.
It has a bunch of registers for rotation, address window, etc.

[ol]Direct port of the Adafruit Arduino source. All of the demo functions performed perfectly but it took 3+ seconds to fill the screen, even with black.
I switched to the Domino and used the ParallelPort class. Improved speed, but repumping 0 high and 0 low just to clear the screen seemed stupid.
Adafruit code had shortcut for floodfill whenever high and low words are the same (like black), where you dont rewrite the value, but just toggle the write pin twice for each pixel to fill after the first. I added a buffer IC to allow me to attach the screen’s write pin to the SPI clock for such fills. Improved performance, but only for high==low colors; pathetic otherwise.
Added two registers. When the registers are chip-selected, data comes from the Cerb; while the screen is chip-selected, any previously loaded value scrolls within the registers. Every byte written to the screen effectively swaps the high and low words within the registers. I also added a binary counter to count the clocks and strobe the write pin on every 8th clock. The counter resets asynchronously at that time, as well, preparing for the next byte. There is no useful data sent to the screen via SPI, only clock. The SPI transmission is just an a array of ushorts (0’s).[/ol]

By now, I feel I’ve learned enough to move on; I’m hoping to get the G400 and a new screen soon.

A few little quirks that I’d like to explain/fix but aren’t critical:
[ul]With the hardware in the image and, at low speeds (1KHz so I can see on my crappy DSO Nano), it works worse! The address window register seems to get locked into full height and (the left) half-width of the screen?! At multiple MHz, it doesn’t do that. In previous hardware revisions, it did not do that.
The current hardware revision clears the screen many multiple times per second in any of 165whatever thousand colors. Every… 10th or so(?) clear gets corrupted, throwing in a few lines of a different color amongst whatever it is supposed to be filling with. Sometimes it will continue with the next color. Other times, it will confuse the screen and do something odd. FYI, you select whether you are writing a register or data before the SPI call with an additional control pin.[/ul]

My test application is just a for loop from 0 to ushort.maxvalue with a fillscreen(i) for each.


ok, what’s the question? Where’s the code? Not much for anyone who doesn’t have this kit to do here really


@ Brett -

In looking at my circuit, do you see anything odd, cumbersome, or just plain dumb?
Could I (you) build it better?
Am I missing components somewhere that would increase the reliability to 100%?
In particular, I suspect I have the timing of the screen’s write pin off a little, but it just happens to work right usually.
My scope is a DSO Nano so I have to change the bus speed to see anything useful, which also changes the behavior. It also doesn’t have an external trigger so I cannot play with delaying the write pulse by a few nanoseconds effectively, even if I knew how to.

No expectations here really… just fishing for any thing “ah-ha” or “duh” before I clear off the breadboard.

The timer IC is usually wired slightly different than the image I posted; it was a test. When counting up from 8 and using the ripple carry to pulse the write pin, instead, it is very reliable and fast. Just glitchy, every so often.


@ Brett - Specifically, I think the ripple carry is occasionally pulsing the write pin just before the registers stabilize. How can I delay the falling edge on the screen’s write pin by, say, 15-20 additional nanoseconds?


@ Kerbal - You might want to try supplying a schematic. It’s asking too much to expect someone to trace all your wires in order to understand your circuit.


I didn’t think anyone would take the time to look that close. Thanks for the feedback!
The attached image shows the major functions.

[ul]There is power and ground to everything
The post-buffer clock lines go to everything’s clock pins
Reset goes to everything and is asynchronous on all of the chips[/ul]

Buffer (74244):
[ul]When the “screen” or registers are chip-selected, the actual screen is also chip-selected
Anything that is driven via the buffer has pull-ups to account for the Z state
All SPI transactions observe the buffer’s propgation time, taken from the datasheet, plus a little fluff (40)[/ul]

Register x 2 (74164):
[ul]When the registers are chip-selected, REG1 input is from MOSI
When the screen is chip-selected, REG1 input is from REG2, bit 8
REG1 feeds the screen[/ul]

Counter (74161):
[ul]Initial reset to 0 (bit 4 low)
When bit 4 is low, next clock pulse loads 9 (bit 4 high)
7 clocks later (8 total), ripple-carry pulses TFT write pin through inverter and bit 4 goes low[/ul]


Identical initializers for “screen” and registers:

spiConfig = SPI1.CreateConfig(pinChipSelect, false, 40, 0, true, true, SpeedKHz);

Fill screen:

regOrDataPin.write(false); //register mode
register.writeRegister(cursorX, 0);
register.writeRegister(cursorY, 0);
register.write(0x22); //prepare for imminent color data
regOrDataPin.write(true); //data mode
screen.write(ushort[width * height - 1]);

The Cerb40 cannot actually create a buffer that big. The last line is actually chopped into multiple writes of BlockSize; I found 2048 works good and fast for BlockSize. Whatever hasn’t been written after the block writes is smaller than BlockSize. An empty array of ushorts the exact size of the remainder is created and passed to screen.write() to finish the last little bit.


[quote=“Kerbal”]Identical initializers for “screen” and registers:

spiConfig = SPI1.CreateConfig(pinChipSelect, false, 40, 0, true, true, SpeedKHz);


I tried a lot of configs and scoped them all.
Why does changing ClockIdleState to false make each write() generate twice the number of clocks?
Is it generating clocks for the read that is supposed to be disregarded when not using WriteRead?


This is what I think my problem is, but my scope does not have the resolution or external trigger to verify it.


I’m using the recommended regulator and cap on the Cerb40.
I thought it was worth a try, but didn’t pay attention and got ceramics. Haven’t been back for electrolytics yet.

Edit: Noticed the quote actually does call for ceramics. Most of the GHI schematics show electrolytics.