Touch Screen

Introduction

For touch controller drivers, we have implemented a unified interface called “touch” rt_device that provides a simple framework for registering different touch controller drivers and automatically selecting the appropriate driver. This chapter mainly introduces the internal framework functionality of Touch device and how to register a new touch controller to this framework.

Touch device implementation consists of 2 parts:

  • rt_device device (drv_touch.c)

    • Registers a device named “touch” that provides interrupt callback registration and touch data reading interfaces

    • Creates a task for slow communication with touch devices (such as initialization, reading touch point data, etc.)

    • Performs caching, filtering (filtering duplicate points), and cache overflow packet dropping for touch data points read

  • Specific touch device driver implementation

    • Registers a new driver with touch_device and provides the following implementations:

      • probe - Identify supported devices

      • init - Initialization after device identification

      • deinit - Deinitialization after device identification

      • read_point - Read a valid data point (Note: Return RT_EOK if there are still unread data points, otherwise return other values)

      • A semaphore - Used to block rt_device layer threads

    • Also needs to implement internally:

      • Touch interrupt detection

      • Communication interface initialization

Figure 1: Touch driver software architecture





Process for Adding New Touch Controller Code

1. Select the corresponding board project under example\rt_driver

  • This project contains a thread touch_read_task that reads touch data and prints it

2. Add new driver to compilation project

  • Add new touch controller code to directory customer\peripherals

    • You can copy code from other existing drivers, then change the name, Slave_Address, read process, etc. to your own

    Note

    Note to modify the depend macro in the Kconfig file under the copied code directory

  • In customer\peripherals\Kconfig, add a hidden option for the newly added driver, for example:

    config TSC_USING_TMA525B
        bool
        default n
    
  • Add the previously added hidden touch controller switch to the board-level configuration screen module switch:

    config LCD_USING_ED_LB55DSI13902_DSI_LB555
        bool "1.39 round 454RGB*454 DSI LCD(ED-LB55DSI13902)"
        select TSC_USING_TMA525B         <--------  Added touch controller switch
        select LCD_USING_RM69330
        select BSP_LCDC_USING_DSI
        if LCD_USING_ED_LB55DSI13902_DSI_LB555
            config LCD_RM69330_VSYNC_ENABLE
                bool "Enable LCD VSYNC (TE signal)"
                def_bool n
        endif
    
  • If using scons compilation, need to enter the project menuconfig selection menu, then select the newly added screen module, finally generating .config and rtconfig.h

  • If using Keil compilation, you can also directly add source code (but still recommend the same method as scons compilation, so it will be automatically included when regenerating Keil project next time)

3. Check pinmux for pins used by new touch controller and reset pin

  • In the SDK’s drv_io.c, functions BSP_TP_PowerUp&BSP_TP_PowerDown perform power up/down and reset operations for touch controllers

Touch Controller Debugging Suggestions

  • First check if power supply and reset pin status are normal

  • Then check if communication interface waveforms are normal, such as whether I2C interface has ack

TMA525B Touch Device Driver Implementation Example Code

TMA525B uses the falling edge of TOUCH_IRQ_PIN as trigger condition, I2C as communication interface at 400KHz speed, I2C read/write timeout of 5ms This implementation releases semaphore in interrupt to make touch_device layer call its own read_point to get touch data Also disables interrupt enable in interrupt, then re-enables interrupt enable after entering read_point, preventing too many interrupts from repeatedly releasing too many semaphores


static struct rt_i2c_bus_device *ft_bus = NULL;
static struct touch_drivers tma525b_driver;



static rt_err_t i2c_write(rt_uint8_t *buf, rt_uint16_t len)
{
    rt_int8_t res = 0;
    struct rt_i2c_msg msgs;

    msgs.addr  = I2C_ADDR;    /* slave address */
    msgs.flags = RT_I2C_WR;        /* write flag */
    msgs.buf   = buf;              /* Send data pointer */
    msgs.len   = len;

    if (rt_i2c_transfer(ft_bus, &msgs, 1) == 1)
    {
        res = RT_EOK;
    }
    else
    {
        res = -RT_ERROR;
    }
    return res;
}



static rt_err_t i2c_read(rt_uint8_t *buf, rt_uint16_t len)
{
    rt_int8_t res = 0;
    struct rt_i2c_msg msgs[2];

    msgs[0].addr  = I2C_ADDR;    /* Slave address */
    msgs[0].flags = RT_I2C_RD;        /* Read flag */
    msgs[0].buf   = buf;              /* Read data pointer */
    msgs[0].len   = len;              /* Number of bytes read */

    if (rt_i2c_transfer(ft_bus, msgs, 1) == 1)
    {
        res = RT_EOK;
    }
    else
    {
        res = -RT_ERROR;
    }
    return res;
}


