基于GC9A01驱动的1.28寸IPS圆屏SPI移植与驱动开发实战

最近在做一个智能手表项目,需要用到一块小巧的圆形屏幕,最终选定了这款1.28寸的IPS圆屏,驱动芯片是GC9A01,接口是SPI。网上资料虽然不少,但很多都语焉不详,移植起来总遇到各种问题。今天我就把自己从零开始,把这块屏幕成功点亮并显示图形的完整过程分享出来,希望能帮你少走弯路。

这篇文章适合有一定嵌入式开发基础的朋友,比如会用过STM32、立创开发板或者其他ARM Cortex-M系列MCU的开发者。我会从硬件接线开始,一步步带你完成驱动移植、代码适配,最后实现图形和文字的显示。整个过程都是基于我实际项目的经验,代码可以直接拿来用。

1. 准备工作:了解屏幕与获取资料

1.1 屏幕规格与资料下载

咱们先来看看这块屏幕的基本情况。这是一块1.28英寸的圆形IPS液晶屏,分辨率是240x240,显示效果非常细腻。驱动芯片是GC9A01,通过SPI接口与主控通信,工作电压是3.3V。

注意:所有开发工作都离不开厂家提供的资料。请务必先下载资料包,里面包含了最重要的参考例程和芯片手册。

资料获取方式

  • 采购链接:可以在电商平台搜索“1.28寸IPS圆屏 GC9A01”找到。
  • 资料下载:通过网盘获取,提取码为 8888

资料包里最关键的是一个完整的工程例程,这是我们移植的基础。拿到资料后,先找到 LCD 这个文件夹,里面包含了驱动芯片GC9A01的所有底层驱动代码。

1.2 引脚定义与连接

这块屏幕通过一个8Pin的FPC排线与主板连接。我们需要搞清楚每个引脚是干什么的,才能正确连接到我们的开发板上。

屏幕的引脚定义如下表所示:

屏幕引脚 功能说明 必须连接吗?
GND 电源地 必须
VCC 电源正极 (3.3V) 必须
SCL SPI时钟线 (SCK) 必须
SDA SPI数据线 (MOSI,主出从入) 必须
RES 复位引脚,低电平有效 建议连接
DC 数据/命令选择引脚 必须
CS SPI片选引脚 必须
BLK 背光控制引脚 可选

这里有几个关键点需要解释一下:

  • SPI主从关系:屏幕是SPI从设备,我们的MCU是主设备。所以SCL对应MCU的SCK,SDA对应MCU的MOSI。
  • RES引脚:如果不方便用GPIO控制,可以将其连接到MCU的复位引脚。这样MCU复位时,屏幕也跟着复位,算是一个省IO的窍门。
  • BLK引脚:背光控制。如果项目不需要调节背光亮度,可以直接接3.3V常亮,或者悬空(有些模块内部已上拉)。

在我的项目中,我使用的是立创开发板,具体的连接关系如下,你可以根据自己板子的GPIO资源进行调整:

屏幕引脚 我的开发板连接
GND GND
VCC 3V3
SCL P302 (作为SPI的SCK)
SDA P207 (作为SPI的MOSI)
RES P403 (普通GPIO)
DC P407 (普通GPIO)
CS P408 (普通GPIO)
BLK P409 (普通GPIO,用于PWM调光)

2. 驱动代码移植详解

硬件接好后,接下来就是软件部分的重头戏了。移植的核心工作就是修改厂家提供的驱动代码,让它能在你的开发板和编译环境下跑起来。

2.1 工程文件准备

首先,把资料包里的 LCD 文件夹整个复制到你自己的工程目录下。通常我会放在 DriversUser 文件夹里,保持工程结构清晰。

然后,在你的IDE(比如Keil、IAR或者立创的RASC)中,将 LCD 文件夹下的 .c.h 文件添加到工程。主要涉及以下四个文件:

  • lcd_init.c/h:屏幕初始化、底层SPI通信、基本命令写入函数。
  • lcd.c/h:上层应用函数,如画点、画线、显示字符图片等。

添加完成后,记得在需要调用LCD功能的主文件里(比如 main.capp.c)包含头文件 #include "lcd.h"

2.2 关键代码修改与解析

直接复制代码肯定会编译不过,因为引脚定义、延时函数、SPI接口都不同。我们需要有针对性地修改。

第一步:修改引脚定义 (lcd_init.h)

这是最需要修改的地方。找到文件中关于引脚宏定义的部分,根据你实际的硬件连接进行修改。

//-----------------LCD端口定义----------------
// 根据你的连接修改以下四个宏定义
#define LCD_RES_PIN     BSP_IO_PORT_04_PIN_03 // RES -> P403
#define LCD_DC_PIN      BSP_IO_PORT_04_PIN_07 // DC  -> P407
#define LCD_CS_PIN      BSP_IO_PORT_04_PIN_08 // CS  -> P408
#define LCD_BLK_PIN     BSP_IO_PORT_04_PIN_09 // BLK -> P409

