STM32CubeMX系列 | 触摸屏

发布时间:2023-03-23  

1.触摸屏简介

目前最常用的触摸屏有两种:电阻式触摸屏和电容式触摸屏

1.1 电阻式触摸屏

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

两个ITO涂层的两端分别引出X-、X+、Y-、Y+四个电极,这是电阻屏最常见的四线结构,通过这些电极,外部电路向这两个涂层可以施加匀强电场或检测电压。

电阻触摸屏的校准:确定位置和对应的电压值之间的比例关系

X0 = Xfac * ADC_X + Xoff
Y0 = Yfac * ADC_Y + Yoff

电阻触摸屏的优缺点

- 优点:精度高、价格便宜、抗干扰能力强、稳定性好
- 缺点:容易被划伤、透光性不太好、不支持多点触控

触摸屏都需要一个AD转换器来将电压变化读取出来,供主机算出触摸的位置。本例程中的TFTLCD模块使用的是四线电阻式触摸屏,触摸屏控制芯片为XPT2046,XPT2046是一款4导线制触摸屏控制器,采用SPI模式进行通讯,内含12位分辨率125KHz转换速率逐步逼近型A/D转换器。XPT2046引脚图(SOP-16封装)及引脚说明如下图示

1.2 电容式触摸屏

与电阻式触摸屏不同,电容式触摸屏不需要通过压力使触点变形。它的基本原理是利用充电时间检测电容大小,若手指触摸屏幕,会影响触摸点附近两个电极之间的耦合,从而改变两个电极之间的电容量,若检测到某电容的电容量发生了改变,即可获知该电容处有触摸动作从而通过检测出电容值的变化来获知触摸信号。

由于本例程使用的是电阻式触摸屏,这里电容式触摸屏不做详细介绍

2. 硬件设计

D1指示灯用来提示系统运行状态,K_UP按键用来强制校准电阻触摸屏(电容屏无需校准),AT24C02用来存储电阻触摸屏校准数据,TFTLCD模块用来显示触摸

  • D1指示灯

  • K_UP按键

  • TFTLCD模块

  • AT24C02

  • USART1

3. 软件设计

3.1 STM32CubeMX设置

  • RCC设置外接HSE,时钟设置为72M

  • PC0设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平

  • PA0设置为GPIO输入模式、下拉模式

  • PD6设置为GPIO输出模式(TOUCH_CS);PD7设置为GPIO输入模式(TOUCH_PEN)

  • USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位

  • 激活I2C2,选择标准传输模式,选择7位寻址地址,其余默认设置

  • 激活FSMC,详细请参考TFTLCD显示章节的设置

  • 激活SPI1,不开启NSS,数据长度8位,MSB先输出,分频因子16,CPOL为HIGH,CPHA为第二个边沿,不开启CRC检验,NSS为软件控制

  • 输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码

3.2 MDK-ARM编程

  • 添加按键驱动文件key.c和key.h,参考按键输入例程

  • 添加AT24C02驱动文件24cxx.c和24cxx.h,参考I2C总线例程

  • 添加TFTLCD驱动文件tftlcd.c 和tftlcd.h,参考TFTLCD显示例程

  • 添加XPT2046触摸芯片驱动文件touch.c和touch.h 两个重要的结构体:

typedef struct{     /*存储触点读取到的数据*/

    uint16_t x;     //x轴物理坐标值

    uint16_t y;     //y轴物理坐标值

    uint16_t lcdx;  //x轴彩屏坐标值

    uint16_t lcdy;  //y轴彩屏坐标值

}TouchTypeDef;


typedef struct{     /*保存校正因素*/

  uint8_t posState; //校正参数标志

  int16_t xOffset;  //x轴偏移量

  int16_t yOffset;  //y轴偏移量

  float   xFactor;  //x轴比例因数

  float   yFactor;  //y轴比例因数

}PosTypeDef;

