跳转至

QSPI 屏驱动适配指南

本文档以 CST820T 触摸 IC 和 CO5300AF-08 屏驱动 IC 为例,详细说明如何在 HiDiTing 开发板上完成 QSPI 屏适配。其中 LCD 图像数据通过 QSPI 传输,CST820T 的触摸数据通过 I2C 读取,两条链路应分别验证。


QSPI 触摸屏驱动背景知识

术语表

缩写 完整拼写 中文说明
QSPI Quad Serial Peripheral Interface 四线串行外设接口
TP Touch Panel 触摸屏
IC Integrated Circuit 集成电路
I2C Inter-Integrated Circuit 内部集成电路总线
SPI Serial Peripheral Interface 串行外设接口
LCD Liquid Crystal Display 液晶显示器
RGB Red Green Blue 红绿蓝色彩空间
FPS Frames Per Second 每秒帧数
HBM High Brightness Mode 高亮度模式
TE Tearing Effect 撕裂效果
INT Interrupt 中断
RST Reset 复位
SCL Serial Clock 串行时钟线
SDA Serial Data 串行数据线
MISO Master In Slave Out 主设备输入从设备输出
MOSI Master Out Slave In 主设备输出从设备输入
API Application Programming Interface 应用程序编程接口
ID Identifier 标识符
GPIO General Purpose Input Output 通用输入输出

QSPI 点屏工作原理

QSPI(四线串行外设接口)点屏通过四条数据线和一条时钟线高速传输图像数据和控制命令,刷新显示屏内容。其工作机制如下:

功能 说明 应用场景
数据传输 通过 QSPI 接口传输图像数据到帧缓冲区 显示图像、文字、UI 界面
命令控制 发送控制命令配置显示屏参数 初始化、亮度调节、睡眠唤醒
帧缓冲刷新 根据帧缓冲区数据刷新屏幕内容 屏幕点亮、画面更新

触摸屏驱动通信接口

TP(触摸屏)驱动通常使用以下接口与主控芯片通信:

接口类型 说明 适用场景
I2C 最常见,适用于电容屏 本案例选用
SPI 高速场景 高刷新率触摸屏

触摸硬件连接

信号线 说明 注意事项
SCL/SCK I2C/SPI 时钟线 需配置正确的时钟频率
SDA/MOSI I2C 数据线/SPI 输出 上拉电阻(通常 4.7KΩ)
MISO SPI 输入线 仅 SPI 模式需要
INT 中断引脚 配置为边沿触发(上升沿/下降沿)
RST 复位引脚 上电时需拉低至少 10ms

触摸事件数据流向

触摸事件数据流向


快速适配 QSPI 触摸屏驱动

功能说明

QSPI 触摸屏驱动适配包含两大核心功能:

功能模块 说明 关键文件
QSPI 点屏驱动 配置上电序列,点亮屏幕 lcd_config.c
TP 触摸驱动 初始化触摸芯片,解析坐标和手势,上报事件 touch_cst820_qspi.c

编译

完成一站式 CLI 环境配置后执行:

# 编译固件,编译生成的固件从 output/3322/fwpkg 中获取 diting-community.fwpkg
fbb set-target pack_diting_community
fbb build

构建成功后,使用一站式 CLI 烧写固件并打开 UART2 串口监视器。以下为 Windows USB DFU 示例;将 COM3 替换为实际日志串口,其他平台和串口烧写参数参见一站式 CLI 开发环境使用指南

fbb flash -f "$env:FBB_SDK_DIR\output\3322\fwpkg\diting-community.fwpkg" --chip 3322 -d --timeout 180
fbb monitor --port COM3 --baud 750000

使用方式

  1. 确认当前 diting_community.config 已设置 CONFIG_INPUT_USING_TPTYPE_CST820_QSPI=y,目标配置已启用 SUPPORT_GPU_QSPI
  2. 烧录固件并启动设备,系统会自动完成屏幕初始化和触摸驱动加载。
  3. 750000 波特率打开 UART2 串口,执行 AT+GPU=smoke 验证 QSPI 显示链路。
  4. 在应用界面上下左右滑动并点击屏幕四角,观察界面是否按实际坐标响应,验证 I2C 触摸链路。

预期结果

屏幕点亮

系统启动后,屏幕应正常点亮并显示内容。

执行 AT+GPU=smoke 后串口返回 OK,屏幕显示测试画面,表示 LCD、帧缓冲、VAU、DPU 和 QSPI 刷新链路已跑通。

触摸事件上报

触摸屏幕时,应用界面应在相同方向和位置响应点击或滑动。仅看到屏幕点亮不能证明触摸 I2C 链路正常;仅有触摸中断日志也不能证明坐标映射正确。


文件结构与代码实现

文件结构

QSPI 触摸屏驱动的文件位于以下路径:

application/3322/input_wear/peripheral/touch/cst820_qspi_touch/
├── touch_cst820_qspi.c          # 驱动逻辑业务接口函数实现
├── touch_cst820_qspi.h          # 宏、数据结构、驱动接口函数定义
├── touch_cst820_qspi_drv.c      # 框架接口实现
└── touch_cst820_qspi_drv.h      # 框架接口定义

各文件职责

