IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 触摸屏TP2046及W25Qxx驱动 -> 正文阅读

[嵌入式]触摸屏TP2046及W25Qxx驱动

代码

首先需要用到W25Qxx的驱动,用于记录校准信息

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __W25QXX_H__
#define __W25QXX_H__


/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx.h"
#include "main.h"
#include "spi.h"



/* W25Q页大小 单位字节 ------------------------------------------------------*/
#define W25Q_PageSize			256



/* 超时时间 ------------------------------------------------------------------*/
#define W25Q_TIME_OUT			0x0000FFFF


/* 片选信号 ------------------------------------------------------------------*/
#define W25Q_CS_LOW		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET)
#define W25Q_CS_HIGH		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET)



uint16_t W25Q_Init(void);

uint8_t W25QWakeUP(void);

void W25QRead(uint32_t addr, uint8_t *readBuffer, uint32_t numByteToRead);
void W25QSectorErase(uint32_t addr);
void W25QWrite(uint32_t addr, uint8_t *writeBuffer, uint32_t numByteToWrite);
void W25QPageWrite(uint32_t addr, uint8_t *writeBuffer, uint32_t numByteToWrite);

#endif /* __W25QXX_H__ */
#include "w25qxx.h"






static __IO uint32_t  W25QTimeout = W25Q_TIME_OUT;
static uint16_t W25QType;


/**
  * @函数功能: 	W25Q进入4字节地址模式
  * @输入参数: 	无
  * @返 回 值: 	无
  * @说    明: 	命令:
  *					0x15:查询状态寄存器3
  *					0xB7:进入4字节地址工作模式
  */
static void W25QEntry4ByteAddressMode(void)
{
	uint8_t temp = 0;
	uint8_t	command = 0x15;
	
	//查询当前工作地址模式
	W25Q_CS_LOW;
	HAL_SPI_Transmit(&hspi1, (void*)&command, 1, 10);		//发送命令
	HAL_SPI_Receive(&hspi1, (void*)&temp, 1, 10);			//查询状态寄存器3
	W25Q_CS_HIGH;
	
	
	//最低位为0 则需设置工作模式
	if((temp & 0x01) == 0)
	{
		command = 0xB7;
		W25Q_CS_LOW;
		HAL_SPI_Transmit(&hspi1, (void*)&command, 1, 10);	//发送命令
		W25Q_CS_HIGH;
	}
}








/**
  * @函数功能: 	唤醒W25Q
  * @输入参数: 	无
  * @返 回 值: 	唤醒成功:1
  *				唤醒失败:0
  * @说    明: 	命令:
  *					0xAB:唤醒指令
  */
uint8_t W25QWakeUP(void)
{
	uint8_t temp = 0;
	uint8_t	command = 0xAB;
	uint8_t Dummy_Byte[3] = {0};
	
	//发送唤醒指令
	W25Q_CS_LOW;
	HAL_SPI_Transmit(&hspi1, (void*)&command, 1, 10);	//发送命令
	HAL_SPI_Transmit(&hspi1, (void*)Dummy_Byte, 3, 10);
	HAL_SPI_Receive(&hspi1, (void*)&temp, 1, 10);		//接收ID7 ~ ID0
	W25Q_CS_HIGH;
	
		return temp;        //返回一个DeviceID,这个ID根据型号不同而不同
	//W25Q16  为 14
	//W25Q128 为17
}






/**
  * @函数功能: 	W25Q初始化
  * @输入参数: 	无
  * @返 回 值: 	初始化成功:1
  *				初始化失败:0
  * @说    明: 	初始化步骤:
  *							1.进入4字节地址模式
  * 						2.唤醒W25Q
	*              3.返回设备容积
  */
uint16_t W25Q_Init(void)
{	
	//1.进入4字节地址模式
	W25QEntry4ByteAddressMode();
	//2.唤醒W25Q
	uint8_t DeviceID=W25QWakeUP();//0x14:Q25W16  15:Q25W32   16:Q25W64 17:Q25W128  (这之前不启动4字节地址)   18:Q25W256,并启动4字节地址

	switch(DeviceID)
	{
		case 0x14:
			W25QType=16;
		break;
		case 0x15:
			W25QType=32;
		break;
		case 0x16:
			W25QType=64;
		break;
		case 0x17:
			W25QType=128;
		break;
		case 0x18:
			W25QType=256;
		break;
		case 0x19:
			W25QType=512;
		break;
		default:
			W25QType=0;
		break;
	}	
	
	return W25QType;
}