触摸屏初始化及读取物理坐标(ADC值)函数:

void TOUCH_Init(void){

    //读取AT24C02的TOUCH_ADJ_ADDR地址处的值

    HAL_I2C_Mem_Read(&hi2c2,ADDR_24CXX_READ,TOUCH_ADJ_ADDR,I2C_MEMADD_SIZE_8BIT,&TouchAdj.posState,sizeof(TouchAdj),0xff);  

    if(TouchAdj.posState != TOUCH_ADJ_OK){  //检查是否有校正数据

        TOUCH_Adjust(); //如果没有校正数据,则启动校正函数  

    }

}

/*封装SPI读取数据函数*/

uint8_t WR_Cmd(uint8_t cmd){

    uint8_t Tx_DATA = cmd;

    uint8_t Rx_DATA = 0;

    HAL_SPI_TransmitReceive(&hspi1,&Tx_DATA,&Rx_DATA,1,500);

    return Rx_DATA;     

}

/*读取X轴或Y轴的ADC值,并进行滤波处理*/

uint16_t TOUCH_Read_AD(uint8_t cmd){

    uint8_t i, j;

    uint16_t NUMH,NUML;

    uint16_t NUM[TOUCH_READ_TIMES] = {0};

    uint16_t temp,value;

    uint32_t totalValue;    //注意数据类型


    for(i=0; i        TOUCH_CS_LOW(); //开始SPI通讯   

        WR_Cmd(cmd);

        NUMH = WR_Cmd(0XFF);

        NUML = WR_Cmd(0XFF);

        NUM[i] = (NUMH<<8)+ NUML;

        NUM[i] >>= 3;   //最低三位无用    

        TOUCH_CS_HIGH();    //结束SPI通讯

    }

    //滤波处理:1.从大到小排序

    for(i=0; i        for(j=i+1; j            if(NUM[i] < NUM[j]){

                temp = NUM[i];

                NUM[i] = NUM[j];

                NUM[j] = temp;

            }   

        }       

    }

    //滤波处理:2.去掉最大值和最小值,求余下的平均值

    j = TOUCH_READ_TIMES - 1;

    totalValue = 0;

    for(i=1; i        totalValue += NUM[i];

    }

    value = totalValue/(TOUCH_READ_TIMES - 2);  

    return value;

}

/*读取X轴和Y轴的ADC值,再次进行滤波*/

uint8_t TOUCH_ReadXY(uint16_t *xValue, uint16_t *yValue){   

    uint16_t xValue1, yValue1, xValue2, yValue2;

    xValue1 = TOUCH_Read_AD(TOUCH_X_CMD);

    yValue1 = TOUCH_Read_AD(TOUCH_Y_CMD);

    xValue2 = TOUCH_Read_AD(TOUCH_X_CMD);

    yValue2 = TOUCH_Read_AD(TOUCH_Y_CMD);

    //计算两点之间的采样差值

    if(xValue1 > xValue2)

        *xValue = xValue1 - xValue2;

    else

        *xValue = xValue2 - xValue1;


    if(yValue1 > yValue2)

        *yValue = yValue1 - yValue2;

    else

        *yValue = yValue2 - yValue1;

    //判断采样差值是否在可控范围内

    if((*xValue > TOUCH_MAX+0) || (*yValue > TOUCH_MAX+0))  

        return 0xFF;

    //求平均值

    *xValue = (xValue1 + xValue2) / 2;

    *yValue = (yValue1 + yValue2) / 2;

    //判断得到的值,是否在取指范围内,避免出现飞点现象  

    if((*xValue > TOUCH_X_MAX+0) || (*xValue < TOUCH_X_MIN) || (*yValue > TOUCH_Y_MAX+0) || (*yValue < TOUCH_Y_MIN))                 

        return 0xFF;


    return 0; 

}

触摸屏校准函数:

/*读校准点的物理坐标*/