文件 职责 关键内容
touch_cst820_qspi.c 驱动逻辑实现,包含数据读取、坐标解析、手势识别 cst820_qspi_parse_coord(), cst820_qspi_gesture_judge()
touch_cst820_qspi.h 宏定义、数据结构、接口函数声明 CST820_REG_CHIP_ID, cst820_chip_data_t
touch_cst820_qspi_drv.c 框架接口实现,注册触摸驱动 API g_cst820_qspi_api, touch_screen_get_api()
touch_cst820_qspi_drv.h 框架接口定义 函数声明
lcd_config.c(已存在) 点屏驱动配置,包含上电序列和屏配置信息 g_co5300_3v3_screen_init_on[], 屏配置结构体

关键数据结构声明位置

数据结构 说明
cst820_chip_data_t 芯片数据,包含chip_id、firmware_version等
cst820_mode_status_t 芯片工作模式状态枚举
input_event_info_t 触摸事件信息结构体
tp_event_type_t 触摸事件类型枚举
touch_peripheral_api 触摸驱动框架API接口
tp_dev_info_t 触摸点坐标信息

代码实现

点屏驱动配置 (lcd_config.c)

业务说明:点屏驱动配置用于初始化 LCD 屏幕。文件位于 lcd_config.c

上电序列包含了解锁扩展命令、电源配置、像素格式、扫描方向、背光亮度等初始化命令。屏配置信息指定了QSPI总线类型、屏幕分辨率(466x466)、刷新率(60FPS)等参数。

序列命令结构体定义(位于 soc_lcd_api.h):

typedef struct lcd_cmd_sequ {
    uint8_t  dcs_flag; /* 当前数据是否通过DCS协议命令下发,为0表示不是DCS命令 */
    uint8_t  data_len; /* payload length, 小于等于64 */
    uint8_t  data[MAX_DATA_LENGTH]; /* data内包含cmd和参数,有效数据最短为1(例如0x11),最长为5 */
    uint16_t delay_ms; /* 发送命令后等待时间,单位ms */
} lcd_cmd_sequ;

上电序列配置

#define LCD_CO5300_QSPI_SCREEN_ID  0x1133

const static lcd_cmd_sequ g_co5300_3v3_screen_init_on[] = {
    {0, 2, {0xfe, 0x00}, 0},
    {0, 2, {0xc4, 0x80}, 0},
    {0, 2, {0x3a, 0x77}, 0},
    {0, 2, {0x36, 0x00}, 0},
    {0, 2, {0x35, 0x00}, 0},
    {0, 2, {0x53, 0x20}, 0},
    {0, 2, {0x51, 0x00}, 0},
    {0, 2, {0x63, 0xff}, 0},
    {0, 5, {0x2a, 0x00, 0x06, 0x01, 0xd7}, 0},
    {0, 5, {0x2b, 0x00, 0x00, 0x01, 0xd1}, 0},
    {0, 1, {0x11}, 80},
    {0, 1, {0x29}, 100},
};

初始化配置

static lcd_cmds_sequ g_co5300_3v3_screen_init_on_cmds = {
    .cmd = (lcd_cmd_sequ *)g_co5300_3v3_screen_init_on,
    .cmd_cnt = array_size(g_co5300_3v3_screen_init_on),
};

屏配置信息

    {
        .bus_type             = BUS_DISPLAY_QSPI,
        .bus_cfg              = (combo_dev_cfg_t *)(&g_panel_466x466_qspi_config),
        .x_start              = 0x6,
        .y_start              = 0,
        .fps                  = 60,
        .lcd_id               = LCD_CO5300_QSPI_SCREEN_ID,
        .display_on_cmds      = &g_co5300_3v3_screen_init_on_cmds,
        .display_off_cmds     = &g_display_off_cmds,
        .enter_idle_cmds      = &g_idle_in_cmds,
        .exit_idle_cmds       = &g_idle_out_cmds,
        .te_on_cmds           = &g_te_on_cmds,
        .te_off_cmds          = &g_te_off_cmds,
        .set_brightnes_cmds   = &g_brightness_cmds,
        .hbm_brightness_cmds  = &g_hbm_brightness_cmds,
        .sleep_in_cmds        = &g_sleep_in_cmds,
        .sleep_out_cmds       = &g_sleep_out_cmds,
        .lcd_id_info.reg_addr = 0x04,
        .lcd_id_info.read_len = 2,
    },

触摸驱动头文件 (touch_cst820_qspi.h)

业务说明:此文件定义驱动相关的宏、数据结构和函数接口。

  • 芯片ID定义CST820_QSPI_CHIP_ID = 0xB6 用于识别QSPI版本的CST820芯片
  • 寄存器地址:芯片内部寄存器的地址定义,用于读取芯片ID、版本信息等
  • 触摸数据结构cst820_chip_data_t 保存芯片ID、固件版本、项目ID等信息
  • 工作模式枚举cst820_mode_status_t 包括正常工作、待机、休眠等模式

完整代码

/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2026. All rights reserved.
 * Description: TP CST820(QSPI) Driver header
 * Create: 2026-06-25
 */

#ifndef MC_TOUCH_CST820_QSPI_H
#define MC_TOUCH_CST820_QSPI_H

#include "stdint.h"
#include "input_feature.h"
#include "touch_ctrl.h"

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */

#define CST820_REG_CHIP_ID 0xA7
#define CST820_REG_FW_VERSION 0xA9
#define CST820_REG_SLEEP_MODE 0xE5
#define CST820_CHIP_ID 0xB7
#define CST820_QSPI_CHIP_ID 0xB6

