1. 准备工作

1.1 物料清单

  • 支持8080接口的并口屏
  • 主控使用STM32F103ZET6

1.2 开发环境及工具

  • 开发工具选择VSCode+Keil的方式

2. 环境搭建及工程创建

  • STM32F1这里采用标准库来进行开发

  • 这里下载对应的标准库驱动

  • 工程结构如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    ├─Bsp
    │ ├─Inc
    │ └─Src
    ├─Core
    │ ├─Inc
    │ └─Src
    ├─Doc
    ├─Drivers
    │ ├─CMSIS
    │ │ ├─CM3
    │ │ │ ├─CoreSupport
    │ │ │ └─DeviceSupport
    │ │ │ └─ST
    │ │ │ └─STM32F10x
    │ │ │ └─startup
    │ │ │ ├─arm
    │ │ │ ├─gcc_ride7
    │ │ │ ├─iar
    │ │ │ └─TrueSTUDIO
    │ │ └─Documentation
    │ └─STM32F10x_StdPeriph_Driver
    │ ├─inc
    │ └─src
    ├─KeilPrj
    │ ├─.vscode
    │ ├─DebugConfig
    │ ├─Listings
    │ └─Objects
    └─Libraries
  • image.png

  • image.png

  • image.png

  • image.png

  • image.png

3.FSMC及8080时序

3.1 FSMC简介

FSMC是Flexible Static Memory Controller的缩写,译为灵活的静态存储控制器。 它可以用于驱动包括SRAM、NOR FLASH以及NANDFLSAH类型的存储器, 不能驱动如SDRAM这种动态的存储器而在STM32F429系列的控制器中, 它具有FMC外设,支持控制SDRAM存储器。

  • FSMC功能框图如下,其中划分了四组信号分别是
    • 公共信号(Shared signals):
      • FSMC_A[25:0]:地址锁存线
      • FSMC_D[15:0]:数据锁存线
      • FSMC_NOE:片选线
      • FSMC_NOE:输出使能
      • FSMC_NWE:写使能
      • FSMC_NWAIT:等待
    • NOR/PSRAM信号、NAND信号和PC Card信号image.png
  • FSMC将外设的地址映射到CPU中通过指针直接操作的方式独写外设中的数据。在STM32中的External Memory(FSMC映射)被划分成为如下四个Bank。每个Bank的大小为256Mbytes如下图所示

    • Bank1又划分为四个子Bank(支持四个设备),通过片选可以选择如下子Bank
      • Bank 1 - NOR/PSRAM 1
      • Bank 1 - NOR/PSRAM 2
      • Bank 1 - NOR/PSRAM 3
      • Bank 1 - NOR/PSRAM 4image.pngimage.png
    • Bank2和Bank3用于NAND Flash设备,每个Bank只支持一个设备
    • Bank4用于PC Card的扩展
    • 对应的地址映射区间如下图所示image.pngimage.png
  • 对应的FSMC管脚可从Datasheet中获取,以下仅部分截图image.png

  • 学过微机原理的应该都知道8086芯片,其实FSMC跟8086的操作类似,都是写地址、数据和使能。不过8086地址线和数据线采用分时复用的机制通过锁存将地址锁存后再读写数据。如图所示image.png

  • 74273是锁存芯片,输入是啥输出就是啥只有在CLK上升沿的时候对其写入数据

  • 74154是4-16译码器芯片,将二进制译码成对应的电平信号

3.1.1 FSMC时序

  • FSMC时序分不同的模式使用不同的时序进行通信,以下是NOR Flash Mode2/B的读写时序。其他模式的时序可以到官方的参考手册了解。这里选择Mode B的写时序模拟8080,原因是其时序和8080时序相似度极高。
  • 读时序image.png
  • 写时序image.png
  • 可以看到写入数据那一部中心虚线右边,当数据准备好后只要拉高NWE刚好模拟8080的WDX上升沿写入数据。而地址线有26位取最低位(或其他)当作D/CX那么将最低位接入到8080的D/CX当发送数据的时候如果地址线最低位是0则为命令,1则为数据。

