STM32 SPI驱动触摸屏(XPT2046)(上)

接口/总线/驱动

1139人已加入

描述

触摸屏又称触控面板,它是一种把触摸位置转化成坐标数据的输入设备触摸屏可以分为电阻式触摸屏和电容式触摸屏。这里用电阻式触摸屏来实现触摸控制。

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

XPT2046

两个 ITO 涂层的两端分别引出 X-、X+、Y-、Y+四个电极,通过这些电极,外部电路向这两个涂层可检测电压。

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

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

x =Vy+/ V ref *Width

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

y =Vy+/ V ref *Height

XPT2046

XPT2046

这里采用XPT2046芯片作为触摸控制芯片,XPT2046芯片控制4线电阻触摸屏,STM32与XPT2046采用SPI通讯获取采集得到的电压,然后转换成坐标。

XPT2046原理框

XPT2046

XPT2046 典型应用图

XPT2046

XPT2046 是一种典型的逐次逼近型模数转换器(SAR ADC),包含了采样/保持、模数转换、串口数据输出等功能。同时芯片集成有一个 2.5V的内部参考电压源、温度检测电路,工作时使用外部时钟。XPT2046 可以单电源供电,电源电压范围为 2.7V~5.5V。参考电压值直接决定ADC的输入范围,参考电压可以使用内部参考电压,也可以从外部直接输入1V~VCC范围内的参考电压(要求外部参考电压源输出阻抗低)。X、Y、Z、VBAT、Temp和AUX模拟信号经过片内的控制寄存器选择后进入ADC,ADC可以配置为单端或差分模式。选择VBAT 、Temp和AUX时可以配置为单端模式;作为触摸屏应用时,可以配置为差分模式,这可有效消除由于驱动开关的寄生电阻及外部的干扰带来的测量误差,提高转换准确度。

XPT2046 的内部 2.5V 参考电压源可通过控制位 PD1进行关闭或者打开。一般地,内部参考电压只用于单端模式下 Vbatt、Temp 和 AUX 输入测量。使用差分模式,触摸屏可以获得最佳性能。

外部参考电压

+REF 和-REF之间的电压差决定了模拟输入的电压范围。XPT2046 的参考电压输入范围为 1V~ VCC。参考电压越低,则 ADC 输出的二进制数据结果每一个数字位所代表的模拟电压也越低。

在 12 位工作方式下,数据结果的最低位所代表的模拟电压为 VREF/4096,其余位依此类推。因此,参考电压越低,干扰引入的误差会越大,此时要求尽可能使用低噪声、低波动的参考电压源;在设计电路板时,尽可能减少干扰,输入的信号噪音也不能太高,否则会直接影响转换精度。

单端工作模式

SER/ DFR 置为高电平时,XPT2046 工作在为单端模式,单端工作模式的应用原理如下图所示。

XPT2046

单端模式简单,在采样过程完成后,转换过程中可以关闭驱动开关,降低功耗。但这种模式的缺点是精度直接受参考电压源的精度限制,同时由于内部驱动开关的导通电阻存在,导通电阻与触摸屏电阻的分压作用,也会带来测量误差。

差分工作模式

SER/ DFR 置为低电平时,XPT2046 为差分工作模式,如下图所示。

XPT2046

差分模式的优点是:+REF 和-REF 的输入分别直接接到 YP、YN 上,可消除由于驱动开关的导通电阻引入的坐标测量误差。

缺点是:无论是采样还是转换过程中,驱动开关都需要接通,相对单端模式而言,功耗增加了。

XPT2046模拟输入简图

XPT2046

差分模式输入配置

XPT2046

表中说明了A2、A1、A0 和SER/DFR 控制位与XPT2046 的配置关系。这些控制位来自DIN脚的串行数据。

XPT2046是用来采集触摸屏触摸点的水平位置与垂直位置的。由表可知,测量Y位置时,需将A2A1A0设置为001,此时驱动的模拟输入为YP和YN:测量X位置时,需将A2A1A0设置为101,此时驱动的模拟输入为XP和XN。其他两行用于测量触模时作用于屏幕上的压力,这里忽略。

控制字的控制位命令

XPT2046

控制字节各位描述

XPT2046

