GLCD 128×64 ST7920 interfacing with STM32

Today in this tutorial, we will interface ST7920 GLCD (128×64) display with STM32 using the Serial mode. This way we only have to use 4 pins from the microcontroller to control the entire display.

We will start with displaying simple texts, than we will look into some bitmaps and finally we will draw some shapes on the LCD. I am using STM32F103C8 microcontroller.

Setup

I will start this tutorial by setting up the CubeMx. In Cube Mx, for the connection purpose, we only need to select 4 pin as output as shown below


Also some of the delays will be required in the microseconds, and so, I will use the TIMER 1 to create delays in microseconds. The setup is shown below. I already did a tutorial on this. You can check it HERE

This is all the setup, that is needed for the ST7920. Let’s see some functions that we are going to use to interface this display.



Some Insight into the CODE

void SendByteSPI(uint8_t byte)

Sends a byte to the MOSI / SID Pin. This is Just like How the SPI transfers the data.


Sending Command

void ST7920_SendCmd (uint8_t cmd)
{

	HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET);  // PUll the CS high

	SendByteSPI(0xf8+(0<<1));  // send the SYNC + RS(0)
	SendByteSPI(cmd&0xf0);  // send the higher nibble first
	SendByteSPI((cmd<<4)&0xf0);  // send the lower nibble
	delay_us(50);

	HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET);  // PUll the CS LOW

}

According to the datasheet of ST7920, In order to send a command to the display, We need to perform the following operations:-
1. Pull the CS Pin HIGH
2. Send the Synchronizing byte (0xF8) along with R/W and RS
3. Send the Higher command Byte
4. Send the Lower command Byte
5. Pull the CS Pin LOW to end the transfer


Sending Data

void ST7920_SendData (uint8_t data)
{

	HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET);  // PUll the CS high

	SendByteSPI(0xf8+(1<<1));  // send the SYNC + RS(1)
	SendByteSPI(data&0xf0);  // send the higher nibble first
	SendByteSPI((data<<4)&0xf0);  // send the lower nibble
	delay_us(50);
	HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET);  // PUll the CS LOW
}

Data is also sent in the same manner as the command, except the RS will be a ‘1’. Following are the operations:-
1. Pull the CS Pin HIGH
2. Send the Synchronizing byte (0xF8) along with R/W and RS
3. Send the Higher data Byte
4. Send the Lower data Byte
5. Pull the CS Pin LOW to end the transfer


Initializing Display

void ST7920_Init (void)
{
	HAL_GPIO_WritePin(RST_PORT, RST_PIN, GPIO_PIN_RESET);  // RESET=0
	HAL_Delay(10);   // wait for 10ms
	HAL_GPIO_WritePin(RST_PORT, RST_PIN, GPIO_PIN_SET);  // RESET=1

	HAL_Delay(50);   //wait for >40 ms


	ST7920_SendCmd(0x30);  // 8bit mode
	delay_us(110);  //  >100us delay

	ST7920_SendCmd(0x30);  // 8bit mode
	delay_us(40);  // >37us delay

	ST7920_SendCmd(0x08);  // D=0, C=0, B=0
	delay_us(110);  // >100us delay

	ST7920_SendCmd(0x01);  // clear screen
	HAL_Delay(12);  // >10 ms delay


	ST7920_SendCmd(0x06);  // cursor increment right no shift
	HAL_Delay(1);  // 1ms delay

	ST7920_SendCmd(0x0C);  // D=1, C=0, B=0
    HAL_Delay(1);  // 1ms delay

	ST7920_SendCmd(0x02);  // return to home
	HAL_Delay(1);  // 1ms delay

}

To initialize the display, we have to follow some sequence of command. And they are mentioned below:
1. Reset the display and wait for more than 40 ms
2. Send the Function Set command and wait for more than 100 us
3. Send the Function Set command and wait for more than 37 us
4. Send the Display ON/OFF Control command and wait for more than 100 us
5. Send the Display Clear command and wait for more than 10 ms
6. Send the Entry Mode set command and this will end the initialization of the display.


Graphic Mode

void ST7920_GraphicMode ()   
{
  ST7920_SendCmd(0x30);  // 8 bit mode
  HAL_Delay (1);
  ST7920_SendCmd(0x34);  // switch to Extended instructions
  HAL_Delay (1);
  ST7920_SendCmd(0x36);  // enable graphics
  HAL_Delay (1);
}

To enable the Graphic mode, we have to
-> Switch to Extended Instruction Set
-> Enable the Graphics
Note that we have to perform both of these operations in separate commands as according to it is mentioned in the datasheet.


Drawing Bitmaps