#define CST820_TOUCHINFO_DATA_LEN 4

#define CST820_TOUCHINFO_HEAD_LEN 3
#define CST820_POINTINFO_LEN 4
#define CST820_TOUCHINFO_LEN (CST820_TOUCHINFO_HEAD_LEN + CST820_POINTINFO_LEN)

#define CST820_XH_OFFSET 3
#define CST820_XL_OFFSET 4
#define CST820_YH_OFFSET 5
#define CST820_YL_OFFSET 6
#define CST820_WIDTH_OFFSET 8

#define CST820_TOUCHINFO_MASK 0x0F
#define CST820_TOUCH_STATE_MASK 0xC0
#define CST820_TOUCH_STATE_BYTE_SHIFT 6

typedef enum {
    CST820_WORK_NORMAL = 0,
    CST820_WORK_STANDBY,
    CST820_WORK_GETCMCP,
    CST820_WORK_SLEEP,
    CST820_WORK_SUSPEND,
    CST820_WORK_RESUME,
    CST820_WORK_INVALID,
} cst820_mode_status_t;

typedef struct {
    uint16_t chip_id;
    uint16_t firmware_version;
    uint16_t project_id;
    cst820_mode_status_t mode_status;
} cst820_chip_data_t;

int32_t cst820_qspi_init(void);
int32_t cst820_qspi_set_power_mode(input_dev_workmode_t powerMode);
int32_t cst820_qspi_irq_callback(input_event_info_t *eventInfo, uint8_t data_len);

#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */

#endif /* MC_TOUCH_CST820_QSPI_H */

触摸驱动实现 (touch_cst820_qspi.c)

业务说明:此文件实现核心业务逻辑。

  1. 硬件复位流程 (cst820_qspi_hardware_reset):

    • 先拉高RST引脚等待1ms
    • 拉低RST引脚10ms进行复位
    • 再拉高RST引脚等待5ms完成复位
  2. 工作模式切换 (cst820_qspi_set_power_mode):

    • 支持睡眠模式(INPUT_DEV_SLEEP_MODE)
    • 支持正常工作模式(INPUT_DEV_NORMAL_MODE)
    • 支持待机模式(INPUT_DEV_STANDBY_MODE)
  3. 坐标解析 (cst820_qspi_parse_coord):

    • 从芯片寄存器地址0x00读取触摸数据(7字节)
    • 解析X坐标:高字节低4位与低字节拼接,形成12位坐标
    • 解析Y坐标:同上
    • 解析触摸状态:tp_data[1]==0x00表示普通触摸事件,否则为手势事件
    • 普通触摸事件中,tp_data[XH_OFFSET]高2位表示触摸类型(按下/移动/释放)
  4. 手势识别 (cst820_qspi_gesture_judge):

    • 上滑(0x01)、下滑(0x02)
    • 左滑(0x03)、右滑(0x04)
    • 单击(0x05)、双击(0x0B)
    • 长按(0x0C)、覆盖(0xAA)

完整代码

/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2026. All rights reserved.
 * Description: TP CST820(QSPI) Driver
 * Create: 2026-06-27
 */

#include "stdio.h"
#include "soc_errno.h"
#include "soc_osal.h"
#include "platform_core.h"
#include "gpio.h"
#include "cmsis_os.h"
#include "touch_cst820_qspi.h"

#define EC_SUCCESS 0
#define EC_FAILURE (-1)
#define CUSTOM_SENSOR_NUM (10)

static int cst820T_qspi_updata_tpinfo(void);
static int cst820T_qspi_init(void);

static cst820_chip_data_t g_cst820_chip_data;

static void cst820_qspi_hardware_reset(void)
{
    uapi_gpio_set_dir(TOUCH_RESET_GPIO, GPIO_DIRECTION_OUTPUT);

    uapi_gpio_set_val(TOUCH_RESET_GPIO, GPIO_LEVEL_HIGH);
    /* delay 1ms */
    osDelay(1);
    uapi_gpio_set_val(TOUCH_RESET_GPIO, GPIO_LEVEL_LOW);
    /* delay 10ms */
    osDelay(10);
    uapi_gpio_set_val(TOUCH_RESET_GPIO, GPIO_LEVEL_HIGH);
    /* delay 5ms */
    osDelay(5);
}

static void cst820_qspi_power_control(BOOL enable)
{
    UNUSED(enable);
    if (enable == TRUE) {
        cst820_qspi_hardware_reset();
    }
}

int32_t cst820_qspi_reboot(void)
{
    cst820_qspi_power_control(FALSE);
    cst820_qspi_power_control(TRUE);

    return EC_SUCCESS;
}

static int32_t cst820_qspi_set_standby_mode(void)
{
    uint8_t data = CST820_WORK_SLEEP;
    tp_i2c_data_write(CST820_REG_SLEEP_MODE, &data, 1);
    uapi_gpio_set_val(TOUCH_RESET_GPIO, GPIO_LEVEL_LOW);
    uapi_gpio_disable_interrupt(TOUCH_INT_GPIO);
    g_cst820_chip_data.mode_status = CST820_WORK_STANDBY;
    return EC_SUCCESS;
}

static int32_t cst820_qspi_set_normal_mode(void)
{
    cst820_qspi_reboot();
    uapi_gpio_enable_interrupt(TOUCH_INT_GPIO);
    g_cst820_chip_data.mode_status = CST820_WORK_NORMAL;
    return EC_SUCCESS;
}

