这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【gd32f527移植Zephyr】实现一键配置TLI

共1条 1/1 1 跳转至

【gd32f527移植Zephyr】实现一键配置TLI

高工
2026-02-23 15:06:55     打赏
GD32 TLI显示控制器Zephyr驱动移植详解
摘要
本文详细介绍如何将GD32F527的TLI(Touch LCD Interface)显示控制器移植到Zephyr RTOS中,实现一键配置功能。通过Zephyr的MEMC子系统实现SDRAM自动初始化,DISPLAY子系统实现TLI自动初始化,让用户无需编写任何初始化代码即可使用LCD显示功能。
 一、概述
1.1 背景
GD32F527I-EVAL开发板板载480x272分辨率RGB LCD显示屏,需要通过TLI(Touch LCD Interface)接口驱动。以往的裸机开发需要用户手动编写大量初始化代码,包括:
- EXMC SDRAM初始化- TLI GPIO配置- PLLSAI像素时钟配置- TLI时序参数配置- 帧缓冲区内存管理
本文的目标是将这些初始化工作移植到Zephyr驱动层,实现自动初始化,用户只需在配置文件中指定LCD参数即可。
 1.2 系统架构

image.png二、驱动设计
 2.1 设备树绑定
首先需要定义TLI设备的设备树绑定(Device Tree Binding),让Zephyr能够识别并配置TLI硬件。
创建文件 `dts/bindings/display/gd,gd32-tli.yaml`
# SPDX-License-Identifier: Apache-2.0
description: GD32 TLI (Touch LCD Interface) Display Controller
include: base.yaml
properties:
  compatible:
    const: "gd,gd32-tli"
  reg:
    description: TLI registers base address
    required: true
  interrupts:
    description: TLI interrupt
    required: true
  clocks:
    description: TLI clock source
    required: true
  # Display parameters
  width:
    type: int
    required: true
    description: Display width in pixels
  height:
    type: int
    required: true
    description: Display height in pixels
  hsync:
    type: int
    required: false
    default: 41
    description: Horizontal sync pulse width
  hbp:
    type: int
    required: false
    default: 2
    description: Horizontal back porch
  hfp:
    type: int
    required: false
    default: 2
    description: Horizontal front porch
  vsync:
    type: int
    required: false
    default: 10
    description: Vertical sync pulse width
  vbp:
    type: int
    required: false
    default: 2
    description: Vertical back porch
  vfp:
    type: int
    required: false
    default: 2
    description: Vertical front porch
  framebuffer:
    type: phandle
    required: false
    description: Framebuffer memory location (typically SDRAM)
 2.2 SoC设备树
在GD32F5xx SoC设备树中添加TLI节点:
修改 `dts/arm/gd/gd32f5xx/gd32f5xx.dtsi`
tli: tli@40017000 {
    compatible = "gd,gd32-tli";
    reg = <0x40017000 0x400>;
    interrupts = <84 0>;
    clocks = <&cctl GD32_CLOCK_TLI>;
    status = "disabled";
};
 2.3 板级设备树
在GD32F527-EVAL开发板设备树中启用TLI:
修改 `boards/gd/gd32f527_eval/gd32f527_eval.dts`
&tli {
    status = "okay";
    compatible = "gd,gd32-tli";
    reg = <0x40017000 0x400>;
    interrupts = <84 0>;
    width = <480>;
    height = <272>;
    hsync = <41>;
    hbp = <2>;
    hfp = <2>;
    vsync = <10>;
    vbp = <2>;
    vfp = <2>;
    framebuffer = <0xC0000000>;
};
 三、驱动实现
 3.1 Kconfig配置
创建 `drivers/display/Kconfig.gd32_tli`
# SPDX-License-Identifier: Apache-2.0
config DISPLAY_GD32_TLI
    bool "GD32 TLI Display Controller support"
    depends on DISPLAY && GPIO
    help
      Enable support for GD32 TLI (Touch LCD Interface) display controller.
      This driver configures the TLI peripheral for RGB LCD displays
      and provides framebuffer access.