void ST7920_DrawBitmap(const unsigned char* graphic)
{
	uint8_t x, y;
	for(y = 0; y < 64; y++)
	{
		if(y < 32)
		{
			for(x = 0; x < 8; x++)	/ Draws top half of the screen.
			{			
                 		// In extended instruction mode, vertical and horizontal coordinates must be specified before sending data in.
				ST7920_SendCmd(0x80 | y);	// Vertical coordinate of the screen is specified first. (0-31)
				ST7920_SendCmd(0x80 | x);	// Then horizontal coordinate of the screen is specified. (0-8)
				ST7920_SendData(graphic[2*x + 16*y]);	// Data to the upper byte is sent to the coordinate.
				ST7920_SendData(graphic[2*x+1 + 16*y]);	// Data to the lower byte is sent to the coordinate.
			}
		}
		else
		{
			for(x = 0; x < 8; x++)	// Draws bottom half of the screen.
			{
                           // Actions performed as same as the upper half screen.
			   ST7920_SendCmd(0x80 | (y-32));// Vertical coordinate must be scaled back to 0-31 as it is dealing with another half of the screen.
			  ST7920_SendCmd(0x88 | x);
			  ST7920_SendData(graphic[2*x + 16*y]);
			  ST7920_SendData(graphic[2*x+1 + 16*y]);
			}
		}

	}
}

In order to draw a bitmap, we need to draw on the two half of the screen. First upper half and than the lower half.
To draw, first set the coordinate of the Y axis and than the coordinate of the X axis.
After the coordinates are set, send the upper byte of the data and than the lower byte.



RESULT

Check out the Video Below




Info

You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.

Subscribe
Notify of

42 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
sushma gawde
1 year ago

how to set cursor position at particular row and column

nick
1 year ago

Can we display character other than ASCII? I want to display character of language other than english. Please sugges.t

lp0
2 years ago

Hello! there’s a way to rotate 180 degrees the LCD matrix?? i devenloped a project but didnt notice that i made a 3d case on the wrong direction, and now i cant print anoter case.

ThanhG
2 years ago

Hi, I want to clear pixel. What I have to do?
Pls help me

Adam Hamilton
2 years ago

Just want to share one experience I had.

I came here because I had trouble with my display and couldn’t get it working properly.

I used the default SPI mode that CubeIDE had given me during program creation and it seemed that certain data wouldn’t work properly. I was getting areas where I the display ram was not affected.

I noticed that the SPI was a roll your own type, so I thought maybe the SPI mode was wrong in my setup so I changed it to the following
CPOL=Low
CPHA=2 Edge

It works now so if anyone comes here who has problems if they have used the SPI peripheral, then maybe this can work for you

Grzegorz
3 years ago

Hello. Will this program work with stm32f446re – 180MHz?

Ake
3 years ago

Hello everyone!

I tried at least 4 weeks running this program in the STM32F108 with the LCD 20 x 4 ( type is 12864 V 2.0 ) but the LCD doesn’t show anything!! When I put voltage on or reset the systems, the LCD only flashes one time. I used use CubeIDE, not CubeMX, but think so that is not make any sense make program wrong way….

  • I tried download program many time to the STM-board.
  • Reset card
  • Used some other card of STM32F108
  • Used some other card of LCD ( It was similar 12864 V2.0 )
  • I checked wiring many times….
  • I tried change some time-parameter too….but few time the LCD showed some letters in the screen but only some microsecond.
  • So..I think that some timing problem it should be!

Does somebody else this kind of problems? What should be done for timings?

Ake
Reply to  Ake
3 years ago

One correction: STM32F103C8 is model of card.

Mahdi
Reply to  Ake
1 year ago

Hi Ake,
Yeah I’ve got the same problem. I would appreciate it if you could tell me what you did finally to fix it.
Tnx

Mstf
3 years ago

Can you make Nokia 5110 LCD example?

Zero
3 years ago

ST7920_DrawBitmap(image);
what is image? int or char

Maker Garage
3 years ago

Can change font size?

msj
4 years ago

how can i fix this error?
Error: L6200E: Symbol image multiply defined (by st7920_serial.o and main.o).

truongst
Reply to  admin
3 years ago

uint8_t image[(128 * 64)/8];

Morteza
Reply to  truongst
3 years ago

This is my problem too. I work with keil software

truongst
Reply to  Morteza
3 years ago

Not enough information to list image symbols.
Not enough information to list load addresses in the image map.
I don’t know how to handle it

ambaliya sharda
4 years ago

can i use this library for jhd12864 glcd ?

ambaliya sharda
4 years ago

can i use this library for jhd12864 lcd

Pranay Mohod
4 years ago

How to display wifi scanning gif on lcd with stm32.please suggest me

