代码
首先需要用到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文库
|