3.1.2 标准库结构体

  • 以下皆为AI生成,建议参考《STM32 Reference Manual》21.5.6节「NOR/PSRAM control registers」
  • FSMC_NORSRAMInitTypeDef结构体
  • FSMC_Bank
    • 可选值:FSMC_Bank1_NORSRAM1/2/3/4
    • 作用:选择使用哪一路 FSMC NOR/SRAM bank(对应硬件的 NE1..NE4 引脚和地址映射)。
    • 注意:要与硬件片选(外设芯片选线)和后续的 FSMC_NORSRAMCmd 一致。
  • FSMC_DataAddressMux
    • 可选值:FSMC_DataAddressMux_Disable / FSMC_DataAddressMux_Enable
    • 作用:是否启用地址/数据复用(AD 总线)。Enable 时地址与数据复用在同一组引脚(节省引脚),Disable 时地址与数据独立。
    • 建议:并口 LCD(ILI9341)通常使用非复用并行,总线独立 -> Disable。
  • FSMC_MemoryType
    • 可选值:FSMC_MemoryType_SRAM / PSRAM / NOR
    • 作用:告诉 FSMC 外设类型以使用不同的控制时序/信号。
    • 建议:并口 LCD 一般选 NOR 或 SRAM(两者差别主要在驱动策略),常用 FSMC_MemoryType_NOR。
  • FSMC_MemoryDataWidth
    • 可选值:FSMC_MemoryDataWidth_8b / FSMC_MemoryDataWidth_16b
    • 作用:外设数据总线宽度(FSMC 传输位宽)。
    • 建议:ILI9341 并口若为 16-bit 数据模式用 16b;若为 8-bit 串行并口则 8b。
  • FSMC_BurstAccessMode
    • 可选值:FSMC_BurstAccessMode_Disable / Enable
    • 作用:允许/禁止突发访问(仅对支持同步突发的 Flash 有意义)。
    • 建议:异步并口 LCD 不使用 -> Disable。
  • FSMC_AsynchronousWait
    • 可选值:FSMC_AsynchronousWait_Disable / Enable
    • 作用:在异步传输中是否允许等待信号(NWAIT)用于插入等待周期。
    • 建议:外设提供 NWAIT 时可以 Enable;ILI9341 不提供 -> Disable。
  • FSMC_WaitSignalPolarity
    • 可选值:FSMC_WaitSignalPolarity_Low / High
    • 作用:NWAIT 的有效电平(低或高)。
    • 仅在启用等待信号/突发模式相关时有意义。
  • FSMC_WrapMode
    • 可选值:FSMC_WrapMode_Disable / Enable
    • 作用:包裹式突发(Wrapped burst)模式,只在同步突发访问时有意义。
    • 建议:通常 Disable(对异步 LCD 无用)。
  • FSMC_WaitSignalActive
    • 可选值:FSMC_WaitSignalActive_BeforeWaitState / DuringWaitState
    • 作用:FSMC 何时检测等待信号(是在等待状态之前还是在等待状态中)。
    • 仅对启用了等待信号/突发模式的情形有意义。
  • FSMC_WriteOperation
    • 可选值:FSMC_WriteOperation_Disable / Enable
    • 作用:是否允许向该 bank 写操作。
    • 建议:若外设需要写(LCD 需要写数据/命令)则 Enable;只读存储可 Disable。
  • FSMC_WaitSignal
    • 可选值:FSMC_WaitSignal_Disable / Enable
    • 作用:是否启用通过 NWAIT 插入等待态(主要针对 Flash 突发访问)。
    • 建议:ILI9341 不使用 -> Disable。
  • FSMC_ExtendedMode
    • 可选值:FSMC_ExtendedMode_Disable / Enable
    • 作用:Enable 时可为写操作使用单独的时序结构(FSMC_WriteTimingStruct),Disable 时读写共用 FSMC_ReadWriteTimingStruct。
    • 建议:如果写和读时序差别较大(需要单独优化)可 Enable,否则 Disable 即可。对频繁写入的 LCD,若需要更快的读写分别调优,可开启扩展模式并提供单独写时序。
  • FSMC_WriteBurst
    • 可选值:FSMC_WriteBurst_Disable / Enable
    • 作用:允许写突发(仅对支持同步突发的设备有意义)。
    • 建议:异步 LCD -> Disable。
  • FSMC_ReadWriteTimingStruct
    • 类型:FSMC_NORSRAMTimingInitTypeDef*
    • 作用:指向读/写共用的时序结构(当 FSMC_ExtendedMode = Disable 时使用)。包含 AddressSetup、DataSetup 等具体周期配置。
    • 重要性:对异步设备稳定性影响大,需根据外设数据手册调节(AddressSetupTime、DataSetupTime 是关键)。
  • FSMC_WriteTimingStruct
    • 类型:FSMC_NORSRAMTimingInitTypeDef*
    • 作用:当 FSMC_ExtendedMode = Enable 时,用于单独配置写操作的时序。读操作仍用 FSMC_ReadWriteTimingStruct。

  • FSMC_NORSRAMTimingInitTypeDef结构体
  • FSMC_AddressSetupTime
    • 取值范围:0 ~ 0xF(HCLK 周期数)
    • 作用:地址总线有效到第一个数据相位开始之间的建立时间(以 HCLK 周期计)。
    • 建议(并口 LCD):通常 1 ~ 4(0 表示最短但可兼容性差)。
  • FSMC_AddressHoldTime
    • 取值范围:0 ~ 0xF(HCLK 周期数)
    • 作用:地址保持时间(地址在数据相位后的保持周期数),仅在地址/数据复用时有意义。
    • 建议(非复用并口 LCD):设 0。若复用则按器件时序设置(通常 0~1)。
  • FSMC_DataSetupTime
    • 取值范围:1 ~ 0xFF(注意 header 的 IS 宏要求 >0)
    • 作用:数据建立/保持时间(HCLK 周期数),是影响读写稳定性与速度的关键参数。
    • 建议(并口 LCD):写操作常需较大值,典型 3~20,视 HCLK 与 LCD 允许的传输时间而定。
  • FSMC_BusTurnAroundDuration
    • 取值范围:0 ~ 0xF(HCLK 周期数)
    • 作用:总线切换(bus turnaround)延迟,仅在地址/数据复用或读写切换时有用。
    • 建议(非复用并口 LCD):设 0。
  • FSMC_CLKDivision
    • 取值范围:1 ~ 0xF(以 header 所述,但在异步访问中不使用)
    • 作用:输出给外设的 CLK 分频(用于同步访问,如 CRAM/同步 NOR)。
    • 建议(异步并口 LCD):设 0 或不使用(你一般设 0)。
  • FSMC_DataLatency
    • 取值范围:0 ~ 0xF(对同步 burst NOR 有效;异步访问通常无意义)
    • 作用:在同步模式下,表示在第一数据到来前的额外内存时钟延迟。
    • 建议(异步 LCD):设 0。
  • FSMC_AccessMode
    • 可选值:FSMC_AccessMode_A / _B / _C / _D
    • 作用:不同访问模式改变总线相位(地址/数据相对 HCLK 的时序)。
    • 建议:常用 FSMC_AccessMode_B(你当前使用的),若遇时序问题可尝试切换 A/B/C/D 以微调相位。