uint8_t TOUCH_ReadAdjust(uint16_t x, uint16_t y, uint16_t *xValue, uint16_t *yValue){

    uint8_t i;

    uint32_t timeCont;


    LCD_Clear(BACK_COLOR);

    LCD_DrowSign(x, y, RED);    //x和y为指定的校准点的彩屏坐标值

    i = 0;

    while(1){   //读该指定校准点的物理坐标值

        if(!TOUCH_ReadXY(xValue, yValue)){

            i++;

            if(i > 10){

                LCD_DrowSign(x, y, BACK_COLOR);

                return 0;

            }               

        }


        timeCont++;

        if(timeCont > 0xFFFFFFFE){  //超时退出 

            LCD_DrowSign(x, y, BACK_COLOR); 

            return 0xFF;

        } 

    }       

}

/*触摸屏校准函数*/

void TOUCH_Adjust(void){

    uint16_t px[2], py[2], xPot[4], yPot[4];

    float xFactor, yFactor;

    //读取4个校准点的物理坐标值

    if(TOUCH_ReadAdjust(LCD_ADJX_MIN, LCD_ADJY_MIN, &xPot[0], &yPot[0]))

        return;

    HAL_Delay(500);

    if(TOUCH_ReadAdjust(LCD_ADJX_MIN, LCD_ADJY_MAX, &xPot[1], &yPot[1]))

        return;

    HAL_Delay(500);

    if(TOUCH_ReadAdjust(LCD_ADJX_MAX, LCD_ADJY_MIN, &xPot[2], &yPot[2]))

        return;

    HAL_Delay(500);

    if(TOUCH_ReadAdjust(LCD_ADJX_MAX, LCD_ADJY_MAX, &xPot[3], &yPot[3]))

        return; 

    HAL_Delay(500);

    //处理读取到的四个点的数据,整合成对角的两个点

    px[0] = (xPot[0] + xPot[1]) / 2;

    py[0] = (yPot[0] + yPot[2]) / 2;

    px[1] = (xPot[3] + xPot[2]) / 2;

    py[1] = (yPot[3] + yPot[1]) / 2;

    //求出比例因数

    xFactor = (float)LCD_ADJ_X / (px[1] - px[0]);

    yFactor = (float)LCD_ADJ_Y / (py[1] - py[0]);  

    //求出偏移量

    TouchAdj.xOffset = (int16_t)LCD_ADJX_MAX - ((float)px[1] * xFactor);

    TouchAdj.yOffset = (int16_t)LCD_ADJY_MAX - ((float)py[1] * yFactor);

    //将比例因数进行数据处理,并保存

    TouchAdj.xFactor = xFactor ;

    TouchAdj.yFactor = yFactor ;        

    TouchAdj.posState = TOUCH_ADJ_OK;

    //将得出的校正因数保存到AT24C02中的TOUCH_ADJ_ADDR地址处

    HAL_I2C_Mem_Write(&hi2c2,ADDR_24CXX_WRITE,TOUCH_ADJ_ADDR,I2C_MEMADD_SIZE_8BIT,&TouchAdj.posState,sizeof(TouchAdj),0xff);    

}

触摸屏扫描函数:

uint8_t TOUCH_Scan(void){

    if(TOUCH_ReadXY(&TouchData.x, &TouchData.y)) 

        return 0xFF;    //没有触摸,直接返回0xFF

    //根据物理坐标值,计算出彩屏坐标值

    TouchData.lcdx = TouchData.x * TouchAdj.xFactor + TouchAdj.xOffset;

    TouchData.lcdy = TouchData.y * TouchAdj.yFactor + TouchAdj.yOffset;

    //查看彩屏坐标值是否超过彩屏大小

    if(TouchData.lcdx > tftlcd_data.width)

        TouchData.lcdx = tftlcd_data.width;

    if(TouchData.lcdy > tftlcd_data.height)

        TouchData.lcdy = tftlcd_data.height;

    return 0;     

}

