Using the Texas Instruments DLP4710EVM-LC for generating visual stimuli at 180 and 1440 Hz

Notes by Mark Wexler

Table of contents

In brief: what it is and what it can do
In detail
>> Before using the projector for the first time
>>>> Update the firmware
>>>> Set the video options for 1440 Hz mode to work right
>> Projector display modes
>>>> Setting mode manually using the GUI
>>>> Changing mode using the dlpmode program
>> Programming: How to squeeze multiple frames into every frame
>>>> 60 Hz
>>>> 180 Hz
>>>> 1440 Hz
>>>> Using my libraries

In brief: what it is and what it can do

The DLP4710EVM-LC is an evaluation module made by Texas Instruments in order for engineers who design projectors and other equipment to be able to evaluate their latest components. Out of the box, it works as a ordinary DLP video projector, taking in video signals through its HDMI port and outputting images at 1920 × 1080 × 60 Hz × 24 bits per pixel. But with a little additional work, it can be made to operate in two unusual modes:

At 180 Hz the unit makes an excellent CRT replacement. At 1440 Hz it gives access to stimuli (for instance very high-speed motion) that can only be matched by one other commercially available display, but for a very small fraction of the price.

At 60 and 180 Hz, the projector should be compatible with any system. (But to switch to 180 Hz mode, you will need need a Windows PC — or figure out how to send I2C signals from your Mac or Linux machine.) In 1440 Hz mode, you will need to make sure that your video card sends RGB signals without modification. I've verified that this works on:

On the other hand, this does not seem to be possible on MacOS because it doesn't allow access to low-level graphics card parameters. To summarize, you should have no trouble displaying in 180 Hz mode on any system (but will need a Windows machine to switch modes). Your best bet for 1440 Hz seems to be Windows with an NVIDIA graphics card.

In terms of software, if you use either Matlab + Psychophysics Toolbox version 3, or OpenGL directly (from Python or C), you should be able to display stimuli at 180 or 1440 Hz with minimal modification of your code.

This page gives advice, pointers, and sample code for using this projector to display at 180 and 1440 Hz, from the point of view of a psychophysicist. If you find any errors in this document, or if something isn't clear, or if you've figured out something that I haven't, please let me know.

In detail

As far as your graphics card is concerned, the projector operates as a 60 Hz display. In order to go to higher refresh rates, you have to do two things:

Before using the projector for the first time

Update the firmware

The units we've seen came with outdated firmware. In order to use 180 and 1440 Hz modes, the firmware on the projector needs to be updated. This has to be done on a Windows PC using TI's graphic control tool.

To check the version of firmware that you've installed, click the "Get" button in the lower-right corner of the first tab (Information) of graphic control tool.

Set the video options for 1440 Hz mode to work right

A crucial issue for doing 1440 Hz is "RGB pass-through". Video cards try to 'improve' your output signal in various ways. Since each RGB bit codes the time that a particular pixel is displayed (see below), all of these 'improvements' will result in your pixel being displayed at the wrong time(s). You need to be able to turn off these 'improvements' for your video card to actually output the RGB values you set, in order to get the timing right.

Whether you can do this or not is highly dependent on your operating system and video card. Here are the systems that this can be achieved on:

And here's a list of machines that it didn't work on:

On Windows, the main option in the NVIDIA (and Intel) drivers that needs to be set is to output the full (0-255) 8-bit range rather than a restricted range (thank you Guillaume Strub). Drivers will think that the DLP4710 is a TV, and for some reason output the restricted range, which messes up the RGB pass-through. Here's how to set this option:

Once you've done this, you can test that the 1440 Hz mode works as follows:

On Linux, we've been able to accomplish the same thing — expanding the dynamic range to full 0-255 and removing gamma correction, thereby achieving RGB pass-through — using the xrandr command (thank you Rodrigo Balp):

xrandr --output <output-port> --size 1920x1080 --gamma 1:1:1 --set "Broadcast RGB" "Full"

This worked on a Debian system with an NVIDIA GF119 GPU.

Projector display modes

At 60 Hz, the DLP4710 outputs standard 24-bit RGB. I'm not sure if the light is continuous or if it flickers, haven't measured it.

At 180 Hz, the projector outputs 8 bits of luminance in either red, green or blue (but not in combination). You also have to specify the pre-exposure (the blank before the frame), exposure (the frame), and post-exposure (the blank after the frame) durations. (In most cases you only care about the sum of pre- and post-exposure durations, as this constitutes the between-frame blank interval.) The three durations should add up to 5556 microsec (1,000,000/180), but not all combinations are allowed. An example of an allowed combination is 350, 5000, 206 microsec for pre-exposure, exposure and post-exposure durations, respectively. (See below for how to determine which combinations are allowed.) In principle there's also a no-flicker mode with zero pre- and post-exposure 0, 5556, 0, but in the current firmware (ver. 7.0.1) this mode is broken. TI is aware of the problem and has promised a fix in the next firmware release.