3.2 8080接口

  • 8080又叫做MCU接口是一种用于并行数据通信的接口时序标准,源于 Intel 8080 微处理器。Intel 公司在 1974 年推出了这款 8 位微处理器, 8080 微处理器的接口通信规则逐渐演变成了被广泛应用的 8080 时序标准。

  • 8080时序的信号(X表示低电平)组成如下:

    • CSX:片选线,低电平有效
    • RESX:复位线,低电平复位
    • D/CX:命令/数据选择,高电平时数据,低电平是命令
    • RDX:允许设备将数据输出到数据总线上,供主机读取。上升沿读取
    • WRX:主机可将数据写入到设备中,上升沿写入
    • D[17:0]:用于传输数据或命令,数据总线宽度也有 8 位和 16 位等多种
    • 下图为8086的时序图总共有两个时钟周期,分别是写入指令和写入数据image.png
  • 以下是关于8080接口详细时序电气参数image.png

3.3 FSMC时序和8080时序对比

  • 以下是两个时序的对比
  • 8080时序image.png
  • FSMC时序image.png
  • 信号对比
    FSMC信号 功能 8080信号 功能
    A[25:0] 地址线 D/CX 指令选择
    NEx 片选 CSX 片选
    NOE 读使能 RDX 读数据
    NWE 写使能 WDX 写数据
    D[15:0] 数据总线 D[17:0] 数据总线

4.BSP程序