在main.c文件下编写触摸屏测试代码

int main(void){

    uint8_t key;

    uint16_t penColor = BLUE;

    HAL_Init();

    SystemClock_Config();

    MX_GPIO_Init();

    MX_FSMC_Init();

    MX_I2C2_Init();

    MX_SPI1_Init();

    MX_USART1_UART_Init();

  /* USER CODE BEGIN 2 */

    TFTLCD_Init();

    AT24CXX_Init();

    HAL_Delay(2000);    

    LCD_Clear(WHITE);

    TOUCH_Init();

  /* USER CODE END 2 */

  while (1){

    key=KEY_Scan(0);

    if(key==KEY_UP_PRES){   //按下KEY_UP键进入校准函数

        TOUCH_Adjust(); 

    }       


    if(TOUCH_Scan() == 0){   

        if(TouchData.lcdy > tftlcd_data.height - 18){

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    iPhone 6重大缺陷导致没法用:苹果这做法太无语;著名的第三方拆解网站iFixit之前曾指出,iPhone 6、6 Plus在设计上存在严重缺陷,具体症状是触摸屏存在触控没反应或反应不灵敏,这个问题出现后将导致手机完全没......
    如何通过程序判断触摸屏与PLC通讯出现故障?;1、威纶通触摸屏和西门子PLC通讯不上,老是提示PLC没反应,该怎么设置呢? 解决思路: 你设置同样的波特率,同样的位数,同样的停止位,站号......
    、威纶通触摸屏和西门子PLC通讯不上,老是提示PLC没反应,该怎么设置呢? 解决思路: 你设置同样的波特率,同样的位数,同样的停止位,站号不能相同,就是说plc和触摸屏地址不能相同,然后......
    法操作,这无疑也是相当危险的。 英国交通运输研究实验室曾经针对触控屏设计做过实验。结果发现,触摸屏对驾驶者的影响甚至比驾驶时玩手机更严重。驾驶者在使用触摸屏完成某些操作时,所做出的反应时间更长。 所以......
    与施瓦茨频谱仪FSV 30 二、 报修故障:显示花屏 三、 分析、测试和判断 工程师接到这台罗德与施瓦茨频谱仪FSV 30后,立马对其分析检测,开机后发现显示花屏、单元故障,处理后显示正常。但是触摸屏没反应......
    里程清零;仪表亮度调到最暗,关闭中控屏。 整个过程全部用手指操作,不涉及语音系统。  结果是触摸屏完败,成绩最好的是全物理按键的沃尔沃V70,完成所有功能只用了10秒,在此期间行驶了306米。成绩......
    。单相供电时防雷模块的接线方式选用P+N接线方式。充电桩具备急停按钮,以便在紧急情况时能够强行终止充电。 6 软件设计 充电桩通过触摸屏完成交互式控制,运行时如果进行刷卡操作则触发中断进行读卡,确定......
    嵌入式系统领域不可或缺的操作系统之一。触摸屏是一种输入设备,操作简单易学,可靠性高,不占额外的空间,是最常用的便携式系统的输入设备。特别是电阻式触摸屏,它结构简单,成本低,透光效果好,工作环境和外界完全隔离,不怕灰尘和水气,同时......
    依靠触碰屏幕时人体与电极之间形成的电容来实现触点定位,通常具有灵敏度高、支持多点触控,透光性优良,耐磨损寿命长等优点。 目前智能手机和平板电脑普遍采用电容屏,这种触摸屏也已广泛应用在汽车信息娱乐系统中。曾有观点认为,电容......
    耗的特性,它已广泛应用于科学研究、工程设计,军事技术,商业文化艺术及消费产品。而触摸屏作为一种最新的电脑输入设备,具有坚固耐用、反应速度快、节省空间、易于交流等优点。主要用于公共信息的查询、工业控制、军事......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>