static int32_t cst820_qspi_set_sleep_mode(void)
{
    // write data(0xE503)
    uint8_t data = CST820_WORK_SLEEP;
    tp_i2c_data_write(CST820_REG_SLEEP_MODE, &data, 1);
    uapi_gpio_set_val(TOUCH_RESET_GPIO, GPIO_LEVEL_LOW);
    uapi_gpio_disable_interrupt(TOUCH_INT_GPIO);
    g_cst820_chip_data.mode_status = CST820_WORK_SLEEP;
    return EC_SUCCESS;
}

int32_t cst820_qspi_set_power_mode(input_dev_workmode_t powerMode)
{
    INPUT_FEATURE_PRINT_INFO(APP_TOUCH, "Enter mode=%d\r\n", powerMode);
    switch (powerMode) {
        case INPUT_DEV_SLEEP_MODE:
            return cst820_qspi_set_sleep_mode();
        case INPUT_DEV_NORMAL_MODE:
            return cst820_qspi_set_normal_mode();
        case INPUT_DEV_STANDBY_MODE:
            return cst820_qspi_set_standby_mode();
    }

    return EC_SUCCESS;
}

static BOOL cst820_qspi_check_int_valid(void)
{
    if ((g_cst820_chip_data.mode_status != CST820_WORK_STANDBY) &&
        (g_cst820_chip_data.mode_status != CST820_WORK_NORMAL)) {
        INPUT_FEATURE_PRINT_DEBUG(
            APP_TOUCH, "TOUCH:CST820_QSPI invalid int err status %d\r\n", g_cst820_chip_data.mode_status);
        return FALSE;
    }

    return TRUE;
}

static void cst820_qspi_gesture_judge(input_event_info_t *input_event_info, uint8_t data_tag)
{
    switch (data_tag) {
        case 0x01:
            input_event_info->tp_event = MC_TP_SLIDE_UP;
            break;
        case 0x02:
            input_event_info->tp_event = MC_TP_SLIDE_DOWN;
            break;
        case 0x03:  // left
            input_event_info->tp_event = MC_TP_MOVE;
            break;
        case 0x04:  // right
            input_event_info->tp_event = MC_TP_MOVE;
            break;
        case 0x05:
            input_event_info->tp_event = MC_TP_SHORT_CLICK;
            break;
        case 0x0B:
            input_event_info->tp_event = MC_TP_DOUBLE_CLICK;
            break;
        case 0x0C:  // long press
            input_event_info->tp_event = MC_TP_PRESS;
            break;
        case 0xAA:
            input_event_info->tp_event = MC_TP_COVER;
            break;
        default:
            break;
    }
}

static int32_t cst820_qspi_parse_coord(input_event_info_t *input_event_info)
{
    ext_errno ret;
    uint8_t tp_data[CST820_TOUCHINFO_LEN];
    uint16_t x_axis = 0;
    uint16_t y_axis = 0;
    uint8_t state_id = 0;

    ret = tp_i2c_data_read(0x0, &tp_data[0], CST820_TOUCHINFO_LEN);
    if (ret != EC_SUCCESS) {
        INPUT_FEATURE_PRINT_ERR(APP_TOUCH, "TOUCH:CST820_QSPI read input fail \r\n");
        input_event_info->tp_event = MC_TP_INVALID;
        return EC_SUCCESS;
    }
    x_axis = (uint16_t)(tp_data[CST820_XH_OFFSET] & CST820_TOUCHINFO_MASK);
    x_axis = (x_axis << CST820_WIDTH_OFFSET) + (uint16_t)tp_data[CST820_XL_OFFSET];

    y_axis = (uint16_t)(tp_data[CST820_YH_OFFSET] & CST820_TOUCHINFO_MASK);
    y_axis = (y_axis << CST820_WIDTH_OFFSET) + (uint16_t)tp_data[CST820_YL_OFFSET];

    input_event_info->tp_event_info.x_axis[0] = x_axis;
    input_event_info->tp_event_info.y_axis[0] = y_axis;
    input_event_info->point_num = tp_data[2]; /* 2: number of touch points */
    if (tp_data[1] == 0x00) {
        uint8_t touch_type = tp_data[CST820_XH_OFFSET] >> CST820_TOUCH_STATE_BYTE_SHIFT;
        if (touch_type == 0x00) {
            input_event_info->tp_event = MC_TP_PRESS;
        } else if (touch_type == 0x02) {
            input_event_info->tp_event = MC_TP_MOVE;
        } else {  // 0x01
            input_event_info->tp_event = MC_TP_RELEASE;
        }
    } else {
        cst820_qspi_gesture_judge(input_event_info, tp_data[1]);
    }
    return EC_SUCCESS;
}

int32_t cst820_qspi_irq_callback(input_event_info_t *eventInfo, uint8_t data_len)
{
    int32_t ret;

    if (eventInfo == NULL) {
        return EC_FAILURE;
    }

    if (cst820_qspi_check_int_valid() == (unsigned int)FALSE) {
        return EC_SUCCESS;
    }

    if (g_cst820_chip_data.mode_status == CST820_WORK_NORMAL) {
        ret = cst820_qspi_parse_coord(eventInfo);
    } else {
        ret = EC_FAILURE;
    }

    return ret;
}

static int32_t cst820_qspi_deinit(void)
{
    return EC_SUCCESS;
}

