[Revised]Working with STM32 and Graphics LCD: GLCD 128×64 ST7920

In this revised guide, we shall see how interface GLCD 128×64 with STM32 using SPI method (Serial mode).

In this guide, we shall cover the following:

  • GLCD 12864.
  • Interface with STM32.
  • Developing the driver.
  • Code
  • Demo.

1. GLCD 12864:

At first glance, the 128×64 Graphical LCD Module seems like a bigger brother to the famous 16×2 LCD or 20×4 LCD Modules, with their similar construction and almost similar pin layout.

But there is a significant difference between those two. 16×2 or 20×4 LCDs are essentially character displays. They can only display alpha-numeric characters and some simple custom characters that are confined to a 5×8 matrix.

Coming to the 128×64 Graphical LCD, as the name suggests, it is a Graphical Display consisting of 128×64 i.e., 8192 individually controllable dots.

By using different combinations of pixels, we can basically display characters of various sizes. But the magic doesn’t end there. You can display images and graphics (small animations) as well. In a 128×64 LCD Module, there are 64 rows and 128 columns.

ST7920 LCD Controller

There are several versions of the Graphical LCD in the market. Even though the usage, application and implementations are almost identical, the main difference lies in the internal LCD Controller used to drive the dot matrix display.

Some of the commonly used LCD Controllers are KS0108, SSD1306, ST7920, SH1106, SSD1322, etc. The pin out of the final LCD Module might vary depending on the LCD Controller used. So, please verify the LCD Controller as well as the pin out before making a purchase.

The Graphical LCD Module I purchased consists of ST7920 Controller. It is manufactured by Sitronix and supports three types of bus interfaces i.e., 8-bit mode, 4-bit mode and Serial interface.

If you have used 16×2 LCD Display earlier, then you might be familiar with both 4-bit as well as 8-bit parallel interfaces. The serial interface is something new and we will explore this option in this project.

128×64 LCD Pinout

As I already mentioned, double-check with the manufacturer about the pinout of the Graphical LCD Module. The following table describes the pinout of the 128×64 LCD Module that I have.

Pin NumberPin NamePin Description
1GNDGround
2VCCSupply Voltage
3VOContrast Adjust
4RSRegister Select (CS in Serial)
5RWRead / Write Control (Serial Data In)
6EEnable (Serial CLK)
7 – 14D0 – D7Data
15PSBInterface Selection (0: Serial, 1: 8-bit/4-bit Parallel)
16NCNot Connected
17RSTReset
18VOUTLCD Voltage Doubler Output
19BLABacklight LED Anode
20BLKBacklight LED Cathode

2. Interface with STM32:

The connection as following:

STM32F411 Nucleo-64GLCD12864
5VVdd and LED+ (A)
GNDVss, LED- (K) and PSB
PA0RS
PA1RST
PA7 (MOSI)RW
PA5 (SCK)En

3. Developing the driver:

Before we develop the driver, take a look at the following:

  • SPI and how to use it from here.

We start off by creating new spi.h header file.

Within the header file, include the header guard:

#ifndef SPI_H_
#define SPI_H_





#endif /* SPI_H_ */

Include stdint as following:

#include "stdint.h"

Declare the following functions:

void st7920_spi_pins_init();
void st7920_spi_config();
void st7920_spi_transmit(uint8_t *data,uint32_t size);

Hence, the header file as following:

#ifndef SPI_H_
#define SPI_H_

#include "stdint.h"

void st7920_spi_pins_init();
void st7920_spi_config();
void st7920_spi_transmit(uint8_t *data,uint32_t size);

#endif /* SPI_H_ */

Now, create spi.c source file.

Within the source file, include the following:

  • Main spi header file.
  • stm32f4 main header file
#include "spi.h"
#include "stm32f4xx.h"

Fot st7920_spi_pins_init function:

  • Enable clock access to GPIOA.
  • Set PA5, PA6 and PA5 to alternate function and set AF05 (SPI1) as alternate function.
  • Set PA0 and PA1 as general output