3.2 驱动源代码
创建 `drivers/display/display_gd32_tli.c`
/*
 * GD32 TLI (Touch LCD Interface) Display Driver
 * Simplified version - uses fixed configuration
 */
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/display.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/logging/log.h>
#include <gd32f5xx_tli.h>
#include <gd32f5xx_rcu.h>
#include <gd32f5xx_gpio.h>
LOG_MODULE_REGISTER(display_gd32_tli, CONFIG_DISPLAY_LOG_LEVEL);
/* SystemCoreClock is defined in GD32 HAL/startup code */
extern uint32_t SystemCoreClock;
/* LCD Configuration - matches GD32F527I-EVAL with 480x272 display */
#define LCD_WIDTH  480
#define LCD_HEIGHT 272
#define LCD_HSYNC  41
#define LCD_HBP    2
#define LCD_HFP    2
#define LCD_VSYNC  10
#define LCD_VBP    2
#define LCD_VFP    2
/* Framebuffer at SDRAM base */
#define FRAMEBUFFER_ADDR 0xC0000000
struct display_gd32_tli_data {
    uint8_t *frame_buffer;
    enum display_pixel_format current_format;
    uint8_t pixel_size;
};
struct display_gd32_tli_config {
    uint32_t base_addr;
};
static int gd32_tli_init(const struct device *dev)
{
    struct display_gd32_tli_data *data = (struct display_gd32_tli_data *)dev->data;
    LOG_INF("GD32 TLI Display Driver initializing...");
    LOG_INF("Display size: %dx%d", LCD_WIDTH, LCD_HEIGHT);
    LOG_INF("Framebuffer: 0x%08X", FRAMEBUFFER_ADDR);
    /* Enable TLI clock */
    rcu_periph_clock_enable(RCU_TLI);
    /* Configure GPIO for TLI */
    /* GPIOB - Backlight control */
    rcu_periph_clock_enable(RCU_GPIOB);
    gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_15);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_15);
    gpio_bit_set(GPIOB, GPIO_PIN_15);
    /* GPIOE - TLI signals */
    rcu_periph_clock_enable(RCU_GPIOE);
    gpio_af_set(GPIOE, GPIO_AF_14, GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6);
    gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6);
    gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6);
    /* GPIOH - TLI data bus */
    rcu_periph_clock_enable(RCU_GPIOH);
    gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_8 | GPIO_PIN_9 |
            GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
    gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE,
            GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_8 | GPIO_PIN_9 |
            GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
    gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,
            GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_8 | GPIO_PIN_9 |
            GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
    /* GPIOI - TLI data bus */
    rcu_periph_clock_enable(RCU_GPIOI);
    gpio_af_set(GPIOI, GPIO_AF_14, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 |
            GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_9 | GPIO_PIN_10);
    gpio_mode_set(GPIOI, GPIO_MODE_AF, GPIO_PUPD_NONE,
            GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 |
            GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_9 | GPIO_PIN_10);
    gpio_output_options_set(GPIOI, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,
            GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 |
            GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_9 | GPIO_PIN_10);
    /* GPIOG - TLI signals */
    rcu_periph_clock_enable(RCU_GPIOG);
    gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);
    gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);
    gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12);
    /* GPIOF - TLI clock */
    rcu_periph_clock_enable(RCU_GPIOF);
    gpio_af_set(GPIOF, GPIO_AF_14, GPIO_PIN_10);
    gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
    gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
    LOG_INF("TLI GPIO configured");
    /* Configure PLLSAI for pixel clock */
    /* PLLSAI = 240MHz, DIV8 = 30MHz pixel clock for 480x272 */
    if (ERROR == rcu_pllsai_r_config(240, 2)) {
        LOG_ERR("Failed to configure PLLSAI!");
        return -EIO;
    }
    rcu_tli_clock_div_config(RCU_PLLSAIR_DIV8);
    rcu_osci_on(RCU_PLLSAI_CK);
    if (ERROR == rcu_osci_stab_wait(RCU_PLLSAI_CK)) {
        LOG_ERR("PLLSAI not stable!");
        return -EIO;
    }
    LOG_INF("TLI clock configured");
    /* Configure TLI */
    tli_parameter_struct tli_init_struct;
    tli_layer_parameter_struct tli_layer_init_struct;
    /* Signal polarity */
    tli_init_struct.signalpolarity_hs = TLI_HSYN_ACTLIVE_LOW;
    tli_init_struct.signalpolarity_vs = TLI_VSYN_ACTLIVE_LOW;
    tli_init_struct.signalpolarity_de = TLI_DE_ACTLIVE_LOW;
    tli_init_struct.signalpolarity_pixelck = TLI_PIXEL_CLOCK_TLI;
    /* Timing */
    tli_init_struct.synpsz_hpsz = LCD_HSYNC - 1;
    tli_init_struct.synpsz_vpsz = LCD_VSYNC - 1;
    tli_init_struct.backpsz_hbpsz = LCD_HSYNC + LCD_HBP - 1;
    tli_init_struct.backpsz_vbpsz = LCD_VSYNC + LCD_VBP - 1;
    tli_init_struct.activesz_hasz = LCD_HSYNC + LCD_HBP + LCD_WIDTH - 1;
    tli_init_struct.activesz_vasz = LCD_VSYNC + LCD_VBP + LCD_HEIGHT - 1;
    tli_init_struct.totalsz_htsz = LCD_HSYNC + LCD_HBP + LCD_WIDTH + LCD_HFP - 1;
    tli_init_struct.totalsz_vtsz = LCD_VSYNC + LCD_VBP + LCD_HEIGHT + LCD_VFP - 1;
    /* Background color */
    tli_init_struct.backcolor_red = 0;
    tli_init_struct.backcolor_green = 0;
    tli_init_struct.backcolor_blue = 0;
    tli_init(&tli_init_struct);
    /* Layer configuration */
    tli_layer_init_struct.layer_window_leftpos = LCD_HSYNC + LCD_HBP;
    tli_layer_init_struct.layer_window_rightpos = LCD_HSYNC + LCD_HBP + LCD_WIDTH - 1;
    tli_layer_init_struct.layer_window_toppos = LCD_VSYNC + LCD_VBP;
    tli_layer_init_struct.layer_window_bottompos = LCD_VSYNC + LCD_VBP + LCD_HEIGHT - 1;
    tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565;
    tli_layer_init_struct.layer_sa = 0xFF;
    tli_layer_init_struct.layer_default_blue = 0;
    tli_layer_init_struct.layer_default_green = 0;
    tli_layer_init_struct.layer_default_red = 0;
    tli_layer_init_struct.layer_default_alpha = 0;
    tli_layer_init_struct.layer_acf1 = LAYER_ACF1_PASA;
    tli_layer_init_struct.layer_acf2 = LAYER_ACF2_PASA;
    tli_layer_init_struct.layer_frame_bufaddr = FRAMEBUFFER_ADDR;
    tli_layer_init_struct.layer_frame_line_length = ((LCD_WIDTH * 2) + 3);
    tli_layer_init_struct.layer_frame_buf_stride_offset = (LCD_WIDTH * 2);
    tli_layer_init_struct.layer_frame_total_line_number = LCD_HEIGHT;
    tli_layer_init(LAYER0, &tli_layer_init_struct);
    /* Enable layer */
    tli_layer_enable(LAYER0);
    /* Reload configuration */
    tli_reload_config(TLI_FRAME_BLANK_RELOAD_EN);
    /* Enable TLI */
    tli_enable();
    /* Store framebuffer info */
    data->frame_buffer = (uint8_t *)FRAMEBUFFER_ADDR;
    data->current_format = PIXEL_FORMAT_RGB_565;
    data->pixel_size = 2;
    LOG_INF("GD32 TLI initialized successfully");
    return 0;
}
/* Display driver API implementation */
static void gd32_tli_get_capabilities(const struct device *dev,
                                      struct display_capabilities *capabilities)
{
    ARG_UNUSED(dev);
    capabilities->x_resolution = LCD_WIDTH;
    capabilities->y_resolution = LCD_HEIGHT;
    capabilities->supported_pixel_formats = BIT(PIXEL_FORMAT_RGB_565);
    capabilities->current_pixel_format = PIXEL_FORMAT_RGB_565;
    capabilities->screen_info = 0;
}
static void *gd32_tli_get_framebuffer(const struct device *dev)
{
    struct display_gd32_tli_data *data = (struct display_gd32_tli_data *)dev->data;
    return data->frame_buffer;
}
/* Device instantiation */
DEVICE_DEFINE(gd32_tli, "GD32_TLI",
              gd32_tli_init,
              NULL,
              &display_gd32_tli_data,
              &display_gd32_tli_config,
              POST_KERNEL, CONFIG_DISPLAY_INIT_PRIORITY,
              &gd32_tli_api);