/**
  * @函数功能: 	等待W25Q准备好
  * @输入参数: 	无
  * @返 回 值: 	无
  * @说    明: 	命令:
  *					0x05:查询状态寄存器1指令
  */
static void W25QWaitForReady(void)
{
	uint8_t temp = 0;
	uint8_t	command = 0x05;
	W25QTimeout = W25Q_TIME_OUT;
	
	//查询状态寄存器1
	W25Q_CS_LOW;
	HAL_SPI_Transmit(&hspi1, (void*)&command, 1, 10);	//发送命令
	do
	{
		HAL_SPI_Receive(&hspi1, (void*)&temp, 1, 10);	//接收数据
		if(W25QTimeout-- == 0)
			return ;
	}while((temp & 0x01) == 1);
	W25Q_CS_HIGH;
}






/**
  * @函数功能: 	读取W25Q数据
  * @输入参数: 	addr:			读取地址
  *				readBuffer:	存放读出数据的指针
  *				numByteToRead:	需要读取数据的长度
  * @返 回 值: 	无
  * @说    明: 	命令:
  *					0x03:读取数据指令
  */
void W25QRead(uint32_t addr, uint8_t *readBuffer, uint32_t numByteToRead)
{
	uint8_t	command = 0x03;
	uint8_t temp[4] = {0};
	
	//计算地址
	temp[0] = (addr & 0xFF000000) >> 24;
	temp[1] = (addr & 0x00FF0000) >> 16;
	temp[2] = (addr & 0x0000FF00) >> 8;
	temp[3] = (addr & 0x000000FF);
	
	//等待W25Q准备好
	W25QWaitForReady();
	
	//读取数据
	W25Q_CS_LOW;
	HAL_SPI_Transmit(&hspi1, (void*)&command, 1, 10);	//发送命令
	
	if(W25QType>=256)                        //需要注意256以下的W25Qxx不支持4位地址
	HAL_SPI_Transmit(&hspi1, (void*)temp, 4, 10);		//发送地址
	else
	HAL_SPI_Transmit(&hspi1, (void*)(temp+1),3, 10);		//发送地址	
	
	HAL_SPI_Receive(&hspi1, (void*)readBuffer, numByteToRead, 1000);//接收数据
	W25Q_CS_HIGH;
}





/**
  * @函数功能: 	W25Q写使能
  * @输入参数: 	无
  * @返 回 值: 	无
  * @说    明: 	命令:
  *					0x06:写使能指令
  */
static void W25QWriteEnable(void)
{
	uint8_t	command = 0x06;
	
	//等待W25Q准备好
	W25QWaitForReady();
	
	//写使能
	W25Q_CS_LOW;
	HAL_SPI_Transmit(&hspi1, (void*)&command, 1, 10);	//发送命令
	W25Q_CS_HIGH;
}





/**
  * @函数功能: 	擦除W25Q扇区
  * @输入参数: 	addr:			要擦除的扇区地址
  * @返 回 值: 	无
  * @说    明: 	命令:
  *					0x20:扇区擦除指令  一个扇区大小4096B = 4KB
  */
void W25QSectorErase(uint32_t addr)
{
	uint8_t	command = 0x20;
	uint8_t temp[4] = {0};
	
	//计算地址
	temp[0] = (addr & 0xFF000000) >> 24;
	temp[1] = (addr & 0x00FF0000) >> 16;
	temp[2] = (addr & 0x0000FF00) >> 8;
	temp[3] = (addr & 0x000000FF);
	
	//发送写使能指令
	W25QWriteEnable();
	
	//等待W25Q准备好
	W25QWaitForReady();
	
	//擦除扇区
	W25Q_CS_LOW;
	HAL_SPI_Transmit(&hspi1, (void*)&command, 1, 10);	//发送命令						//发送命令
	
		if(W25QType>=256)                        //需要注意256以下的W25Qxx不支持4位地址
	HAL_SPI_Transmit(&hspi1, (void*)temp, 4, 10);		//发送地址
	else
	HAL_SPI_Transmit(&hspi1, (void*)(temp+1),3, 10);		//发送地址	
	
	W25Q_CS_HIGH;
	
	//等待W25Q擦除完毕
	W25QWaitForReady();
}






/**
  * @函数功能: 	按页向W25Q写入数据
  * @输入参数: 	addr:			写入地址
  *				writeBuffer:	要写入数据的指针
  *				numByteToWrite:需要写入数据的长度
  * @返 回 值: 	无
  * @说    明: 	命令:
  *					0x02:写入数据指令
  */