static int cst820T_qspi_updata_tpinfo(void)
{
    INPUT_FEATURE_PRINT_INFO(APP_TOUCH, "Enter\r\n");
    uint8_t buf[CST820_TOUCHINFO_DATA_LEN];
    int ret = 0;
    ret = tp_i2c_data_read_reg(CST820_REG_CHIP_ID, buf, CST820_TOUCHINFO_DATA_LEN);
    if (ret == FALSE) {
        INPUT_FEATURE_PRINT_ERR(APP_TOUCH, " failed to red reg, ret=%d\r\n", ret);
        return FALSE;
    }
    g_cst820_chip_data.chip_id = buf[0];
    g_cst820_chip_data.project_id = buf[1];
    g_cst820_chip_data.firmware_version = buf[2]; /* 2: number of buf */
    if (g_cst820_chip_data.chip_id != CST820_QSPI_CHIP_ID) {
        INPUT_FEATURE_PRINT_WARN(
            APP_TOUCH, " CST820_QSPI_CHIP_ID(0x%x)!= read_id(0x%x)\r\n", CST820_QSPI_CHIP_ID, buf[0]);
    }

    INPUT_FEATURE_PRINT_INFO(APP_TOUCH, "IC_info fw_project_id:%04x ictype:%04x fw_ver:%x ", buf[1], buf[0], buf[2]); /* 2: number of buf */
    return TRUE;
}

int32_t cst820_qspi_init(void)
{
    INPUT_FEATURE_PRINT_INFO(APP_TOUCH, "Enter");
    int ret = EXT_ERR_SUCCESS;

    cst820_qspi_hardware_reset();
    mdelay(45); /* 45: Delay for 45 milliseconds */
    ret = cst820T_qspi_updata_tpinfo();
    ret |= cst820_qspi_set_power_mode(INPUT_DEV_NORMAL_MODE);
    if (ret != EXT_ERR_SUCCESS) {
        INPUT_FEATURE_PRINT_ERR(APP_TOUCH, "Update tp info failed! ret = 0x%x", ret);
    }
    return ret;
}

驱动框架头文件 (touch_cst820_qspi_drv.h)

业务说明:定义框架接口,touch_screen_get_api 是框架获取驱动的入口函数。

完整代码

/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2026. All rights reserved.
 * Description: TP CST820(QSPI) touch driver.
 * Create: 2026-06-27
 */

#ifndef HYNITRON_FIRMWARE_H
#define HYNITRON_FIRMWARE_H

void *touch_screen_get_api(void);

#endif

驱动框架实现 (touch_cst820_qspi_drv.c)

业务说明:实现与触摸屏框架的对接。框架定义了统一的驱动接口结构 touch_peripheral_api,驱动需要填充其中的函数指针。

  • init函数:完成总线初始化和芯片初始化
  • suspend/resume/sleep函数:电源管理相关,框架在设备进入低功耗时调用
  • g_cst820_qspi_api:注册到框架的API结构体,包含所有驱动操作函数

完整代码

/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2026. All rights reserved.
 * Description: TP CST820(QSPI) touch driver.
 * Create: 2026-06-28
 */

#include "touch_ctrl.h"
#include "debug_print.h"
#include "touch_cst820_qspi.h"
#include "pinctrl.h"
#include "pinctrl_porting.h"
#include "touch_cst820_qspi_drv.h"

static ext_errno cst820_qspi_drv_init(void *attr)
{
    unused(attr);
    ext_errno ret;
    touch_ctrl_ops_t *ops = touch_screen_get_peri_attr();

    if (ops == NULL) {
        INPUT_FEATURE_PRINT_ERR(APP_TOUCH, "peripheral ops is empty!");
        return EXT_ERR_FAILURE;
    }

    if (ops->bus_init == NULL) {
        INPUT_FEATURE_PRINT_ERR(APP_TOUCH, "peripheral bus_init func is empty!");
        return EXT_ERR_FAILURE;
    }

    ret = ops->bus_init(&(ops->attr));
    if (ret != EXT_ERR_SUCCESS) {
        INPUT_FEATURE_PRINT_ERR(APP_TOUCH, "cst820_qspi bus_init fail! ret = 0x%x", ret);
        return ret;
    }

    uapi_pin_set_ie(TOUCH_INT_GPIO, PIN_IE_ENABLE);
    ret = cst820_qspi_init();
    if (ret != EXT_ERR_SUCCESS) {
        INPUT_FEATURE_PRINT_ERR(APP_TOUCH, "cst820 qspi init fail! ret = 0x%x", ret);
    }

    return ret;
}

static ext_errno cst820_qspi_drv_deinit(void)
{
    return EXT_ERR_SUCCESS;
}

static ext_errno cst820_qspi_drv_get_info(uint8_t *data, uint8_t data_len)
{
    return cst820_qspi_irq_callback((input_event_info_t *)data, data_len);
}

static ext_errno cst820_qspi_drv_suspend(void)
{
    return cst820_qspi_set_power_mode(INPUT_DEV_STANDBY_MODE);
}

static ext_errno cst820_qspi_drv_resume(void)
{
    return cst820_qspi_set_power_mode(INPUT_DEV_NORMAL_MODE);
}

static ext_errno cst820_qspi_drv_sleep(void)
{
    return cst820_qspi_set_power_mode(INPUT_DEV_SLEEP_MODE);
}