Pranay Mohod
4 years ago

How do I set Pixels of lcd to make semicircle. Actually I want to make Wifi graphics over LCD,& reset pixels after high strength vice versa.

PRANAY MOHOD
4 years ago

Also Done this but after completing one loop. it only display large triangle actually small triangles overlap over large triangle,
Im also try with increasing delay, but it only display large triangle.Please help me.

while( i < 10)
{
ST7920_GraphicMode(1);
DrawFilledTriangle(60,60,68,60,64,64);// min Triangle Values
ST7920_Update();
HAL_Delay(1000);
DrawFilledTriangle(40,40,88,40,64,64);// mid Triangle Values
ST7920_Update();
HAL_Delay(1000);
DrawFilledTriangle(20,20,108,20,64,64);// Max Triangle Values
ST7920_Update();
HAL_Delay(1000);
ST7920_Clear();
HAL_Delay(1000);
i++;
}

Pranay Mohod
Reply to  admin
4 years ago

I m also Try with this changes , in 1st loop it display perfect but in second loop it only prints large or medium which is greater than small triangle.

Pranay Mohod
4 years ago

Hi Sir, I have a question regarding your library ,
when Im using draw filled triangle in loop from increasing to decreasing order ,It doesnt display smaller filled triangle, only display large triangle, Actually its Clear_St7920 issue, How to solve this problem, Here is my code
while(uint8_t i<10)
{
ST7920_GraphicMode(1);
DrawFilledTriangle(60,60,68,60,64,64);// min searching Values
ST7920_Update();
HAL_Delay(1000);
DrawFilledTriangle(40,40,88,40,64,64);// mid searching Values
ST7920_Update();
HAL_Delay(1000);
DrawFilledTriangle(20,20,108,20,64,64);// Max searching Values
ST7920_Update();
HAL_Delay(1000);
ST7920_GraphicMode(0);
ST7920_Clear();
HAL_Delay(1000);
i++;
}

Last edited 4 years ago by Pranay Mohod
Pranay Mohod
Reply to  admin
4 years ago

Also Done this but after completing one loop. it only display large triangle actually small triangles overlap over large triangle,
Im also try with increasing delay, but it only display large triangle.Please help me.

while( i < 10)
{
ST7920_GraphicMode(1);
DrawFilledTriangle(60,60,68,60,64,64);// min Triangle Values
ST7920_Update();
HAL_Delay(1000);
DrawFilledTriangle(40,40,88,40,64,64);// mid Triangle Values
ST7920_Update();
HAL_Delay(1000);
DrawFilledTriangle(20,20,108,20,64,64);// Max Triangle Values
ST7920_Update();
HAL_Delay(1000);
ST7920_Clear();
HAL_Delay(1000);
i++;
}

ptn
4 years ago

.

John
4 years ago

Hello, would this be compatible for an STM32L432KC. If yes, then how would you initialise the display?

Abhishek Gupta
4 years ago

I used the code for SINDA SDGB12864-02 graphical LCD but it is not showing any DATA on LCD since I do all needful steps to do in stm32f030r8t6 Evaluation board.
I am using Keil v5 for coding.

Abhishek Gupta
Reply to  Abhishek Gupta
4 years ago

Can anyone provide me the solution to use this code with the LCD I had?

Thomas
4 years ago

Hello, in the upper left corner are some randomly set /reset pixel. (in graphical mode) I have the same issue. Does somebody found the reason?

4 years ago

Hi
Code work’s fine on my stm32103c8t6, i’m using stm32cubeide
How i can add & print custom character?

Thomas
Reply to  Rytis
4 years ago

Hi, I step over to use only graphical mode. ther any pixcel can be set/reset. Many font are possible or own chars.
look at:
https://www.mikrocontroller.net/topic/54860
That was very helpful

Thomas
4 years ago

Hello, in the left upper corner are sone random set/reset Pixel – in graphical mode. Did you found the reason? I habe the Sam Problem.

QNgoc
5 years ago

hello guy. Thanks your document very much. but I have a question. I didnt use STM32f1. I used STM32f030C8, and it didnot work. I modified delay function in F0 because F0 on ly have 8M HSI, to create 1us delay. But it seem didnt work. Can you give me any idea?

QNgoc
Reply to  QNgoc
5 years ago

void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV8;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}

QNgoc
Reply to  QNgoc
5 years ago

static void MX_TIM1_Init(void)
{

/* USER CODE BEGIN TIM1_Init 0 */

/* USER CODE END TIM1_Init 0 */

TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};

/* USER CODE BEGIN TIM1_Init 1 */

/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 1-1;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 0xffff-1;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */

/* USER CODE END TIM1_Init 2 */

}