teensy fast tft display in stock

CC3000 is troublesome, partly because it uses SPI_MODE1, partly because it uses the SPI port from within an interrupt. Adafruit’s CC3000 library has code to backup the AVR’s SPI registers, change them to MODE1, and then restore when it’s done, so the conflicting clock polarity isn’t (usually) an issue on AVR. But on Due, Teensy 3.1 and all other non-AVR chips, their specific SPI registers aren’t also in the code, so you can pretty easily end up with the SPI port left in the wrong mode.

Interrupts are also a huge problem. Using the CC3000 in simple blocking ways, where you fully complete all communication before you try to write to the display or read the touch screen or access the SD card tends to work. But if you use another device while the CC3000 generates an interrupt at just the wrong moment, it can run its SPI code while another device has chip select asserted, causing all sorts of terribly wrong results.

The touch controller on Adafruit’s displays comes in a couple different types, which need different SPI data modes, and some require very slow clock speeds. Again, they have AVR-only register save/restore, so usually you don’t get wrong settings into other libraries, but there’s no hardware specific code in those libs for non-AVR chips.

My recent work on SPI transactions, which will be in Teensyduino 1.20 (already in the latest release candidate and on github) and is planned for Arduino 1.5.8 (already in their github source and nightly builds) aims to solve both the settings and interrupt problems, in a hardware independent way. Adafruit has already merged my patches to their libs, at least for these most common ones, so they use the new SPI transaction stuff when compiled on those new versions.

teensy fast tft display in stock

This library implements a SPI driver for the ILI9341 screen controller providing the ability to display memory framebuffers onto the screen very efficiently.

Optimized for use with the LVGL library Easy to integrate with the lvgl library. Blazzingly fast (tear free) GUI is obtained using partial differential updates.

The library can work with any SPI bus. Multiples instances of the driver can manage multiple displays on different SPI buses. A significant speedup is possible when the DC pin from the ILI9341 screen is connected to a hardware CS (chip select) capable pin on the Teensy... Yes, this requirement may seems weird at first ! In that case, the library will use the SPI FIFO and DMA to their full capabilities which increases the framerate (around 35% faster) while reducing CPU usage (by around 20%).

ADVICE: Set DC to a hardware chip select capable pin of the Teensy whenever possible (the CS pin from the screen can be connected to any pin on the Teensy, it does not matter...)

On teensy 4/4.1, one can use either SPI0 or SPI1 (SPI2 should also work but is not readily accessible and untested). Here are possible wirings in both cases:

The above buffer has been placed in the upper 512K (dmamem) portion of the memory to preserve the RAM in the faster lower portion (dtcm). Buffers can be placed anywhere in RAM (even in EXTMEM if external ram is present but there will be a speed penalty in that case).

When the library is working in double buffering mode, it can take advantage of the fact that the last internal buffer uploaded to the screen mirrors the screen content. Therefore, when a new framebuffer is to be uploaded, the library can quickly check which pixels have changed and only upload those. This provides a huge speed bump when only a fraction of pixels changes at each frame (which is common when displaying a user interface for instance). The library is said to use differential updates in that case. When performing differential updates, the library first creates a diff log of all the changes between the new framebuffer and the previous one. Once this is accomplished, it can overwrite the internal frame buffer containing the old frame with the new one and, when an update is needed, just read the diff to select the pixels that must be pushed onto the screen. Updates can be carried away asynchronously while the user code draws the next frame.

The template parameter above specifies the size (in bytes) of the buffer. It should range from 1K to 10K depending on the kind of content displayed. The method diff1.printStats() can be used to check the diff buffer memory consumption at runtime to help choose its correct size.

The constructor of the main ILI9341Driver object tft does not initialize anything. We must do that when ready by calling the ubiquitous arduino begin() method (usually within setup()) like so:

This method returns true if the screen was correctly initialized. Note that there is no corresponding end() method so begin() should normally be called only once (but it can still be called again to issue a hard reset). The SPI_WRITE_SPEED and SPI_READ_SPEED parameters can be omitted so default speeds for write/read SPI will be used. The SPI read speed does not really matter and should not be changed. On the other hand, the maximum possible framerate is proportional to the SPI write speed so it should be set as high as possible while still keeping a stable connexion. With short wires, many displays will easily accept speeds upward of 60Mhz...

