The main advantage of distributing the open source code is that anyone who gets it can edit and improve the code for his own needs. In this additional article we will look deeper inside how current firmware works, what are the most important points and how to adjust it for your own needs.
Main features of current firmware are 64 different colors and maximum FPS rate. It is written for the 2x2 panels size (64x64 pixels) screen size, but even that can be changed when you know what the important points are and which parts of the code should be rewritten.
Conversion
There are a lot of different types of LEDs. Some of them are capable of displaying all colors from the 16-bit RGB spectrum, but in this case each of the 1024 LEDs on one panel is capable of displaying only 8 different colors. Each LED is composed of 3 smaller LEDs ( Red, Green and Blue) - they can only be turned on or off, which means 8 different combinations, which also means 8 different colors. Because of this, firmware uses technique of shadow frames to simulate more than 8 colors. In case of our firmware we have 3 shadow frames which means that at least 3 shadow frames are displayed for each real frame. Let's pay attention to the rgb.c file, mainly to the _convert_pix function.
static void _convert_pix( uint8_t x, uint8_t y ) { uint16_t input; uint8_t red; uint8_t gre; uint8_t blu; uint8_t tmp; uint8_t xp; uint8_t yp; uint8_t pan; uint8_t row; uint8_t col; uint8_t sh; uint8_t lsb; uint8_t msb; uint8_t mask; // Position calculation xp = x % 32; yp = y % 32; pan = x / 32 + y / 16; row = yp % 8; col = ( ( yp % 16 ) >= 8 ) ? ( xp ) : ( 32 + xp ); if( yp >= 16 ) { sh = 3; col += 64 * ( ( pan + 3 ) % 4 ); } else { sh = 0; col += 64 * pan; } mask = ~( 0x7 << sh ); lsb = _ram[ ( y * 64 + x ) * 2 ]; msb = _ram[ ( y * 64 + x ) * 2 + 1 ]; // Mapping input = msb; input <<= 8; input |= lsb; red = ( 1 << ( ( input & 0xC000 ) >> 14 ) ) - 1; gre = ( 1 << ( ( input & 0x0600 ) >> 9 ) ) - 1; blu = ( 1 << ( ( input & 0x0018 ) >> 3 ) ) - 1; tmp = _shadow[0][row][col]; tmp &= mask; tmp |= ( ( red & 1 ) | ( ( gre & 1 ) << 1 ) | ( ( blu & 1 ) << 2 ) ) << sh; _shadow[0][row][col] = tmp; red >>= 1; gre >>= 1; blu >>= 1; tmp = _shadow[1][row][col]; tmp &= mask; tmp |= ( ( red & 1 ) | ( ( gre & 1 ) << 1 ) | ( ( blu & 1 ) << 2 ) ) << sh; _shadow[1][row][col] = tmp; red >>= 1; gre >>= 1; blu >>= 1; tmp = _shadow[2][row][col]; tmp &= mask; tmp |= ( ( red & 1 ) | ( ( gre & 1 ) << 1 ) | ( ( blu & 1 ) << 2 ) ) << sh; _shadow[2][row][col] = tmp; }
The picture we are sending to the click board must be in RGB-565 format. When new picture is uploaded via SPI, it is first converted to the shadow frames depending on the intensity of each color. Therefore we have 3 shadow frames, and we took 2 most significant bits for conversion.
red = ( 1 << ( ( input & 0xC000 ) >> 14 ) ) - 1; gre = ( 1 << ( ( input & 0x0600 ) >> 9 ) ) - 1; blu = ( 1 << ( ( input & 0x0018 ) >> 3 ) ) - 1;
The code above might look confusing, but it is just an optimized way of catching the 2 most significant bits for each pixel - all because of the highest FPS possible. This technique alongside with high FPS gives illusion that our display is capable of displaying more than 8 colors. For example dark red is converted in the manner that all shadow frames have red LED turned on while, while some brighter shades of RED will have it turned off in some of the shadow frames.
It is important to note that 3 shadow frames are enough to have 64 different colors - 4 combinations ( 2 bits ) per color or 6-bit color spectrum. 64 colors are quite enough for some basic pictures, but if you need even more colors, you will have to make more than 3 shadow frames and the new conversion part. Also, in that case, Display function should be changed to loop through all the shadow frames before exiting the function and allowing other other operations, such as SPI transfer of the new picture or intensity setup.
You've probably noted the additional mapping parts inside the _convert_pix function. It is a calculation of the position for each pixel, because the first transferred pixel is not the first pixel on the panel. If you want to have more than 2x2 panels size, the whole mapping part inside this function must be rewritten.
Brightness and FPS
Because there is no option for hardware settable brightness of the panel, that feature is implemented through variable delay between displaying of each panel row in the display function. According to the current tests, values between 0 and 100 are capable of visible change of the brightness. Brightness command changes nothing more than delay in the display function.
void display( void ) { uint8_t sh, rw; uint16_t dt, cn; GPIO_PIN17_bit = 1; for( sh = 0; sh < 3; sh++ ) { for( rw = 0; rw < 8; rw++ ) { PAN_A = _rw_map[rw] & 0x01; PAN_B = (_rw_map[rw] & 0x02) >> 1; PAN_C = (_rw_map[rw] & 0x04) >> 2; PAN_D = (_rw_map[rw] & 0x08) >> 3; for( dt = 0; dt < 256; dt++ ) { PAN_STB = 0; PAN_DATA = _shadow[sh][rw][dt]; PAN_CLK = 1; PAN_CLK = 0; PAN_STB = 1; } if( _power ) { PAN_OE = 0; } for( cn = 0; cn < _brightness; cn++ ) // BRIGHTNESS LOOP { Delay_1us(); } PAN_OE = 1; } } PAN_OE = 1; GPIO_PIN17_bit = 0; }
Looking at this function would lead to a logical conclusion that higher brightness level has impact on the FPS rate, but in most cases, that impact is hardly noticable.
The only situation where we've found that higher brightness impacts the performance of the display, is when you are combining it with high scroll speeds - with scroll steps faster than 50 ms, and it will only cause slight flickering. Solution in that case might be the lower speed of scroll steps, with higher offset steps.
Summary
FT900 placed on the Matrix RGB click is fast enough to control all 4 panels at really high FPS rate. For our hexiwear demo on embedded world, 64 colors were enough, but open firmware that we are providing gives you a chance to adjust it exactly for what you need and even make your own 3x3 version with any color depth you need.