void W25QPageWrite(uint32_t addr, uint8_t *writeBuffer, uint32_t numByteToWrite)
{
	uint8_t	command = 0x02;
	uint8_t temp[4] = {0};
	
	//计算地址
	temp[0] = (addr & 0xFF000000) >> 24;
	temp[1] = (addr & 0x00FF0000) >> 16;
	temp[2] = (addr & 0x0000FF00) >> 8;
	temp[3] = (addr & 0x000000FF);
	
	//发送写使能指令
	W25QWriteEnable();
	
	//等待W25Q准备好
	W25QWaitForReady();
	
	//写入数据
	W25Q_CS_LOW;
	HAL_SPI_Transmit(&hspi1, (void*)&command, 1, 10);	//发送命令						//发送命令

	if(W25QType>=256)                        //需要注意256以下的W25Qxx不支持4位地址
	HAL_SPI_Transmit(&hspi1, (void*)temp, 4, 10);		//发送地址
	else
	HAL_SPI_Transmit(&hspi1, (void*)(temp+1),3, 10);		//发送地址	
	
	HAL_SPI_Transmit(&hspi1, (void*)writeBuffer, numByteToWrite, 1000);//发送数据
	W25Q_CS_HIGH;
	
	//等待写入完毕
	W25QWaitForReady();
}









/**
  * @函数功能: 	向W25Q写入数据 内置擦除
  * @输入参数: 	addr:			写入地址
  *				writeBuffer:	要写入数据的指针
  *				numByteToWrite:需要写入数据的长度
  * @返 回 值: 	无
  * @说    明: 	命令:
  *					0x02:写入数据指令
  */
void W25QWrite(uint32_t addr, uint8_t *writeBuffer, uint32_t numByteToWrite)
{
	uint8_t isAlignToPage = 0;
	uint8_t count = 0;
	uint16_t pageNum = 0;
	uint8_t	pageLave = 0;
	
	isAlignToPage = addr % W25Q_PageSize;					//地址是否按页对齐
	count = W25Q_PageSize - isAlignToPage;					//计算差多少个数据才能对齐
	pageNum = numByteToWrite / W25Q_PageSize;				//计算整页数量
	pageLave = numByteToWrite % W25Q_PageSize;				//计算剩余不满一页的数据
	
	//擦除
	W25QSectorErase(addr);
	
	if(isAlignToPage == 0)		//刚好按页对齐
	{
		if(pageNum == 0)		//不足一页
		{
			W25QPageWrite(addr, writeBuffer, numByteToWrite);
		}
		else
		{
			while(pageNum--)	//写整页
			{
				W25QPageWrite(addr, writeBuffer, W25Q_PageSize);
				addr += W25Q_PageSize;			//地址增加一页
				writeBuffer += W25Q_PageSize;	//写入数据的指针增加一页
			}
			W25QPageWrite(addr, writeBuffer, pageLave);  //写不足整页
		}
	}
	else						//地址没有按页对齐
	{
		if(pageNum == 0)		//不足一页
		{
			W25QPageWrite(addr, writeBuffer, numByteToWrite);
		}
		else
		{
			numByteToWrite -= count;
			pageNum = numByteToWrite / W25Q_PageSize;				//计算整页数量
			pageLave = numByteToWrite % W25Q_PageSize;				//计算剩余不满一页的数据
			
			W25QPageWrite(addr, writeBuffer, count);
			addr += count;
			writeBuffer +=count;
			
			while(pageNum--)					//写整页
			{
				W25QPageWrite(addr, writeBuffer, W25Q_PageSize);
				addr += W25Q_PageSize;			//地址增加一页
				writeBuffer += W25Q_PageSize;	//写入数据的指针增加一页
			}
			
			W25QPageWrite(addr, writeBuffer, pageLave);  //写不足整页
		}
	}


}







这里需要注意W25Qxx对于256以下的片,地址只支持3位,对于256以上的片,在读取128M以上地址时,需要用到4位地址,所有就有一条启动4位地址的函数。所以这里增加了一个函数,在初始化时,读取硬件ID,根据ID判断容量,然后是否启动四位地址。所有的读写函数都需要判断ID后再选择3位或者4位地址。

touch.h

#ifndef _TOUCH_H_
#define _TOUCH_H_

#include "stm32f4xx_hal.h"  
//	 

