Files
arcs/arcs-sdk/drivers/lisa_touch/lisa_touch_cst816d.c
2026-08-13 16:50:52 +08:00

493 lines
17 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (c) 2025, LISTENAI
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file lisa_touch_cst816d.c
* @brief CST816D 触摸芯片驱动 - LISA Touch 设备实现
*/
#include "lisa_touch.h"
#include "lisa_i2c.h"
#include "lisa_gpio.h"
#include "lisa_thread.h"
#include <stddef.h>
#include <string.h>
#include <lisa_mutex.h>
#include "FreeRTOS.h"
#include "task.h"
#include "portmacro.h"
#define LOG_TAG "lisa_touch_cst816d"
#include <lisa_log.h>
/* CST816D I2C 地址 */
#define CST816D_I2C_SLAVE_ADDRESS 0x15
/* CST816D 寄存器定义 */
#define CST816D_TOUCH_INFO_REG 0xD000
/* CST816D 触摸能力 */
#define CST816D_MAX_X 240
#define CST816D_MAX_Y 240
#define CST816D_MAX_POINTS 1
/* 自动判断上下文的互斥锁宏(支持中断和线程上下文) */
/* 自动判断上下文的互斥锁宏lisa_mutex_lock/unlock 已自动判断上下文) */
#define DEVICE_LOCK(priv) \
do { \
if (priv->mutex) { \
lisa_mutex_lock(priv->mutex, LISA_OS_WAIT_FOREVER); \
} \
} while (0)
#define DEVICE_UNLOCK(priv) \
do { \
if (priv->mutex) { \
lisa_mutex_unlock(priv->mutex); \
} \
} while (0)
/* ===== CST816D 设备私有数据 ===== */
typedef struct {
lisa_device_t *i2c_dev; /* I2C 设备指针 */
lisa_device_t *int_gpio; /* 中断 GPIO 设备指针 */
lisa_device_t *rst_gpio; /* 复位 GPIO 设备指针 */
uint32_t int_pin; /* 中断引脚号 */
uint32_t rst_pin; /* 复位引脚号 */
lisa_touch_callback_t callback; /* 触摸事件回调函数 */
void *callback_user_data; /* 回调用户数据 */
lisa_mutex_t *mutex; /* 互斥锁 */
bool initialized; /* 初始化标志 */
bool enabled; /* 使能标志 */
TaskHandle_t read_task_handle; /* I2C 读取任务句柄(用于中断模式) */
bool interrupt_mode_active; /* 中断模式是否激活 */
} lisa_touch_cst816d_priv_t;
/* ===== CST816D 设备静态实例 ===== */
static lisa_touch_cst816d_priv_t touch_cst816d_priv;
/* ===== 内部辅助函数 ===== */
/* 前向声明 */
static int cst816d_touch_read_event_internal(lisa_touch_cst816d_priv_t *priv, lisa_touch_event_t *event);
/**
* @brief 读取 CST816D 寄存器
*/
static int cst816d_read_reg(lisa_device_t *i2c_dev, uint16_t reg, uint8_t *data, uint32_t len)
{
if (!i2c_dev || !data || len == 0) {
return LISA_DEVICE_ERR_INVALID;
}
/* 准备寄存器地址(小端序:低字节在前) */
uint8_t reg_buf[2] = {(uint8_t)(reg & 0xFF), (uint8_t)((reg >> 8) & 0xFF)};
/* 构造 I2C 消息:先写寄存器地址,再读数据 */
lisa_i2c_msg_t msgs[2] = {
{.addr = CST816D_I2C_SLAVE_ADDRESS, .flags = LISA_I2C_FLAG_NONE, .len = sizeof(reg_buf), .buf = reg_buf},
{.addr = CST816D_I2C_SLAVE_ADDRESS, .flags = LISA_I2C_FLAG_READ, .len = len, .buf = data}
};
int ret = lisa_i2c_transfer(i2c_dev, msgs, 2);
if (ret != LISA_DEVICE_OK) {
LISA_LOGE(LOG_TAG, "I2C transfer failed: %d", ret);
return ret;
}
return LISA_DEVICE_OK;
}
/**
* @brief I2C 读取任务(用于中断模式)
* @note 此任务在中断模式下运行,等待任务通知后读取 I2C 数据
*/
static void cst816d_read_task(void *pvParameters)
{
lisa_touch_cst816d_priv_t *priv = (lisa_touch_cst816d_priv_t *)pvParameters;
while (1) {
/* 等待任务通知(中断会发送通知)
* 使用 pdFALSE 清除所有累积的通知,避免重复处理
*/
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
if (!priv->enabled || !priv->interrupt_mode_active) {
continue;
}
/* 在任务上下文中读取完整的触摸数据 */
lisa_touch_event_t event = {0};
int ret = cst816d_touch_read_event_internal(priv, &event);
if (ret == LISA_DEVICE_OK && priv->callback) {
/* 通过回调函数返回完整数据 */
priv->callback(&event, priv->callback_user_data);
}
}
}
/**
* @brief GPIO 中断回调
*/
static void cst816d_gpio_irq_callback(uint32_t pin, void *user_data)
{
(void)pin;
lisa_touch_cst816d_priv_t *priv = (lisa_touch_cst816d_priv_t *)user_data;
if (!priv || !priv->interrupt_mode_active) {
return;
}
/* 在中断上下文中发送任务通知,唤醒读取任务 */
if (priv->read_task_handle != NULL) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(priv->read_task_handle, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
/**
* @brief 硬件复位芯片
*/
static int cst816d_hardware_reset(lisa_touch_cst816d_priv_t *priv)
{
if (!priv->rst_gpio || !lisa_device_ready(priv->rst_gpio)) {
LISA_LOGW(LOG_TAG, "Reset GPIO not available, skip reset");
return LISA_DEVICE_OK;
}
/* 配置复位引脚为输出 */
int ret = lisa_gpio_configure(priv->rst_gpio, priv->rst_pin, LISA_GPIO_OUTPUT);
if (ret != LISA_DEVICE_OK) {
LISA_LOGE(LOG_TAG, "Failed to configure reset pin: %d", ret);
return ret;
}
/* 复位序列:高->低->高 */
lisa_gpio_write_pin(priv->rst_gpio, priv->rst_pin, 1);
lisa_thread_mdelay(20);
lisa_gpio_write_pin(priv->rst_gpio, priv->rst_pin, 0);
lisa_thread_mdelay(20);
lisa_gpio_write_pin(priv->rst_gpio, priv->rst_pin, 1);
lisa_thread_mdelay(400);
LISA_LOGI(LOG_TAG, "CST816D hardware reset completed");
return LISA_DEVICE_OK;
}
/* ===== LISA Touch API 实现 ===== */
/**
* @brief 获取触摸设备能力
*/
static int cst816d_touch_get_capabilities(lisa_device_t *dev, lisa_touch_capabilities_t *caps)
{
if (!lisa_device_is_initialized(dev) || !caps) {
return LISA_DEVICE_ERR_INVALID;
}
memset(caps, 0, sizeof(lisa_touch_capabilities_t));
caps->max_x = CST816D_MAX_X;
caps->max_y = CST816D_MAX_Y;
caps->max_points = CST816D_MAX_POINTS;
caps->has_pressure = false;
caps->has_gesture = false;
caps->supported_gestures = 0;
return LISA_DEVICE_OK;
}
/**
* @brief 内部读取触摸事件函数(供任务和 API 调用)
*/
static int cst816d_touch_read_event_internal(lisa_touch_cst816d_priv_t *priv, lisa_touch_event_t *event)
{
if (!priv || !event || !priv->enabled) {
return LISA_DEVICE_ERR_INVALID;
}
if (!priv->i2c_dev || !lisa_device_ready(priv->i2c_dev)) {
return LISA_DEVICE_ERR_NOT_READY;
}
DEVICE_LOCK(priv);
/* 读取触摸信息寄存器6字节 */
uint8_t read_buf[6] = {0};
int ret = cst816d_read_reg(priv->i2c_dev, CST816D_TOUCH_INFO_REG, read_buf, sizeof(read_buf));
if (ret != LISA_DEVICE_OK) {
DEVICE_UNLOCK(priv);
return ret;
}
/* 解析触摸状态和坐标 */
bool pressed = (read_buf[0] != 0);
uint16_t x = ((read_buf[2] & 0x0F) << 8) | read_buf[3];
uint16_t y = ((read_buf[4] & 0x0F) << 8) | read_buf[5];
/* 填充事件结构体 */
memset(event, 0, sizeof(lisa_touch_event_t));
if (pressed) {
event->type = LISA_TOUCH_EVENT_PRESS;
event->point_count = 1;
event->points[0].id = 0;
event->points[0].x = x;
event->points[0].y = y;
event->points[0].state = LISA_TOUCH_POINT_PRESSED;
LISA_LOGD(LOG_TAG, "Touch: x=%d, y=%d", x, y);
} else {
event->type = LISA_TOUCH_EVENT_RELEASE;
event->point_count = 0;
}
DEVICE_UNLOCK(priv);
return LISA_DEVICE_OK;
}
/**
* @brief 读取触摸事件API 接口)
*/
static int cst816d_touch_read_event(lisa_device_t *dev, lisa_touch_event_t *event)
{
if (!lisa_device_is_initialized(dev) || !event) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_touch_cst816d_priv_t *priv = (lisa_touch_cst816d_priv_t *)dev->priv_data;
/* 中断模式下禁止轮询读取,避免与中断任务产生资源竞争 */
if (priv->interrupt_mode_active) {
LISA_LOGW(LOG_TAG, "Cannot read_event in interrupt mode, use callback instead");
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return cst816d_touch_read_event_internal(priv, event);
}
/**
* @brief 启用触摸设备
*/
static int cst816d_touch_enable(lisa_device_t *dev)
{
if (!lisa_device_is_initialized(dev)) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_touch_cst816d_priv_t *priv = (lisa_touch_cst816d_priv_t *)dev->priv_data;
DEVICE_LOCK(priv);
priv->enabled = true;
DEVICE_UNLOCK(priv);
LISA_LOGI(LOG_TAG, "CST816D touch device enabled");
return LISA_DEVICE_OK;
}
/**
* @brief 禁用触摸设备
*/
static int cst816d_touch_disable(lisa_device_t *dev)
{
if (!lisa_device_is_initialized(dev)) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_touch_cst816d_priv_t *priv = (lisa_touch_cst816d_priv_t *)dev->priv_data;
DEVICE_LOCK(priv);
priv->enabled = false;
DEVICE_UNLOCK(priv);
LISA_LOGI(LOG_TAG, "CST816D touch device disabled");
return LISA_DEVICE_OK;
}
/**
* @brief 附加触摸总线接口
*/
static int cst816d_touch_attach_bus(lisa_device_t *dev, const lisa_touch_bus_config_t *bus_config)
{
if (!lisa_device_is_initialized(dev) || !bus_config) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_touch_cst816d_priv_t *priv = (lisa_touch_cst816d_priv_t *)dev->priv_data;
if (bus_config->bus_type != LISA_TOUCH_BUS_I2C) {
LISA_LOGE(LOG_TAG, "CST816D only supports I2C bus");
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
DEVICE_LOCK(priv);
/* 保存总线配置 */
priv->i2c_dev = bus_config->config.i2c.i2c_dev;
priv->int_gpio = bus_config->config.i2c.int_gpio;
priv->rst_gpio = bus_config->config.i2c.rst_gpio;
priv->int_pin = bus_config->config.i2c.int_pin;
priv->rst_pin = bus_config->config.i2c.rst_pin;
/* 检查必需的设备 */
if (!priv->i2c_dev || !lisa_device_ready(priv->i2c_dev)) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "I2C device not ready");
return LISA_DEVICE_ERR_NOT_READY;
}
/* 自动配置 I2C 总线(标准速度,主机模式) */
lisa_i2c_config_t i2c_config = {
.speed = LISA_I2C_SPEED_STANDARD,
.master_mode = true,
.slave_addr = 0,
};
int ret = lisa_i2c_configure(priv->i2c_dev, &i2c_config);
if (ret != LISA_DEVICE_OK) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "I2C configuration failed: %d", ret);
return ret;
}
LISA_LOGI(LOG_TAG, "I2C auto-configured (speed: %d Hz)", i2c_config.speed);
/* 硬件复位芯片 */
ret = cst816d_hardware_reset(priv);
if (ret != LISA_DEVICE_OK) {
DEVICE_UNLOCK(priv);
return ret;
}
/* 配置中断引脚(如果提供) */
if (priv->int_gpio && lisa_device_ready(priv->int_gpio)) {
ret = lisa_gpio_configure(priv->int_gpio, priv->int_pin,
LISA_GPIO_INPUT | LISA_GPIO_PULL_UP);
if (ret != LISA_DEVICE_OK) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "Failed to configure interrupt pin: %d", ret);
return ret;
}
LISA_LOGI(LOG_TAG, "CST816D interrupt pin configured");
}
DEVICE_UNLOCK(priv);
LISA_LOGI(LOG_TAG, "CST816D touch bus attached successfully");
return LISA_DEVICE_OK;
}
/**
* @brief 设置触摸事件回调函数
*/
static int cst816d_touch_set_callback(lisa_device_t *dev, lisa_touch_callback_t callback, void *user_data)
{
if (!lisa_device_is_initialized(dev)) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_touch_cst816d_priv_t *priv = (lisa_touch_cst816d_priv_t *)dev->priv_data;
DEVICE_LOCK(priv);
priv->callback = callback;
priv->callback_user_data = user_data;
DEVICE_UNLOCK(priv);
LISA_LOGD(LOG_TAG, "CST816D touch callback %s", callback ? "set" : "cleared");
return LISA_DEVICE_OK;
}
/**
* @brief 设置中断模式
*/
static int cst816d_touch_set_int_mode(lisa_device_t *dev, lisa_touch_int_mode_t mode)
{
if (!lisa_device_is_initialized(dev)) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_touch_cst816d_priv_t *priv = (lisa_touch_cst816d_priv_t *)dev->priv_data;
if (!priv->int_gpio || !lisa_device_ready(priv->int_gpio)) {
LISA_LOGW(LOG_TAG, "Interrupt GPIO not available");
return LISA_DEVICE_ERR_NOT_READY;
}
DEVICE_LOCK(priv);
int ret = LISA_DEVICE_OK;
if (mode == LISA_TOUCH_INT_MODE_INTERRUPT) {
/* 如果任务不存在,创建读取任务 */
if (priv->read_task_handle == NULL) {
/* 使用 Kconfig 配置的任务优先级 */
UBaseType_t task_priority = tskIDLE_PRIORITY + CONFIG_LISA_TOUCH_ARCS_CST816D_READ_TASK_PRIORITY;
xTaskCreate(cst816d_read_task, "cst816d_read", 1024, priv,
task_priority, &priv->read_task_handle);
if (priv->read_task_handle == NULL) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "Failed to create read task");
return LISA_DEVICE_ERR_INIT_FAIL;
}
LISA_LOGI(LOG_TAG, "CST816D read task created with priority %d", task_priority);
}
/* 配置并启用中断(下降沿触发) */
ret = lisa_gpio_configure_irq(priv->int_gpio, priv->int_pin,
LISA_GPIO_IRQ_EDGE_FALLING,
cst816d_gpio_irq_callback, priv);
if (ret == LISA_DEVICE_OK) {
ret = lisa_gpio_enable_irq(priv->int_gpio, priv->int_pin);
if (ret == LISA_DEVICE_OK) {
priv->interrupt_mode_active = true;
LISA_LOGI(LOG_TAG, "CST816D interrupt mode enabled");
}
}
} else {
/* 禁用中断 */
priv->interrupt_mode_active = false;
ret = lisa_gpio_disable_irq(priv->int_gpio, priv->int_pin);
if (ret == LISA_DEVICE_OK) {
LISA_LOGI(LOG_TAG, "CST816D interrupt mode disabled (polling mode)");
}
/* 注意:不删除任务,以便下次切换回中断模式时重用 */
}
DEVICE_UNLOCK(priv);
return ret;
}
/* ===== CST816D Touch API 实例 ===== */
static const lisa_touch_api_t cst816d_touch_api = {
.get_capabilities = cst816d_touch_get_capabilities,
.read_event = cst816d_touch_read_event,
.enable = cst816d_touch_enable,
.disable = cst816d_touch_disable,
.attach_bus = cst816d_touch_attach_bus,
.set_callback = cst816d_touch_set_callback,
.set_int_mode = cst816d_touch_set_int_mode,
};
/* ===== 设备初始化函数 ===== */
static int lisa_touch_cst816d_init(void)
{
memset(&touch_cst816d_priv, 0, sizeof(lisa_touch_cst816d_priv_t));
touch_cst816d_priv.mutex = lisa_mutex_create();
if (!touch_cst816d_priv.mutex) {
LISA_LOGE(LOG_TAG, "Failed to create mutex");
return LISA_DEVICE_ERR_INIT_FAIL;
}
touch_cst816d_priv.enabled = false;
touch_cst816d_priv.initialized = true;
LISA_LOGI(LOG_TAG, "CST816D touch device initialized successfully");
return LISA_DEVICE_OK;
}
/* ===== 设备注册 ===== */
LISA_DEVICE_REGISTER(touch_cst816d, /* 设备名称 */
&cst816d_touch_api, /* API指针 */
&touch_cst816d_priv, /* 私有数据指针 */
NULL, /* 用户数据 */
lisa_touch_cst816d_init, /* 初始化函数 */
LISA_DEVICE_PRIORITY_NORMAL); /* 优先级 */