touch_peripheral_api g_cst820_qspi_api = {
    .touch_init = cst820_qspi_drv_init,
    .touch_deinit = cst820_qspi_drv_deinit,
    .touch_get_tpinfo = cst820_qspi_drv_get_info,
    .touch_suspend = cst820_qspi_drv_suspend,
    .touch_resume = cst820_qspi_drv_resume,
    .touch_sleep = cst820_qspi_drv_sleep,
    .touch_bus_init = (ext_errno(*)(void *))touch_host_peripheral_init,
    .register_callback = (ext_errno(*)(void *, touch_callback))touch_register_handle,
    .unregister_callback = (ext_errno(*)(void *))touch_unregister_handle,
};

void *touch_screen_get_api(void)
{
    return ((void *)&g_cst820_qspi_api);
}

关键数据结构 (touch_def.h)

业务说明:以下数据结构由触摸框架定义,驱动需要使用这些结构体与框架交互。

触摸点信息

typedef struct {
    uint32_t x_axis[TP_FIGURE_NUM_SUPPORT];
    uint32_t y_axis[TP_FIGURE_NUM_SUPPORT];
    uint32_t z_axis[TP_FIGURE_NUM_SUPPORT];
} tp_dev_info_t;

typedef struct {
    tp_dev_info_t tp_event_info;
    tp_event_type_t tp_event;
    uint32_t finger_id;
    uint32_t point_num;
    uint32_t tv_usec;
} input_event_info_t;

触摸事件枚举

typedef enum {
    MC_TP_PRESS = 0,
    MC_TP_RELEASE = 1,
    MC_TP_MOVE = 2,
    MC_TP_COVER = 3,
    MC_TP_SHORT_CLICK = 4,
    MC_TP_DOUBLE_CLICK = 5,
    MC_TP_SLIDE_UP = 6,
    MC_TP_SLIDE_DOWN = 7,
    MC_TP_INVALID,
} tp_event_type_t;

配置文件与组件接入

Kconfig 配置

application/3322/input_wear/Kconfig 通过 choice 配置触摸芯片类型,SDK 中的定义如下:

choice
    prompt "TOUCH TYPE CONFIG."
    default INPUT_USING_TPTYPE_ZTW622
    config INPUT_USING_TPTYPE_ZTW622
        bool "ztw622"
    config INPUT_USING_TPTYPE_ZTW523
        bool "ztw523"
    config INPUT_USING_TPTYPE_CST820
        bool "cst820"
    config INPUT_USING_TPTYPE_CST820_QSPI
        bool "cst820_qspi"
endchoice

CMakeLists.txt 配置

CMakeLists.txt 通过 CONFIG_INPUT_USING_TPTYPE_CST820_QSPI 选择 QSPI 触摸驱动源码和头文件路径:

if(DEFINED CONFIG_INPUT_USING_TPTYPE_ZTW523)
set(SOURCES ${SOURCES}
    ${CMAKE_CURRENT_SOURCE_DIR}/ztw523_touch/touch_ztw523.c
    ${CMAKE_CURRENT_SOURCE_DIR}/ztw523_touch/touch_ztw523_drv.c

)
elseif(DEFINED CONFIG_INPUT_USING_TPTYPE_CST820)
set(SOURCES ${SOURCES}
    ${CMAKE_CURRENT_SOURCE_DIR}/cst820_touch/touch_cst820.c
    ${CMAKE_CURRENT_SOURCE_DIR}/cst820_touch/touch_cst820_drv.c
)
elseif(DEFINED CONFIG_INPUT_USING_TPTYPE_CST820_QSPI)
set(SOURCES ${SOURCES}
    ${CMAKE_CURRENT_SOURCE_DIR}/cst820_qspi_touch/touch_cst820_qspi.c
    ${CMAKE_CURRENT_SOURCE_DIR}/cst820_qspi_touch/touch_cst820_qspi_drv.c
)
if(DEFINED CONFIG_INPUT_USING_TPTYPE_ZTW523)
set(PUBLIC_HEADER
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/ztw523_touch
)
elseif(DEFINED CONFIG_INPUT_USING_TPTYPE_CST820)
set(PUBLIC_HEADER
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/cst820_touch
)
elseif(DEFINED CONFIG_INPUT_USING_TPTYPE_CST820_QSPI)
set(PUBLIC_HEADER
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/cst820_qspi_touch
)

touch_ctrl.h 配置

touch_ctrl.h 根据触摸芯片类型选择 I2C 地址:

#ifdef CONFIG_INPUT_USING_TPTYPE_CST820
#define TOUCH_I2C_ADDR 0x15
#else
#ifdef CONFIG_INPUT_USING_TPTYPE_CST820_QSPI
#define TOUCH_I2C_ADDR 0x15
#else
#define TOUCH_I2C_ADDR 0x20
#endif
#endif

I2C 读写指令长度定义如下:

#ifdef CONFIG_INPUT_USING_TPTYPE_CST820
#define TOUCH_REG_WRITE_LEN 2
#define TOUCH_CMD_SEND_LEN 1
#define TOUCH_REG_DATA_LEN 1
#else
#ifdef CONFIG_INPUT_USING_TPTYPE_CST820_QSPI
#define TOUCH_REG_WRITE_LEN 2
#define TOUCH_CMD_SEND_LEN 1
#define TOUCH_REG_DATA_LEN 0
#else
#define TOUCH_REG_WRITE_LEN 4
#define TOUCH_CMD_SEND_LEN 2
#define TOUCH_REG_DATA_LEN 2
#endif
#endif

