Jump to content

TFT Display ILI9325 Driver with ARM M3 Core (STM32F103)


Recommended Posts

Posted

Hi all,

I am using the STM32F103 board with 240x320 TFT (ILI9325). I use uGFX in GOS port mode.

In file board_ILI9325.h, I use the STM stdperiph library functions to control ILI9325 peripheral.

But I have a small problem.

With this code there is no display.

int main(void)
{
coord_t width, height;

/* Setup STM32 system (clock, PLL and Flash configuration) */
SystemInit();

gfxInit();

// Get the screen size
width = gdispGetWidth();
height = gdispGetHeight();

gdispClear(Green);

while(TRUE)
{
gfxSleepMilliseconds(1000); // Wait 1 Sec;
}
}

board_ILI9325.h

static inline void init_board(GDisplay *g) {
// As we are not using multiple displays we set g->board to NULL as we don't use it.
g->board = 0;

switch(g->controllerdisplay)
{
case 0: // Set up for Display 0
LCD_Init();
break;
}
}
static inline void write_index(GDisplay *g, uint16_t index) {
(void) g;

LCD_CS = 0;
LCD_RS = 0;
GPIOC->ODR = (GPIOC->ODR & 0xff00) | (index & 0x00ff);
GPIOB->ODR = (GPIOB->ODR & 0x00ff) | (index & 0xff00);
LCD_WR = 0;
LCD_WR = 1;
LCD_CS = 1;
}

static inline void write_data(GDisplay *g, uint16_t data) {
(void) g;

LCD_CS = 0;
LCD_RS = 1;
GPIOC->ODR = (GPIOC->ODR & 0xff00) | (data & 0x00ff);
GPIOB->ODR = (GPIOB->ODR & 0x00ff) | (data & 0xff00);
LCD_WR = 0;
LCD_WR = 1;
LCD_CS = 1;
}

But with this code, background display is green !

int main(void)
{
coord_t width, height;

/* Setup STM32 system (clock, PLL and Flash configuration) */
SystemInit();

gfxInit();
LCD_Init();

// Get the screen size
width = gdispGetWidth();
height = gdispGetHeight();

gdispClear(Green);

while(TRUE)
{
gfxSleepMilliseconds(1000); // Wait 1 Sec;
}
}

Posted

in init_board(GDisplay *g)

"g"	0x2000085c	
g {...}
Width 0
Height 0
Orientation GDISP_ROTATE_0
Powermode powerOff
Backlight 0
Contrast 0
priv 0x00000000
board 0x00000000
systemdisplay 0
controllerdisplay 0
flags 0
clipx0 0
clipy0 0
clipx1 0
clipy1 0
p {...}
x 0
y 0
cx 0
cy 0
x1 0
y1 0
x2 0
y2 0
color 0
ptr 0x00000000

in gdispGClear(GDisplay *g, color_t color)

"g"	0x2000085c	
g {...}
Width 240
Height 320
Orientation GDISP_ROTATE_0
Powermode powerOn
Backlight 'd'
Contrast '2'
priv 0x00000000
board 0x00000000
systemdisplay 0
controllerdisplay 0
flags 0
clipx0 0
clipy0 0
clipx1 240
clipy1 320
p {...}
x 0
y 0
cx 240
cy 320
x1 0
y1 0
x2 0
y2 0
color 0
ptr 0x00000000

Posted

So when I understand you correctly, when you manually issue the LCD_Init() routine after gfxInit(), you still use your board file to write the data, right? Do you still have the LCD_Init() routine within the init_board() routine? If not, can you check if the code in LCD_Init() does actually get executed when init_board() is being called? Does it get stuck somewhere?

Sorry, I have no clue what the problem is yet - maybe inmarket will have an idea.

~ Tectu

Posted

when you manually issue the LCD_Init() routine after gfxInit(), you still use your board file to write the data, right?

Yes.

Do you still have the LCD_Init() routine within the init_board() routine?

Yes. LCD_Init() get executed when init_board() is being called.

static inline void init_board(GDisplay *g) {
// As we are not using multiple displays we set g->board to NULL as we don't use it.
g->board = 0;

switch(g->controllerdisplay)
{
case 0: // Set up for Display 0
LCD_Init();
break;
}
}

Posted

I suspect one of three things...

1. It is requiring LCD_Init() to be called twice.

2. There is a timing issue and calling it later saves the day, or

3. There is some difference in the IO state between the two locations.

I would try putting a delay before and after LCD_Init() in the board file, checking the LCD_Init() is actually being called, putting a double call to LCD_Init() in the board file (possibly with a decent delay between them).