4.1 初始化及参数计算

  • 初始化GPIO口,若是开发板那么需要查看对应的原理图。若不是可以自己根据FSMC(NOR/PSRAM/SRAM)的管脚图连接。
  • 需要注意的是地址线有A0-A25但我们只需要其中的Ax,由于我使用的是野火的霸道开发板其原理图如下image.png
  • 它使用A23作为D/CX,NE4作为片选也就是说我们需要选择FSMC_Bank1_NORSRAM4
  • 根据上面8080时序的电气参数可知
    • 写周期(twc)最小是66ns
    • 数据建立时间(tdst)最小10ns
  • 根据FSMC Mode B可知每次发送数据分为两个周期
    • 发送地址周期:ADDSET(Address Setup)+1
    • 发送数据周期:DATAST(Data Setup)+1
    • 发送一次数据的总周期是:发送地址周期+发送数据周期,这个结果不能小于8080的写时序周期(twc)
    • 1个HCLK等于1/72MHz ≈ 13.8ns
    • 66ns / 13.8ns ≈ 4.78 ≈ 5个HCLK
    • 若DATAST = 3,实际3 + 1 = 4
    • 则ADDSET = 2,实际2 + 1 = 3
    • 4 * 13.8ns = 55.2ns > tdst
    • 3 * 13.8ns = 41.4ns
    • 55.2ns + 41.4ns = 96.8ns >> 66ns
    • 实际可根据示波器或者逻辑分析仪进行分析,优化参数可以使其通信速率更快

  • 实际写入地址计算:

  • 根据《STM32F10x Reference Manual》21.4.1节「NOR/PSRAM address mapping」image.png

  • 实际上我们写的地址是AHB的地址,随后映射到外部内存地址image.png

  • 根据上图可知起始地址为6000 0000h,由于使用的是Bank 1 - NOR/PSRAM 4

  • 将其27和26位置1,得到结果6C00 0000

  • image.png

  • 根据如下表格,可知如果我们使用的是16bit的数据位宽地址还需要右移1位image.png

  • 则对应的地址如下

    • 写命令的地址0x6C000000
    • 其中A23作为D/CX,又因为使用16bit的位宽最终左移为(23+1),得到最终写数据的地址0x6C000000 |= (1 << (23 + 1)) = image.png
  • ili9341.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    #ifndef __ILI9341_JAMIEXU_H__

    #define __ILI9341_JAMIEXU_H__

    #include "main.h"



    #define ILI9341_WIDTH  240 // ILI9341 屏幕宽度

    #define ILI9341_HEIGHT 320 // ILI9341 屏幕高度



    #define ILI9341_CMD_ADDRESS ((uint32_t)0x6C000000) // ILI9341 命令地址

    #define ILI9341_DATA_ADDRESS ((uint32_t)0x6D000000) // ILI9341 数据地址



    #define ILI9341_BACKGROUND_COLOR 0xF800 // 默认背景色(黑色)

    #define ILI9341_FOREGROUND_COLOR 0xFFFF // 默认前景色(白色)



    void ilI9341Init(void);



    void ilI9341FillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color);



    void ilI934Clear(void);





    #endif
  • ili9341.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    /**

     * @file ili9341.c

     * @author Jamiexu(Jamie793/Jamies19) (doxm@foxmail.com)

     * @brief

     * @version 0.1

     * @date 2025-08-16

     *

     * @copyright Copyright (c) 2025

     *

     */



    #include "ili9341.h"

    #include "delay.h"

    #include "fsmc.h"  



    static inline void ili_write_cmd(uint16_t cmd) {

        // 发送命令到 ILI9341

       *(__IO uint16_t *)ILI9341_CMD_ADDRESS = cmd;

    }



    static inline void ili_write_data(uint16_t data) {

        // 发送数据到 ILI9341

        *(__IO uint16_t *)ILI9341_DATA_ADDRESS = data;

    }



    static inline uint16_t ili_write(uint16_t cmd, uint16_t data) {

        // 发送命令和数据到 ILI9341

        ili_write_cmd(cmd);

        ili_write_data(data);

    }



    static inline void ili_delay(uint32_t ms) {

        delayMs(ms);

    }




    /* 设置绘图窗口(含发出 Memory Write 命令) */

    static void ili_set_window(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)

    {

        /* 边界裁剪 */

        if (x1 >= ILI9341_WIDTH)  x1 = ILI9341_WIDTH - 1;

        if (x2 >= ILI9341_WIDTH)  x2 = ILI9341_WIDTH - 1;

        if (y1 >= ILI9341_HEIGHT) y1 = ILI9341_HEIGHT - 1;

        if (y2 >= ILI9341_HEIGHT) y2 = ILI9341_HEIGHT - 1;



        /* 列地址 */

        ili_write_cmd(0x2A);

        ili_write_data((uint8_t)(x1 >> 8));

        ili_write_data((uint8_t)(x1 & 0xFF));

        ili_write_data((uint8_t)(x2 >> 8));

        ili_write_data((uint8_t)(x2 & 0xFF));



        /* 行地址 */

        ili_write_cmd(0x2B);

        ili_write_data((uint8_t)(y1 >> 8));

        ili_write_data((uint8_t)(y1 & 0xFF));

        ili_write_data((uint8_t)(y2 >> 8));

        ili_write_data((uint8_t)(y2 & 0xFF));



        /* 准备写入像素数据 */

        ili_write_cmd(0x2C);

    }



    /* 用 16-bit 颜色填充矩形区域(color: RGB565) */

    void ilI9341FillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color)

    {

        if (w == 0 || h == 0) return;

        uint32_t x1 = x + w - 1;

        uint32_t y1 = y + h - 1;

        if (x >= ILI9341_WIDTH || y >= ILI9341_HEIGHT) return;

        if (x1 >= ILI9341_WIDTH)  x1 = ILI9341_WIDTH - 1;

        if (y1 >= ILI9341_HEIGHT) y1 = ILI9341_HEIGHT - 1;

        ili_set_window(x, y, (uint16_t)x1, (uint16_t)y1);



        uint32_t total = (uint32_t)(x1 - x + 1) * (uint32_t)(y1 - y + 1);

        for (uint32_t i = 0; i < total; i++) {

            ili_write_data(color);

        }

    }



    /* 填充整个屏幕 */

    void ilI934Clear(void)

    {

        ilI9341FillRect(0, 0, ILI9341_WIDTH, ILI9341_HEIGHT, ILI9341_BACKGROUND_COLOR);

    }



    void ilI9341Init(void) {

         /* 硬件复位 */

        GPIO_ILI9341_RST_PORT->ODR &= ~GPIO_ILI9341_RST_PIN; // 复位引脚低电平

        ili_delay(120); // 等待复位

        GPIO_ILI9341_RST_PORT->ODR |= GPIO_ILI9341_RST_PIN;


        /* 退出睡眠 */

        ili_write_cmd(0x11);

        ili_delay(120);


        /* 电源与驱动设置(常见序列,按模块手册可调整) */

        ili_write_cmd(0xCB);

        ili_write_data(0x39); ili_write_data(0x2C); ili_write_data(0x00); ili_write_data(0x34); ili_write_data(0x02);


        ili_write_cmd(0xCF);

        ili_write_data(0x00); ili_write_data(0xC1); ili_write_data(0x30);


        ili_write_cmd(0xE8);

        ili_write_data(0x85); ili_write_data(0x00); ili_write_data(0x78);


        ili_write_cmd(0xEA);

        ili_write_data(0x00); ili_write_data(0x00);


        ili_write_cmd(0xED);

        ili_write_data(0x64); ili_write_data(0x03); ili_write_data(0x12); ili_write_data(0x81);


        /* 单字节命令可直接用 ili_write */

        ili_write(0xF7, 0x20);      // Pump ratio

        ili_write(0xC0, 0x23);      // Power control 1

        ili_write(0xC1, 0x10);      // Power control 2


        ili_write_cmd(0xC5);

        ili_write_data(0x3E); ili_write_data(0x28); // VCOM control


        ili_write_cmd(0x36); ili_write_data(0x48);  // MADCTL (方向/顺序,按需调整)

        ili_write(0x3A, 0x55);                      // Pixel Format = 16-bit



        /* 帧率与显示功能 */

        ili_write_cmd(0xB1);

        ili_write_data(0x00); ili_write_data(0x18);



        ili_write_cmd(0xB6);

        ili_write_data(0x0A); ili_write_data(0x82); ili_write_data(0x27);



        /* Gamma 校正(示例值) */

        ili_write_cmd(0xE0);

        ili_write_data(0x0F); ili_write_data(0x1A); ili_write_data(0x0F); ili_write_data(0x18);

        ili_write_data(0x2F); ili_write_data(0x28); ili_write_data(0x20); ili_write_data(0x22);

        ili_write_data(0x1F); ili_write_data(0x1B); ili_write_data(0x23); ili_write_data(0x37);

        ili_write_data(0x00); ili_write_data(0x07); ili_write_data(0x02); ili_write_data(0x10);



        ili_write_cmd(0xE1);

        ili_write_data(0x0F); ili_write_data(0x1B); ili_write_data(0x0F); ili_write_data(0x17);

        ili_write_data(0x33); ili_write_data(0x2C); ili_write_data(0x29); ili_write_data(0x2E);

        ili_write_data(0x30); ili_write_data(0x30); ili_write_data(0x39); ili_write_data(0x3F);

        ili_write_data(0x00); ili_write_data(0x07); ili_write_data(0x03); ili_write_data(0x10);



        /* 再次确保退出睡眠并打开显示 */

        ili_write_cmd(0x11);

        ili_delay(120);


        ili_write_cmd(0x29); // Display ON

        ili_delay(20);


        ilI934Clear(); // 清屏

        ili_delay(50);



        GPIO_ILI9341_BL_PORT->ODR &= ~GPIO_ILI9341_BL_PIN; // 打开背光

    }