//触摸屏驱动(XPT2046) 代码	   
//STM32F4工程模板-库函数版本
						   
//********************************************************************************
//修改说明
//修正MDK在-O2优化时,触摸屏数据无法读取的bug.在TP_Write_Byte函数添加一个延时,解决问题.
//

//滤波参数
#define READ_TIMES 7 	//读取次数
#define LOST_VAL 1	  	//丢弃最大最小值的个数
#define ERR_RANGE 200 //连续多次取样,去掉LOST_VAL后,误差允许ADC范围 
//写入的命令类型,写入命令后,再读取,可读到相应的X轴或Y轴的ADC
#define CMD_RDX 0XD0
#define CMD_RDY 0X90

#define DEFAULT_DIR 0
#define TP_PRES_DOWN 0x80  //触屏被按下	  
#define TP_CATH_PRES 0x40  //有按键按下了 
#define TP_COODDOWN 200      //两次触摸的最小间隔ms
 
 
 
//触摸屏控制器
typedef struct
{
	void (*init)(uint16_t,uint16_t);			//初始化触摸屏控制器
	uint8_t (*scan)(void);				//扫描触摸屏.0,屏幕扫描;1,物理坐标;	 
	void (*adjust)(void);		//触摸屏校准 
	uint16_t x; 		//当前坐标
	uint16_t y;		
	uint8_t Dir;    //屏幕状态 0位代表是否旋转90°      
	uint8_t  sta;					//笔的状态 
								//b7:按下1/松开0; 
	                            //b6:0,没有按键按下;1,有按键按下. 
								//b5:保留
								//b4~b0:电容触摸屏按下的点数(0,表示未按下,1表示按下)
/触摸屏校准参数(电容屏不需要校准)//	
uint16_t LCD_Width;
uint16_t LCD_Height;
	
	float xfac;					
	float yfac;
	short xoff;
	short yoff;	   
//新增的参数,当触摸屏的左右上下完全颠倒时需要用到.
//b0:0,竖屏(适合左右为X坐标,上下为Y坐标的TP)
//   1,横屏(适合左右为Y坐标,上下为X坐标的TP) 
//b1~6:保留.
//b7:0,电阻屏
//   1,电容屏 
	uint8_t touchtype;
	uint32_t lastclick;
}_m_tp_dev;

extern _m_tp_dev tp_dev;	 	//触屏控制器在touch.c里面定义

//电阻屏芯片连接引脚	   
#define TPEN  		GPIOC,GPIO_PIN_5         //PCin(5)  	//TPEN
#define DOUT 		GPIOB,GPIO_PIN_14         //PBin(14)   	//T_MISO
#define TDIN 		GPIOB,GPIO_PIN_15           //PBout(15)  	//T_MOSI
#define TCLK 		GPIOB,GPIO_PIN_13            //PBout(13)  	//T_SCK
#define TCS  		GPIOB,GPIO_PIN_12            //PBout(12)  	//T_CS  
   
//电阻屏函数
void TP_Write_Byte(uint8_t num);						//向控制芯片写入一个数据
uint16_t TP_Read_AD(uint8_t CMD);							//读取AD转换值
uint8_t TP_Read_XOY(uint8_t CMD,uint16_t* Result);							//带滤波的坐标读取(X/Y)
uint8_t TP_Read_XY(uint16_t *x,uint16_t *y);					//双方向读取(X+Y)
void TP_Drow_Touch_Point(uint16_t x,uint16_t y,uint16_t color);//画一个坐标校准点
void TP_Draw_Big_Point(uint16_t x,uint16_t y,uint16_t color);	//画一个大点

void TP_Adjust(void);							//触摸屏校准
void TP_Adj_Info_Show(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2,uint16_t x3,uint16_t y3,uint16_t fac);//显示校准信息
uint8_t TP_Scan();								//扫描
void TP_Init(uint16_t LCD_Width,uint16_t LCD_Height);							//初始化
																 

 		  


#endif















touch.c



#include "touch.h" 
#include "lcd.h"
#include "stdlib.h"
#include "math.h"
#include "stdio.h"
#include "w25qxx.h"
//	 

//触摸屏驱动(XPT2046) 
//建立在HAL库基础上					   
//********************************************************************************
//修改说明
//V1.1 20140721
//修正MDK在-O2优化时,触摸屏数据无法读取的bug.在TP_Write_Byte函数添加一个延时,解决问题.
//