void st7920_spi_pins_init()
{
	RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN; //enable clock for GPIOA
	GPIOA->MODER|=GPIO_MODER_MODE0_0;
	GPIOA->MODER&=~GPIO_MODER_MODE0_1;

	GPIOA->MODER|=GPIO_MODER_MODE1_0;
	GPIOA->MODER&=~GPIO_MODER_MODE1_1;

	GPIOA->MODER|=GPIO_MODER_MODE4_0;
	GPIOA->MODER&=~GPIO_MODER_MODE4_1;

	GPIOA->MODER |=(GPIO_MODER_MODE5_1|GPIO_MODER_MODE6_1|GPIO_MODER_MODE7_1);
	GPIOA->MODER &=~(GPIO_MODER_MODE5_0|GPIO_MODER_MODE6_0|GPIO_MODER_MODE7_0);

	#define SPI1_AF 0x05

	GPIOA->AFR[0]|=(SPI1_AF<<GPIO_AFRL_AFSEL5_Pos)|(SPI1_AF<<GPIO_AFRL_AFSEL6_Pos)|(SPI1_AF<<GPIO_AFRL_AFSEL7_Pos);
}

For st7920_spi_config function:

  • Enable clock access to SPI1.
  • Set SPI to master mode.
  • slave management to be software.
  • SPI mode to mode1 (CPHA1 and CPOL0)
  • Enable the module.
void st7920_spi_config()
{
	/*Enable clock access to SPI1 module*/
	RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
	
	/*Set MSB first*/
	SPI1->CR1 &=~ SPI_CR1_LSBFIRST;
	
	/*Set mode to MASTER*/
	SPI1->CR1 |= SPI_CR1_MSTR;
	
	/*Select software slave management by
	 * setting SSM=1 and SSI=1*/
	SPI1->CR1 |= SPI_CR1_SSM;
	SPI1->CR1 |= SPI_CR1_SSI;
	
	/*Set SPI mode to be MODE1 (CPHA1 CPOL0)*/
	SPI1->CR1|=SPI_CR1_CPHA;
	
	/*Enable SPI module*/
	SPI1->CR1 |= SPI_CR1_SPE;
}

For st7920_spi_transmit, it takes two parameters:

  • Pointer to uint8_t array that holds the data to be written to GLCD.
  • Size of the buffer.
void st7920_spi_transmit(uint8_t *data,uint32_t size)
{
	uint32_t i=0;

	while(i<size)
	{
		/*Wait until TXE is set*/
		while(!(SPI1->SR & (SPI_SR_TXE))){}

		/*Write the data to the data register*/
		SPI1->DR = data[i];
		i++;
	}
	/*Wait until TXE is set*/
	while(!(SPI1->SR & (SPI_SR_TXE))){}

	/*Wait for BUSY flag to reset*/
	while((SPI1->SR & (SPI_SR_BSY))){}

	/*Clear OVR flag*/
	(void)SPI1->DR;
	(void)SPI1->SR;
}

Hence, the source file as following:

#include "spi.h"
#include "stm32f4xx.h"


void st7920_spi_pins_init()
{
	RCC->AHB1ENR|=RCC_AHB1ENR_GPIOAEN; //enable clock for GPIOA
	GPIOA->MODER|=GPIO_MODER_MODE0_0;
	GPIOA->MODER&=~GPIO_MODER_MODE0_1;

	GPIOA->MODER|=GPIO_MODER_MODE1_0;
	GPIOA->MODER&=~GPIO_MODER_MODE1_1;

	GPIOA->MODER|=GPIO_MODER_MODE4_0;
	GPIOA->MODER&=~GPIO_MODER_MODE4_1;

	GPIOA->MODER |=(GPIO_MODER_MODE5_1|GPIO_MODER_MODE6_1|GPIO_MODER_MODE7_1);
	GPIOA->MODER &=~(GPIO_MODER_MODE5_0|GPIO_MODER_MODE6_0|GPIO_MODER_MODE7_0);

	#define SPI1_AF 0x05

	GPIOA->AFR[0]|=(SPI1_AF<<GPIO_AFRL_AFSEL5_Pos)|(SPI1_AF<<GPIO_AFRL_AFSEL6_Pos)|(SPI1_AF<<GPIO_AFRL_AFSEL7_Pos);
}

void st7920_spi_config()
{
	/*Enable clock access to SPI1 module*/
	RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
	
	/*Set MSB first*/
	SPI1->CR1 &=~ SPI_CR1_LSBFIRST;
	
	/*Set mode to MASTER*/
	SPI1->CR1 |= SPI_CR1_MSTR;
	
	/*Select software slave management by
	 * setting SSM=1 and SSI=1*/
	SPI1->CR1 |= SPI_CR1_SSM;
	SPI1->CR1 |= SPI_CR1_SSI;
	
	/*Set SPI mode to be MODE1 (CPHA1 CPOL0)*/
	SPI1->CR1|=SPI_CR1_CPHA;
	
	/*Enable SPI module*/
	SPI1->CR1 |= SPI_CR1_SPE;
}