/* LCD信号控制宏定义 */
// 这些宏定义了如何控制上述引脚的高低电平
// 通常使用你的HAL库或SDK提供的GPIO写函数
#define LCD_RES_Clr()  R_IOPORT_PinWrite(&g_ioport_ctrl, LCD_RES_PIN, 0) // RES拉低
#define LCD_RES_Set()  R_IOPORT_PinWrite(&g_ioport_ctrl, LCD_RES_PIN, 1) // RES拉高
// ... DC, CS, BLK 同理

第二步:修改SPI通信函数 (lcd_init.c)

厂家例程可能使用软件模拟SPI(GPIO翻转)或硬件SPI。我推荐使用硬件SPI,速度更快,不占用CPU。你需要将 LCD_Writ_Bus(u8 dat) 这个函数替换成你自己平台的SPI发送函数。

这是我的实现,基于立创开发板的FSP库:

void LCD_Writ_Bus(u8 dat)
{
    LCD_CS_Clr(); // 拉低片选,开始通信

    /* 使用硬件SPI发送一个字节数据 */
    fsp_err_t err = R_SPI_WriteRead(&g_spi0_ctrl,
                                    &dat,      // 要发送的数据指针
                                    NULL,       // 接收缓冲区(我们只发不收,设为NULL)
                                    1,          // 发送数据长度(1字节)
                                    SPI_BIT_WIDTH_8_BITS); // 数据位宽8位
    if (err != FSP_SUCCESS)
    {
        printf("SPI发送错误\r\n");
        return;
    }

    /* 等待SPI发送完成(这里用了中断标志位,也可用查询方式) */
    while (false == g_transfer_complete);
    g_transfer_complete = false; // 清除标志位

    LCD_CS_Set(); // 拉高片选,结束本次通信
}

提示g_spi0_ctrlg_transfer_complete 需要你在别处定义和初始化。g_transfer_complete 是一个在SPI发送完成中断回调函数里置位的标志位。

第三步:修改延时函数 (lcd_init.h)

驱动初始化需要用到毫秒和微秒级的延时。你需要将下面的宏定义指向你工程里已有的延时函数。

#ifndef delay_ms
#define delay_ms(x)   R_BSP_SoftwareDelay(x, BSP_DELAY_UNITS_MILLISECONDS)
#endif
#ifndef delay_us
#define delay_us(x)   R_BSP_SoftwareDelay(x, BSP_DELAY_UNITS_MICROSECONDS)
#endif

第四步:理解核心函数

修改完硬件相关的部分,我们来看看几个最核心的函数,理解它们才能用好驱动。

  1. LCD_WR_REG(u8 dat)LCD_WR_DATA(u16 dat)

    • LCD_WR_REG 用于向GC9A01芯片写入命令(比如设置显示方向、开显示等)。在写命令前,需要把DC引脚拉低。
    • LCD_WR_DATA 用于向芯片写入数据(比如要显示的颜色值)。在写数据前,需要把DC引脚拉高。
    • 这两个函数是所有高级功能(画图、写字)的基础。
  2. LCD_Address_Set(u16 x1, u16 y1, u16 x2, u16 y2)

    • 这是最关键的函数之一。它用于设置接下来要写入数据的屏幕区域(窗口)。
    • 例如,LCD_Address_Set(10, 20, 50, 100) 就设定了一个从(10,20)到(50,100)的矩形区域。之后调用 LCD_WR_DATA 写入的颜色数据,就会依次填充这个区域。
    • 画点、填充矩形、显示图片等操作前,都必须先调用这个函数设置好范围。
  3. LCD_Init(void)

    • 初始化函数。里面是一长串按照GC9A01数据手册编写的寄存器配置序列。除非你非常了解这颗驱动芯片,否则不要随意修改里面的数值。 这些“魔法数字”负责初始化屏幕的伽马、电压、扫描方向等参数。

2.3 屏幕方向设置

lcd_init.h 文件中,有一个重要的宏定义,用于设置屏幕是横屏还是竖屏显示:

#define USE_HORIZONTAL 0  //设置横屏或者竖屏显示 0或1为竖屏 2或3为横屏

这个设置会影响 LCD_Init() 函数中对扫描方向的配置。如果你发现显示的内容方向不对,修改这个值(0,1,2,3)试试看。

3. 应用层函数使用与显示测试

底层驱动调通后,我们就可以使用 lcd.c 中提供的高级函数来显示内容了。这些函数都是基于前面讲的核心函数构建的。

3.1 基本图形绘制