_m_tp_dev tp_dev=
{
	TP_Init,
	TP_Scan,
	TP_Adjust,
	0,
	0, 
	0,
	0,
	0,
	0,	  	 		
	0,
	0,
  0,	
};					

 	 			    					   
//SPI写数据
//向触摸屏IC写入1byte数据    
//num:要写入的数据
void TP_Write_Byte(uint8_t num)    
{  
	uint8_t count=0;   
	for(count=0;count<8;count++)  
	{ 	  
		if(num&0x80){HAL_GPIO_WritePin(TDIN,GPIO_PIN_SET);}  
		else HAL_GPIO_WritePin(TDIN,GPIO_PIN_RESET);   
		num<<=1;    
		HAL_GPIO_WritePin(TCLK,GPIO_PIN_RESET); 
		for(int i=0;i<10;i++);
		HAL_GPIO_WritePin(TCLK,GPIO_PIN_SET);		//上升沿有效	        
	}		 			    
} 		 
//SPI读数据 
//从触摸屏IC读取adc值
//CMD:指令
//返回值:读到的数据	   
uint16_t TP_Read_AD(uint8_t CMD)	  
{ 	 
	uint8_t count=0; 	  
	uint16_t Num=0; 
	HAL_GPIO_WritePin(TCLK,GPIO_PIN_RESET);		//先拉低时钟 	 
	HAL_GPIO_WritePin(TDIN,GPIO_PIN_RESET); 	//拉低数据线
	HAL_GPIO_WritePin(TCS,GPIO_PIN_RESET); 		//选中触摸屏IC
	TP_Write_Byte(CMD);//发送命令字
	for(int i=0;i<60;i++);//ADS7846的转换时间最长为6us
	HAL_GPIO_WritePin(TCLK,GPIO_PIN_RESET); 	     	    
	for(int i=0;i<10;i++);   	   
	HAL_GPIO_WritePin(TCLK,GPIO_PIN_SET);		//给1个时钟,清除BUSY
	for(int i=0;i<10;i++);  
	HAL_GPIO_WritePin(TCLK,GPIO_PIN_RESET); 	     	    
	for(count=0;count<16;count++)//读出16位数据,只有高12位有效 
	{ 				  
		Num<<=1; 	 
		HAL_GPIO_WritePin(TCLK,GPIO_PIN_RESET);	//下降沿有效  				
		for(uint8_t i=0;i<10;i++){};
 		HAL_GPIO_WritePin(TCLK,GPIO_PIN_SET);
 		if(HAL_GPIO_ReadPin(DOUT))Num++; 		 
	}  	
	Num>>=4;   	//只有高12位有效.
	HAL_GPIO_WritePin(TCS,GPIO_PIN_SET);		//释放片选	 
	return(Num);   
}
//读取一个坐标值(x或者y)
//连续读取READ_TIMES次数据,对这些数据升序排列,
//然后去掉最低和最高LOST_VAL个数
//判断剩下的最大最小差值不超过ERROR_RANGE
//取平均值,写入Result 
//CMD:指令(CMD_RDX/CMD_RDY)
//返回值:0 失败 1成功
uint8_t TP_Read_ADMean(uint8_t CMD,uint16_t* Result)
{
	uint16_t i, j;
	uint16_t buf[READ_TIMES];
	uint16_t sum=0;
	uint16_t temp;
	if(HAL_GPIO_ReadPin(TPEN)!=0)	{return 0;}//有按键按下
	for(i=0;i<READ_TIMES;i++)buf[i]=TP_Read_AD(CMD);		 		    
	for(i=0;i<READ_TIMES-1; i++)//排序
	{
		for(j=i+1;j<READ_TIMES;j++)
		{
			if(buf[i]>buf[j])//升序排列
			{
				temp=buf[i];
				buf[i]=buf[j];
				buf[j]=temp;
			}
		}
	}	  
	
	if(buf[READ_TIMES-LOST_VAL]-buf[LOST_VAL]>ERR_RANGE)
	{
		*Result=0;
		return 0;
	}
	else
	{
		sum=0;
		for(i=LOST_VAL;i<READ_TIMES-LOST_VAL;i++)sum+=buf[i];
		*Result=sum/(READ_TIMES-2*LOST_VAL);	
		return 1;
	} 
} 
//读取XY的ADC
//返回值 0失败 1成功
uint8_t TP_Read_XY(uint16_t *x,uint16_t *y)
{
	uint8_t Result1,Result2;
	uint16_t ADCx,ADCy;
	Result1=TP_Read_ADMean(CMD_RDX,&ADCx);
	Result2=TP_Read_ADMean(CMD_RDY,&ADCy);
	if(Result1&&Result2)
	{
		if(tp_dev.Dir)
		{
		*x=(int)(tp_dev.xoff)+tp_dev.xfac*(int)ADCy;
		*y=(int)(tp_dev.yoff)+tp_dev.yfac*(int)ADCx;
		}
		else
		{
		*x=(int)(tp_dev.xoff)+tp_dev.xfac*(int)ADCx;
		*y=(int)(tp_dev.yoff)+tp_dev.yfac*(int)ADCy;
		}
	}
	if(*x<=tp_dev.LCD_Width&&*y<=tp_dev.LCD_Height&&Result1&&Result2)
		{return 1;}
	else
		{return 0;}
	
}