touch_ctrl.c 寄存器读取

业务说明:框架提供通用 I2C 读写能力,CST820 QSPI 驱动通过寄存器读取函数获取芯片信息。

tp_i2c_data_read_reg 实现如下:

ext_errno tp_i2c_data_read_reg(uint16_t reg_addr, uint8_t *data_buf, uint32_t data_len)
{
    errcode_t ret;
    uint32_t retry;
    i2c_data_t data;

    // 参数检查
    ret = tp_i2c_rd_wr_param_check(data_buf, data_len);
    if (ret != 0) {
        INPUT_FEATURE_PRINT_ERR(APP_TOUCH, "tp data read param invalid! ret=0x%x", ret);
        return ret;
    }

    if (memset_s(g_tp_recv_buf, data_len, 0, data_len) != EOK) {
        INPUT_FEATURE_PRINT_ERR(APP_TOUCH, "tp read buff set fail! ret=0x%x", ret);
        return EXT_ERR_FAILURE;
    }
    // 构造要发送的寄存器地址(2字节)
    g_tp_send_buf[TOUCH_I2C_SEND_INDEX0] = reg_addr & 0xff;
    g_tp_send_buf[TOUCH_I2C_SEND_INDEX1] = (reg_addr >> OFFSET_8_BITS) & 0xff;
    // 配置I2C传输数据结构
    data.send_buf = g_tp_send_buf;
    data.send_len = TOUCH_CMD_SEND_LEN;
    data.receive_buf = g_tp_recv_buf;
    data.receive_len = data_len;
    // 带重试机制的I2C读取
    for (retry = 0; retry < TOUCH_I2C_TIME_MAX; retry++) {
        ret = uapi_i2c_master_read(TOUCH_I2C_BUS, reg_addr, &data);
        if (ret == EXT_ERR_SUCCESS) {
            break;
        }
    }
    if (ret != EXT_ERR_SUCCESS) {
        INPUT_FEATURE_PRINT_ERR(APP_TOUCH, "tp data read fail! ret=0x%x", ret);
        return ret;
    }

    // 将读取到的数据拷贝到用户缓冲区
    ret = (uint32_t)memcpy_s(data_buf, data_len, g_tp_recv_buf, data_len);
    if (ret != EOK) {
        INPUT_FEATURE_PRINT_ERR(APP_TOUCH, "tp data buf content copy fail! ret=0x%x", ret);
        return ret;
    }

    return EXT_ERR_SUCCESS;
}

构建目标配置

diting-community 构建目标在 config.py 中关闭 MIPI ULPS 并启用 QSPI 显示:

'-:MIPI_ULPS_SUPPORT',
'SUPPORT_GPU_QSPI',

diting_community.config 选择 CST820 QSPI 触摸芯片;其他 diting-community 派生目标使用同样的配置方式:

# CONFIG_INPUT_USING_TPTYPE_ZTW622 is not set
# CONFIG_INPUT_USING_TPTYPE_ZTW523 is not set
# CONFIG_INPUT_USING_TPTYPE_CST820 is not set
CONFIG_INPUT_USING_TPTYPE_CST820_QSPI=y

编译构建

编译命令

完成一站式 CLI 环境配置后执行:

fbb set-target pack_diting_community
fbb build

固件获取

编译生成的固件位于 output/3322/fwpkg/diting-community.fwpkg

测试验证

  1. 烧录固件并启动设备,确认启动日志中没有 LCD 或 CST820T 初始化失败信息。
  2. 执行 AT+GPU=smoke,确认串口返回 OK 且屏幕显示测试画面。
  3. 在应用界面点击四角并沿水平、垂直方向滑动,确认位置和方向与触摸一致。
  4. 熄屏后重新亮屏,再次执行显示和触摸验证,确认 suspend/resume 链路正常。

适配其他厂家的 QSPI 触摸屏驱动

基于 CST820T 和 CO5300AF-08 的驱动实现,可以通过以下步骤适配新的触摸 IC 或屏幕:

代码清单

新建一个 QSPI 屏驱动应用(以 my_qspi_driver 为例)通常只需要以下改动:

  • 新建 my_qspi_driver.c 源文件(驱动逻辑实现)
  • 新建 my_qspi_driver.h 头文件(宏定义和数据结构)
  • 新建 my_qspi_driver_drv.c 源文件(框架接口实现)
  • 新建 my_qspi_driver_drv.h 头文件(框架接口定义)
  • 修改 lcd_config.c 添加上电序列和屏配置信息
  • 修改 CMakeLists.txt 添加编译规则
  • 修改 Kconfig 添加菜单选项

文件结构

application/3322/input_wear/peripheral/touch/my_qspi_driver/
├── my_qspi_driver.c
├── my_qspi_driver.h
├── my_qspi_driver_drv.c
└── my_qspi_driver_drv.h

CMakeLists.txt 修改示例

# 在父级 CMakeLists.txt 中添加条件编译
elseif(DEFINED CONFIG_INPUT_USING_TPTYPE_MY_QSPI)
set(SOURCES ${SOURCES}
    ${CMAKE_CURRENT_SOURCE_DIR}/my_qspi_driver/my_qspi_driver.c
    ${CMAKE_CURRENT_SOURCE_DIR}/my_qspi_driver/my_qspi_driver_drv.c
)