void st7920_spi_transmit(uint8_t *data,uint32_t size)
{
	uint32_t i=0;

	while(i<size)
	{
		/*Wait until TXE is set*/
		while(!(SPI1->SR & (SPI_SR_TXE))){}

		/*Write the data to the data register*/
		SPI1->DR = data[i];
		i++;
	}
	/*Wait until TXE is set*/
	while(!(SPI1->SR & (SPI_SR_TXE))){}

	/*Wait for BUSY flag to reset*/
	while((SPI1->SR & (SPI_SR_BSY))){}

	/*Clear OVR flag*/
	(void)SPI1->DR;
	(void)SPI1->SR;
}

Thats all for the SPI section.

Now create new header file with name of ST7920.h .

With the header file, include the following:

#include "stdint.h"

Declare the following functions:

void ST7920_SendString(int row, int col, char* string);
void ST7920_GraphicMode (int enable) ;
void ST7920_DrawBitmap(const unsigned char* graphic);
void ST7920_Update(void);
void ST7920_Clear();
void ST7920_init();
void DrawLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1);
void DrawRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
void DrawFilledRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
void DrawCircle(uint8_t x0, uint8_t y0, uint8_t radius);
void DrawFilledCircle(int16_t x0, int16_t y0, int16_t r);
void DrawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3);
void DrawFilledTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3);

Hence the entire header file as following:

#ifndef ST7920_H_
#define ST7920_H_

#include "stdint.h"

void ST7920_SendString(int row, int col, char* string);
void ST7920_GraphicMode (int enable) ;
void ST7920_DrawBitmap(const unsigned char* graphic);
void ST7920_Update(void);
void ST7920_Clear();
void ST7920_init();
void DrawLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1);
void DrawRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
void DrawFilledRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
void DrawCircle(uint8_t x0, uint8_t y0, uint8_t radius);
void DrawFilledCircle(int16_t x0, int16_t y0, int16_t r);
void DrawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3);
void DrawFilledTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3);


#endif /* ST7920_H_ */

Now, create new source file with name of ST7920.c.

Within the source, include the following:

#include "ST7920.h"
#include "spi.h"
#include "delay.h"

Then define some macros for CS and RST pins as following:

#define CS_LOW 		GPIOA->BSRR=GPIO_BSRR_BR0
#define CS_HIGH		GPIOA->BSRR=GPIO_BSRR_BS0

#define RST_LOW 	GPIOA->BSRR=GPIO_BSRR_BR1
#define RST_HIGH	GPIOA->BSRR=GPIO_BSRR_BS1

Array to hold the data to be sent out to the display:

uint8_t lcd_data[3];

Variables to hold the display control:

uint8_t startRow, startCol, endRow, endCol; // coordinates of the dirty rectangle
uint8_t numRows = 64;
uint8_t numCols = 128;
uint8_t Graphic_Check = 0;
uint8_t image[(128 * 64)/8];

Some functions required for the operation:

#define LCD_CLS         0x01
#define LCD_HOME        0x02
#define LCD_ADDRINC     0x06
#define LCD_DISPLAYON   0x0C
#define LCD_DISPLAYOFF  0x08
#define LCD_CURSORON    0x0E
#define LCD_CURSORBLINK 0x0F
#define LCD_BASIC       0x30
#define LCD_EXTEND      0x34
#define LCD_GFXMODE     0x36
#define LCD_TXTMODE     0x34
#define LCD_STANDBY     0x01
#define LCD_SCROLL      0x03
#define LCD_SCROLLADDR  0x40
#define LCD_ADDR        0x80
#define LCD_LINE0       0x80
#define LCD_LINE1       0x90
#define LCD_LINE2       0x88
#define LCD_LINE3       0x98

In order to send data/command, the datasheet clearly states the following:

  • Set CS pin to high.
  • Send 5bits of 1 to sync the display.
  • Send RW and RS state.
  • Followed by zero.
  • Then send data/command as two byte as shown in figure below:

Hence, the send data command functions as following which have been defined as static:

static void ST7920_SendCmd (uint8_t cmd)
{
	CS_HIGH;  // PUll the CS high

	lcd_data[0]=0xF8;
	lcd_data[1]=(cmd&0xf0);
	lcd_data[2]=((cmd<<4)&0xf0);

	st7920_spi_transmit(lcd_data,3);

	delayuS(30);

	CS_LOW;  // PUll the CS LOW

}