3.3 CMakeLists.txt更新
修改 `drivers/display/CMakeLists.txt`,添加条件编译:
zephyr_library_sources_ifdef(CONFIG_DISPLAY_GD32_TLI display_gd32_tli.c)

修改 `drivers/display/Kconfig`
source "drivers/display/Kconfig.gd32_tli"

 四、应用示例
4.1 项目配置
用户只需在 `prj.conf` 中启用相关配置:
# Enable Display subsystem
CONFIG_DISPLAY=y
CONFIG_DISPLAY_GD32_TLI=y
# Enable MEMC for SDRAM
CONFIG_MEMC=y
CONFIG_MEMC_GD32_EXMC=y
# LCD Configuration
CONFIG_LCD_WIDTH=480
CONFIG_LCD_HEIGHT=272
CONFIG_LCD_FRAMEBUFFER_ADDR=0xC0000000

4.2 图形库使用
图形库头文件定义了基本的宏和API:
#include "graphics.h"
/* LCD配置(从Kconfig获取) */
#define LCD_WIDTH  CONFIG_LCD_WIDTH
#define LCD_HEIGHT CONFIG_LCD_HEIGHT
#define FRAMEBUFFER_ADDR CONFIG_LCD_FRAMEBUFFER_ADDR
/* 颜色定义 */
#define COLOR_BLACK   0x0000
#define COLOR_WHITE   0xFFFF
#define COLOR_RED     0xF800
#define COLOR_GREEN   0x07E0
#define COLOR_BLUE    0x001F
图形API:
// 填充屏幕
void fill_screen(uint16_t color);
// 画点
void draw_pixel(uint16_t x, uint16_t y, uint16_t color);
// 画线
void draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);
// 画圆
void draw_circle(uint16_t x0, uint16_t y0, uint16_t radius, uint16_t color);
// 画填充圆
void draw_filled_circle(uint16_t x0, uint16_t y0, uint16_t radius, uint16_t color);
// 字符显示
void draw_string(uint16_t x, uint16_t y, const char *str, uint16_t fg_color, uint16_t bg_color);
 4.3 主程序示例
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include "graphics.h"
extern uint32_t SystemCoreClock;
int main(void)
{
    printk("GD32F527 LCD Demo\r\n");
    /* LCD和SDRAM已由驱动自动初始化 */
    while (1) {
        /* 测试画点 */
        fill_screen(COLOR_BLACK);
        for (int i = 0; i < 100; i++) {
            draw_pixel(50 + i, 50, COLOR_RED);
        }
        k_msleep(2000);
        /* 测试画线 */
        fill_screen(COLOR_BLACK);
        draw_line(10, 10, 400, 200, COLOR_GREEN);
        k_msleep(2000);
        /* 测试画圆 */
        fill_screen(COLOR_BLACK);
        draw_circle(240, 136, 80, COLOR_BLUE);
        draw_filled_circle(240, 136, 50, COLOR_RED);
        k_msleep(2000);
    }
}
 五、关键实现点
 5.1 驱动初始化时机