//触摸按键扫描
//返回值:当前触屏状态.
//0,失败 1成功
uint8_t TP_Scan()
{
	uint32_t nowTick=HAL_GetTick();
	if(TP_Read_XY(&(tp_dev.x),&(tp_dev.y))&&nowTick-tp_dev.lastclick>TP_COODDOWN)
	{	
	tp_dev.lastclick=nowTick;		
	return 1;
	}
	else
	{
			tp_dev.lastclick=nowTick;	
		return 0;
	}
	
}	  

//触摸屏初始化  		    
void TP_Init(uint16_t LCD_Width,uint16_t LCD_Height)
{
	tp_dev.LCD_Height=LCD_Height;
	tp_dev.LCD_Width=LCD_Width;
	tp_dev.Dir=DEFAULT_DIR;
	
	tp_dev.xfac=0;
	tp_dev.yfac=0;
	tp_dev.xoff=0;
	tp_dev.yoff=0;

	
	uint8_t W25QBuffer[12]={0,0,0,0,0,0,0,0,0,0,0,0};
	W25QRead(0x00000000,W25QBuffer,12);
	tp_dev.xfac=*((float*)(W25QBuffer+0));
	tp_dev.yfac=*((float*)(W25QBuffer+4));
	tp_dev.xoff=*((short*)(W25QBuffer+8));
	tp_dev.yoff=*((short*)(W25QBuffer+10));
	
//---------------------添加读取触摸屏校准参数
	
//如果没有读到校准参数,或者屏幕在启动时按下,则进行校准
	if((tp_dev.xoff==0 && tp_dev.xfac==0.0 && tp_dev.yoff==0 && tp_dev.yfac==0.0) || (!HAL_GPIO_ReadPin(TPEN)))
	{
		tp_dev.adjust();
	}	
}


//		  
//与LCD部分有关的函数