At 1440 Hz, the projector outputs 1 bit of luminance in either red, green or blue. In contrast to 180 Hz, there is also a gray mode, in which the R, G and B light sources are flashed in sequence. As for 180 Hz, you have to specify pre-exposure, exposure, and post-exposure durations, they should add up to 694 microsec, and not all combinations are possible. One possible combination is 193, 451, 50 microsec. As for 180 Hz, there is a no-flicker mode, but which is currently broken, awaiting the next firmware release.

Here's an example of the luminance profile of the projector at 1440 Hz with typical on and off timing, as measured using a photodiode:

The duration of light on each frame is the exposure time, and the blank is the sum of pre- and post-exposure durations. The rise and fall times are below 25 μs (because DMD micromirrors are very fast).

In addition to its HDMI input for video data, the DLP4710 also has a USB port that you can use to switch modes (60 to 1440 Hz, for example). There are two ways to switch modes, one manual and one programmatic, both of which only work on Windows. If you are using Linux to display stimuli, you could use a Windows machine to switch the mode at the start of your experiment.

Setting mode manually using the GUI

One way to switch modes is by using TI's Windows-only graphic control tool. First, make sure your computer is connected to the projector's USB port. Run the application and click "Get" at the bottom right of the "Information" tab to make sure that the projector is connected and communicating over the USB port. Then:

Changing mode using the dlpmode program

Because the manual mode changes can become slightly painful, I wrote a little program, dlpmode, to change modes. Its command-line syntax goes like this:

dlpmode.exe freq color pre-exp-dur exp-dur post-exp-dur

freq is the frequency (60, 180, or 1440). For 180 and 1440 Hz, you should also specify color (R, G, B, or RGB), as well as the pre-exposure, exposure, and post-exposure durations. (If the durations are omitted, the program will set some sensible default values.) Like the GUI, this program only works on Windows.

An executable version of the program is available here (change the ".ex_" extension to ".exe"). Depending on your machine, you may also need a few DLLs, which can be downloaded here (unzip to the same directory as dlpmode, change .dl_ to .dll. If you want the source code (which can be compiled using the free Microsoft compiler with an API provided by TI), you can get it here.

Programming: How to squeeze multiple frames into every frame

The "180 Hz" and "1440 Hz" sections explain the what you need to do if you write your own code. If you want to use my Matlab or Python libraries which do the work for you, skip below to "Using my libraries".

60 Hz

In 60 Hz mode, nothing special needs to be done. The projector operates as a regular display, with each pixel having 24 bits of color and luminance: 8 bits for red (256 luminance levels), 8 for blue, and 8 for green.

180 Hz

180 Hz mode is 8-bit monochrome. You must choose which color you want: red, green, or blue, but not all three simultaneously — so no gray. You will get 256 luminance levels in your chosen color. Because 180 = 60 × 3, you will need to squeeze 3 frames into every image. You do this by drawing the chronologically first of three frames in the blue channel, the second in the green, and the third and last in the red. After drawing these three frames you do an ordinary buffer flip. When the projector receives the data, it will show the three frames in chronological sequence, all in the same color, each frame being shown for 1000/180 ≈ 5.6 ms.

You might be tempted to use your regular drawing code but to draw one frame in blue, the next in green, the next in red, and so on. It's not that simple, though, because in ordinary drawing mode the color you assign to a pixel overwrites the color that was there before — whereas what you want is to accumulate or add up colors. For example, if you draw your first image in shades of blue, and then the next images in shades of green (so with 0 for blue and red), this will erase the previous blue bits — and the same thing for the following red image.

You can solve this problem by using color masks. If you mask a particular color or colors, any drawing command will have no effect on the masked colors. Thus, you can mask green and red while drawing the first frame in blue, then mask blue and red while drawing the second frame in green (which won't erase the blue image because it's now masked), and finally mask the blue and green while drawing the third frame in red.

In Matlab + Psychophysics Toolbox, this can be done as follows:

Screen('BlendFunction', window, GL_ONE, GL_ZERO, [0 0 1 1])

before drawing the first (blue) frame, then

Screen('BlendFunction', window, GL_ONE, GL_ZERO, [0 1 0 1])

before drawing the second (green) frame, and finally

Screen('BlendFunction', window, GL_ONE, GL_ZERO, [1 0 0 1])

before drawing the last (red) frame. (If you're using alpha blending, then you'll want to use other values in place of GL_ONE and GL_ZERO.)

If you're using OpenGL directly, then the equivalent calls are:

glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE)
glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE)
glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE)

