单片机---HLK-W801驱动触摸屏

52 篇文章 71 订阅
14 篇文章 1 订阅

背景介绍

最近在学习lvgl,这是个开源的嵌入式图像显示框架,足够支撑一些资源匮乏的单片机,来显示一些看起来比较专业的界面,例如下面这种
在这里插入图片描述
还有这种
在这里插入图片描述
是不是很酷
在这里插入图片描述

放下Lvgl姑且不表。因为是用户操作的界面,现在的大部分屏幕都可以配备触摸操作,不再需要额外的键盘鼠标,所以,今天就来学习一下配置我手中这块支持触摸的屏幕。

电阻屏

不像我们的手机,嵌入式设备大部分配备的是电阻屏,因为精准度高,并且廉价。
在这里插入图片描述

最常见的就是我这种4线电阻屏。就是4根线,X+,X-,Y+,Y-。
原理如下:
四线主要是由镀有ITO镀层的两层薄膜所组成。其中的一层在屏幕的左右边缘,各有一条垂直的总线,另外一层是在屏幕的顶部和底部都各有一条水平的总线。如果你在一层的薄膜两条总线上施加以电压,那么在ITO的镀层之上就会形成一个均匀的电场。当其使用者触击到触摸屏时,触击点的两层薄膜就会发生接触,在另外一层的薄膜之上就可以测量到接触点的电压值了。
在这里插入图片描述
那么4根线如何测量呢
首先第一步:在X-上施加于0V电压,X+上施加于VCC,然后测量Y-(或者Y+)电极上的电压值VPX,然后计算出接触点P的X座标。
第二步:在Y-上施加于0V的电压,在Y+上施加VCC,测量出X-(或X+)电极上的电压值VPY。然后计算出接触点P的Y座标。
以上的两个步骤可以组成一个测量周期,可以得到一组(X,Y)的座标。

以上内容出自《四线式电阻屏工作时的基本工作原理介绍 》
在这里插入图片描述

ADC测量法(免费法)

原理上的方法,我暂且称之为ADC测量法,因为是通过ADC测量电压得到的。既然原理清晰了,那么就可以开始解决了,利用4个GPIO,连接屏幕的4线,按照测量方法,进行编程。这叫需求转化代码。
在这里插入图片描述

关键代码

//获取x y 的值 x,y 为指针,指向存储的变量
void get_map_x_y(int* x,int* y)
{
	GPIO_InitTypeDef GPIO_InitStruct;

	//读 X 值
	//配置 X+为高 推挽  X-为低   推挽 	
	GPIO_InitStruct.Pin = P_TOUCH_X_JIA;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
	HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_X_JIA, GPIO_PIN_SET);

	GPIO_InitStruct.Pin = P_TOUCH_X_JIAN;
	HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
	HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_X_JIAN, GPIO_PIN_RESET);

	//配置 Y+为adc模式 Y-为浮空输入
	hadcy.Instance = ADC;
	hadcy.Init.channel = P_TOUCH_Y_CH;
	hadcy.Init.freq = 1000;
	if (HAL_ADC_Init(&hadcy) != HAL_OK)
	{
		Error_Handler();
	}
	GPIO_InitStruct.Pin = P_TOUCH_Y_JIAN;
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
	HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
	
	*x=HAL_ADC_GET_INPUT_VOLTAGE(&hadcy);
	HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_X_JIA, GPIO_PIN_RESET);
	HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_X_JIAN, GPIO_PIN_RESET);

	//读 Y 值
	//配置 y+为高 推挽  y-为低  推挽 	
	GPIO_InitStruct.Pin = P_TOUCH_Y_JIA;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
	HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_Y_JIA, GPIO_PIN_SET);

	GPIO_InitStruct.Pin = P_TOUCH_Y_JIAN;
	HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
	HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_Y_JIAN, GPIO_PIN_RESET);

	//配置 x+为adc模式 x-为浮空输入
	hadcx.Instance = ADC;
	hadcx.Init.channel = P_TOUCH_X_CH;
	hadcx.Init.freq = 1000;

	if (HAL_ADC_Init(&hadcx) != HAL_OK)
	{
		Error_Handler();
	}

	GPIO_InitStruct.Pin = P_TOUCH_X_JIAN;
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
	HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
	
	*y=HAL_ADC_GET_INPUT_VOLTAGE(&hadcx);
	HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_Y_JIA, GPIO_PIN_RESET);
	HAL_GPIO_WritePin(P_TOUCH_PORT, P_TOUCH_Y_JIAN, GPIO_PIN_RESET);
}

完全就是按照原理图,翻译出来的操作,这里我们需要注意一下啊,测量点X+与Y+要在ADC模式和输出模式下转换,所以,我们要选择既支持GPIO,又支持ADC转化的引脚。否则就无法测量出来值。

不过这种方式我研究了5个小时,然后就发现,X坐标测量出来的电压,全屏幕都在变化,然后我就细心的发现了,板子上自带了一个ADC芯片,并且已经接好了线。所以这种电压的变化,我怀疑和外部接了芯片有关系,遂放弃了这种做法。
不过这种做法完全是可行的。并且还不需要额外的芯片,换句话说,这种做法,免费。
在这里插入图片描述

ADS7846

板子上自带的芯片,就是ads7846,这是一款非常主流的电阻屏驱动芯片,它将4线电阻的测量,转化为SPI总线的数据读取,并且提供了中断,对接单片机,那叫一个6。
在这里插入图片描述

提供了6根线

