Low-Level Display Driver for GC9A01 with Menu Functionality
This is a lightweight library for working with the GC9A01 display driver. It includes basic functions required for interacting with displays and can be easily ported to other display drivers if needed.
If you want to port this library, modify the functions in GC9A01.c
(lines 10 to 342). Most of this section contains initialization code, so adapting it should not be overly complex.
-
Open CubeMX
Select your MCU. On the Connectivity tab, enable SPI in Transmit Master Only mode: -
Enable DMA
-
Setup Pins
Configure the pins as output, as shown below (or use different pins, but match their names): -
Switch to LL Drivers
In Project Manager → Advanced Settings, select LL instead of HAL: -
Other Project Properties
-
Copy Library Files
Open the project folder and copy the following files:GC9A01.h
andfonts.h
→Core/Inc/
GC9A01.c
,font8.c
,font12.c
,font16.c
,font20.c
, andfont24.c
→Core/Src/
-
Add Files to the Project
Open Keil and add the files to your project: -
Include Header Files
Openmain.h
and include the following headers:#include "stdio.h" #include "stdlib.h" #include "string.h" #include "math.h" #include "fonts.h" #include "GC9A01.h"
-
Declare Variables
Inmain.c
, declare this variable:volatile uint8_t dma_spi_fl1 = 0;
-
Handle DMA Flags
Openstm32f4xx_it.c
and declare the variable asextern
:extern volatile uint8_t dma_spi_fl1;
Paste this code in your DMA IRQ Handler:
if (LL_DMA_IsActiveFlag_TC(DMA_NO) == 1) { LL_DMA_ClearFlag_TC(DMA_NO); dma_spi_fl1 = 1; } else if (LL_DMA_IsActiveFlag_TE(DMA_NO) == 1) { LL_DMA_ClearFlag_TE(DMA_NO); Error_Handler(); }
-
Configure GC9A01
InGC9A01.h
, fill in these lines correctly:#define LCD_RST_1 LL_GPIO_SetOutputPin(RES_GPIO_Port, RES_Pin) // LCD reset pin high #define LCD_RST_0 LL_GPIO_ResetOutputPin(RES_GPIO_Port, RES_Pin) // LCD reset pin low #define LCD_CS_1 LL_GPIO_SetOutputPin(CS_GPIO_Port, CS_Pin) // LCD chip select high #define LCD_CS_0 LL_GPIO_ResetOutputPin(CS_GPIO_Port, CS_Pin) // LCD chip select low #define LCD_DC_1 LL_GPIO_SetOutputPin(DC_GPIO_Port, DC_Pin) // Data/Command pin high #define LCD_DC_0 LL_GPIO_ResetOutputPin(DC_GPIO_Port, DC_Pin) // Data/Command pin low #define SPI_NO SPI1 // SPI instance #define DMA_NO DMA2 // DMA instance #define DMA_STREAM LL_DMA_STREAM_3 // DMA stream #define LL_DMA_ClearFlag_TC LL_DMA_ClearFlag_TC3 // Adjust for your stream #define LL_DMA_ClearFlag_TE LL_DMA_ClearFlag_TE3 #define LL_DMA_IsActiveFlag_TC LL_DMA_IsActiveFlag_TC3 #define LL_DMA_IsActiveFlag_TE LL_DMA_IsActiveFlag_TE3
-
Update
main.c
Add these initializations in your main function:/* USER CODE BEGIN Init */ LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG); LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR); NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0)); /* USER CODE END Init */
And this:
/* USER CODE BEGIN 2 */ LL_DMA_DisableStream(DMA_NO, DMA_STREAM); LL_DMA_ClearFlag_TC(DMA_NO); LL_DMA_ClearFlag_TE(DMA_NO); LL_DMA_EnableIT_TC(DMA_NO, DMA_STREAM); LL_DMA_EnableIT_TE(DMA_NO, DMA_STREAM); LL_SPI_EnableDMAReq_TX(SPI_NO); LL_SPI_Enable(SPI_NO); /* USER CODE END 2 */
-
Test the Library
Verify functionality with this code:GC9A01_Initial(); GC9A01_ClearScreen(WHITE); GC9A01_SetBackColor(BLACK); GC9A01_SetFont(&Font16); GC9A01_Text("Hello world", 1); GC9A01_DrawCircle(120, 120, 60, BLUE);
The library provides the following functions for interacting with the display:
void GC9A01_ClearScreen(uint16_t bColor);
Fill the entire screen with a specified color.
void GC9A01_ClearWindow(uint8_t startX, uint8_t startY, uint8_t endX, uint8_t endY, uint16_t bColor);
Fill a rectangular area with a specified color.
void GC9A01_show_picture(uint16_t *picture, uint16_t x, uint16_t y, uint16_t width, uint16_t height);
Display an image starting at position (x, y).
void GC9A01_DrawPixel_2x2(uint8_t x, uint8_t y, uint16_t color);
Draw a 2x2 pixel at (x, y) with a specified color.
void GC9A01_draw_line(uint16_t color, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
Draw a line between two points.
void GC9A01_DrawRect(uint16_t color, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
Draw a rectangle.
void GC9A01_DrawCircle(uint16_t x0, uint16_t y0, uint8_t r, uint16_t color);
Draw a circle.
void GC9A01_FilledDrawCircle(uint16_t x0, uint16_t y0, uint8_t r, uint16_t color);
Draw a filled circle.
void GC9A01_SetTextColor(uint16_t color);
Set the text color.
void GC9A01_SetBackColor(uint16_t color);
Set the background color.
void GC9A01_SetFont(sFONT *pFonts);
Set the font.
void GC9A01_DrawChar(uint16_t x, uint16_t y, uint8_t c);
Draw a single character.
void GC9A01_String(uint16_t x, uint16_t y, char *str);
Draw a string of text at (x, y).
void GC9A01_Text(char *str, uint8_t page);
Display long text, scrolling across multiple pages.
void GC9A01_Rainbow_String(uint16_t x, uint16_t y, char *str);
Draw a string of text with a rainbow gradient.
The library includes menu-related functions for displaying and interacting with menus or lists.
To create a menu, define a structure like this: c struct MenuMember Members[8]; // Replace 8 with the desired number of menu items
This structure have 3 main parameters:
Each menu item in the structure has the following parameters:
Members[i].text – The text for the menu item.
Members[i].number – The index of the menu item.
Members[i].state – The state of the menu item (e.g., active or inactive).
If you want to change the number of menu members, menu location, or other parameters, update the following lines in the GC9A01.h
file:
/******************** TEXT STYLE **********************/
#define default_text_color BLACK // Default text color
#define default_background_color WHITE // Default background color
#define default_active_color GREEN // Color for the active menu item
#define default_font &Font20 // Default font used
/******************************************************/
/******************** MENU PARAMETERS *****************/
#define X_POS 35 // X-coordinate of the menu
#define Y_POS 25 // Y-coordinate of the menu
#define X_END_POS 205 // X-coordinate limit of the menu
#define STEP 6 // Space between menu lines in pixels
#define AMOUNT_OF_MENU_MEMBERS 8 // Number of menu items
/******************************************************/
/******************** TEXT POSITION *******************/
//only for GC9A01_Text
#define OUT_TEXT_LEFT_INDENTATION 35 // Left margin for text
#define OUT_TEXT_RIGHT_INDENTATION 35 // Right margin for text
/******************************************************/
After setting up the parameters, you can use the following functions:
void ShowMenu(struct MenuMember Members[], uint8_t page_num);
// Display the menu on the screen.
void refresh_menu_member(struct MenuMember Members[], uint8_t pos);
// Update the state of a specific menu item.
uint8_t get_active_menu_member(struct MenuMember Members[]);
// Retrieve the index of the currently active menu item.
void menu_active_member_running_text_animation(struct MenuMember Members[], uint8_t pos);
// Animate the text of the active menu item by scrolling it to the left.