Another thing to try is to ensure the uGFX startup logo is turned on and see if that displays or you at least get the delay associated with its display (a few seconds I think).

Hope that helps.

Posted

Another quick thought...

uGFX assumes that the .bss segment has been cleared to zero as per the C standard. The .bss segment stores variables that have not been explicitly initialised in your code at compile time. This task is usually performed by the C runtime before reaching your main function.

It is however possible that because you have no C runtime library that your cpu startup code is not clearing the .bss segment. That will cause all sorts of strange issues.

This is one of those embedded development traps.

Posted
uGFX assumes that the .bss segment has been cleared to zero as per the C standard. The .bss segment stores variables that have not been explicitly initialised in your code at compile time. This task is usually performed by the C runtime before reaching your main function.

It is however possible that because you have no C runtime library that your cpu startup code is not clearing the .bss segment. That will cause all sorts of strange issues.

This is one of those embedded development traps.

We should start a wiki article about these traps in the 'Deverlopers Guide' section.

~ Tectu

Posted

I use base C library

-mcpu=cortex-m3; -mthumb; -g; -nostartfiles; -Map=Test_uGFX.map; -O0; --gc-sections; -lm; -lgcc; -lc; -L${linkdir}; -T${linkdir}/arm-gcc-link.ld;

If I do

static inline void init_board(GDisplay *g) {
(void) g;
}

static inline void post_init_board(GDisplay *g) {
(void) g;
LCD_Init();
}

with no call to LCD_Init() in main program, I see uGFX startup logo & background display is green

Strange Not ?

Posted

I do not understand what I need to do more that inmarket has suggested.

I directly put LCD_Init() code into post_init_board().

board_ILI9325.h

/*
* This file is subject to the terms of the GFX License. If a copy of
* the license was not distributed with this file, you can obtain one at:
*
* http://ugfx.org/license.html
*/

#ifndef GDISP_LLD_BOARD_H
#define GDISP_LLD_BOARD_H

#include

#define _STDINT_H


// IO port operations macro definition
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
// LCD control pin configuration
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n)

#define LCD_CS PCout(8)
#define LCD_RS PCout(9)
#define LCD_WR PCout(10)
#define LCD_RD PCout(11)


#include "stm32f10x.h"

static inline void write_index(GDisplay *g, uint16_t index);
static inline void write_data(GDisplay *g, uint16_t data);
static inline void write_reg(GDisplay *g, uint16_t reg, uint16_t data);
static inline uint16_t read_reg(GDisplay *g, uint16_t reg);

static inline void init_board(GDisplay *g) {
(void) g;
}

static inline void post_init_board(GDisplay *g) {
(void) g;
GPIO_InitTypeDef GPIO_InitStructure;
uint16_t DeviceCode;

// Open the clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC,ENABLE);

// Configuration data IO connected to GPIOB
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11
| GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // Push-pull output
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // Output IO ports for maximum speed 50MHZ
GPIO_Init(GPIOB, &GPIO_InitStructure);

// Configuration Control IO connected to PD12.PD13.PD14.PD15/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3
| GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7
| GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);

write_reg(g, 0x0000, 0x0001);
gfxSleepMilliseconds(50);
DeviceCode = read_reg(g, 0x0000); //LCD_ReadReg(0x0000);