static void ST7920_SendData (uint8_t data)
{

	CS_HIGH;

	lcd_data[0]=0xFA;
	lcd_data[1]=(data&0xf0);
	lcd_data[2]=((data<<4)&0xf0);

	st7920_spi_transmit(lcd_data,3);

	delayuS(30);

	CS_LOW;  // PUll the CS LOW
}

The delay will allow the GLCD to process the data since it doesn’t have internal buffer to store the data.

For the GLCD initializing sequence:

  • Wait at least 40ms to ensure that GLCD is powered up
  • Reset the GLCD by setting the reset pin low for 40 ms, then high.
  • Send the initializing command.
void ST7920_init()
{
	delay(100);
	st7920_spi_pins_init();
	st7920_spi_config();

	RST_LOW;
	delay(50);
	RST_HIGH;
	delay(100);
	ST7920_SendCmd(0x30);  		// 8bit mode
	delayuS(110);  				//  >100us delay

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

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

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


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

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

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

}

Functions that allow us to display shapes, text pictures etc:

void ST7920_SendString(int row, int col, char* string)
{
    switch (row)
    {
        case 0:
            col |= 0x80;
            break;
        case 1:
            col |= 0x90;
            break;
        case 2:
            col |= 0x88;
            break;
        case 3:
            col |= 0x98;
            break;
        default:
            col |= 0x80;
            break;
    }

    ST7920_SendCmd(col);

    while (*string)
    	{
    		ST7920_SendData(*string++);
    	}
}



// switch to graphic mode or normal mode::: enable = 1 -> graphic mode enable = 0 -> normal mode

void ST7920_GraphicMode (int enable)   // 1-enable, 0-disable
{
	if (enable == 1)
	{
		ST7920_SendCmd(0x30);  // 8 bit mode
		delay (1);
		ST7920_SendCmd(0x34);  // switch to Extended instructions
		delay (1);
		ST7920_SendCmd(0x36);  // enable graphics
		delay (1);
		Graphic_Check = 1;  // update the variable
	}

	else if (enable == 0)
	{
		ST7920_SendCmd(0x30);  // 8 bit mode
		delay (1);
		Graphic_Check = 0;  // update the variable
	}
}

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]);
			}
		}

	}
}


// Update the display with the selected graphics
void ST7920_Update(void)
{
	ST7920_DrawBitmap(image);
}



void ST7920_Clear()
{
	if (Graphic_Check == 1)  // if the graphic mode is set
	{
		uint8_t x, y;
		for(y = 0; y < 64; y++)
		{
			if(y < 32)
			{
				ST7920_SendCmd(0x80 | y);
				ST7920_SendCmd(0x80);
			}
			else
			{
				ST7920_SendCmd(0x80 | (y-32));
				ST7920_SendCmd(0x88);
			}
			for(x = 0; x < 8; x++)
			{
				ST7920_SendData(0);
				ST7920_SendData(0);
			}
		}
	}

	else
	{
		ST7920_SendCmd(0x01);   // clear the display using command
		delay(20); // delay >1.6 ms
	}
}




static void SetPixel(uint8_t x, uint8_t y)
{
  if (y < numRows && x < numCols)
  {
    uint8_t *p = image + ((y * (numCols/8)) + (x/8));
    *p |= 0x80u >> (x%8);

    *image = *p;

    // Change the dirty rectangle to account for a pixel being dirty (we assume it was changed)
    if (startRow > y) { startRow = y; }
    if (endRow <= y)  { endRow = y + 1; }
    if (startCol > x) { startCol = x; }
    if (endCol <= x)  { endCol = x + 1; }


  }

}

/* draw a line
 * start point (X0, Y0)
 * end point (X1, Y1)
 */
void DrawLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1)
{
  int dx = (x1 >= x0) ? x1 - x0 : x0 - x1;
  int dy = (y1 >= y0) ? y1 - y0 : y0 - y1;
  int sx = (x0 < x1) ? 1 : -1;
  int sy = (y0 < y1) ? 1 : -1;
  int err = dx - dy;

  for (;;)
  {
    SetPixel(x0, y0);
    if (x0 == x1 && y0 == y1) break;
    int e2 = err + err;
    if (e2 > -dy)
    {
      err -= dy;
      x0 += sx;
    }
    if (e2 < dx)
    {
      err += dx;
      y0 += sy;
    }
  }
}