Kconfig 修改示例

config INPUT_USING_TPTYPE_MY_QSPI
    bool "my_qspi_driver"

关键代码片段

my_qspi_driver.h —— 宏定义和数据结构

#ifndef MY_QSPI_DRIVER_H
#define MY_QSPI_DRIVER_H

#include "touch_ctrl.h"

#define MY_QSPI_CHIP_ID     0x1234
#define MY_QSPI_TOUCHINFO_LEN  8
#define MY_QSPI_XH_OFFSET   3
#define MY_QSPI_XL_OFFSET   4
#define MY_QSPI_YH_OFFSET   5
#define MY_QSPI_YL_OFFSET   6

// 函数声明
int32_t my_qspi_parse_coord(input_event_info_t *input_event_info);
void my_qspi_gesture_judge(input_event_info_t *input_event_info, uint8_t data_tag);

#endif

my_qspi_driver.c —— 驱动逻辑实现

#include "my_qspi_driver.h"
#include "debug_print.h"

int32_t my_qspi_parse_coord(input_event_info_t *input_event_info)
{
    // 实现数据读取和坐标解析
    // 参考 cst820_qspi_parse_coord 的实现
    return EC_SUCCESS;
}

void my_qspi_gesture_judge(input_event_info_t *input_event_info, uint8_t data_tag)
{
    // 实现手势识别
    // 参考 cst820_qspi_gesture_judge 的实现
}

my_qspi_driver_drv.c —— 框架接口实现

#include "my_qspi_driver_drv.h"
#include "touch_ctrl.h"

touch_peripheral_api g_my_qspi_api = {
    .touch_init = my_qspi_drv_init,
    .touch_deinit = my_qspi_drv_deinit,
    .touch_get_tpinfo = my_qspi_drv_get_info,
    .touch_suspend = my_qspi_drv_suspend,
    .touch_resume = my_qspi_drv_resume,
    .touch_sleep = my_qspi_drv_sleep,
    .touch_bus_init = (ext_errno(*)(void *))touch_host_peripheral_init,
    .register_callback = (ext_errno(*)(void *, touch_callback))touch_register_handle,
    .unregister_callback = (ext_errno(*)(void *))touch_unregister_handle,
};

void *touch_screen_get_api(void)
{
    return ((void *)&g_my_qspi_api);
}

测试验证

完成一站式 CLI 环境配置后执行:

# 编译固件
fbb set-target pack_diting_community
fbb build
  1. 烧录固件并启动设备
  2. 观察屏幕是否正常点亮
  3. 触摸屏幕,观察串口日志输出与应用界面事件响应

注意事项

点屏驱动配置注意事项

  • 上电序列中的延时参数(delay_ms)需根据具体厂商芯片手册设置,确保时序正确。
  • 屏 ID(lcd_id)必须唯一,避免与其他屏幕冲突。
  • 显示区域设置(0x2a0x2b)需与屏幕分辨率匹配。

触摸驱动注意事项

  • I2C 地址需与具体厂商芯片手册一致,否则通信失败。
  • INT 引脚必须配置为中断模式,否则无法触发触摸事件。
  • 读取触摸数据前,需确保芯片已正确初始化。

编译配置注意事项

  • 在目标的 config.py 配置中关闭 MIPI_ULPS_SUPPORT 并启用 SUPPORT_GPU_QSPI
  • 在对应的 menuconfig 文件中选择 CONFIG_INPUT_USING_TPTYPE_CST820_QSPI
  • 新增触摸芯片时,需要同步扩展 Kconfig 选项和 CMakeLists.txt 条件分支。

常见问题

无触摸响应

  • 检查 I2C 地址是否正确。
  • 确认 INT 引脚已配置为中断模式。

I2C地址冲突排查方法

排查步骤 操作 预期结果 异常处理
1. 确认地址 查阅芯片手册 获取正确的I2C地址 检查芯片型号和版本
2. 扫描总线 在连接了外部 I2C 调试器的 Linux 主机上执行 i2cdetect,或在板端增加一次设备 ID 读取日志 在预期地址处检测到设备或读到正确 ID 检查硬件连接和上电
3. 检查冲突 查看总线设备列表 无地址冲突 更换地址或使用不同总线
4. 硬件排查 测量上拉电阻和ADDR引脚 电阻4.7KΩ,ADDR电平正确 更换电阻或修改ADDR配置
5. 软件排查 添加调试日志 通信正常,设备ID匹配 根据错误码定位问题

i2cdetect 是 Linux 主机工具,不是 LiteOS 串口命令;开发板未通过外部 I2C 调试器连接到 Linux 主机时,不应在板端串口中执行该命令。

常见错误代码速查表

错误码(ret值) 宏定义 含义 原因
0x00000000 EXT_ERR_SUCCESS / ERRCODE_SUCC 操作成功
0xFFFFFFFF EXT_ERR_FAILURE 操作失败 通用错误,需结合日志分析
0x80001300 ERRCODE_I2C_NOT_INIT I2C 控制器未初始化 未调用初始化函数直接操作
0x80001301 ERRCODE_I2C_ALREADY_INIT 重复初始化 多次调用初始化函数
0x80001302 ERRCODE_I2C_INVALID_PARAMETER 参数非法 空指针/数值越界等
0x80001314 ERRCODE_I2C_ACK_ERR 从设备无应答 地址错误/设备未响应

附录

参考驱动