// Check if the LCD is ILI9325 Controller
if(DeviceCode == 0x9325 || DeviceCode == 0x9328)
{
write_reg(g,0x00e5,0x78F0);

/* Start Initial Sequence ------------------------------------------------*/
write_reg(g,0x0001,0x0100); // Driver output control (makes it scan top left to bottom right, odd for top half, even for bottom)
write_reg(g,0x0002,0x0700); // Line inversion on the LCD Driving Wave Control
write_reg(g,0x0003,0x1030); // Entry mode set. Set GRAM write direction and BGR=1.
write_reg(g,0x0004,0x0000); // Resizing register

write_reg(g,0x0008,0x0202); // (Display control 2) Set 2 lines for both front and back porch (minimum number, helps VSYNC)
write_reg(g,0x0009,0x0000); // (Display control 3) Sets 0 frame scan cycle and normal scan. Set non-display area refresh cycle ISC[3]
write_reg(g,0x000a,0x0000); // FMARK signal function
write_reg(g,0x000c,0x0001); // 16-bit RGB interface (1 transfer/pixel), using internal system clock and system interface
write_reg(g,0x000d,0x0000); // Frame Marker at position 0 (starts outputting frame right away)
write_reg(g,0x000f,0x0000); // RGB interface polarit. Data input on rising edge of DOTCLK, data written when ENABLE=0,HSPL/VSPL(H/VSYNC pins) Low polarit = active

/* Power On sequence -----------------------------------------------------*/
write_reg(g,0x0010,0x0000); // (Power control 1) Enable power supply
write_reg(g,0x0011,0x0007); // (Power control 2) Ref. voltage(VciOUT) = Vci1, operating frequency = Fosc in circuit 1 and Fosc/16 in circuit 2
write_reg(g,0x0012,0x0000); // (Power control 3) Enable VGL output, use internal electric volume(VCM) to set VcomH(Vcom center voltage level
write_reg(g,0x0013,0x0000); // (Power control 4) VCOM Amplitude = VREG1OUT x 0.90
write_reg(g,0x0007,0x0001); // (Display control 1) Set display to operate, VCOM output to GND
gfxSleepMilliseconds(50); // Delay 50ms
write_reg(g,0x0010,0x1690); // (Power control 1) Enable power supply(0x10c0)
write_reg(g,0x0011,0x0227);
gfxSleepMilliseconds(50);
write_reg(g,0x0012,0x009d);
gfxSleepMilliseconds(50);
write_reg(g,0x0013,0x1900); // (Power control 4) VCOM Amplitude = VREG1OUT x 0.90
write_reg(g,0x0029,0x0025); // (Power control 7) VcomH voltage = VREG1OUT x .69
write_reg(g,0x002b,0x000d); // ??Hz frame rate, internal resistor.
gfxSleepMilliseconds(50);
write_reg(g,0x0020,0x0000); // GRAM horizontal Address
write_reg(g,0x0021,0x0000); // GRAM Vertical Address */
gfxSleepMilliseconds(50);

write_reg(g,0x0030,0x0007);
write_reg(g,0x0031,0x0303);
write_reg(g,0x0032,0x0003);
write_reg(g,0x0035,0x0206);
write_reg(g,0x0036,0x0008);
write_reg(g,0x0037,0x0406);
write_reg(g,0x0038,0x0304);
write_reg(g,0x0039,0x0007);
write_reg(g,0x003c,0x0602);
write_reg(g,0x003d,0x0008);
gfxSleepMilliseconds(50);
write_reg(g,0x0050,0x0000); // Set X Star. X starts at 0.
write_reg(g,0x0051,0x00ef); // Set X End. X ends at 239.
write_reg(g,0x0052,0x0000); // Set Y Star. Y starts at 0.
write_reg(g,0x0053,0x013f); // Set Y End. Y ends at 319.

write_reg(g,0x0060,0xa700); // (Driver output control) Gate scans at 0, ends after 320 lines
write_reg(g,0x0061,0x0001); // (Driver output control) grayscale inversion.
write_reg(g,0x006a,0x0000); // (Vertical scroll control) not used
write_reg(g,0x0080,0x0000); // Display position for partial image 1
write_reg(g,0x0081,0x0000); // RAM address for start of partial image 1
write_reg(g,0x0082,0x0000); // RAM address for end of partial image 1
write_reg(g,0x0083,0x0000); // Display position for partial image 2
write_reg(g,0x0084,0x0000); // RAM address for start of partial image 2
write_reg(g,0x0085,0x0000); // RAM address for end of partial image 2

write_reg(g,0x0090,0x0010); // (Panel interface control 1) Sets clock number for internal clock
write_reg(g,0x0092,0x0600); // (Panel interface control 2) 0 Clocks of overlap when synced

write_reg(g,0x0007,0x0133); // (Display control 1) Set display to operate, base image display, normal display
}
}

static inline void setpin_reset(GDisplay *g, bool_t state) {
(void) g;
(void) state;
}

static inline void set_backlight(GDisplay *g, uint8_t percent) {
(void) g;
(void) percent;
}

static inline void acquire_bus(GDisplay *g) {
(void) g;
}

static inline void release_bus(GDisplay *g) {
(void) g;
}

static inline void write_index(GDisplay *g, uint16_t index) {
(void) g;

LCD_CS = 0;
LCD_RS = 0;
GPIOC->ODR = (GPIOC->ODR & 0xff00) | (index & 0x00ff);
GPIOB->ODR = (GPIOB->ODR & 0x00ff) | (index & 0xff00);
LCD_WR = 0;
LCD_WR = 1;
LCD_CS = 1;
}

static inline void write_data(GDisplay *g, uint16_t data) {
(void) g;

LCD_CS = 0;
LCD_RS = 1;
GPIOC->ODR = (GPIOC->ODR & 0xff00) | (data & 0x00ff);
GPIOB->ODR = (GPIOB->ODR & 0x00ff) | (data & 0xff00);
LCD_WR = 0;
LCD_WR = 1;
LCD_CS = 1;
}

