打印
[STM32F1]

STM32基础篇 触摸屏实验

[复制链接]
楼主: aizaixiyuanqian
手机看帖
扫描二维码
随时随地手机跟帖
21
aizaixiyuanqian|  楼主 | 2018-3-12 18:34 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
1988020566 发表于 2018-3-12 17:14
这个是什么接口?

SPI接口

使用特权

评论回复
22
aizaixiyuanqian|  楼主 | 2018-3-12 18:35 | 只看该作者
51xlf 发表于 2018-3-12 17:14
直接来软件吧。

接着来,不要急。

使用特权

评论回复
23
aizaixiyuanqian|  楼主 | 2018-3-12 18:35 | 只看该作者
i1mcu 发表于 2018-3-12 17:15
原理性的东西不需要了解吧。

原理加实践呗

使用特权

评论回复
24
aizaixiyuanqian|  楼主 | 2018-3-12 18:35 | 只看该作者
pmp 发表于 2018-3-12 17:15
没有硬件和代码

继续分享

使用特权

评论回复
25
aizaixiyuanqian|  楼主 | 2018-3-12 18:36 | 只看该作者
mmbs 发表于 2018-3-12 17:15
现在的触摸屏都是串口的。

还没用过串口的触摸屏

使用特权

评论回复
26
aizaixiyuanqian|  楼主 | 2018-3-12 18:36 | 只看该作者
51xlf 发表于 2018-3-12 17:16
硬件一般都是别人设计好的。

对啊,拿人家设计好的东西来学习。

使用特权

评论回复
27
aizaixiyuanqian|  楼主 | 2018-3-12 18:37 | 只看该作者
1988020566 发表于 2018-3-12 17:17
不都是并口的TFT吗?

是并口的FTF只是加了一个电阻触摸屏而已

使用特权

评论回复
28
aizaixiyuanqian|  楼主 | 2018-3-12 18:38 | 只看该作者
1988020566 发表于 2018-3-12 17:17
不都是并口的TFT吗?

是并口啊,只是触摸是SPI的

使用特权

评论回复
29
aizaixiyuanqian|  楼主 | 2018-3-12 18:38 | 只看该作者
mmbs 发表于 2018-3-12 17:17
串口集成屏幕高端的很,内部指令很简单。

有机会学习下

使用特权

评论回复
30
aizaixiyuanqian|  楼主 | 2018-3-12 18:39 | 只看该作者
pmp 发表于 2018-3-12 17:17
楼主的硬件和代码在哪下载呢

还会接着分享。

使用特权

评论回复
31
aizaixiyuanqian|  楼主 | 2018-3-12 18:50 | 只看该作者
PZ6808L  触摸屏的原理图

使用特权

评论回复
32
aizaixiyuanqian|  楼主 | 2018-3-12 18:51 | 只看该作者
XPT2046 的初始化
XPT2046 说起来其实就是一个 AD 转换器,所以它适合不需要什么初始化设置的,
而具体的初始化其实也就是单片机 IO 的初始化和 SPI 的初始化。
这次 STM32 是使用 SPI1 来进行操作,SPI 的设置其实在前几节课已经讲过了,这
里就不重复讲了,初始化的具体代码如下:
/**********************************************************************
* Function Name : TOUCH_Init
* Description : 初始化触摸屏
* Input : None
* Output : None
* Return : None
**********************************************************************/
void TOUCH_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* SPI 的 IO 口和 SPI 外设打开时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
/* TOUCH-CS 的 IO 口设置 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* TOUCH-PEN 的 IO 口设置 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOD, &GPIO_InitStructure);
SPI1_Config();
/* 要使用 FLASH 来存储校正参数,所以注意之前要初始化 */
/* 检测是否有校正参数 */
FLASH_ReadData(&TouchAdj.posState,  TOUCH_ADJ_ADDR,
sizeof(TouchAdj));
if(TouchAdj.posState != TOUCH_ADJ_OK)
{
TOUCH_Adjust(); //校正
}
}

使用特权

评论回复
33
aizaixiyuanqian|  楼主 | 2018-3-12 18:51 | 只看该作者
在这个函数中,调用了 SPI1 的初始化函数,和触摸屏的校正程序,下面是 SPI1 的
初始化程序,校正原理我们在后面在讲述。
/**********************************************************************
* Function Name : SPI1_Config
* Description : 初始化 SPI2
* Input : None
* Output : None
* Return : None
*********************************************************************/
void SPI1_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
/* SPI 的 IO 口和 SPI 外设打开时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
/* SPI 的 IO 口设置 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7); //PA5.6.7 上拉
/********************************************************************/
/******************* 设置 SPI 的参数 ***********************************/
/*********************************************************************/
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//选择全双
工 SPI 模式
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主机模式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8 位 SPI
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //时钟悬空高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //在第二个时钟采集数据
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;  //Nss 使用软件控制
/* 选择波特率预分频为 256 */
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//从最高位开始传输
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Cmd(SPI1, ENABLE);
SPI_Init(SPI1, &SPI_InitStructure);
}

使用特权

评论回复
34
aizaixiyuanqian|  楼主 | 2018-3-12 18:54 | 只看该作者
XPT2046 读取 X、Y 值
我们知道,触摸屏根据方向,分为 X 轴和 Y 轴两个部分,通过读取 X 轴和 Y 轴的
数据,我们就可以知道触摸屏触摸的位置了,就像数学上面的,知道了 x 坐标和 y 坐标,
那么就可以确定在坐标轴上面一个点的位置。

