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
使用方式
- 确认当前
diting_community.config已设置CONFIG_INPUT_USING_TPTYPE_CST820_QSPI=y,目标配置已启用SUPPORT_GPU_QSPI。 - 烧录固件并启动设备,系统会自动完成屏幕初始化和触摸驱动加载。
- 以
750000波特率打开 UART2 串口,执行AT+GPU=smoke验证 QSPI 显示链路。 - 在应用界面上下左右滑动并点击屏幕四角,观察界面是否按实际坐标响应,验证 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)
业务说明:此文件实现核心业务逻辑。
-
硬件复位流程 (
cst820_qspi_hardware_reset):- 先拉高RST引脚等待1ms
- 拉低RST引脚10ms进行复位
- 再拉高RST引脚等待5ms完成复位
-
工作模式切换 (
cst820_qspi_set_power_mode):- 支持睡眠模式(
INPUT_DEV_SLEEP_MODE) - 支持正常工作模式(
INPUT_DEV_NORMAL_MODE) - 支持待机模式(
INPUT_DEV_STANDBY_MODE)
- 支持睡眠模式(
-
坐标解析 (
cst820_qspi_parse_coord):- 从芯片寄存器地址0x00读取触摸数据(7字节)
- 解析X坐标:高字节低4位与低字节拼接,形成12位坐标
- 解析Y坐标:同上
- 解析触摸状态:tp_data[1]==0x00表示普通触摸事件,否则为手势事件
- 普通触摸事件中,tp_data[XH_OFFSET]高2位表示触摸类型(按下/移动/释放)
-
手势识别 (
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 显示:
menuconfig 配置
diting_community.config 选择 CST820 QSPI 触摸芯片;其他 diting-community 派生目标使用同样的配置方式:
编译构建
编译命令
完成一站式 CLI 环境配置后执行:
固件获取
编译生成的固件位于 output/3322/fwpkg/diting-community.fwpkg
测试验证
- 烧录固件并启动设备,确认启动日志中没有 LCD 或 CST820T 初始化失败信息。
- 执行
AT+GPU=smoke,确认串口返回OK且屏幕显示测试画面。 - 在应用界面点击四角并沿水平、垂直方向滑动,确认位置和方向与触摸一致。
- 熄屏后重新亮屏,再次执行显示和触摸验证,确认 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 修改示例
关键代码片段
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 环境配置后执行:
- 烧录固件并启动设备
- 观察屏幕是否正常点亮
- 触摸屏幕,观察串口日志输出与应用界面事件响应
注意事项
点屏驱动配置注意事项
- 上电序列中的延时参数(
delay_ms)需根据具体厂商芯片手册设置,确保时序正确。 - 屏 ID(
lcd_id)必须唯一,避免与其他屏幕冲突。 - 显示区域设置(
0x2a和0x2b)需与屏幕分辨率匹配。
触摸驱动注意事项
- 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 |
从设备无应答 | 地址错误/设备未响应 |
附录
参考驱动
- cst820_qspi TP 驱动文件:touch_cst820_qspi.c
- 点屏驱动配置文件:lcd_config.c