5. 移植LVGL

5.1 移植屏幕设备

  • 首先看一下LVGL(v8.3)所需的配置信息image.png

  • 来到工程下Libraries目录执行命令将lvgl库克隆到本地image.png

  • 若我们的工程已有git版本管理,可以通过添加子模块的方式下载lvgl

  • git submodule init

  • git submodule add https://github.com/lvgl/lvgl

  • 将lvgl目录下lv_conf_template.h复制一份命名为lv_conf.h移动到Core/Inc目录image.png

  • 修改文件将#if 0修改成#if 1启用头文件的配置

  • LV_MEM_SIZE用于LVGL分配内存使用的内存池大小,可以将前面的系数修改成合适的大小,这里修改成了12根据不同的板子RAM大小配置,越大越好但是越大板子使用的RAM越多后面可能会内存不足。还需要留给其他模块一些内存

  • 其他的配置信息可根据官方文档image.png

  • 复制porting目录下的文件lv_port_disp_template.c/.h放置到Core中对应的文件夹中并添加到Keil工程,可以删除后缀template

  • image.pngimage.png

  • 修改lv_port_disp.h文件,将#if 0修改成#if 1启用头文件的配置,注释掉多余宏命令或者将宏添加到工程中。image.png

  • 修改lv_port_disp.c文件,配置lvgl缓存区用来存放用于渲染的像素数据。可根据自己的配置选择三种不同的缓冲区,这里选择第二种image.png

  • 修改disp_flush函数添加自己的打点函数或者是填充函数,不推荐打点效率太慢了。这里使用的是区域填充image.png

    • 可选:可将屏幕初始化函数放到disp_init
  • 添加lvgl/src目录下所有文件到工程中,部分文件可根据自己的需求选择。可建立分组存放不同目录下的文件,这里图省事直接全部放在一起并且所有文件都添加了image.png

  • 将lvgl目录添加到头文件列表中 image.png

  • 添加LVGL宏定到工程中,否则会出现找不到lv_conf.h文件image.png

  • 创建一个TIM定时器用来给LVGL提供心跳,终端周期是1msimage.png

  • 在启动文件将堆栈改大一点否则可能出现添加控件后进入HardFaultimage.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/**

 * @file main.c

 * @author Jamiexu(Jamie793/Jamies19) (doxm@foxmail.com)

 * @brief

 * @version 0.1

 * @date 2025-08-16

 *

 * @copyright Copyright (c) 2025

 *

 */

#include "main.h"  

#include "gpio.h"

#include "fsmc.h"  

#include "delay.h"

#include "tim.h"

#include "ili9341.h"

#include "lv_conf.h"



#include "lvgl.h"

#include "lv_port_disp.h" // Include the LVGL display port header



int main(void)

{

    // Initialize the GPIO and FSMC

    coreSystickInit();

    coreGPIOInit();

    coreFSMCInit();

    coreTIMInit(); // Initialize the timer for LVGL tick handling

    // Initialize the ILI9341 LCD

    ilI9341Init();

    lv_init(); // Initialize LVGL

    lv_port_disp_init(); // Initialize the display port for LVGL




    // Create a simple label to test LVGL

    lv_obj_t * label = lv_label_create(lv_scr_act());

    lv_label_set_text(label, "Hello, LVGL on ILI9341!");

    lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);

    // Align the label to the center of the screen

    lv_obj_set_style_text_color(label, lv_color_hex(0x000000), 0); // Set text color to white

    lv_obj_set_style_text_font(label, &lv_font_montserrat_14, 0); // Set font to Montserrat 14

    lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0); // Center the text



    while (1)

    {

        // Handle LVGL tasks

        lv_task_handler(); //注意调用这个处理函数

        // Main loop

    }

    return 0;

}
  • 最后的效果图如下e3ef5f6119d20e4cac154e946bbb96d5.jpeg