/* Draw rectangle
 * start point (x,y)
 * w -> width
 * h -> height
 */
void DrawRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
	/* Check input parameters */
	if (
		x >= numCols ||
		y >= numRows
	) {
		/* Return error */
		return;
	}

	/* Check width and height */
	if ((x + w) >= numCols) {
		w = numCols - x;
	}
	if ((y + h) >= numRows) {
		h = numRows - y;
	}

	/* Draw 4 lines */
	DrawLine(x, y, x + w, y);         /* Top line */
	DrawLine(x, y + h, x + w, y + h); /* Bottom line */
	DrawLine(x, y, x, y + h);         /* Left line */
	DrawLine(x + w, y, x + w, y + h); /* Right line */
}




/* Draw filled rectangle
 * Start point (x,y)
 * w -> width
 * h -> height
 */
void DrawFilledRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
	uint8_t i;

	/* Check input parameters */
	if (
		x >= numCols ||
		y >= numRows
	) {
		/* Return error */
		return;
	}

	/* Check width and height */
	if ((x + w) >= numCols) {
		w = numCols - x;
	}
	if ((y + h) >= numRows) {
		h = numRows - y;
	}

	/* Draw lines */
	for (i = 0; i <= h; i++) {
		/* Draw lines */
		DrawLine(x, y + i, x + w, y + i);
	}
}




/* draw circle
 * centre (x0,y0)
 * radius = radius
 */
void DrawCircle(uint8_t x0, uint8_t y0, uint8_t radius)
{
  int f = 1 - (int)radius;
  int ddF_x = 1;

  int ddF_y = -2 * (int)radius;
  int x = 0;

  SetPixel(x0, y0 + radius);
  SetPixel(x0, y0 - radius);
  SetPixel(x0 + radius, y0);
  SetPixel(x0 - radius, y0);

  int y = radius;
  while(x < y)
  {
    if(f >= 0)
    {
      y--;
      ddF_y += 2;
      f += ddF_y;
    }
    x++;
    ddF_x += 2;
    f += ddF_x;
    SetPixel(x0 + x, y0 + y);
    SetPixel(x0 - x, y0 + y);
    SetPixel(x0 + x, y0 - y);
    SetPixel(x0 - x, y0 - y);
    SetPixel(x0 + y, y0 + x);
    SetPixel(x0 - y, y0 + x);
    SetPixel(x0 + y, y0 - x);
    SetPixel(x0 - y, y0 - x);
  }
}


// Draw Filled Circle

void DrawFilledCircle(int16_t x0, int16_t y0, int16_t r)
{
	int16_t f = 1 - r;
	int16_t ddF_x = 1;
	int16_t ddF_y = -2 * r;
	int16_t x = 0;
	int16_t y = r;

    SetPixel(x0, y0 + r);
    SetPixel(x0, y0 - r);
    SetPixel(x0 + r, y0);
    SetPixel(x0 - r, y0);
    DrawLine(x0 - r, y0, x0 + r, y0);

    while (x < y) {
        if (f >= 0) {
            y--;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;

        DrawLine(x0 - x, y0 + y, x0 + x, y0 + y);
        DrawLine(x0 + x, y0 - y, x0 - x, y0 - y);

        DrawLine(x0 + y, y0 + x, x0 - y, y0 + x);
        DrawLine(x0 + y, y0 - x, x0 - y, y0 - x);
    }
}



// Draw Traingle with coordimates (x1, y1), (x2, y2), (x3, y3)
void DrawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3)
{
	/* Draw lines */
	DrawLine(x1, y1, x2, y2);
	DrawLine(x2, y2, x3, y3);
	DrawLine(x3, y3, x1, y1);
}



