
Displaying a full-screen color image on an E-Ink display demonstrates the device’s ability to render rich visuals using its reflective, low-power technology. In this guide, we will cover how to send and refresh full-frame image data to achieve a complete color update on the E-Paper screen.
In this guide, we shall cover the following:
- Developing header and source files.
- Main code development.
- Results.
1. Developing Header and Source Files:
First, we need to create new source and header files.
To create Source file, right click on Src folder and add new source file as following:

Give a name for the source file, we shall name it as epaper.c as following:

In similar manner, right click on Inc folder and add new header file with name of epaper.h.

In the header file, include the following header files:
#include "stdint.h" #include "main.h" #include "spi.h"
Next, declare the size of the display as follows:
// Display resolution #define ePaper_WIDTH 160 #define ePaper_HEIGHT 296
Declare the following enumeration for bust IDLE state as follows:
typedef enum
{
	IDLE=0,
	BUSY=1
}StateTypedef;Declare the following functions:
void ePaper_Init(void); void ePaper_Clear(void); void ePaper_Clear_Black(void); void ePaper_Clear_Red(void); void ePaper_Sleep(void);
The functions are as follows:
- Display initialization which will initialize the display.
- Clear the display, it will clear the display and make it into while color.
- Clear red and black, as name suggests, it will clear the display into red and black color respectively.
Hence, the entire header file as follows:
#ifndef INC_EPAPER_H_
#define INC_EPAPER_H_
#include "stdint.h"
#include "main.h"
#include "spi.h"
// Display resolution
#define ePaper_WIDTH       160
#define ePaper_HEIGHT      296
typedef enum
{
	IDLE=0,
	BUSY=1
}StateTypedef;
void ePaper_Init(void);
void ePaper_Clear(void);
void ePaper_Clear_Black(void);
void ePaper_Clear_Red(void);
void ePaper_Sleep(void);
#endif /* INC_EPAPER_H_ */Next, we shall develop the source file.
In the source file, include epaper.h header file as follows:
#include "epaper.h"
Next, we need two functions that will write either data or command to the display.
To write data, first, set DC line to high, pull CS line low, transmit data and pul CS line high. Hence the function as follows:
static void ePaper_WriteData(uint8_t data)
{
	HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin,1);
	HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, 0);
	HAL_SPI_Transmit(&hspi1, &data, 1, 10);
	HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, 1);
}Next, we need to write command, similar to writing data, just DC line to low, hence the function as follows:
static void ePaper_WriteCmd(uint8_t cmd)
{
	HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin,0);
	HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, 0);
	HAL_SPI_Transmit(&hspi1, &cmd, 1, 10);
	HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, 1);
}Next, we need to reset the display physically as follows:
static void ePaper_Reset(void)
{
	HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, 1);
	HAL_Delay(200);
	HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, 0);
	HAL_Delay(10);
	HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, 1);
	HAL_Delay(200);
}Next, we need a function that will read BUSY pin status as follows:
static StateTypedef ePaper_State(void)
{
	return HAL_GPIO_ReadPin(BUSY_GPIO_Port, BUSY_Pin);
}Next, we need a function to update the display after filling the GRAM of the display. This can be done by sending command 0x20 and wait until BUSY line becomes low:

static void ePaper_Display_On(void)
{
	ePaper_WriteCmd(0x20);
	while(ePaper_State()==BUSY)
	{
		HAL_Delay(1);
	}
}Next, set the address window, this is necessarily when drawing an image:

static void ePaper_SetWindows(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend)
{
	ePaper_WriteCmd(0x44); // SET_RAM_X_ADDRESS_START_END_POSITION
	ePaper_WriteData((Xstart>>3) & 0x1F);
	ePaper_WriteData((Xend>>3) & 0x1F);
    ePaper_WriteCmd(0x45); // SET_RAM_Y_ADDRESS_START_END_POSITION
    ePaper_WriteData(Ystart & 0xFF);
    ePaper_WriteData((Ystart >> 8) & 0x01);
    ePaper_WriteData(Yend & 0xFF);
    ePaper_WriteData((Yend >> 8) & 0x01);
}
Next, set cursor function:

static void ePaper_SetCursor(uint16_t Xstart, uint16_t Ystart)
{
	ePaper_WriteCmd(0x4E); // SET_RAM_X_ADDRESS_COUNTER
	ePaper_WriteData(Xstart & 0x1F);
    ePaper_WriteCmd(0x4F); // SET_RAM_Y_ADDRESS_COUNTER
    ePaper_WriteData(Ystart & 0xFF);
    ePaper_WriteData((Ystart >> 8) & 0x01);
}These function shall be used when drawing some pictures on the display.
Next, the initialization:
First, reset the display and wait for the display to be IDLE as follows:
void ePaper_Init(void)
{
	ePaper_Reset();
	while(ePaper_State()==BUSY)
	{
		HAL_Delay(1);
	}Soft reset the display and wait until it becomes idle:

	ePaper_WriteCmd(0x12);
	while(ePaper_State()==BUSY)
	{
		HAL_Delay(1);
	}Enable automatic address incrementing:

ePaper_WriteCmd(0x11); //0x00 ePaper_WriteData(0x03);
Set the address the window to be the entire display as follows:
ePaper_SetWindows(0,0,ePaper_WIDTH-1,ePaper_HEIGHT-1);
Set the Boarder Wave control to 0:
ePaper_WriteCmd(0x3C); //0x00 ePaper_WriteData(0x00);
Set the cursor to 0,0:
ePaper_SetCursor(0,0);
Wait for the display to be idle:
	while(ePaper_State()==BUSY)
	{
		HAL_Delay(1);
	}Hence, the function as follows:
void ePaper_Init(void)
{
	ePaper_Reset();
	while(ePaper_State()==BUSY)
	{
		HAL_Delay(1);
	}
	ePaper_WriteCmd(0x12);
	while(ePaper_State()==BUSY)
	{
		HAL_Delay(1);
	}
	ePaper_WriteCmd(0x11);	//0x00
	ePaper_WriteData(0x03);
	ePaper_SetWindows(0,0,ePaper_WIDTH-1,ePaper_HEIGHT-1);
	ePaper_WriteCmd(0x3C);	//0x00
	ePaper_WriteData(0x00);
	ePaper_SetCursor(0,0);
	while(ePaper_State()==BUSY)
	{
		HAL_Delay(1);
	}
}To set certain pixel to one of the three colors Black, White or Red, you need to set the register 0x24 to 0 for black and 1 for white and register 0x26 for red, 0x00 will be no color and 0x01 will be red.
Hence, the filling functions as follows:
void ePaper_Clear(void)
{
    uint16_t Width, Height;
    Width = (ePaper_WIDTH % 8 == 0)? (ePaper_WIDTH / 8 ): (ePaper_WIDTH / 8 + 1);
    Height = ePaper_HEIGHT;
    ePaper_WriteCmd(0x24);
    for (uint16_t j = 0; j < Height; j++) {
        for (uint16_t i = 0; i < Width; i++) {
        	ePaper_WriteData(0xff);
        }
    }
    ePaper_WriteCmd(0x26);
    for (uint16_t j = 0; j < Height; j++) {
        for (uint16_t i = 0; i < Width; i++) {
        	ePaper_WriteData(0x00);
        }
    }
    ePaper_Display_On();
}
void ePaper_Clear_Black(void)
{
    uint16_t Width, Height;
    Width = (ePaper_WIDTH % 8 == 0)? (ePaper_WIDTH / 8 ): (ePaper_WIDTH / 8 + 1);
    Height = ePaper_HEIGHT;
    ePaper_WriteCmd(0x24);
    for (uint16_t j = 0; j < Height; j++) {
        for (uint16_t i = 0; i < Width; i++) {
        	ePaper_WriteData(0x00);
        }
    }
    ePaper_WriteCmd(0x26);
    for (uint16_t j = 0; j < Height; j++) {
        for (uint16_t i = 0; i < Width; i++) {
        	ePaper_WriteData(0x00);
        }
    }
    ePaper_Display_On();
}
void ePaper_Clear_Red(void)
{
	uint16_t Width, Height;
    Width = (ePaper_WIDTH % 8 == 0)? (ePaper_WIDTH / 8 ): (ePaper_WIDTH / 8 + 1);
    Height = ePaper_HEIGHT;
    ePaper_WriteCmd(0x24);
    for (uint16_t j = 0; j < Height; j++) {
        for (uint16_t i = 0; i < Width; i++) {
        	ePaper_WriteData(0x00);
        }
    }
    ePaper_WriteCmd(0x26);
    for (uint16_t j = 0; j < Height; j++) {
        for (uint16_t i = 0; i < Width; i++) {
        	ePaper_WriteData(0xFF);
        }
    }
    ePaper_Display_On();
}Finally, sleep function:

void ePaper_Sleep(void)
{
	ePaper_WriteCmd(0x10); // POWER_OFF
	ePaper_WriteData(0X01);
}Hence the entire source as follows:
#include "epaper.h"
static void ePaper_WriteData(uint8_t data)
{
	HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin,1);
	HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, 0);
	HAL_SPI_Transmit(&hspi1, &data, 1, 10);
	HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, 1);
}
static void ePaper_WriteCmd(uint8_t cmd)
{
	HAL_GPIO_WritePin(DC_GPIO_Port, DC_Pin,0);
	HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, 0);
	HAL_SPI_Transmit(&hspi1, &cmd, 1, 10);
	HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, 1);
}
static void ePaper_Reset(void)
{
	HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, 1);
	HAL_Delay(200);
	HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, 0);
	HAL_Delay(10);
	HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, 1);
	HAL_Delay(200);
}
static StateTypedef ePaper_State(void)
{
	return HAL_GPIO_ReadPin(BUSY_GPIO_Port, BUSY_Pin);
}
static void ePaper_Display_On(void)
{
	ePaper_WriteCmd(0x20);
	while(ePaper_State()==BUSY)
	{
		HAL_Delay(1);
	}
}
static void ePaper_SetWindows(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend)
{
	ePaper_WriteCmd(0x44); // SET_RAM_X_ADDRESS_START_END_POSITION
	ePaper_WriteData((Xstart>>3) & 0x1F);
	ePaper_WriteData((Xend>>3) & 0x1F);
    ePaper_WriteCmd(0x45); // SET_RAM_Y_ADDRESS_START_END_POSITION
    ePaper_WriteData(Ystart & 0xFF);
    ePaper_WriteData((Ystart >> 8) & 0x01);
    ePaper_WriteData(Yend & 0xFF);
    ePaper_WriteData((Yend >> 8) & 0x01);
}
static void ePaper_SetCursor(uint16_t Xstart, uint16_t Ystart)
{
	ePaper_WriteCmd(0x4E); // SET_RAM_X_ADDRESS_COUNTER
	ePaper_WriteData(Xstart & 0x1F);
    ePaper_WriteCmd(0x4F); // SET_RAM_Y_ADDRESS_COUNTER
    ePaper_WriteData(Ystart & 0xFF);
    ePaper_WriteData((Ystart >> 8) & 0x01);
}
void ePaper_Init(void)
{
	ePaper_Reset();
	while(ePaper_State()==BUSY)
	{
		HAL_Delay(1);
	}
	ePaper_WriteCmd(0x12);
	while(ePaper_State()==BUSY)
	{
		HAL_Delay(1);
	}
	ePaper_WriteCmd(0x11);	//0x00
	ePaper_WriteData(0x03);
	ePaper_SetWindows(0,0,ePaper_WIDTH-1,ePaper_HEIGHT-1);
	ePaper_WriteCmd(0x3C);	//0x00
	ePaper_WriteData(0x00);
	ePaper_SetCursor(0,0);
	while(ePaper_State()==BUSY)
	{
		HAL_Delay(1);
	}
}
void ePaper_Clear(void)
{
    uint16_t Width, Height;
    Width = (ePaper_WIDTH % 8 == 0)? (ePaper_WIDTH / 8 ): (ePaper_WIDTH / 8 + 1);
    Height = ePaper_HEIGHT;
    ePaper_WriteCmd(0x24);
    for (uint16_t j = 0; j < Height; j++) {
        for (uint16_t i = 0; i < Width; i++) {
        	ePaper_WriteData(0xff);
        }
    }
    ePaper_WriteCmd(0x26);
    for (uint16_t j = 0; j < Height; j++) {
        for (uint16_t i = 0; i < Width; i++) {
        	ePaper_WriteData(0x00);
        }
    }
    ePaper_Display_On();
}
void ePaper_Clear_Black(void)
{
    uint16_t Width, Height;
    Width = (ePaper_WIDTH % 8 == 0)? (ePaper_WIDTH / 8 ): (ePaper_WIDTH / 8 + 1);
    Height = ePaper_HEIGHT;
    ePaper_WriteCmd(0x24);
    for (uint16_t j = 0; j < Height; j++) {
        for (uint16_t i = 0; i < Width; i++) {
        	ePaper_WriteData(0x00);
        }
    }
    ePaper_WriteCmd(0x26);
    for (uint16_t j = 0; j < Height; j++) {
        for (uint16_t i = 0; i < Width; i++) {
        	ePaper_WriteData(0x00);
        }
    }
    ePaper_Display_On();
}
void ePaper_Clear_Red(void)
{
	uint16_t Width, Height;
    Width = (ePaper_WIDTH % 8 == 0)? (ePaper_WIDTH / 8 ): (ePaper_WIDTH / 8 + 1);
    Height = ePaper_HEIGHT;
    ePaper_WriteCmd(0x24);
    for (uint16_t j = 0; j < Height; j++) {
        for (uint16_t i = 0; i < Width; i++) {
        	ePaper_WriteData(0x00);
        }
    }
    ePaper_WriteCmd(0x26);
    for (uint16_t j = 0; j < Height; j++) {
        for (uint16_t i = 0; i < Width; i++) {
        	ePaper_WriteData(0xFF);
        }
    }
    ePaper_Display_On();
}
void ePaper_Sleep(void)
{
	ePaper_WriteCmd(0x10); // POWER_OFF
	ePaper_WriteData(0X01);
}
3. Main file:
In main.c in user code begin 2 in main function:
First, set PWR pin to high as follows:
HAL_GPIO_WritePin(PWR_GPIO_Port, PWR_Pin, 1);
Initialize the display:
ePaper_Init();
In while 1 loop:
We shall loop through the clear display functions.
ePaper_Clear(); HAL_Delay(5000); ePaper_Clear_Black(); HAL_Delay(5000); ePaper_Clear_Red(); HAL_Delay(5000);
Thats all.
Save the project, build it and run it on your board as following:

4. Results:
You should get the following:

Note: It requires almost half minute to update the display. The video is accelerated.
Happy coding 😉
Add Comment