引脚说明
CLKSPI时钟
CS片选
MOSI主机输出从机输入
MISO主机输入从机输出
BUSY忙信号
PEN中断信号

这里使用了一下GPIO模拟的方式。
GPIO初始化

static void TOUCH_GPIO_Init(void)
{	
	
	GPIO_InitTypeDef GPIO_InitStruct;

	__HAL_RCC_GPIO_CLK_ENABLE();

	GPIO_InitStruct.Pin = TDIN;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
	HAL_GPIO_WritePin(P_TOUCH_PORT, TDIN, GPIO_PIN_RESET);
	
	GPIO_InitStruct.Pin = TCLK;
	HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
	HAL_GPIO_WritePin(P_TOUCH_PORT, TCLK, GPIO_PIN_RESET);
	
	GPIO_InitStruct.Pin = TCS;
	HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
	HAL_GPIO_WritePin(P_TOUCH_PORT, TCS, GPIO_PIN_RESET);
	
	GPIO_InitStruct.Pin = DOUT;
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
	HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = PEN;
	GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
	HAL_GPIO_Init(P_TOUCH_PORT, &GPIO_InitStruct);
	
	HAL_NVIC_SetPriority(GPIOA_IRQn, 0);
	HAL_NVIC_EnableIRQ(GPIOA_IRQn);
		
	
} 

这里注意一下,要打开中断,这个PEN引脚,在有触摸的时候,是低电平,当手拿开的时候,就是高电平了。所以如果要持续读取,要不断的判断PEN引脚的电瓶。

BUSY信号也可以用上,初始化为读入方式就可以。

核心函数,读取坐标

#define CMD_RDX 0X90  //0B10010000即用差分方式读X坐标
#define CMD_RDY	0XD0  //0B11010000即用差分方式读Y坐标 

//读取一次X,Y值
//读到的X,Y坐标值必须都大于100
//成功返回1,不成功返回0
//读数限制在100~3800之间.			   
const float y_sin=0.1348;
const float x_sin=0.1739;

int get_map_x_y(int* X,int* Y)
{
	int px=0,py=0;
	start_spi();//启动SPI  
	
	WriteByteADS(CMD_RDX); 
	//ADS7846的转换时间最长为6us
	TCLK_SET(1);
	delay_us(3);   	    
	TCLK_SET(0);
	delay_us(3);
	
	px=ReadWordADS();  
	WriteByteADS(CMD_RDY); 
	//ADS7846的转换时间最长为6us
	TCLK_SET(1);
	delay_us(3);   	    
	TCLK_SET(0);
	delay_us(3);   
	
	py=ReadWordADS();//读Y轴坐标   
	TCS_SET(1); 
	if((px>100)&&(py>100)&&(px<3800)&&(py<3800))
	{
		int abs_px,abs_py;
		abs_px=px-100;
		abs_py=py-100;
		*X=x_sin*abs_px;
		*Y=y_sin*abs_py;
		return 1;//读数成功(范围限制)
	}
	else
	{
		return 0;							 //读数失败
	} 
}

我先大概试了一下4个角的坐标,然后估算出了x轴和y轴的单位,即读取到的坐标1个单位表示多少个像素。得到了下面两个值。
const float y_sin=0.1348;
const float x_sin=0.1739;

然后就可以按照读取到的值去掉起始值,再乘以刚才的系数,就能够得到我这个320*240屏幕的具体坐标了。
abs_px=px-100;
abs_py=py-100;
X=x_sinabs_px;
Y=y_sinabs_py;
在这里插入图片描述

绘图板

在前面一篇文章中,显示屏16线的驱动已经完成,《单片机—HLK-W801并口驱动ST7789》
那么结合起来今天的触摸屏,就可以做一个绘图板了。
在这里插入图片描述

中断判断,这里只是置一个标志位,有触摸操作会触发。

 void HAL_GPIO_EXTI_Callback(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin)
{
	if ((GPIOx == P_TOUCH_PORT) && (GPIO_Pin == PEN))
	{
		key_flag = 1;
	}
}

然后主函数中,进行持续读点画点就可以了。

	while (1)
	{
		if (key_flag == 1)
		{
			HAL_Delay(20);
			while(HAL_GPIO_ReadPin(P_TOUCH_PORT, PEN) == GPIO_PIN_RESET)
			{
				int pos_x=0, pos_y=0;
				get_map_x_y(&pos_x,&pos_y);
				LCD_DrawPoint(320-pos_x,pos_y, 0xf000);
				HAL_Delay(5);
			}
			key_flag = 0;
		}
	}

在HAL_Delay不同的情况下,有不同的绘制效果。
HAL_Delay(20)
在这里插入图片描述
HAL_Delay(5)
在这里插入图片描述
去掉延迟
在这里插入图片描述

可以看出,有延迟的时候,坐标比较干净,没有延迟的时候,在落笔和起笔的时候,会有很多漂移的点,这个漂移的效果,倒像是沙画的效果,撒出来的。

结束语

这篇文章只是为了学习lvgl做了一个技术储备,用来作为lvgl的输入方式。顺带也了解了一下电阻屏的驱动方式,并且掌握了两种测量方法。
在这里插入图片描述

昨天是妇女节,姑娘都嫌弃这个名字,显得土,后来就叫女神节。其实当初这个节日是女工为了争取平等的福利而创立的,全称是“联合国妇女权益和国际和平日”,同事说,其实也应该给已婚妇女的老公也放半天,这样能更好的给女性放个假,我觉得说的有道理啊。
在这里插入图片描述

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖哥王老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值