使用特权

评论回复
35
aizaixiyuanqian|  楼主 | 2018-3-12 18:56 | 只看该作者

XPT2046 完成一个完整的转换需要 24 个串行时钟,也就是需要 3 个字节的 SPI 时
钟。对照上图,XPT2046 前 8 个串行时钟,是接收 1 个字节的转换命令,接收到转换
命令了之后,然后使用 1 个串行时钟的时间来完成数据转换(当然在编写程序的时候,
为了得到精确的数据,你可以适当的延时一下),然后返回 12 个字节长度(12 个字节
长度也计时 12 个串行时钟)的转换结果。然后最后 3 个串行时钟返回三个无效数据。

使用特权

评论回复
36
aizaixiyuanqian|  楼主 | 2018-3-12 18:58 | 只看该作者
读取一个完整转换过程为:
1)  发送 1 个 8 字节的控制命令
2)  在这里可以小延时一下,如果你 SPI 时钟周期比 XPT2046 转换周期慢许多,不
用延时也可以。
3)  读取 2 个字节的返回数据。
4)  进行数据处理。也就是丢弃最后读取到的 3 位数据。

使用特权

评论回复
37
aizaixiyuanqian|  楼主 | 2018-3-12 19:01 | 只看该作者
我们需要读取两个数据,一个 X 轴数据和一个 Y 轴数据,所以我们这里需要两个控
制命令。一个完整的控制命令的结构为:


使用特权

评论回复
38
aizaixiyuanqian|  楼主 | 2018-3-12 19:02 | 只看该作者
采样物理坐标值代码
static uint16_t TOUCH_ReadData(uint8_t cmd)
{
uint8_t i, j;
uint16_t readValue[TOUCH_READ_TIMES], value;
uint32_t totalValue;
/* SPI 的速度不宜过快 */
SPI2_SetSpeed(SPI_BaudRatePrescaler_16);
/* 读取 TOUCH_READ_TIMES 次触摸值 */
for(i=0; i<TOUCH_READ_TIMES; i++)
{ /* 打开片选 */
TOUCH_CS_CLR;
/* 在差分模式下,XPT2046 转换需要 24 个时钟,8 个时钟输入命令,之后 1
个时钟去除 */
/* 忙信号,接着输出 12 位转换结果,剩下 3 个时钟是忽略位 */
SPI1_WriteReadData(cmd); // 发送命令,选择 X 轴或者 Y 轴
/* 读取数据 */
readValue[i] = SPI1_WriteReadData(0xFF);
readValue[i] <<= 8;
readValue[i] |= SPI1_WriteReadData(0xFF);
/* 将数据处理,读取到的 AD 值的只有 12 位,最低三位无用 */
readValue[i] >>= 3;
TOUCH_CS_SET;
}
/* 滤波处理 */
/* 首先从大到小排序 */
for(i=0; i<(TOUCH_READ_TIMES - 1); i++)
{
for(j=i+1; j<TOUCH_READ_TIMES; j++)
{
/* 采样值从大到小排序排序 */
读取数据部分
if(readValue[i] < readValue[j])
{
value = readValue[i];
readValue[i] = readValue[j];
readValue[j] = value;
}
}
}
/* 去掉最大值,去掉最小值,求平均值 */
j = TOUCH_READ_TIMES - 1;
totalValue = 0;
for(i=1; i<j; i++) //求 y 的全部值
{
totalValue += readValue[i];
}
value = totalValue / (TOUCH_READ_TIMES - 2);
return value;
}

使用特权

评论回复
39
aizaixiyuanqian|  楼主 | 2018-3-12 19:55 | 只看该作者
物理坐标值的数据处理
在读取 X 轴和 Y 轴的物理坐标值,也就是 AD 值的时候,需要进行一些必要的数据处
理,这也是为了获取更准确的数据值,否则就会出现飞点等误差。
比较常用的程序滤波的方法为平均值法。也就是多次读取结果,然后去掉它们的最大值
和最小值,最后求取它们的平均值。这种方法读取的次数越多,得到的数据就更准确。而上
面我们读取数据程序里面使用的滤波方法也是这种方法。
不过为了更好的滤波,还使用了另外一种方式进行滤波。也就是当读取到两次数据之后,
然后检查两个数据之间的差值,如果超过理想的误差,那么丢弃数据。这种方法也是很多的
处理飞点的程序方法。

使用特权

评论回复
40
aizaixiyuanqian|  楼主 | 2018-3-12 19:56 | 只看该作者
读取触摸屏的 X 轴 Y 轴的物理坐标值
static uint8_t TOUCH_ReadXY(uint16_t *xValue, uint16_t *yValue)
{
uint16_t xValue1, yValue1, xValue2, yValue2;
xValue1 = TOUCH_ReadData(TOUCH_X_CMD);
yValue1 = TOUCH_ReadData(TOUCH_Y_CMD);
xValue2 = TOUCH_ReadData(TOUCH_X_CMD);
yValue2 = TOUCH_ReadData(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) || (*yValue > TOUCH_MAX))
{
return 0xFF;
}
/* 求平均值 */
*xValue = (xValue1 + xValue2) / 2;
*yValue = (yValue1 + yValue2) / 2;
/* 判断得到的值,是否在取值范围之内 */
if((*xValue > TOUCH_X_MAX) || (*xValue < TOUCH_X_MIN)
|| (*yValue > TOUCH_Y_MAX) || (*yValue < TOUCH_Y_MIN))
{
return 0xFF;
}
return 0;
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则