//校准触摸屏 通过三个点确定比例尺和旋转与否
void TP_Adjust(void)    
{	
	uint16_t P1x=20,P1y=20,P2x=tp_dev.LCD_Width-20,P2y=20,P3x=20,P3y=tp_dev.LCD_Height-20;	
	uint16_t P1ADCx=0,P1ADCy=0,P2ADCx=0,P2ADCy=0,P3ADCx=0,P3ADCy=0;
	uint8_t  Step=0;	
	uint16_t temp;
	int xDif21=0,yDif21=0,xDif32=0,yDif32=0,xDif31=0,yDif31=0;
	uint8_t	W25QBuffer[12]={0};
	
 	Step=0;				
	tp_dev.sta=0;//消除触发信号 
	tp_dev.xfac=0;//xfac用来标记是否校准过,所以校准之前必须清掉!以免错误

	while(Step<7)      //校准过程
	{
		switch(Step)
		{
			case 0:
				POINT_COLOR=BLUE;
				BACK_COLOR =WHITE;
				LCD_Clear(WHITE);//清屏   
				POINT_COLOR=RED;//红色 
				LCD_Clear(WHITE);//清屏 	   
				POINT_COLOR=BLACK;
				sprintf(PrintData,"Please click the cross on the screen.The cross will always move until the screen adjustment is completed.");
				LCD_ShowString(40,40,160,100,16,PrintData);//显示提示信息
				TP_Drow_Touch_Point(P1x,P1y,RED);//画点1 
				Step=1;
				break;
			case 1:    //检测第一个输入点
				if(TP_Read_ADMean(CMD_RDX,&P1ADCx)&&TP_Read_ADMean(CMD_RDY,&P1ADCy))
				{
					LCD_Clear(WHITE);//清屏 
					POINT_COLOR=BLACK;
					sprintf(PrintData,"Please click the cross on the screen.The cross will always move until the screen adjustment is completed.");
					LCD_ShowString(40,40,160,100,16,PrintData);//显示提示信息
					TP_Drow_Touch_Point(P2x,P2y,RED);//画点1 
					Step=2;
				}
				break;
			case 2:      //检测第二个输出点
				if(TP_Read_ADMean(CMD_RDX,&P2ADCx)&&TP_Read_ADMean(CMD_RDY,&P2ADCy))
				{
					xDif21=(int)P2ADCx-(int)P1ADCx;
					yDif21=(int)P2ADCy-(int)P1ADCy;
					if((xDif21>1000||xDif21<-1000)||(yDif21>1000||yDif21<-1000))
					{
						LCD_Clear(WHITE);//清屏 
						POINT_COLOR=BLACK;
						sprintf(PrintData,"Please click the cross on the screen.The cross will always move until the screen adjustment is completed.");
						LCD_ShowString(40,40,160,100,16,PrintData);//显示提示信息
						TP_Drow_Touch_Point(P3x,P3y,RED);//画点1 
						Step=3;
					}
				}
				break;
			case 3:     //检测第三个输入点
				if(TP_Read_ADMean(CMD_RDX,&P3ADCx)&&TP_Read_ADMean(CMD_RDY,&P3ADCy))
				{
					xDif32=(int)P3ADCx-(int)P2ADCx;
					yDif32=(int)P3ADCy-(int)P2ADCy;
					if((xDif32>1000||xDif32<-1000)||(yDif32>1000||yDif32<-1000))
					{	
						Step=4;
					}
				}
				break;
			case 4:       //计算并检测校准结果
					xDif21=(int)P2ADCx-(int)P1ADCx;
					yDif21=(int)P2ADCy-(int)P1ADCy;
					xDif31=(int)P3ADCx-(int)P1ADCx;
					yDif31=(int)P3ADCy-(int)P1ADCy;
			if(yDif21>-200 && yDif21<200 && xDif31>-200 && xDif31<200)      //检测是否正向
			{
				tp_dev.Dir=0;
				tp_dev.xfac=(float)(tp_dev.LCD_Width-40)/(float)(xDif21);
				tp_dev.yfac=(float)(tp_dev.LCD_Height-40)/(float)(yDif31);
				tp_dev.xoff=P1x-P1ADCx*tp_dev.xfac;
				tp_dev.yoff=P1y-P1ADCy*tp_dev.yfac;					
				Step=6;
			}
			else if(xDif21>-200 && xDif21<200 && yDif31>-200 && yDif31<200)  //检测是否90°旋转
			{
				tp_dev.Dir=1;
				tp_dev.xfac=(float)(tp_dev.LCD_Width-40)/(float)(yDif21);
				tp_dev.yfac=(float)(tp_dev.LCD_Height-40)/(float)(xDif31);
				tp_dev.xoff=P1x-P1ADCy*tp_dev.xfac;
				tp_dev.yoff=P1y-P1ADCx*tp_dev.yfac;
				Step=6;
			}
			else
			{
				Step=5;
			}
			break;
			case 5:
				LCD_Clear(WHITE);//清屏 
				POINT_COLOR=BLACK;
				sprintf(PrintData,"Calibration Fail.Retry.");
				LCD_ShowString(40,40,160,100,16,PrintData);//显示提示信息
				for(int i=0;i<10000;i++){for(int j=0;j<10000;j++){}}	

				Step=0;
				break;
			case 6:
				LCD_Clear(WHITE);//清屏 
				POINT_COLOR=BLACK;
				sprintf(PrintData,"Calibration Success.");
				LCD_ShowString(40,40,160,100,16,PrintData);//显示提示信息	
				sprintf(PrintData," xfac is :%.4f",tp_dev.xfac);
				LCD_ShowString(40,60,160,100,16,PrintData);//显示提示信息
				sprintf(PrintData," xoff is :%.4f",(float)tp_dev.xoff);
				LCD_ShowString(40,80,160,100,16,PrintData);//显示提示信息
				sprintf(PrintData," yfac is :%.4f",tp_dev.yfac);
				LCD_ShowString(40,100,160,100,16,PrintData);//显示提示信息
				sprintf(PrintData," yoff is :%.4f",(float)tp_dev.yoff);
				LCD_ShowString(40,120,160,100,16,PrintData);//显示提示信息
				for(int i=0;i<10000;i++){for(int j=0;j<10000;j++){}}	
				LCD_Clear(WHITE);//清屏
				Step=7;
				
					*((float*)(W25QBuffer+0))=tp_dev.xfac;
					*((float*)(W25QBuffer+4))=tp_dev.yfac;
					*((short*)(W25QBuffer+8))=tp_dev.xoff;
					*((short*)(W25QBuffer+10))=tp_dev.yoff;
					W25QWrite(0x00000000,W25QBuffer,12);				
				break;
			}
		}	
}	 