使用 `DEVICE_DEFINE` 宏注册设备,配合 `POST_KERNEL` 初始化级别,确保驱动在其他系统组件初始化之前完成初始化:
DEVICE_DEFINE(gd32_tli, "GD32_TLI",
              gd32_tli_init,
              NULL,
              &display_gd32_tli_data,
              &display_gd32_tli_config,
              POST_KERNEL, CONFIG_DISPLAY_INIT_PRIORITY,
              &gd32_tli_api);
 5.2 帧缓冲区管理
帧缓冲区直接映射到SDRAM地址 `0xC0000000`,这是EXMC映射的起始地址。TLI控制器会自动从这个地址读取像素数据并发送到LCD显示。
5.3 像素时钟配置
TLI需要特定的像素时钟频率。使用PLLSAI产生30MHz像素时钟:/* PLLSAI = 240MHz, DIV8 = 30MHz */
rcu_pllsai_r_config(240, 2);
rcu_tli_clock_div_config(RCU_PLLSAIR_DIV8);
六、总结
通过本文的移植方案,实现了以下目标:
1. 一键配置:用户只需在prj.conf中配置相关宏即可,无需编写初始化代码2. 自动初始化:SDRAM和TLI均由Zephyr驱动自动初始化3. 统一接口:通过Zephyr标准DISPLAY子系统接口访问4. 易于扩展:驱动架构清晰,便于后续添加新功能
该方案已成功在GD32F527I-EVAL开发板上验证,LCD可正常显示图形内容。
## 参考资料
- Zephyr Project Documentation: https://docs.zephyrproject.org/- GD32F5xx User Manual- GD32F5xx Firmware Library

附工程源码:

lcd_sdram.zip






关键词: gd32f527     Zephyr     tli     lcd    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]