如果采用单端模式测量 X 坐标、Y 坐标和触摸压力,则需要添加一个外部参考电压,并且 XPT2046 的电源也必须来自这个外部参考源。需要特别注意的一点是,当使用单端模式时,输入 ADC 的电压不能超过内部参考电压,尤其是当工作电压大于 2.7V 的时候。

注意 :差分模式仅用于 X 坐标、Y 坐标和触摸压力的测量,其它测量要求采用单端模式。

XPT2046的数据接口是串行的,通过该接口可轻易地与单片机或处理器完成互连,处理器和转换器之间的通信需要8个时钟周期,通过这8个时钟周期来确定XPT72046转换的模拟通道及采用的转换模式。一次完整的转换需要24个串行同步时钟来完成。

前8个时钟用来通过DIN引脚输入控制命令(字节),当转换器获取有关下一次转换的足够信息后,接着根据获得的信息设置输入多路选择器和参考源输入,并进入采样模式,如果需要,将启动触摸面板驱动器,3个多时钟周期后,控制字节设置完成,转换器进入转换状态。这时,输入采样保持器进入保持状态,触摸面板驱动器停止工作(单端工作模式)。接着的12个时钟周期将完成真正的模数转换。在差分模式时,驱动器在转换过程中将一直工作,第13个时钟将输出转换结果的最后一位。剩下的3个多时钟周期将用来完成被转换器忽略的最后字节(DOUT输出0)。XPI2046 24周期的转换时序如图所示:

XPT2046

由图可知。控制字节由DIN送入XPT2046,其包括启动转换位、寻址位,ADC分辨率设置位,单端/差分设置位和掉电控制位,起始位是拉制字的首位,取值恒为1,在DIN引脚检测到起始位前,所有的输入都将被忽略。寻址位即A2A1A0,由表可知,当A2A1A0为001时,XPT2046采集Y位置,当A2A1A0为101时,XPT2046采集X位置。MODE位为模式选择位,其用于设置ADC分辨率,当MODE为0时,下一次转换将是12位模式:当MODE为1时,下一次转换将是8位模式。SER/DFR位用于选择参考源的模式,当 SER/DFR为1时,选择单端模式;当 SER/DFR为0时,选择差分模式,为提高AD转换的精度并消除测量误差,这里选择12位转换模式和差分模式。最后两位用于掉电控制,这里将之配置为00,即在两次AD转换之间掉电,下次转换一开始,芯片立即进入完全上电状态,而无需额外延时,另外,此时 PENIRQ是一直使能的。

分析可得转换X通道时所对应的控制字为0xD0,转换Y通道时所对应的控制字为0x90。为方便使用,将这两个控制字定义为宏:

#define     X_CMD          0XD0
#define     Y_CMD          0X90

接着开始编写触摸屏程序,原理图如下:

XPT2046

引脚分别是T_MOSI:PF11、T_MISO:PB2 、T_CS:PC13、T_CLK:PB0、T_PEN:PB1。

为方便调用对引脚进行的控制进行宏定义,。

#define     T_CS_H()      do{GPIOC- >BSRRL = 1< < 13;}while(0)
#define     T_CS_L()      do{GPIOC- >BSRRH = 1< < 13;}while(0)
#define     T_CLK_H()     do{GPIOB- >BSRRL = 1< < 0;}while(0)
#define     T_CLK_L()     do{GPIOB- >BSRRH = 1< < 0;}while(0)
#define     T_MOSI_H()    do{GPIOF- >BSRRL = 1< < 11;}while(0)
#define     T_MOSI_L()    do{GPIOF- >BSRRH = 1< < 11;}while(0)

初始化引脚:

void Touch_gpio_Init()
{
  //1.开时钟
  RCC- >AHB1ENR  |= 1< < 1 | 1< < 2 | 1< < 5;
  //2. 模式、类型、速度、上下拉
  //PB1/2:输入
  GPIOB- >MODER  &= ~(0XF< < 2);
  GPIOB- >PUPDR  &= ~(0XF< < 2);
  GPIOB- >PUPDR  |= (0x1< < 2);
  //PB0:
  GPIOB- >MODER  &= ~(0X3< < 0);
  GPIOB- >MODER  |= 1< < 0;
  GPIOB- >OTYPER  &= ~(1< < 0);    //推挽
  GPIOB- >OSPEEDR  &= ~(0X3< < 0);//2mHZ
  GPIOB- >PUPDR  &= ~(0X3< < 0);  //无上下拉
  //PC13
  GPIOC- >MODER  &= ~(0X3< < 26);
  GPIOC- >MODER  |= 1< < 26;
  GPIOC- >OTYPER  &= ~(1< < 13);    //推挽
  GPIOC- >OSPEEDR  &= ~(0X3< < 26);//2mHZ
  GPIOC- >PUPDR  &= ~(0X3< < 26);  //无上下拉
  //PF11
  GPIOF- >MODER  &= ~(0X3< < 22);
  GPIOF- >MODER  |= 1< < 22;
  GPIOF- >OTYPER  &= ~(1< < 11);    //推挽
  GPIOF- >OSPEEDR  &= ~(0X3< < 22);//2mHZ
  GPIOF- >PUPDR  &= ~(0X3< < 22);  //无上下拉
  //3. 初始状态
  T_CS_H();    //片选信号低电平有效,初始时不片选
  T_CLK_L();
}

初始化完成,根据时序编写对X或Y通道完成一次转换的函数实现

u16 Touch_GetADC(u8 cmd)
{
  u8 i = 0;
  u16 res = 0;

  T_CLK_L();        
  T_CS_L();
  Delay_us(1);
  for(i=0;i< 8;i++)
  {
    if(cmd & 0x80)
      T_MOSI_H();    //数据有效输出高电平
    else
      T_MOSI_L();    //数据有效输出低电平
    Delay_us(1);
    T_CLK_H();      
    Delay_us(1);
    T_CLK_L();
    Delay_us(1);
    cmd < <= 1;
  }
  T_CLK_H();        //ADC需要一个周期
  Delay_us(1);

  res = 0;
  for(i=0;i< 15;i++)
  {
    res < <= 1;    //空出最低位,准备接收
    T_CLK_L();
    Delay_us(1);
    T_CLK_H();
    Delay_us(1);
    if(GPIOB- >IDR & (1< < 2))
      res |= 1;
    Delay_us(1);
  }

  T_CLK_L();
  Delay_us(1);
  T_CS_H();        

  res > >= 3;      //最低3位是补充的0,移掉不要

  return res;
}

有了上面的函数,只需依次送入X通道的控制字节和Y通道的控制字节,即可在一次接触后,获取接触点x、y方向的AD值。但针对AD转换,为了提高转换的准确性,这里采用中值平均滤波算法进行滤波。中值平均算法即将获取的AD值通过冒泡法排序,然后掐头去尾,取平均值并返回。具体如下:

u16 Touch_GetAvgADC(u8 cmd, u16 * buf,u8 size)
{
  u8 i = 0,j=0;
  u16 temp = 0;
  float res = 0;

  for(i=0;i< size;i++)
    buf[i] = Touch_GetADC(cmd);


  for(i=0;i< size-1;i++)
  {
    for(j=i+1;j< size;j++)
    {
      if(buf[i]

接着,就可以用这个函数得到一个点的坐标。先把点的坐标定义为结构体:

typedef struct 
{
  u16 x;
  u16 y;
}Point_Typedef;

通过笔中断引脚判断是否有触摸动作,然后分别读出X和Y的ADC值并返回出一个点的坐标ADC。

Point_Typedef Touch_GetPointADC()
{
  Point_Typedef ts = {0xffff,0xffff};

  if((GPIOB- >IDR & (1< < 1))==0)        //有触摸动作
  {
    ts.x = Touch_GetAvgADC(X_CMD, Point_buf[0],10);
    ts.y = Touch_GetAvgADC(Y_CMD, Point_buf[1],10);
  }

  return ts;
}

现在已经可以获得点的坐标ADC,开始进行测试程序是否正确。

主函数

#include "stm32f4xx.h"
#include "usart.h"
#include "delay.h"
#include "stdio.h"
#include "touch.h"


int main()
{
  Point_Typedef ts = {0xffff,0xffff};

  Usart1_Init(115200);
  Touch_gpio_Init();

  while(1)
  {
    ts = Touch_GetPointADC();
    printf("(%d,%d)rn",ts.x,ts.y);
    Delay_ms(2000);
  }
}

运行程序,发现没有触摸时点的坐标是获取不到的,有触摸动作时,点的ADC坐标就是在12位ADC的范围内,获取点的坐标ADC测试成功。

XPT2046

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分