There are 4 possible orientations for the display (matching, of course, to the four physical orientations). Each one is obtained from the previous one by rotating the screen 90 degrees clockwise. Orientation 0 and 2 are in portrait mode 240x320 whereas orientation 1 and 3 are in landscape mode 320x240. In all cases, the framebuffer layout is in row-major format: if the screen has size LX x LY, then

The refreshrate is the number of times per second that the display redraws the pixels on the screen. Each screen refresh starts from the top-left corner and progresses along horizontal lines toward the bottom-right corner (with the display held in 240x320 portrait orientation mode 0). The row of pixels currently being refreshed is referred to as the scanline.

The framerate on the other hand is the number of times the screen content is actually updated by the driver every second. When pixel upload and pixel refresh are not synchronized, we end up with the screen displaying part of the old and the new frame simultaneously which create a visual defect called screen tearing.

The first condition depends heavily on the SPI speed. For example, with SPI set at 60Mhz, it is possible to upload up to 45 full frames per second. Being cautious, we can set a refresh rate at 80Hz and a framerate at 40Hz = (80/2) to obtain tear free frames on the screen. However, this computation is a worst case scenario: for most usage, differential updates boost the upload speed tremendously so it is often possible to get "tear free" display at 60Hz framerate (and 120Hz refresh rate) with only 20Mhz SPI speed !

The exact refresh rate varies from display to display. The driver will select the closest refresh rate available for this particular display. The actual refresh rate can subsequently be queried with the getRefreshRate() method.

Now, we must tell the driver the actual framerate/vsync method that we want. This is done with the tft.setVsyncSpacing() method. It takes as input a vsync_spacing parameter (what a surprise!)  which has the following meaning:

vsync_spacing = -1. Do not synchronize display updates with screen refreshes (no vsync).  Each new frame is drawn immediately onto the screen or saved into the internal framebuffer to be drawn asap if there is already an update in progress. If the internal framebuffer is in use, the frame is simply dropped...

vsync_spacing = 0. Do not synchronize display updates with screen refreshes (no vsync). Each new frame is drawn immediately onto the screen or saved into the internal framebuffer to be drawn asap if there is already an update in progress. If the internal framebuffer is in use, the method waits for a buffer to become available before returning (same as above but no frame is ever dropped).

vsync_spacing = N > 0. Synchronize uploads with screen refreshes to mitigate screen tearing. Perform upload every N screen refreshes so that the actual framerate is equal to framerate=refreshrate/N (provided, of course, that frames are pushed fast enough to the driver to achieve this rate).

In practice, vsync_spacing=-1 will give the fastest apparent framerate but will usually provide very poor visual quality. Setting vsync_spacing=-0 will give slightly better results (but still with screen tearing) and still leaves the responsibility to the user of setting a stable framerate by pushing frames at regular intervals... In most cases, the best choice is to set vsync_spacing = 2 and then adjust the refresh rate so that uploads can be performed at refreshrate/2 FPS... Using vsync_spacing = 1 should be reserved for drawing very simple frames which can be uploaded very quickly onto the screen (in less than a refresh period). Using vsync_pacing >= 3 can be used to artificially reduce the framerate but I do not know of a really compelling reason to do so.

What actually happens when the method is called depends on the buffering and vsync mode but, as a general rule, the method will try to return to the caller as soon as possible. It will use its internal framebuffer (if present) to save a copy of fb and return while uploading the frame asynchronously via DMA. So, when the method returns, fb may, or not, already be displayed on the screen but, in any case, the framebuffer can be immediately reused for drawing the next frame, changing its content will not affect the display on the screen !.

Drawing text on the framebuffer The overlayText() method can be used to draw some text onto the framebuffer. This is useful for displaying basic informations or debugging. Similarly, the method overlayFPS() draws the current instantaneous framerate onto a given framebuffer. Both methods overlayText() and overlayFPS() are typically called just before calling update().

Last but not least: drawing things on the frame buffer. The library itself provides not drawing primitive. It simply pushes/mirror a memory frame buffer onto the screen. You can draw on the framebuffer directly "by hand" or use any library you wish to do so... If you want a lightweight, fast, full featured 2D and 3D graphics library optimized for microcontrollers, you should check out my tgx library