lcd.c 提供了丰富的绘图函数,我们挑几个最常用的来看看:

  • 清屏或填充颜色LCD_Fill(0, 0, LCD_W, LCD_H, WHITE) 可以将整个屏幕填充为白色。
  • 画点LCD_DrawPoint(100, 100, RED) 在坐标(100,100)处画一个红点。
  • 画线LCD_DrawLine(10, 10, 100, 50, BLUE) 从(10,10)到(100,50)画一条蓝线。
  • 画矩形LCD_DrawRectangle(30, 30, 80, 80, GREEN) 画一个绿色矩形框。
  • 画圆Draw_Circle(120, 120, 40, YELLOW) 以(120,120)为圆心,画一个半径为40的黄色圆。

3.2 显示文字与数字

显示文字稍微复杂一点,因为需要字库。例程中通常包含英文字符的点阵数组(在 lcdfont.h 中)。显示汉字则需要额外的汉字字库,并调用对应的函数。

  • 显示英文字符串LCD_ShowString(10, 50, "Hello World!", RED, WHITE, 16, 0) 在(10,50)位置以16号字体、红字白底显示字符串。
  • 显示整数LCD_ShowIntNum(100, 80, 12345, 5, BLUE, BLACK, 24) 显示5位整数12345。
  • 显示汉字LCD_ShowChinese(30, 40, "中景园电子", RED, WHITE, 32, 0) 显示汉字。注意:这里的汉字字符串“中景园电子”需要提前用取模软件生成点阵数组,并放入字库中。例程中的 tfont16, tfont24 等就是字库数组。

3.3 编写测试程序

最后,我们在主函数里写一个简单的测试程序,把上面功能都试一遍。下面是一个参考例程,它会显示一些文字和一个递增的数字,并贴上一张小图片。

#include "lcd_init.h"
#include "lcd.h"
#include "pic.h" // 假设这里存放了图片数组

void main_app(void)
{
    u8 i, j;
    float t = 0;

    LCD_Init(); // 初始化屏幕
    LCD_Fill(0, 0, LCD_W, LCD_H, WHITE); // 清屏为白色

    while(1)
    {
        // 显示汉字
        LCD_ShowChinese(30, 40, "中景园电子", RED, WHITE, 32, 0);
        // 显示英文字符串
        LCD_ShowString(32, 80, "LCD_Diameter:", RED, WHITE, 16, 0);
        // 显示屏幕宽度(整数)
        LCD_ShowIntNum(134, 80, LCD_W, 3, RED, WHITE, 16);
        // 显示递增的数字(浮点数)
        LCD_ShowString(32, 100, "Increaseing Num:", RED, WHITE, 16, 0);
        LCD_ShowFloatNum1(160, 100, t, 4, RED, WHITE, 16);
        t += 0.11;

        // 显示一张40x40的小图片,平铺显示
        for(j = 0; j < 3 ; j++)
        {
            for(i = 0; i < 6; i++)
            {
                LCD_ShowPicture(40*i, 120+j*40, 40, 40, gImage_1);
            }
        }
        // 可以加个延时,控制刷新速度
        delay_ms(200);
    }
}

将代码编译下载到开发板,如果一切顺利,你就能看到屏幕上显示出文字和图案了。那个浮点数会不断累加,形成动态效果。

4. 常见问题与调试心得

  1. 屏幕白屏或花屏

    • 首先检查电源和地线,确保电压是稳定的3.3V。
    • 检查SPI时钟极性(CPOL)和相位(CPHA)。GC9A01通常工作在模式0(CPOL=0, CPHA=0)或模式3(CPOL=1, CPHA=1)。我的例程使用的是模式0。如果不对,屏幕无法正确解析数据。
    • 检查复位时序。在初始化最开始,确保给了足够长的复位低电平脉冲(通常几十毫秒)。
  2. 显示方向或颜色不对

    • 检查 USE_HORIZONTAL 的设定。
    • 检查颜色格式。GC9A01通常使用RGB565格式(16位色),即一个像素点用2个字节表示。确保你发送的颜色数据格式正确。
  3. SPI通信失败

    • 用逻辑分析仪或示波器抓一下SCK、MOSI、CS、DC这几个引脚的波形,是最直接的调试方法。看是否有数据发出,时序是否符合要求。
    • 确认SPI的时钟频率是否合适。刚开始调试时,可以先把频率设低一点(比如1MHz),稳定后再提高。
  4. 显示内容错位

    • 重点检查 LCD_Address_Set 函数。确保你设置的坐标区域没有超出屏幕范围(0-239)。
    • 画点函数 LCD_DrawPoint 内部调用了 LCD_Address_Set(x,y,x,y),如果你自己设置区域后忘记改回来,会影响后续操作。

这块GC9A01的屏幕性价比很高,圆形设计也很适合做穿戴设备。驱动移植的关键在于耐心,一步步对照引脚、修改底层通信函数、理解初始化序列。一旦底层打通,上面的图形界面开发就是顺水推舟了。希望这篇实战记录能帮你快速点亮手中的小圆屏。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