// Draw Filled Traingle with coordimates (x1, y1), (x2, y2), (x3, y3)
void DrawFilledTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3)
{
	int16_t deltax = 0, deltay = 0, x = 0, y = 0, xinc1 = 0, xinc2 = 0,
	yinc1 = 0, yinc2 = 0, den = 0, num = 0, numadd = 0, numpixels = 0,
	curpixel = 0;

#define ABS(x)   ((x) > 0 ? (x) : -(x))

	deltax = ABS(x2 - x1);
	deltay = ABS(y2 - y1);
	x = x1;
	y = y1;

	if (x2 >= x1) {
		xinc1 = 1;
		xinc2 = 1;
	} else {
		xinc1 = -1;
		xinc2 = -1;
	}

	if (y2 >= y1) {
		yinc1 = 1;
		yinc2 = 1;
	} else {
		yinc1 = -1;
		yinc2 = -1;
	}

	if (deltax >= deltay){
		xinc1 = 0;
		yinc2 = 0;
		den = deltax;
		num = deltax / 2;
		numadd = deltay;
		numpixels = deltax;
	} else {
		xinc2 = 0;
		yinc1 = 0;
		den = deltay;
		num = deltay / 2;
		numadd = deltax;
		numpixels = deltay;
	}

	for (curpixel = 0; curpixel <= numpixels; curpixel++)
	{
		DrawLine(x, y, x3, y3);

		num += numadd;
		if (num >= den) {
			num -= den;
			x += xinc1;
			y += yinc1;
		}
		x += xinc2;
		y += yinc2;
	}
}

In main.c:

#include "delay.h"
#include "bitmap.h"
#include "stdio.h"
#include "horse.h"
#include "ST7920.h"


int main(void)
{

	ST7920_init();

	ST7920_SendString(0,0, "HELLO WORLD");
	ST7920_SendString(1,0, "FROM");
	ST7920_SendString(2,0, "EmbeddedExpertIO");
	ST7920_SendString(3,0, "1234567890!@#$%^");
	delay(2000);
	ST7920_Clear();
	ST7920_GraphicMode(1);
	ST7920_DrawBitmap(logo);
	delay(2000);
	ST7920_Clear();
	DrawCircle(110, 31, 12);

	DrawCircle(110, 31, 16);

	DrawLine(3, 60, 127, 33);

	ST7920_Update();

	delay(2000);

	DrawRectangle (100, 12, 20, 14);

	ST7920_Update();

	delay(2000);

	DrawFilledRectangle(30, 20, 30, 10);

	ST7920_Update();

	delay(2000);

	DrawFilledCircle(15, 30, 6);

	ST7920_Update();

	DrawFilledTriangle(1,5,10,5,6,15);

	ST7920_Update();

	delay(2000);

	ST7920_Clear();


	while(1)
	{
		ST7920_DrawBitmap(horse1);
		ST7920_DrawBitmap(horse2);
		ST7920_DrawBitmap(horse3);
		ST7920_DrawBitmap(horse4);
		ST7920_DrawBitmap(horse5);
		ST7920_DrawBitmap(horse6);
		ST7920_DrawBitmap(horse7);
		ST7920_DrawBitmap(horse8);
		ST7920_DrawBitmap(horse9);
		ST7920_DrawBitmap(horse10);
	}

}

4. Code:

You may download the entire code from here:

5. Results:

Happy coding 🙂

12 Comments

  • Paul Posted November 10, 2023 1:28 am

    Good evening, I would like to contact you to find out if there is a solution to correct the pixel bug at the top left of the screen, thank you in advance for your response. Paul

    • Husamuldeen Posted November 10, 2023 4:10 am

      Hi,
      post your bug here and we will look at it.

      • Paul Posted November 10, 2023 11:08 am

        In your YouTube video, you can see that there are extra pixels at the top left of the screen when you draw a shape.

        • Paul Posted November 10, 2023 11:16 am

          I have the solution, you just have to delete the line “*image = *p;” in the “SetPixel()” function

  • TESSON Paul Posted November 10, 2023 11:06 am

    I have the solution to fix the pixel bug at the top left of the screen, just remove “*image = *p;” in ST7920.c

  • Paul Posted November 10, 2023 3:50 pm

    I have another question how I could display a custom font and font size with free coordinates?

    • Husamuldeen Posted November 14, 2023 4:31 pm

      Hi,
      you could use graphic mode to display string in the desired size.

      • Paul Posted December 3, 2023 5:46 pm

        Ok thanks you!

  • Paul Posted December 3, 2023 5:46 pm

    Hello, I would like to bother you again because I cannot get the program to work to display on a st7920 with an stm32f767zi even though I have adapted each command for the microcontroller, is it possible to help me, thank you in advance

  • Paul Posted December 10, 2023 9:35 pm
    • Husamuldeen Posted December 13, 2023 3:48 am

      Hi,
      what was the issue?

      • Paul Posted December 30, 2023 9:12 pm

        The issue stemmed from the SPI speed, the corrected output voltage of PA7 (2.6V) achieved by removing a jumper on the card, and the required data format for transmission.

Add Comment

Your email address will not be published. Required fields are marked *