44. 电阻式触摸屏

一、电阻式触摸屏简介

  电阻触摸屏 的主要部分是一块与显示器表面非常贴合的电阻薄膜屏,这是一种多层的复合薄膜,具体结构如下图所示。

电阻触摸屏多层结构图

  它主要由表面硬涂层、两个 ITO 层、间隔点以及玻璃底层构成,这些结构层都是透明的,整个触摸屏覆盖在液晶面板上,透过触摸屏可看到液晶面板。表面涂层起到保护作用,玻璃底层起承载的作用,而两个 ITO 层是触摸屏的关键结构,它们是涂有铟锡金属氧化物的导电层。两个 ITO 层之间使用间隔点使两层分开,当触摸屏表面受到压力时,表面弯曲使得上层 ITO 与下层 ITO 接触,在触点处连通电路。

  当手指触摸屏幕时,两个 ITO 层在触摸点位置就有接触,电阻发生变化,在 X 和 Y 两个方向上产生电信号,然后送到触摸屏控制器。触摸屏控制器侦测到这一接触并计算出 X 和 Y 方向上的 AD 值,简单来讲,电阻触摸屏将触摸点 (X, Y) 的物理位置转换为代表 X 坐标和 Y 坐标的电压值。单片机与触摸屏控制器进行通信获取到 AD 值,通过一定比例关系运算,获得 X 和 Y 轴坐标值。

  电阻触摸屏的优点:精度高、价格便宜、抗干扰能力强、稳定性好。

  电阻触摸屏的缺点:容易被划伤、透光性不太好、不支持多点触摸。

  电阻式触摸屏 都需要一个 AD 转换器,一般来说是需要一个控制器。

二、触摸控制原理

  要实现触摸功能,就是要把触摸点和屏幕坐标对应起来。我们以 LCD 显示屏为例,我们知道屏幕的扫描方向是可以编程设定的,而触摸点,在触摸传感器安装好后,AD 值的变化向方向则是固定的。

电阻触摸屏检测原理

  当触摸屏被按下时,两个 ITO 层相互接触,从触点处把 ITO 层分为两个电阻,且由于 ITO 层均匀导电,两个电阻的大小与触点离两电极的距离成比例关系, 利用这个特性,可通过以下过程来检测坐标,这也正是电阻触摸屏名称的由来。其等效电路图如下:

电阻式触摸屏按下时等效电路图

  计算 X 坐标时,在 X+ 电极施加驱动电压 \(V_{ref}\) ,X- 极接地,所以 X+ 与 X- 处形成了匀强电场,而触点处的电压通过 Y+ 电极采集得到,由于 ITO 层均匀导电,触点电压与 \(V_{ref}\) 之比等于触点 X 坐标与屏宽度之比,从而:

\[x = \frac{V_{Y+}}{V_{ref}} * Width \]

  计算 Y 坐标时,在 Y+ 电极施加驱动电压 \(V_{ref}\) ,Y- 极接地,所以 Y+ 与 Y- 处形成了匀强电场,而触点处的电压通过 X+ 电极采集得到,由于 ITO 层均匀导电,触点电压与 \(V_{ref}\) 之比等于触点 Y 坐标与屏高度之比,从而:

\[y = \frac{V_{Y+}}{V_{ref}} * Height \]

三、XPT2046控制芯片

  为了方便检测触摸的坐标,一些芯片厂商制作了电阻屏专用的控制芯片,控制上述采集过程、采集电压, 外部微控制器直接与触摸控制芯片通讯直接获得触点的电压或坐标。 这里,我们使用的电阻触摸屏就是采用 XPT2046 芯片作为触摸控制芯片, XPT2046 芯片控制 4 线电阻触摸屏,STM32 与 XPT2046 采用 SPI 通讯获取采集得的电压,然后转换成坐标。

  XPT2046 是一款 4 导线制触摸屏控制器,使用的是 SPI 通信接口,内含 12 位分辨率 125KHz 转换速率逐步逼近型 A/D 转换器。XPT2046 支持从 1.5V 到 5.25V 的低电压 I/O 接口。XPT2046 能通过执行两次 A/D 转换(一次获取 X 位置,一次获取 Y 位置)查出被按的屏幕位置,除此之外,还可以测量加在触摸屏上的压力。内部自带 2.5V 参考电压可以作为辅助输入、温度测量和电池监测模式之用,电池监测的电压范围可以从 0V 到 6V。XPT2046 片内集成有一个温度传感器。在 2.7V 的典型工作状态下,关闭参考电压,功耗可小于 0.75mW。

  XPT2046 的驱动方法也是很简单,它的通信时序图如下:

XPT2046通信时序图

  依照时序图,就可以很好写出这个通信代码,上图具体过程:拉低片选,选中器件 → 发送命令字 → 清除 BUSY → 读取 16 位数据(高 12 位数据有效即转换的 AD 值)→ 拉高片选,结束操作

  XPT2406 的命令字格式如下所示:

命令字详情图

  位 7,开始位,置 1 即可。位 3,为了提供精度,MODE 位清 0 选择 12 位分辨率。位 2,是进行工作模式选择,为了达到最佳性能,首选差分工作模式即该位清 0 即可。位 1-0 是功耗相关的,直接清 0 即可。而位 6-4 的值要取决于工作模式,在确定了差分功能模式后,通道选择位也确定了,如下图所示。

差分模式输入配置图

  从上图,就可以知道:当我们需要检测 Y 轴位置时,A2A1A0 赋值为 001;检测 X 轴位置时,A2A1A0 赋值为 101。结合前面对其他位的赋值,在 X,Y 方向与屏幕相同的情况下,命令字 0xD0 就是读取 X 坐标 AD 值,0x90 就是读取 Y 坐标的 AD 值。假如 X,Y 方向与屏幕相反,0x90 就是读取 X 坐标的 AD 值,而 0xD0 就是读取 Y 坐标的 AD 值。

四、电阻触摸屏校准

  触摸屏的 AD 的 Xad、Yad 可以构成一个逻辑平面,LCD 屏的屏幕坐标 X,Y 是物理平面,由于误差,两个平面并不重合,校准的作用就是要将逻辑平面映射到物理平面上,即得到触点在显示屏上的位置坐标。因此,我们需要建立一个映射函数,进行线性校准,精确算出 X 和 Y 方向的比例缩放系数。

屏幕映射

  这里,推荐使用 5 点校准法。五点定位的物理坐标是已知的,其中 4 点分别设置在 LCD 的角落,一点设置在 LCD 正中心,作为基准矫正点。

5点校准法

  校正步骤如下:

  1. 通过先后点击 LCD 的 4 个角落的矫正点,获取 4 个角落的逻辑坐标值。
  2. 计算屏幕坐标和四点间距:
S1 = x[1]- x[0]
S3 = x[2]- x[3]
S2 = y[2]- y[1]
S4 = y[3]- y[0]

  计算逻辑坐标的四点 “间距”,由于实际触点肯定会存在误差,所以触摸点会落在实际设定点的更大范围内,所以有必要设定一个误差范围。

  1. 点击 LCD 正中心,获取中心点的逻辑坐标,作为矫正的基准点。
  2. 完成以上步骤则校正完成。下次点击触摸屏的时候获取的逻辑值 XL 和 YL,便可以按下以公式转换为物理坐标:
X = (XL - XLC) / KX + XC
Y = (YL - YLC) / KY + YC

五、原理图

接线端子

模式SPI的SCK和MISO引脚

模式SPI的MOSI引脚

设备的片选引脚

六、程序源码

6.1、模拟SPI驱动

#define SPI_SCK_GPIO_PORT               GPIOB
#define SPI_SCK_GPIO_PIN                GPIO_PIN_0
#define RCC_SPI_SCK_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOB_CLK_ENABLE()


#define SPI_MOSI_GPIO_PORT              GPIOF
#define SPI_MOSI_GPIO_PIN               GPIO_PIN_11
#define RCC_SPI_MOSI_GPIO_CLK_ENABLE()  __HAL_RCC_GPIOF_CLK_ENABLE()

#define SPI_MISO_GPIO_PORT              GPIOB
#define SPI_MISO_GPIO_PIN               GPIO_PIN_2
#define RCC_SPI_MISO_GPIO_CLK_ENABLE()  __HAL_RCC_GPIOB_CLK_ENABLE()

#define SPI_SCK(x)                      do{ x ? \
                                            HAL_GPIO_WritePin(SPI_SCK_GPIO_PORT,SPI_SCK_GPIO_PIN, GPIO_PIN_SET):\
                                            HAL_GPIO_WritePin(SPI_SCK_GPIO_PORT,SPI_SCK_GPIO_PIN, GPIO_PIN_RESET);\
                                        }while(0)

#define SPI_MOSI(x)                     do{ x ? \
                                            HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT, SPI_MOSI_GPIO_PIN, GPIO_PIN_SET):\
                                            HAL_GPIO_WritePin(SPI_MOSI_GPIO_PORT, SPI_MOSI_GPIO_PIN, GPIO_PIN_RESET);\
                                        }while(0)

#define SPI_MISO()                      HAL_GPIO_ReadPin(SPI_MISO_GPIO_PORT, SPI_MISO_GPIO_PIN)
/**
 * @brief SPI初始化函数
 * 
 */
