The device had to be able to display a stream of at least 256×256 pixels @ 25 frames per second, with each pixel coded on 24 bits – allowing for more than 4 millions colors. A Gumstix Overo module with integrated Wifi receives a video stream, eventually rescales it with MPlayer, adjusts brightness / contrast / gamma parameters, and sends it to an Altera Cyclone III FPGA (EP3C25) through an home made MPlayer output plugin.
The FPGA acts mainly as a video card. The video stream from the Gumstix is double-buffered in two synchronous SRAM : during one rotation, a frame of the incoming video stream is stored in one of the SRAM, as the other SRAM is read by the FPGA to feed the LED drivers. At the end of the rotation, both RAM are exchanged and the FPGA signals to the Gumstix that it can begin feeding the next frame. The data rate between the Gumstix and FPGA is roughly 5MB/s.
The FPGA also computes the blade position in real time. A magnetic incremental encoder (kindly donated by Vicatronic) sends quadrature encoded pulses 16384 times per revolution to the FPGA, which decodes them and computes the blade’s position on 10 bits (1024 different positions). An index pulse is generated at the start of each revolution, thus avoiding the need for an external vertical reference such as Hall sensor (although an Hall sensor interface has been planned on the FPGA / board to account for a bug of the encoder – but with a simple algorithm fix we managed to avoid to use it).
The frames stored in the SRAM are standard video frames : matrices of RGB pixels. Each pixel is stored at an address formed from its Cartesian coordinates in the frame. But a rotative POV is inherently a polar coordinates display : at each blade position, the FPGA needs to fetch the pixels located on the current radius. The polar coordinate of each LED could be calculated on the fly (Cordic + Bresenham algorithms for example) but we choose to pre-compute these values and store them in a small FRAM. Thus at boot time, the Gumstix compute the LED polar coordinates for each blade position and store them in the FRAM. This eases debugging and allows for easy resampling strategies.
What makes things even more difficult is that a LED on the blade is rarely located on a pixel of the video frame. It is rather located somewhere in between four pixels. To reduce artifacts in the displayed picture, the FPGA have to fetch the values of these four pixels and bilinear-interpolate a virtual pixel.
The LED are driven with current-controlled PWM drivers TLC5951DAP from Texas Instruments. The FPGA drives eight groups of two chained TLC5951. During a configuration phase at startup, each driver is loaded with each own calibration parameters thus allowing to compensate for LED luminosity variation. Each driver drives 8 RGB LEDs, which makes a total of 128 drivable LEDs. The maximum PCB dimension allowed by our supplier led us to build the blade in three part : a main body with 112 LEDs, and two small extensions for the 16 remaining LEDs. We didn’t had time to solder the two extensions boards, so the images on the videos are a bits cropped…
Reprogramming FPGAs can be quite cumbersome, especially during debugging phases. Here it is the Gumstix which reprograms the FPGA automatically at boot time, or every time the developer wants. The design compilation is made on a host PC, then the programming file is rsync’d or copied over NFS to the Gumstix, and an equivalent of Altera STAPL / JAM player is used to reprogram the FPGA through the OMAP’s GPMC.
The power supply is quite simple : the ball bearings supporting the rotating ensemble is used to send a (not so regulated) 12V, which is the converted to 3.3V, 1.8V, 1.2V, 2.5V (FPGA / Gumstix) and 5V (LEDs).
We designed a brushless controller, but this boards was not soldered due to budget issues. We used instead a stepper motor with its driver board which were unused in the lab. As the FPGA compute the blade’s position in real time, the motor speed doesn’t to be precisely regulated. It just has to be high enough (around 20 rev/s) for the display to be pleasant !