5.2 移植触摸设备

  • 复制porting目录下的文件lv_port_indev_template.c/.h放置到Core中对应的文件夹中并添加到Keil工程,可以删除后缀`template
  • 修改lv_port_indev.h/.c文件,将#if 0修改成#if 1启用头文件的配置image.pngimage.pngimage.pngimage.png
  • 修改lv_port_indev.c只保留touchpad相关的函数其他的可以注释掉,修改图中的两个函数使用自己的API判断是否有触摸及其触摸的位置image.png
  • 以下是完整代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    512
    513
    514
    515
    516
    517
    518
    519
    520
    521
    522
    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536
    537
    538
    539
    540
    541
    542
    543
    544
    545
    546
    547
    548
    549
    550
    551
    552
    553
    554
    555
    556
    557
    558
    559
    560
    561
    562
    563
    564
    565
    566
    567
    568
    569
    570
    571
    572
    573
    574
    575
    576
    577
    578
    579
    580
    581
    582
    583
    584
    585
    586
    587
    588
    589
    590
    591
    592
    593
    594
    595
    596
    597
    598
    599
    600
    601
    602
    603
    604
    605
    606
    607
    608
    609
    610
    611
    612
    613
    614
    615
    616
    617
    618
    619
    620
    621
    622
    623
    624
    625
    626
    627
    628
    629
    630
    631
    632
    633
    634
    635
    636
    637
    638
    639
    640
    641
    642
    643
    644
    645
    646
    647
    648
    649
    650
    651
    652
    653
    654
    655
    656
    657
    658
    659
    660
    661
    662
    663
    664
    665
    666
    667
    668
    669
    670
    671
    672
    673
    674
    675
    676
    677
    678
    679
    680
    681
    682
    683
    684
    685
    686
    687
    688
    689
    690
    691
    692
    693
    694
    695
    696
    697
    698
    699
    700
    701
    702
    703
    704
    705
    706
    707
    708
    709
    710
    711
    712
    713
    714
    715
    716
    717
    718
    719
    720
    721
    722
    723
    724
    725
    726
    727
    728
    729
    730
    731
    732
    733
    734
    735
    736
    737
    738
    739
    740
    741
    742
    743
    744
    745
    746
    747
    748
    749
    750
    751
    752
    753
    754
    755
    756
    757
    758
    759
    760
    761
    762
    763
    764
    765
    766
    767
    768
    769
    770
    771
    772
    773
    774
    775
    776
    777
    778
    779
    780
    781
    782
    783
    784
    785
    786
    787
    788
    789
    790
    791
    792
    793
    794
    795
    796
    797
    798
    799
    800
    801
    802
    803
    804
    805
    806
    807
    808
    809
    810
    811
    812
    813
    814
    815
    816
    817
    818
    819
    820
    821
    822
    823
    824
    825
    826
    827
    828
    829
    830
    831
    832
    833
    834
    835
    836
    837
    838
    839
    /**

     * @file lv_port_indev_templ.c

     *

     */



    /*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/

    #if 1



    /*********************

     *      INCLUDES

     *********************/

    #include "lv_port_indev.h"

    #include "lvgl.h"

    #include "xpt2046.h"



    /*********************

     *      DEFINES

     *********************/



    /**********************

     *      TYPEDEFS

     **********************/



    /**********************

     *  STATIC PROTOTYPES

     **********************/



    static void touchpad_init(void);

    static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);

    static bool touchpad_is_pressed(void);

    static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);



    // static void mouse_init(void);

    // static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);

    // static bool mouse_is_pressed(void);

    // static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y);



    // static void keypad_init(void);

    // static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);

    // static uint32_t keypad_get_key(void);



    // static void encoder_init(void);

    // static void encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);

    // static void encoder_handler(void);



    // static void button_init(void);

    // static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);

    // static int8_t button_get_pressed_id(void);

    // static bool button_is_pressed(uint8_t id);



    /**********************

     *  STATIC VARIABLES

     **********************/

    lv_indev_t * indev_touchpad;

    lv_indev_t * indev_mouse;

    lv_indev_t * indev_keypad;

    lv_indev_t * indev_encoder;

    lv_indev_t * indev_button;



    static int32_t encoder_diff;

    static lv_indev_state_t encoder_state;



    /**********************

     *      MACROS

     **********************/



    /**********************

     *   GLOBAL FUNCTIONS

     **********************/



    void lv_port_indev_init(void)

    {

        /**

         * Here you will find example implementation of input devices supported by LittelvGL:

         *  - Touchpad

         *  - Mouse (with cursor support)

         *  - Keypad (supports GUI usage only with key)

         *  - Encoder (supports GUI usage only with: left, right, push)

         *  - Button (external buttons to press points on the screen)

         *

         *  The `..._read()` function are only examples.

         *  You should shape them according to your hardware

         */



        static lv_indev_drv_t indev_drv;



        /*------------------

         * Touchpad

         * -----------------*/



        /*Initialize your touchpad if you have*/

        touchpad_init();



        /*Register a touchpad input device*/

        lv_indev_drv_init(&indev_drv);

        indev_drv.type = LV_INDEV_TYPE_POINTER;

        indev_drv.read_cb = touchpad_read;

        indev_touchpad = lv_indev_drv_register(&indev_drv);



        /*------------------

         * Mouse

         * -----------------*/



        // /*Initialize your mouse if you have*/

        // mouse_init();



        // /*Register a mouse input device*/

        // lv_indev_drv_init(&indev_drv);

        // indev_drv.type = LV_INDEV_TYPE_POINTER;

        // indev_drv.read_cb = mouse_read;

        // indev_mouse = lv_indev_drv_register(&indev_drv);



        // /*Set cursor. For simplicity set a HOME symbol now.*/

        // lv_obj_t * mouse_cursor = lv_img_create(lv_scr_act());

        // lv_img_set_src(mouse_cursor, LV_SYMBOL_HOME);

        // lv_indev_set_cursor(indev_mouse, mouse_cursor);



        /*------------------

         * Keypad

         * -----------------*/



        // /*Initialize your keypad or keyboard if you have*/

        // keypad_init();



        // /*Register a keypad input device*/

        // lv_indev_drv_init(&indev_drv);

        // indev_drv.type = LV_INDEV_TYPE_KEYPAD;

        // indev_drv.read_cb = keypad_read;

        // indev_keypad = lv_indev_drv_register(&indev_drv);



        /*Later you should create group(s) with `lv_group_t * group = lv_group_create()`,

         *add objects to the group with `lv_group_add_obj(group, obj)`

         *and assign this input device to group to navigate in it:

         *`lv_indev_set_group(indev_keypad, group);`*/



        /*------------------

         * Encoder

         * -----------------*/



        // /*Initialize your encoder if you have*/

        // encoder_init();



        // /*Register a encoder input device*/

        // lv_indev_drv_init(&indev_drv);

        // indev_drv.type = LV_INDEV_TYPE_ENCODER;

        // indev_drv.read_cb = encoder_read;

        // indev_encoder = lv_indev_drv_register(&indev_drv);



        /*Later you should create group(s) with `lv_group_t * group = lv_group_create()`,

         *add objects to the group with `lv_group_add_obj(group, obj)`

         *and assign this input device to group to navigate in it:

         *`lv_indev_set_group(indev_encoder, group);`*/



        /*------------------

         * Button

         * -----------------*/



        /*Initialize your button if you have*/

        // button_init();



        // /*Register a button input device*/

        // lv_indev_drv_init(&indev_drv);

        // indev_drv.type = LV_INDEV_TYPE_BUTTON;

        // indev_drv.read_cb = button_read;

        // indev_button = lv_indev_drv_register(&indev_drv);



        // /*Assign buttons to points on the screen*/

        // static const lv_point_t btn_points[2] = {

        //     {10, 10},   /*Button 0 -> x:10; y:10*/

        //     {40, 100},  /*Button 1 -> x:40; y:100*/

        // };

        // lv_indev_set_button_points(indev_button, btn_points);

    }



    /**********************

     *   STATIC FUNCTIONS

     **********************/



    /*------------------

     * Touchpad

     * -----------------*/



    /*Initialize your touchpad*/

    static void touchpad_init(void)

    {

        /*Your code comes here*/

    }



    /*Will be called by the library to read the touchpad*/

    static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)

    {

        static lv_coord_t last_x = 0;

        static lv_coord_t last_y = 0;



        /*Save the pressed coordinates and the state*/

        if(touchpad_is_pressed()) {

            touchpad_get_xy(&last_x, &last_y);

            data->state = LV_INDEV_STATE_PR;

        }

        else {

            data->state = LV_INDEV_STATE_REL;

        }



        /*Set the last pressed coordinates*/

        data->point.x = last_x;

        data->point.y = last_y;

    }



    /*Return true is the touchpad is pressed*/

    static bool touchpad_is_pressed(void)

    {

        /*Your code comes here*/



        return xpt2046IsTouched();

    }



    /*Get the x and y coordinates if the touchpad is pressed*/

    static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)

    {

        /*Your code comes here*/

        xpt2046_point_t point;

        if (xpt2046GetXY(&point) == 0) { // Get touch coordinates

            (*x) = point.x;

            (*y) = point.y;

        } else {

            (*x) = 0; // 如果获取失败,返回默认值

            (*y) = 0;

        }

    }



    /*------------------

    //  * Mouse

    //  * -----------------*/



    // /*Initialize your mouse*/

    // static void mouse_init(void)

    // {

    //     /*Your code comes here*/

    // }



    // /*Will be called by the library to read the mouse*/

    // static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)

    // {

    //     /*Get the current x and y coordinates*/

    //     mouse_get_xy(&data->point.x, &data->point.y);



    //     /*Get whether the mouse button is pressed or released*/

    //     if(mouse_is_pressed()) {

    //         data->state = LV_INDEV_STATE_PR;

    //     }

    //     else {

    //         data->state = LV_INDEV_STATE_REL;

    //     }

    // }



    // /*Return true is the mouse button is pressed*/

    // static bool mouse_is_pressed(void)

    // {

    //     /*Your code comes here*/



    //     return false;

    // }



    // /*Get the x and y coordinates if the mouse is pressed*/

    // static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y)

    // {

    //     /*Your code comes here*/



    //     (*x) = 0;

    //     (*y) = 0;

    // }



    // /*------------------

    //  * Keypad

    //  * -----------------*/



    // /*Initialize your keypad*/

    // static void keypad_init(void)

    // {

    //     /*Your code comes here*/

    // }



    // /*Will be called by the library to read the mouse*/

    // static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)

    // {

    //     static uint32_t last_key = 0;



    //     /*Get the current x and y coordinates*/

    //     mouse_get_xy(&data->point.x, &data->point.y);



    //     /*Get whether the a key is pressed and save the pressed key*/

    //     uint32_t act_key = keypad_get_key();

    //     if(act_key != 0) {

    //         data->state = LV_INDEV_STATE_PR;



    //         /*Translate the keys to LVGL control characters according to your key definitions*/

    //         switch(act_key) {

    //             case 1:

    //                 act_key = LV_KEY_NEXT;

    //                 break;

    //             case 2:

    //                 act_key = LV_KEY_PREV;

    //                 break;

    //             case 3:

    //                 act_key = LV_KEY_LEFT;

    //                 break;

    //             case 4:

    //                 act_key = LV_KEY_RIGHT;

    //                 break;

    //             case 5:

    //                 act_key = LV_KEY_ENTER;

    //                 break;

    //         }



    //         last_key = act_key;

    //     }

    //     else {

    //         data->state = LV_INDEV_STATE_REL;

    //     }



    //     data->key = last_key;

    // }



    // /*Get the currently being pressed key.  0 if no key is pressed*/

    // static uint32_t keypad_get_key(void)

    // {

    //     /*Your code comes here*/



    //     return 0;

    // }



    // /*------------------

    //  * Encoder

    //  * -----------------*/



    // /*Initialize your keypad*/

    // static void encoder_init(void)

    // {

    //     /*Your code comes here*/

    // }



    // /*Will be called by the library to read the encoder*/

    // static void encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)

    // {



    //     data->enc_diff = encoder_diff;

    //     data->state = encoder_state;

    // }



    // /*Call this function in an interrupt to process encoder events (turn, press)*/

    // static void encoder_handler(void)

    // {

    //     /*Your code comes here*/



    //     encoder_diff += 0;

    //     encoder_state = LV_INDEV_STATE_REL;

    // }



    /*------------------

     * Button

     * -----------------*/



    /*Initialize your buttons*/

    // static void button_init(void)

    // {

    //     /*Your code comes here*/

    // }



    // /*Will be called by the library to read the button*/

    // static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)

    // {



    //     static uint8_t last_btn = 0;



    //     /*Get the pressed button's ID*/

    //     int8_t btn_act = button_get_pressed_id();



    //     if(btn_act >= 0) {

    //         data->state = LV_INDEV_STATE_PR;

    //         last_btn = btn_act;

    //     }

    //     else {

    //         data->state = LV_INDEV_STATE_REL;

    //     }



    //     /*Save the last pressed button's ID*/

    //     data->btn_id = last_btn;

    // }



    // /*Get ID  (0, 1, 2 ..) of the pressed button*/

    // static int8_t button_get_pressed_id(void)

    // {

    //     uint8_t i;



    //     /*Check to buttons see which is being pressed (assume there are 2 buttons)*/

    //     for(i = 0; i < 2; i++) {

    //         /*Return the pressed button's ID*/

    //         if(button_is_pressed(i)) {

    //             return i;

    //         }

    //     }



    //     /*No button pressed*/

    //     return -1;

    // }



    // /*Test if `id` button is pressed or not*/

    // static bool button_is_pressed(uint8_t id)

    // {



    //     /*Your code comes here*/



    //     return false;

    // }



    #else /*Enable this file at the top*/



    /*This dummy typedef exists purely to silence -Wpedantic.*/

    typedef int keep_pedantic_happy;

    #endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/**

 * @file main.c

 * @author Jamiexu(Jamie793/Jamies19) (doxm@foxmail.com)

 * @brief

 * @version 0.1

 * @date 2025-08-16

 *

 * @copyright Copyright (c) 2025

 *

 */

#include "main.h"  

#include "gpio.h"

#include "fsmc.h"  

#include "delay.h"

#include "tim.h"

#include "ili9341.h"

#include "xpt2046.h"

#include "lvgl.h"

#include "lv_conf.h"

#include "lv_port_disp.h" // Include the LVGL display port header

#include "lv_port_indev.h" // Include the LVGL input device port header



lv_obj_t *label;

void btn_event_cb(lv_event_t *e)

{

    if(e->code == LV_EVENT_CLICKED) {

        lv_label_set_text(label, "Hi, Here you go!");

        lv_obj_set_style_text_color(label, lv_color_hex(0xFF0000), 0); // Change text color to red

    }

}




int main(void)

{

    // Initialize the GPIO and FSMC

    coreSystickInit();

    coreGPIOInit();

    coreFSMCInit();

    coreTIMInit(); // Initialize the timer for LVGL tick handling

    // Initialize the ili9341 LCD

    iLI9341Init();

    // Initialize the XPT2046 touch controller

    // xpt2046Calibrate();



    lv_init(); // Initialize LVGL

    lv_port_disp_init(); // Initialize the display port for LVGL

    lv_port_indev_init();




    // Create a simple label to test LVGL

    label = lv_label_create(lv_scr_act());

    lv_label_set_text(label, "Hello, LVGL on ili9341!");

    lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);

    // Align the label to the center of the screen

    lv_obj_set_style_text_color(label, lv_color_hex(0x000000), 0); // Set text color to white

    lv_obj_set_style_text_font(label, &lv_font_montserrat_14, 0); // Set font to Montserrat 14

    lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0); // Center the text



    lv_obj_t *btn = lv_btn_create(lv_scr_act()); // Create a button

    lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL); // Add event callback

    lv_obj_set_size(btn, 100, 50); // Set button size

    lv_obj_set_pos(btn, 0, 0); // Set button position  



    lv_obj_align(btn, LV_ALIGN_CENTER, 0, -50); // Align the button to the center of the screen

    lv_obj_set_style_bg_color(btn, lv_color_hex(0x00FF00), 0); // Set background color to black

    lv_obj_set_style_text_color(btn, lv_color_hex(0xFFFFFF), 0); // Set text color to white

    lv_obj_set_style_text_font(btn, &lv_font_montserrat_14, 0); // Set font to Montserrat 14

    lv_obj_set_style_text_align(btn, LV_TEXT_ALIGN_CENTER, 0); // Center the text

    lv_obj_t *btn_label = lv_label_create(btn); // Create a label for the button

    lv_label_set_text(btn_label, "Click Me!"); // Set button label text

    lv_obj_align(btn_label, LV_ALIGN_CENTER, 0, 0); // Center the label in the button







    while (1)

    {

        // Handle LVGL tasks

        lv_task_handler();

        // Main loop

    }

    return 0;

}
  • 将程序烧录后显示如下图e28b59645f05ca99b6938dc04ed15cae.jpeg
  • 点击按钮后变成如下图29efc0a6006748bdf5ff7d4e8631fd9a.jpeg
  • 至此所有的工作完结

6. 开源说明

  1. 嵌入式火. FSMC— 扩展外部 SRAM [EB/OL]]. https://doc.embedfire.com/mcu/stm32/f103badao/std/zh/latest/book/FSMC.html.
  2. STMicroelectronics. RM0008 STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx advanced ARM®-based 32-bit MCUs[Z]. 2024. https://www.st.com/resource/en/reference_manual/rm0008-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf.
  3. STMicroelectronics. STM32F103xE Datasheet[Z]. 2024. https://www.st.com/resource/en/datasheet/stm32f103rc.pdf.
  4. 【Proteus/8086】微机与接口技术实验:计时器 [EB/OL]. 2023. https://blog.csdn.net/qq_61814350/article/details/135140964.
  5. Adafruit Industries. ILI9341 Datasheet [Z]. https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf.
  6. LVGL. LVGL Documentation 8.3 [EB/OL]. https://docs.lvgl.io/8.3/.