void SPI_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // 使能SPI SCK MISO MOSI对应GPIO引脚的时钟
    RCC_SPI_SCK_GPIO_CLK_ENABLE();
    RCC_SPI_MISO_GPIO_CLK_ENABLE();
    RCC_SPI_MOSI_GPIO_CLK_ENABLE();

    GPIO_InitStruct.Pin = SPI_SCK_GPIO_PIN;                                     // SPI的SCL引脚
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;                                 // 推挽输出
    GPIO_InitStruct.Pull = GPIO_NOPULL;                                         // 不使用上下拉
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;                               // 输出速度
    HAL_GPIO_Init(SPI_SCK_GPIO_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = SPI_MOSI_GPIO_PIN;                                    // SPI的MOSI引脚
    HAL_GPIO_Init(SPI_MOSI_GPIO_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = SPI_MISO_GPIO_PIN;                                    // SPI的MISO引脚
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;                                     // 输入模式
    HAL_GPIO_Init(SPI_MISO_GPIO_PORT, &GPIO_InitStruct);


    SPI_SCK(0);                                                                 // SPI的SCK引脚默认为低电平,选择工作模式0或1
    // SPI_SCK(1);                                                                 // SPI的SCK引脚默认为高电平,选择工作模式2或3
}
/**
 * @brief SPI交换一个字节函数
 * 
 * @param data 待交换的数据
 * @return uint8_t 交换后的数据
 */
uint8_t SPI_SwapOneByte(uint8_t data)
{
    for (uint8_t i = 0; i < 8; i++)
    {
        // SCK下降沿
        SPI_SCK(0);
        Delay_us(1);
        // 移出数据
        SPI_MOSI(data & 0x80);
        data <<= 1;
        // 移入数据
        if (SPI_MISO())
        {
            data |= 0x01;
        }
        // SCK上升沿
        SPI_SCK(1);
        Delay_us(1);
    }

    SPI_SCK(0);

    return data;
}

6.2、XPT2046初始化

#define TOUCH_CS_GPIO_PORT                  GPIOC
#define TOUCH_CS_GPIO_PIN                   GPIO_PIN_3
#define RCC_TOUCH_CS_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()

#define TOUCH_PEN_GPIO_PORT                 GPIOB
#define TOUCH_PEN_GPIO_PIN                  GPIO_PIN_1
#define RCC_TOUCH_PEN_GPIO_CLK_ENABLE()     __HAL_RCC_GPIOB_CLK_ENABLE()

#define TOUCH_CS(x)                         do{ x ? \
                                                HAL_GPIO_WritePin(TOUCH_CS_GPIO_PORT, TOUCH_CS_GPIO_PIN, GPIO_PIN_SET):\
                                                HAL_GPIO_WritePin(TOUCH_CS_GPIO_PORT, TOUCH_CS_GPIO_PIN, GPIO_PIN_RESET);\
                                            }while(0)


#define TOUCH_PEN(x)                        do{ x ? \
                                                HAL_GPIO_WritePin(TOUCH_PEN_GPIO_PORT, TOUCH_PEN_GPIO_PIN, GPIO_PIN_SET):\
                                                HAL_GPIO_WritePin(TOUCH_PEN_GPIO_PORT, TOUCH_PEN_GPIO_PIN, GPIO_PIN_RESET);\
                                            }while(0)
/**
 * @brief 触摸屏初始化
 * 
 */
void Touch_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    RCC_TOUCH_CS_GPIO_CLK_ENABLE();
    RCC_TOUCH_PEN_GPIO_CLK_ENABLE();

    GPIO_InitStruct.Pin = TOUCH_CS_GPIO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(TOUCH_CS_GPIO_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = TOUCH_PEN_GPIO_PIN;
    HAL_GPIO_Init(TOUCH_PEN_GPIO_PORT, &GPIO_InitStruct);
}

6.3、触摸屏读取AD值

/**
 * @brief 触摸屏读取AD值
 * 
 * @param command 要写入的命令
 * @return uint16_t 读取的数据
 */
uint16_t Touch_ReadAD(uint8_t command)
{
    uint16_t data = 0;

    TOUCH_CS(0);                                                                // 选中设备

    // MCU向XPT2046发送数据
    SPI_SwapOneByte(command);

    // 过滤忙信号
    SPI_SCK(0);
    Delay_us(1);
    SPI_SCK(1);
    Delay_us(1);

    // MCU读取XTP2046返回数据
    data = SPI_SwapOneByte(0x00);
    data <<= 8;
    data |= SPI_SwapOneByte(0x00);

    TOUCH_CS(1);                                                                // 释放设备

    return (data >> 4);
}
posted @ 2024-01-15 20:39  星光樱梦  阅读(24)  评论(0编辑  收藏  举报