void tma525b_irq_handler(void *arg)
{
    rt_err_t ret = RT_ERROR;

    rt_pin_irq_enable(TOUCH_IRQ_PIN, 0);

    ret = rt_sem_release(tma525b_driver.isr_sem);
    RT_ASSERT(RT_EOK == ret);
}


static rt_err_t read_point(touch_msg_t p_msg)
{
    rt_err_t ret = RT_ERROR;
	rt_uint8_t  touch_report[16];  

    rt_pin_irq_enable(TOUCH_IRQ_PIN, 1);


    //read touch report
    if(RT_EOK == i2c_read((rt_uint8_t *)touch_report, 16))
    {
		if(touch_report[1] == 1)
		{
			p_msg->event = TOUCH_EVENT_DOWN;
		}
		else
		{
			p_msg->event = TOUCH_EVENT_UP;
		}

		p_msg->x     = touch_report[2];
		p_msg->y     = touch_report[3];

		if(touch_report[4] > 1)
			return RT_EOK;       //More pending touch data
		else	
			return RT_EEMPTY;    //No more touch data to be read
    }

	p_msg->event = TOUCH_EVENT_NONE; //Read point fail
    return RT_ERROR;
}

static rt_err_t init(void)
{
    rt_pin_mode(TOUCH_IRQ_PIN, PIN_MODE_INPUT);
    rt_pin_attach_irq(TOUCH_IRQ_PIN, PIN_IRQ_MODE_FALLING, tma525b_irq_handler, (void *)(rt_uint32_t)TOUCH_IRQ_PIN);
    rt_pin_irq_enable(TOUCH_IRQ_PIN, 1); //Must enable before read I2C

    return RT_EOK;

}

static rt_err_t deinit(void)
{
    rt_pin_detach_irq(TOUCH_IRQ_PIN);
    return RT_EOK;
}

static rt_bool_t probe(void)
{
    rt_err_t err;

    ft_bus = (struct rt_i2c_bus_device *)rt_device_find(TOUCH_DEVICE_NAME);
    if (RT_Device_Class_I2CBUS != ft_bus->parent.type)
    {
        ft_bus = NULL;
    }
    if (ft_bus)
    {
        rt_device_open((rt_device_t)ft_bus, RT_DEVICE_FLAG_RDWR);
    }
    else
    {
        LOG_I("bus not find\n");
        return RT_FALSE;
    }

    {
        struct rt_i2c_configuration configuration =
        {
            .mode = 0,
            .addr = 0,
            .timeout = 5,
            .max_hz  = 400000,
        };

        rt_i2c_configure(ft_bus, &configuration);
    }

    LOG_I("tma525b probe OK");

    return RT_TRUE;
}


static struct touch_ops ops =
{
    read_point,
    init,
    deinit
};


static int rt_tma525b_init(void)
{
    tma525b_driver.probe = probe;
    tma525b_driver.ops = &ops;
    tma525b_driver.user_data = RT_NULL;
    tma525b_driver.isr_sem = rt_sem_create("tma525b", 0, RT_IPC_FLAG_FIFO);

    rt_touch_drivers_register(&tma525b_driver);

    return 0;
}
INIT_COMPONENT_EXPORT(rt_tma525b_init);


Touch Device Upper Layer Usage Example Code

Example registers interrupt callback to release semaphore, then semaphore drives reading touch data, then prints touch points

static struct rt_semaphore tp_sema;

static rt_err_t tp_rx_indicate(rt_device_t dev, rt_size_t size)
{
    rt_sem_release(&tp_sema);

    return RT_EOK;
}

static void touch_read_task(void *parameter)
{
    rt_sem_init(&tp_sema, "tpsem", 0, RT_IPC_FLAG_FIFO);

    /*Open touch device*/
    rt_device_t touch_device = NULL;
    touch_device = rt_device_find("touch");
    if (touch_device)
    {
        if (RT_EOK == rt_device_open(touch_device, RT_DEVICE_FLAG_RDONLY))
        {
            touch_device->rx_indicate = tp_rx_indicate;

            while (1)
            {
                rt_err_t err;
                struct touch_message touch_data;

                err = rt_sem_take(&tp_sema, rt_tick_from_millisecond(500));
                if (RT_EOK == err)
                {
                    rt_device_read(touch_device, 0, &touch_data, 1);
                    rt_kprintf("read data %d, [%d,%d]\r\n", touch_data.event, touch_data.x, touch_data.y);
                }
            }
        }
        else
        {
            rt_kprintf("Touch open error!\n");
            touch_device = NULL;
        }
    }
    else
    {
        rt_kprintf("No touch device found!\n");
    }

}