static inline void write_reg(GDisplay *g, uint16_t reg, uint16_t data)
{
write_index(g, reg);
write_data(g, data);
}

static inline uint16_t read_reg(GDisplay *g, uint16_t reg)
{
uint16_t value;

write_index(g, reg);

// configure pins for reading
GPIOB->CRH = (GPIOB->CRH & 0x00000000) | 0x44444444;
GPIOC->CRL = (GPIOC->CRL & 0x00000000) | 0x44444444;
LCD_CS = 0;
LCD_RS = 1;
LCD_RD = 0;
value = ((GPIOB->IDR&0xff00)|(GPIOC->IDR&0x00ff));
LCD_RD = 1;
LCD_CS = 1;

// reconfigure back to normal operation
GPIOB->CRH = (GPIOB->CRH & 0x00000000) | 0x33333333;
GPIOC->CRL = (GPIOC->CRL & 0x00000000) | 0x33333333;

return value;
}
static inline void setreadmode(GDisplay *g) {
(void) g;
}

static inline void setwritemode(GDisplay *g) {
(void) g;
}

static inline uint16_t read_data(GDisplay *g) {
(void) g;
return 0;
}

#endif /* GDISP_LLD_BOARD_H */

Posted

You are NOT supposed to manipulate any register values of the controller in the board file. The only thing that you are supposed to do in there is to set up the GPIO modes and stuff like that.

For starting your post_init_board() routine should be empty and your init_board() should contain all the GPIO code.

Your board file should look something like this (just so you have a structural idea - this will not compile):


/*
* This file is subject to the terms of the GFX License. If a copy of
* the license was not distributed with this file, you can obtain one at:
*
* http://ugfx.org/license.html
*/

#ifndef GDISP_LLD_BOARD_H
#define GDISP_LLD_BOARD_H

static inline void init_board(GDisplay *g) {
(void) g;

GPIO_InitTypeDef GPIO_InitStructure;

// Open the clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC,ENABLE);

// Configuration data IO connected to GPIOB
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11
| GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // Push-pull output
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // Output IO ports for maximum speed 50MHZ
GPIO_Init(GPIOB, &GPIO_InitStructure);

// Configuration Control IO connected to PD12.PD13.PD14.PD15/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3
| GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7
| GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}

static inline void post_init_board(GDisplay *g) {
(void) g;
}

static inline void setpin_reset(GDisplay *g, bool_t state) {
(void) g;
(void) state;
}

static inline void set_backlight(GDisplay *g, uint8_t percent) {
(void) g;
(void) percent;
}

static inline void acquire_bus(GDisplay *g) {
(void) g;
}

static inline void release_bus(GDisplay *g) {
(void) g;
}

static inline void write_index(GDisplay *g, uint16_t index) {
(void) g;

LCD_CS = 0;
LCD_RS = 0;
GPIOC->ODR = (GPIOC->ODR & 0xff00) | (index & 0x00ff);
GPIOB->ODR = (GPIOB->ODR & 0x00ff) | (index & 0xff00);
LCD_WR = 0;
LCD_WR = 1;
LCD_CS = 1;
}

static inline void write_data(GDisplay *g, uint16_t data) {
(void) g;

LCD_CS = 0;
LCD_RS = 1;
GPIOC->ODR = (GPIOC->ODR & 0xff00) | (data & 0x00ff);
GPIOB->ODR = (GPIOB->ODR & 0x00ff) | (data & 0xff00);
LCD_WR = 0;
LCD_WR = 1;
LCD_CS = 1;
}

static inline void setreadmode(GDisplay *g) {
(void) g;
}

static inline void setwritemode(GDisplay *g) {
(void) g;
}

static inline uint16_t read_data(GDisplay *g) {
(void) g;
return 0;
}

#endif /* GDISP_LLD_BOARD_H */

~ Tectu

Posted

Now we hit a different problem: We have many issues with the ILI based display drivers because they exist in many different versions. Often you get an ILI9320 instead of an ILI9325 and vice-versa. In order to finally get your display running, please take the initialization code (that are all the write_reg commands in your LCD_Init() routine and replace the ones in the drivers initialization routine (gdisp_lld_init() routine in /drivers/gdisp/ILI9325/gdisp_lld_ILI9325.c) with them.

Let us know when you have any questions.

We are sorry for this inconvenience but we have not found a good solution to this problem yet. We will add an optional custom initialization code interface in the future.

~ Tectu

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...