stm32plus: ILI9327 TFT driver
The code presented in this article requires a minimum of version 3.0.0 of my stm32plus library. |
The TFT panel
The ILI9327 is a driver IC for 432×240 (WQVGA) panels. The panels are typically found in mobile phones; LG went through a phase of producing lots of phones with resolutions close to this as did several other manufacturers.
I got one of these panels on ebay. It came attached to a handy breakout board, though I have seen others that come with just the FPC tail if you’re feeling adventurous. This particular board seems to have the tail soldered directly to it somewhere underneath the panel.
The front of the board
The back of the board
There’s also an ADS7843-compatible touch screen driver and an SD card cage. This is a configuration we often see on development boards sourced from China.
Pinout
The seller included the pinout for the display. It’s a familiar 16-bit 8080 interface that is easily connected to the FSMC of the STM32 microcontroller. There’s no sign of a step-up DC-DC converter on the board so the white LED’s that make up the backlight must be in a parallel configuration.
The pinout
The touch screen is compatible with the ADS7843 controller and that can be hooked up to my stm32plus driver. I’ve had varying luck with the touch screens attached to these cheap boards. The ADS7843 is an A-D converter which means that the board should be carefully designed to minimise noise and it seems that not all of them are that well thought out.
Panel details
This panel is slightly unusual in that its resolution is 400×240 which is less than the full 432×240 supported by the ILI9327. How does that manifest itself? Well, the co-ordinates from 0-31 are not visible. That is, you can write to them but nothing will appear on the screen. Therefore we have to make allowances for that in the stm32plus driver and you will see how in the demo code.
My demo sequence exercises some of the common functions used in graphics operations such as rectangle, line and ellipse drawing as well as text rendering and hardware scrolling when the panel supports it (the ILI9327 does support hardware scrolling in portrait and landscape modes).
The datasheet for the ILI9327 is readily available on the internet. It’s written to the high standard that I’ve come to expect from ILITek making the job of writing a driver really straightforward.
The panel supports 16 bit colour (5-6-5 format) and 18-bit colour (6-6-6 format). The STM32F103 can easily drive either colour format at a fast pace. I’ve come to realise that the observable difference between 64K and 262K colours is pretty low and generally I’ll use 64K for the extra speed that it offers.
Data transfer protocol for the 16-bit interface (click for larger)
Using a 16-bit interface we can transfer a 64K colour pixel in one operation. A 262K colour pixel costs an extra transfer per-pixel and therefore halves the speed just for those extra 2 bits.
STM32 wiring
Here’s the wiring mapping table that you’re going to need if you intend to hook one of these up to an STM32 MCU. You’re free to change the address line that you use for RS, the bank selector that you use for /CS and the GPIO pin for RESET.
In my example code for this panel I’m using SRAM bank 1 and A16 for RS (register select). This configuration is compatible with the 100 and 144 pin STM32F103 devices.
LCD signal | STM32 port | port function |
---|---|---|
D0 | PD14 | FSMC_D0 |
D1 | PD15 | FSMC_D1 |
D2 | PD0 | FSMC_D2 |
D3 | PD1 | FSMC_D3 |
D4 | PE7 | FSMC_D4 |
D5 | PE8 | FSMC_D5 |
D6 | PE9 | FSMC_D6 |
D7 | PE10 | FSMC_D7 |
D8 | PE11 | FSMC_D8 |
D9 | PE12 | FSMC_D9 |
D10 | PE13 | FSMC_D10 |
D11 | PE14 | FSMC_D11 |
D12 | PE15 | FSMC_D12 |
D13 | PD8 | FSMC_D13 |
D14 | PD9 | FSMC_D14 |
D15 | PD10 | FSMC_D15 |
/WR | PD5 | FSMC_nWE |
/RD | PD4 | FSMC_nOE |
/CS | PD7 | FSMC_nE1 |
/RESET | PE1 | GPIO |
RS (D/CX) | PD11 | FSMC_A16 |
stm32plus driver
stm32plus 2.0.0 comes with an updated ILI9327 demo application. Here’s an extract from the source code that shows how to set it up.
#include "config/stm32plus.h" #include "config/display/tft.h" using namespace stm32plus; using namespace stm32plus::display; class ILI9327Test { protected: typedef Fsmc16BitAccessMode<FsmcBank1NorSram1> LcdAccessMode; typedef ILI9327_400x240_Portrait_262K<LcdAccessMode> LcdPanel; LcdAccessMode *_accessMode; LcdPanel *_gl; Font *_font; public: void run() { // reset is on PE1 and RS (D/CX) is on PD11 GpioE<DefaultDigitalOutputFeature<1> > pe; GpioD<DefaultFsmcAlternateFunctionFeature<11>> pd; // set up the FSMC with RS=A16 (PD11) #if defined(STM32PLUS_F1) Fsmc8080LcdTiming fsmcTiming(0,2); #elif defined(STM32PLUS_F4) Fsmc8080LcdTiming fsmcTiming(4,10); #endif _accessMode=new LcdAccessMode(fsmcTiming,16,pe[1]); // declare a panel _gl=new LcdPanel(*_accessMode); // apply gamma settings ILI9327Gamma gamma(0,0x10,0,0,3,0,0,1,7,5,5,0x25,0,0,0); _gl->applyGamma(gamma); // clear to black while the lights are out _gl->setBackground(0); _gl->clearScreen(); // create the backlight on timer4, channel2 (PD13) DefaultBacklight backlight; // fade up to 100% in 4ms steps backlight.fadeTo(100,4); // create a font _font=new Font_VOLTER__28GOLDFISH_299; *_gl << *_font;
Let’s look at the code in a little more detail.
Includes and namespaces
#include "config/stm32plus.h" #include "config/display/tft.h" using namespace stm32plus; using namespace stm32plus::display;
Firstly we need to include the headers that define the classes we’re going to use. Secondly, since all of stm32plus lives either in the stm32plus namespace or a sub-namespace (in this case stm32plus::display) we will import them into the global namespace to make the declarations of the objects less clumsy looking.
FSMC access mode and reset
typedef Fsmc16BitAccessMode<FsmcBank1NorSram1> LcdAccessMode; typedef ILI9327_400x240_Portrait_262K<LcdAccessMode> LcdPanel; LcdAccessMode *_accessMode; LcdPanel *_gl; Font *_font; public: void run() { // reset is on PE1 and RS (D/CX) is on PD11 GpioE<DefaultDigitalOutputFeature<1> > pe; GpioD<DefaultFsmcAlternateFunctionFeature<11>> pd; // set up the FSMC with RS=A16 (PD11) #if defined(STM32PLUS_F1) Fsmc8080LcdTiming fsmcTiming(0,2); #elif defined(STM32PLUS_F4) Fsmc8080LcdTiming fsmcTiming(4,10); #endif _accessMode=new LcdAccessMode(fsmcTiming,16,pe[1]);
We use an ‘access-mode’ class to control how we communicate with the panel. Here we initialise it to use the FSMC in 16-bit mode with A16 as the RS line (PD11). We will also be wiring up the panel’s RESET line to PE1.
We declare an Fsmc8080Lcdtiming object that takes care of the timing details. The two parameters are the address setup and data setup times in HCLK cycles. At full speed the STM32F1 has a 36MHz FSMC bus and the STM32F4 has a 60MHz bus. Therefore the timings may be different for each MCU if the bus is faster than the panel.
To determine the value of these parameters you need your panel’s data sheet and the equations in the ST Microelectronics application note AN2790 (google it). Alternatively you can just start with some large-ish numbers and decrease until the timings get too tight and the panel doesn’t respond!
Declare a panel
// declare a panel _gl=new LcdPanel(*_accessMode);
Now that we have an access mode we can declare the panel. The constructor will ask the access mode class to reset the panel and then it will do the initialisation sequence.
Our example initialises it in portrait mode, 18 bit colour (262K). If you take a look at TftInterfaces.h you will see that following modes are available:
ILI9327_400x240_Portrait_64K ILI9327_400x240_Landscape_64K ILI9327_400x240_Portrait_262K ILI9327_400x240_Landscape_262K
The predefined drivers are just C++ typedefs that bring together the necessary combination of template instantiations to create a coherent graphics library. If you have an ILI9327 panel that is not 400×240 then you can support it by supplying your own ILI9327400x240PanelTraits class. See the code for details.
Gamma correction
// apply gamma settings ILI9327Gamma gamma(0,0x10,0,0,3,0,0,1,7,5,5,0x25,0,0,0); _gl->applyGamma(gamma);
Gamma correction involves the manual adjustment of the panel’s response to different shades of grey to compensate for differences in the manufacturing process. Every panel will be slightly different.
Setting up the tweaks to the gamma curve are optional, a reasonable display will be obtained by using the default linear curve but if true-to-life colours are a requirement then a custom gamma curve is essential.
stm32plus includes an interactive gamma adjustment application, and that will be introduced in a future blog post. For now, you can just use the default settings and maybe come back to it later.
Backlight
// create the backlight on timer4, channel2 (PD13) DefaultBacklight backlight; // fade up to 100% in 4ms steps backlight.fadeTo(100,4);
stm32plus comes with a PWM backlight controller template class, and a subclass of that called DefaultBacklight that assumes you can connect the backlight regulator to PD13.
This PWM output should be used to drive the base of suitable transistor, or the ‘enable’ pin of a constant current backlight driver. It should not be used to directly power the backlight because it’s likely to draw too much current from the MCU pin.
The fadeTo method allows smooth transitioning of the backlight between levels to give a pleasing user experience.
Font selection
// create a font _font=new Font_VOLTER__28GOLDFISH_299; *_gl << *_font;
stm32plus (as of 1.2.0) now supports the very convenient stream ‘<<' operator to output text and numbers to the display. To do this the operator needs to know which font to use, and we do that by using the << operator with the font object as the operand.
Get the source code
Updated source code is in stm32plus 2.0.0 (or later) available from my downloads page.
Watch the videos
I’ve uploaded a pair of short videos that shows the demo code in action. Firstly, here it is on the STM32F103.
Secondly, here it is on the STM32F4 Discovery board.