Keep in mind that if you use the system or processor time to do any calculations while drawing (for instance, to calculate the position of a moving object), this will fail in 180 Hz mode. During one 16.7 ms interval, you will draw 3 frames that will be displayed at 5.6 ms intervals. If it takes, say, 2 ms to draw one frame, and you read the clock before drawing each frame, your timing will correspond to 2 ms intervals between frames, rather than 5.6 ms. Instead, you should assume that the refresh one every three frames happens every 16.7 ms (after checking the timing), and that each of the 3 frames is drawn at regular 5.6 ms intervals.

To summarize, you can keep using your regular drawing code, but instead of clearing the back buffer, drawing to it, and flipping buffers on every frame, for every three frames you

You'll find examples of this in Matlab and Python, including simple classes that take care of the bookkeeping, in the code below.

1440 Hz

1440 Hz mode is has only 1 bit of luminance, so your resulting images are black and green, black and red, or black and blue (without intermediate shades). You can also have the R, G and B lamps all flash in sequence for a black and white mode. For 1440 Hz, you need to squeeze 24 1-bit images into one 24-bit image, and you do so by drawing them into the 24 bitplanes. Imagine each 24-bit color value as binary number, with the lowest 8 bits representing the blue channel, the next 8 bits the green, and the most significant 8 bits the red. Further, assume that the lowest bit of each 8-bit group represents the least luminous bit in that channel. Thus, e.g., the binary number 1 = 000000000000000000000001 would represent the darkest blue, and 111111110000000000000000 the brightest red. Then the least-significant bit codes the first image to be displayed chronologically, the second-least-significant bit the second image, ..., and the most-significant (24th) bit the 24th and final image.

This is the equivalent of drawing

Since you cannot easily mask out individual bit planes, another simple solution can be used: alpha blending. While alpha blending is usually used to simulated transparency, it can also be used to add every color to the color already at the pixel, rather than overwriting — which is exactly what we want. To do this in Matlab, we need to set the alpha blending factors like this

Screen('BlendFunction', window, GL_ONE, GL_ONE)

Then, for every sequence of 24 frames, on every frame we set the RGB color to the corresponding color in the above list and then draw our stimulus (taking care never to change the color). We clear only on the first of the 24 frames, and flip only the last one. An example can be found below, together with a class to simplify the bookkeeping.

In pure OpenGL, another mechanism can be used: logical operators. By setting the logical operator to bitwise OR, incoming colors are added to previous colors at each pixel. This is done by calling

glEnable(GL_COLOR_LOGIC_OP)
glLogicOp(GL_OR)

Then, on each frame out of groups of 24, we set the corresponding color and draw the stimulus. Be careful not to change the color while doing the drawing. As in Matlab, we clear only on the first of the 24 frames, and flip only the last one. For example in Python + PyOpenGL + pygame:

glEnable(GL_COLOR_LOGIC_OP)
frame = 0
while True:
    f = frame%24                  # frame number mod 24, runs from 0 to 23
    if f == 0:                    # on the first of each 24 frames, actually clear
        glLogicOp(GL_COPY)
        glClear(GL_COLOR_BUFFER_BIT)
    glLogicOp(GL_OR)              # set 
    col = 3*[0]                   # this assigns color (0, 0, 1) to frame 0,
    col[2 - f//8] = 1 << (f%8)    # (0, 0, 2) to frame 1, (0, 0, 4) to frame 2,
    glColor3ubv(col)              # etc.
    # draw stimulus
    # don't change color!
    if f == 23: pygame.display.flip()
    frame += 1

If you want to dynamically update your display at 1440 Hz, you need to be able to draw your stimulus in less than 1000/1440 = 0.694 ms, which may be challenging, especially in Matlab on slower machines. The comments about not using system or processor time to calculate your stimulus configuration (see the "180 Hz" section) also apply here.

Keep in mind that anything that relies on multiple luminance levels such as alpha blending, antialising, etc., will not work and will cause visual artefacts, so you should disable it (easy to overlook if you're displaying text with system-wide antialiasing options).

If you absolutely need more than one luminance level, you can achieve that using lower temporal resolution. For example, if you need a medium luminance level for the background and one dark and one light level, you can achieve this at 720 Hz.

Using my libraries

I've written two small libraries that encapsulate all of the above, one for Matlab + Psychtoolbox ver. 3, and another for Python using pure OpenGL (can be integrated with PsychoPy?). They're nearly drop-in replacements for the regular flip (Matlab) and flip and clear functions (Python). They keep track of the subframe, making sure you draw (using the regular drawing functions) into the correct bitplanes, and doing clears and flips when necessary. They can also automatically switch projector mode using the dlpmode program (see below).

In both the above examples, the projector mode change is done automatically using the external dlpmode program, which must be installed separately—see above for instructions.