//画一个触摸点
//用来校准用的
//x,y:坐标
//color:颜色
void TP_Drow_Touch_Point(uint16_t x,uint16_t y,uint16_t color)
{
	POINT_COLOR=color;
	LCD_DrawLine(x-12,y,x+13,y);//横线
	LCD_DrawLine(x,y-12,x,y+13);//竖线
	LCD_DrawPoint(x+1,y+1);
	LCD_DrawPoint(x-1,y+1);
	LCD_DrawPoint(x+1,y-1);
	LCD_DrawPoint(x-1,y-1);
	LCD_Draw_Circle(x,y,6);//画中心圈
}	  
//画一个大点(2*2的点)		   
//x,y:坐标
//color:颜色
void TP_Draw_Big_Point(uint16_t x,uint16_t y,uint16_t color)
{	    
	LCD_DrawPoint(x,y);//中心点 
	LCD_DrawPoint(x+1,y);
	LCD_DrawPoint(x,y+1);
	LCD_DrawPoint(x+1,y+1);	 	  	
}						  
//		  

//	 
//保存在EEPROM里面的地址区间基址,占用13个字节(RANGE:SAVE_ADDR_BASE~SAVE_ADDR_BASE+12)
#define SAVE_ADDR_BASE 40
//保存校准参数										    


 					  
//提示校准结果(各个参数)
void TP_Adj_Info_Show(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2,uint16_t x3,uint16_t y3,uint16_t fac)
{	  
	POINT_COLOR=RED;

	LCD_ShowString(40,140,lcddev.width,lcddev.height,16,"x1:");
 	LCD_ShowString(40+80,160,lcddev.width,lcddev.height,16,"y1:");
 	LCD_ShowString(40,160,lcddev.width,lcddev.height,16,"x2:");
 	LCD_ShowString(40+80,180,lcddev.width,lcddev.height,16,"y2:");
	LCD_ShowString(40,180,lcddev.width,lcddev.height,16,"x3:");
 	LCD_ShowString(40+80,200,lcddev.width,lcddev.height,16,"y3:");
	LCD_ShowString(40,200,lcddev.width,lcddev.height,16,"x4:");
 	LCD_ShowString(40+80,220,lcddev.width,lcddev.height,16,"y4:");  
 	LCD_ShowString(40,220,lcddev.width,lcddev.height,16,"fac is:");     
	LCD_ShowNum(40+24,140,x0,4,16);		//显示数值
	LCD_ShowNum(40+24+80,160,y0,4,16);	//显示数值
	LCD_ShowNum(40+24,160,x1,4,16);		//显示数值
	LCD_ShowNum(40+24+80,180,y1,4,16);	//显示数值
	LCD_ShowNum(40+24,180,x2,4,16);		//显示数值
	LCD_ShowNum(40+24+80,200,y2,4,16);	//显示数值
	LCD_ShowNum(40+24,200,x3,4,16);		//显示数值
	LCD_ShowNum(40+24+80,220,y3,4,16);	//显示数值
 	LCD_ShowNum(40+56,220,fac,3,16); 	//显示数值,该数值必须在95~105范围之内.

}
		 






touch的代码是从网上下的,从结构上看,应该来源是原子兄的TP2046驱动,为了方便自己习惯,作了一些修改。删除了原来依赖的函数库,完全适配于HAL库驱动。

增加了两次点击之间用Get_Tick计时,方式多次的误触发,前提是用户点击了,就松开。

改用3点校准方式,并通过三个点的位置分布,自动识别是横屏或者竖屏。将ADCx,ADCy和坐标的横向和纵向对其起来。引用了W25Qxx的驱动,校准后会记录到0x00000000地址。

整个cubemx工程连码上传了

?有需要的可以下载源码

(9条消息) STM32F407VE驱动ST7789和XPT2046和W25Q.zip-嵌入式文档类资源-CSDN文库

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-01-04 13:36:08  更:2022-01-04 13:37:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 12:24:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码