基于HAL库的STM32F704的电阻式触摸屏的学习
基于HAL库的STM32F704的电阻式触摸屏的学习
- 一、XPT2046介绍
- 二、XPT2046底层代码的编写
- 1.根据时序图编写读写程序
- 2.XPT2046命令
- 三、主要代码分析
- 1.结构体以及初始化
- 1.判断是否触摸
- 2.校准
- 四、实战
内容基于正点原子的代码分析
一、XPT2046介绍
XPT2046是一款4导线制触摸屏控制器,内含12位分辨率125KHz转换速率逐步逼近型A/D转换器。XPT2046支持从1.5V到5.25V的低电压I/O接口。XPT2046能通过执行两次A/D转换查出被按的屏幕位置, 除此之外,还可以测量加在触摸屏上的压力。内部自带2.5V参考电压可以作为辅助输入、温度测量和电池监测模式之用,电池监测的电压范围可以从0V到6V。XPT2046片内集成有一个温度传感器
红框内为XPT2046与MCU通讯用引脚(错了,NO BUSY;YES PENIRQ )
二、XPT2046底层代码的编写
1.根据时序图编写读写程序
时序图,课根据时序图,写出伪代码,再讲伪代码编辑为实际代码。关于延时部分参考XPT2046数据手册(请点进去后复制网址)。这里不再重复造轮子
/*
*************************************************************
*伪代码,函数功能:MCU发送数据给XPT2046,XPT2046返回数据给MCU
**************************************************************
*/
void xpt2046_write_byte(u8 write_data)
{for(i=0;i<8;i++){DCLK = 0; if(write_data & 0x80)//S为1时有效TDIN = 1;//MOSIelse TDIN = 0;//1us //MCU准备数据完成DCLK = 1;//1usrite_data <<= 1;}
}u16 xpt2046_read(u8 CMD)
{u16 xpt2046_read = 0;CS = 0;//片选DCLK = 0;//拉低DIN = 0;xpt2046_write_byte(CMD);/* 过滤忙信号 */DCLK = 0;//延时 DCLK = 1;//延时for(i = 0 ; i<16;i++){DCLK = 0; //XPT2046开始准备数据//1usDCLK = 1; //XPT2046开始准备数据xpt2046_read<<=1;//空出最低位来保存读取到的数据 xpt2046_read |= DOUT;//MISO//1us}DCLK = 0; //完整周期CS = 1; //取消选择return (xpt2046_read<<=4);
}
/*
*************************************************************
*正点原子的代码
**************************************************************
*/
//SPI写数据
//向触摸屏IC写入1byte数据
//num:要写入的数据
void TP_Write_Byte(u8 num)
{ u8 count=0; for(count=0;count<8;count++) { if(num&0x80)TDIN=1; else TDIN=0; num<<=1; TCLK=0; delay_us(1);TCLK=1; //上升沿有效 }
}
//SPI读数据
//从触摸屏IC读取adc值
//CMD:指令
//返回值:读到的数据
u16 TP_Read_AD(u8 CMD)
{ u8 count=0; u16 Num=0; TCLK=0; //先拉低时钟 TDIN=0; //拉低数据线TCS=0; //选中触摸屏ICTP_Write_Byte(CMD);//发送命令字delay_us(6);//ADS7846的转换时间最长为6usTCLK=0; delay_us(1); TCLK=1; //给1个时钟,清除BUSYdelay_us(1); TCLK=0; for(count=0;count<16;count++)//读出16位数据,只有高12位有效 { Num<<=1; TCLK=0; //下降沿有效 delay_us(1); TCLK=1;if(DOUT)Num++; } Num>>=4; //只有高12位有效.TCS=1; //释放片选 return(Num);
}
2.XPT2046命令
对应的两个命令:
u8 CMD_RDX=0XD0;//0XD0 为 11010000,读取X轴
u8 CMD_RDY=0X90;//0X90 为 10010000,读取Y轴
三、主要代码分析
1.结构体以及初始化
typedef struct
{u8 (*init)(void); //初始化触摸屏控制器u8 (*scan)(u8); //扫描触摸屏.0,屏幕扫描;1,物理坐标; void (*adjust)(void); //触摸屏校准 u16 x[CT_MAX_TOUCH]; //当前坐标u16 y[CT_MAX_TOUCH]; //电容屏有最多5组坐标,电阻屏则用x[0],y[0]代表:此次扫描时,触屏的坐标,用//x[4],y[4]存储第一次按下时的坐标. u8 sta; //笔的状态 //b7:按下1/松开0; //b6:0,没有按键按下;1,有按键按下. //b5:保留//b4~b0:电容触摸屏按下的点数(0,表示未按下,1表示按下)
/触摸屏校准参数(电容屏不需要校准)// float xfac; float yfac;short xoff;short yoff;
//新增的参数,当触摸屏的左右上下完全颠倒时需要用到.
//b0:0,竖屏(适合左右为X坐标,上下为Y坐标的TP)
// 1,横屏(适合左右为Y坐标,上下为X坐标的TP)
//b1~6:保留.
//b7:0,电阻屏
// 1,电容屏 u8 touchtype;
}_m_tp_dev;
/*
*****************************************************
结构体初始化
*****************************************************
*/
_m_tp_dev tp_dev=
{TP_Init,TP_Scan,TP_Adjust,0,0, 0,0,0,0, 0,0,
};
通过结构体方便控制底层,HAL库也是如此,值得学习,包含了函数指针、变量等。其中
TP_Init(),TP_Scan(),TP_Adjust()是重点掌握的函数。
1.判断是否触摸
//
//触摸按键扫描
//tp:0,屏幕坐标;1,物理坐标(校准等特殊场合用)
//返回值:当前触屏状态.
//0,触屏无触摸;1,触屏有触摸
u8 TP_Scan(u8 tp)
{ if(PEN==0)//有按键按下,整个面板相当于一个大的上拉电阻,应该是类似于一个按键的功能{if(tp)TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]);//读取物理坐标else if(TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]))//读取屏幕坐标{tp_dev.x[0]=tp_dev.xfac*tp_dev.x[0]+tp_dev.xoff;//LCDx=xfac*Px+xoff; 将结果转换为屏幕坐标tp_dev.y[0]=tp_dev.yfac*tp_dev.y[0]+tp_dev.yoff;//LCDy=yfac*Py+yoff; 需要校准} if((tp_dev.sta&TP_PRES_DOWN)==0)//之前没有被按下{ tp_dev.sta=TP_PRES_DOWN|TP_CATH_PRES;//按键按下 #define TP_PRES_DOWN 0x80 触屏被按 下 #define TP_CATH_PRES 0x40 tp_dev.x[4]=tp_dev.x[0];//记录第一次按下时的坐标tp_dev.y[4]=tp_dev.y[0]; } }else{if(tp_dev.sta&TP_PRES_DOWN)//之前是被按下的{tp_dev.sta&=~(1<<7);//b1置0,标志标记按键松开 }else//之前就没有被按下{tp_dev.x[4]=0;tp_dev.y[4]=0;tp_dev.x[0]=0xffff;tp_dev.y[0]=0xffff;} }return tp_dev.sta&TP_PRES_DOWN;//返回当前的触屏状态
}
2.校准
//触摸屏校准代码
//得到四个校准参数
void TP_Adjust(void)
{ u16 pos_temp[4][2];//坐标缓存值u8 cnt=0; u16 d1,d2;u32 tem1,tem2;double fac; u16 outtime=0;cnt=0; POINT_COLOR=BLUE;BACK_COLOR =WHITE;LCD_Clear(WHITE);//清屏 POINT_COLOR=RED;//红色 LCD_Clear(WHITE);//清屏 POINT_COLOR=BLACK;LCD_ShowString(40,40,160,100,16,(u8*)TP_REMIND_MSG_TBL);//显示提示信息TP_Drow_Touch_Point(20,20,RED);//画点1 tp_dev.sta=0;//消除触发信号 tp_dev.xfac=0;//xfac用来标记是否校准过,所以校准之前必须清掉!以免错误 while(1)//如果连续10秒钟没有按下,则自动退出{tp_dev.scan(1);//扫描物理坐标if((tp_dev.sta&0xc0)==TP_CATH_PRES)//按键按下了一次(此时按键松开了.){ outtime=0; tp_dev.sta&=~(1<<6);//标记按键已经被处理过了.pos_temp[cnt][0]=tp_dev.x[0];pos_temp[cnt][1]=tp_dev.y[0];cnt++; switch(cnt){ case 1: TP_Drow_Touch_Point(20,20,WHITE); //清除点1 TP_Drow_Touch_Point(lcddev.width-20,20,RED); //画点2break;case 2:TP_Drow_Touch_Point(lcddev.width-20,20,WHITE); //清除点2TP_Drow_Touch_Point(20,lcddev.height-20,RED); //画点3break;case 3:TP_Drow_Touch_Point(20,lcddev.height-20,WHITE); //清除点3TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,RED); //画点4break;case 4: //全部四个点已经得到//对边相等tem1=abs(pos_temp[0][0]-pos_temp[1][0]);//x1-x2tem2=abs(pos_temp[0][1]-pos_temp[1][1]);//y1-y2tem1*=tem1;tem2*=tem2;d1=sqrt(tem1+tem2);//得到1,2的距离tem1=abs(pos_temp[2][0]-pos_temp[3][0]);//x3-x4tem2=abs(pos_temp[2][1]-pos_temp[3][1]);//y3-y4tem1*=tem1;tem2*=tem2;d2=sqrt(tem1+tem2);//得到3,4的距离fac=(float)d1/d2;if(fac<0.95||fac>1.05||d1==0||d2==0)//不合格{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除点4TP_Drow_Touch_Point(20,20,RED); //画点1TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//显示数据 continue;}tem1=abs(pos_temp[0][0]-pos_temp[2][0]);//x1-x3tem2=abs(pos_temp[0][1]-pos_temp[2][1]);//y1-y3tem1*=tem1;tem2*=tem2;d1=sqrt(tem1+tem2);//得到1,3的距离tem1=abs(pos_temp[1][0]-pos_temp[3][0]);//x2-x4tem2=abs(pos_temp[1][1]-pos_temp[3][1]);//y2-y4tem1*=tem1;tem2*=tem2;d2=sqrt(tem1+tem2);//得到2,4的距离fac=(float)d1/d2;if(fac<0.95||fac>1.05)//不合格{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除点4TP_Drow_Touch_Point(20,20,RED); //画点1TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//显示数据 continue;}//正确了//对角线相等tem1=abs(pos_temp[1][0]-pos_temp[2][0]);//x1-x3tem2=abs(pos_temp[1][1]-pos_temp[2][1]);//y1-y3tem1*=tem1;tem2*=tem2;d1=sqrt(tem1+tem2);//得到1,4的距离tem1=abs(pos_temp[0][0]-pos_temp[3][0]);//x2-x4tem2=abs(pos_temp[0][1]-pos_temp[3][1]);//y2-y4tem1*=tem1;tem2*=tem2;d2=sqrt(tem1+tem2);//得到2,3的距离fac=(float)d1/d2;if(fac<0.95||fac>1.05)//不合格{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除点4TP_Drow_Touch_Point(20,20,RED); //画点1TP_Adj_Info_Show(pos_temp[0][0],pos_temp[0][1],pos_temp[1][0],pos_temp[1][1],pos_temp[2][0],pos_temp[2][1],pos_temp[3][0],pos_temp[3][1],fac*100);//显示数据 continue;}//正确了//计算结果tp_dev.xfac=(float)(lcddev.width-40)/(pos_temp[1][0]-pos_temp[0][0]);//得到xfac tp_dev.xoff=(lcddev.width-tp_dev.xfac*(pos_temp[1][0]+pos_temp[0][0]))/2;//得到xofftp_dev.yfac=(float)(lcddev.height-40)/(pos_temp[2][1]-pos_temp[0][1]);//得到yfactp_dev.yoff=(lcddev.height-tp_dev.yfac*(pos_temp[2][1]+pos_temp[0][1]))/2;//得到yoff if(abs(tp_dev.xfac)>2||abs(tp_dev.yfac)>2)//触屏和预设的相反了.{cnt=0;TP_Drow_Touch_Point(lcddev.width-20,lcddev.height-20,WHITE); //清除点4TP_Drow_Touch_Point(20,20,RED); //画点1LCD_ShowString(40,26,lcddev.width,lcddev.height,16,"TP Need readjust!");tp_dev.touchtype=!tp_dev.touchtype;//修改触屏类型.if(tp_dev.touchtype)//X,Y方向与屏幕相反{CMD_RDX=0X90;CMD_RDY=0XD0; }else //X,Y方向与屏幕相同{CMD_RDX=0XD0;CMD_RDY=0X90; } continue;} POINT_COLOR=BLUE;LCD_Clear(WHITE);//清屏LCD_ShowString(35,110,lcddev.width,lcddev.height,16,"Touch Screen Adjust OK!");//校正完成delay_ms(1000);TP_Save_Adjdata(); LCD_Clear(WHITE);//清屏 return;//校正完成 }}delay_ms(10);outtime++;if(outtime>1000){TP_Get_Adjdata();break;} }
}
算了,就这样吧,不想写了
四、实战
CUBUMX相关配置
/*
*************************************************************************************
简易画板
*************************************************************************************
*/
static u8 Dft_Scan_Dir = 0;
u16 Ssd_Hor_Resolution =320; //LCD水平分辨率
u16 Ssd_Ver_Resolution =240; //LCD垂直分辨率void drow_block_colar(void)
{ LCD_Fill(0,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution*1/6,Ssd_Ver_Resolution,RED);LCD_Fill(Ssd_Hor_Resolution*1/6,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution*2/6,Ssd_Ver_Resolution,YELLOW);LCD_Fill(Ssd_Hor_Resolution*2/6,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution*3/6,Ssd_Ver_Resolution,BLUE);LCD_Fill(Ssd_Hor_Resolution*3/6,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution*4/6,Ssd_Ver_Resolution,GREEN);LCD_Fill(Ssd_Hor_Resolution*4/6,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution*5/6,Ssd_Ver_Resolution,BLACK);LCD_ShowString(Ssd_Hor_Resolution*5/6+1,Ssd_Ver_Resolution-40,Ssd_Hor_Resolution,16,16,"ERASER");//显示清屏区域
}
//清空屏幕并在右上角显示"RST"
void Load_Drow_Dialog(void)
{LCD_Clear(WHITE);//清屏 POINT_COLOR=BLUE;//设置字体为蓝色 LCD_ShowString(lcddev.width-24,0,200,16,16,"RST");//显示清屏区域POINT_COLOR=RED;//设置画笔蓝色 drow_block_colar();
}
void rtp_test(void)
{u8 key;u8 i=0; while(1){key=Key_Scan(0);tp_dev.scan(0); if(tp_dev.sta&TP_PRES_DOWN) //触摸屏被按下{ if(tp_dev.x[0]<lcddev.width&&tp_dev.y[0]<lcddev.height){ if(tp_dev.x[0]>(lcddev.width-24)&&tp_dev.y[0]<16)Load_Drow_Dialog();//清除else if(tp_dev.y[0]>Ssd_Ver_Resolution-40&&tp_dev.y[0]<Ssd_Ver_Resolution){ if(tp_dev.x[0]>0&&tp_dev.x[0]<=Ssd_Hor_Resolution*1/6)POINT_COLOR = RED;else if(tp_dev.x[0]>Ssd_Hor_Resolution*1/6&&tp_dev.x[0]<=Ssd_Hor_Resolution*2/6)POINT_COLOR = YELLOW;else if(tp_dev.x[0]>Ssd_Hor_Resolution*2/6&&tp_dev.x[0]<=Ssd_Hor_Resolution*3/6)POINT_COLOR = BLUE;else if(tp_dev.x[0]>Ssd_Hor_Resolution*3/6&&tp_dev.x[0]<=Ssd_Hor_Resolution*4/6)POINT_COLOR = GREEN;else if(tp_dev.x[0]>Ssd_Hor_Resolution*4/6&&tp_dev.x[0]<=Ssd_Hor_Resolution*5/6)POINT_COLOR = BLACK;else if(tp_dev.x[0]>Ssd_Hor_Resolution*5/6&&tp_dev.x[0]<=Ssd_Hor_Resolution)POINT_COLOR = BACK_COLOR; }else{ if(POINT_COLOR == BACK_COLOR)LCD_Fill(tp_dev.x[0],tp_dev.y[0],tp_dev.x[0]+5,tp_dev.y[0]+5,POINT_COLOR);elseTP_Draw_Big_Point(tp_dev.x[0],tp_dev.y[0],POINT_COLOR); //画图} }}else delay_ms(10); //没有按键按下的时候 if(key==KEY0_PRES) //KEY0按下,则执行校准程序{LCD_Clear(WHITE); //清屏TP_Adjust(); //屏幕校准 TP_Save_Adjdata(); Load_Drow_Dialog();}i++;if(i%20==0)PFout(9)=!PFout(9);}
}
/* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 */u8 lcd_id[12]; //存放LCD ID字符串u8 datatemp[SIZE];u8 KEY;u8 test[256];u8 i;/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_FSMC_Init();MX_TIM6_Init();/* USER CODE BEGIN 2 */delay_init(168);HAL_TIM_Base_Start_IT(&htim6);LCD_Init(); //初始化LCD FSMC接口AT24CXX_Init(); //初始化IICif(Dft_Scan_Dir){Ssd_Hor_Resolution = 320;Ssd_Ver_Resolution = 240;} else{Ssd_Hor_Resolution = 240;Ssd_Ver_Resolution = 320; }LCD_Display_Dir(Dft_Scan_Dir);tp_dev.init(); //触摸屏初始化 drow_block_colar();if(tp_dev.touchtype!=0XFF){LCD_ShowString(0,0,200,16,16,"Press KEY0 to Adjust");//电阻屏才显示}delay_ms(1500);Load_Drow_Dialog();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */rtp_test(); }/* USER CODE END 3 */
}
效果图:
基于HAL库的STM32F704的电阻式触摸屏的学习相关推荐
- STM32机器人控制开发教程No.3 使用遥控控制电机/舵机(基于HAL库)
01 前言 本教程使用的机器人控制板拥有4个带编码器的电机接口,4个舵机接口,串口通信接口.SWD下载调试接口.航模遥控接口.USB5V输出接口以及方便与树莓派直接连接的40PIN接口等,板载资源丰富 ...
- STM32单片机基于HAL库开发HC-SR04 超声波测距模块(终极版)
参考: 某宝HC-SR04 超声波测距模块商品详情页 STM32L051C8T6 HAL库 输入捕获进行超声波测距 案例 STM32CubeMX学习笔记3--TIM2输入捕获(SR-04测距) cub ...
- STM32 串口DMA接收 Openmv / K210 整数、小数字符串数据 (基于HAL库)
目录 前言 一.工程配置 二.串口DMA部分代码 1.源文件UART_DMA.c 2.头文件UART_DMA.h 3.stm32f1xx_it.c的修改 4.串口收发DMA测试 三.字符串数字提取代码 ...
- STM32CubeMX基于HAL库实现简单串口通信
STM32CubeMX基于HAL库实现简单串口通信 板子:STM32F103C8 编译工具:Keil+STM32CubeMX 烧录工具:FlyMcu 串口调试程序:XCOM 实验要求: 1)设置波特率 ...
- STM32外挂FLASH模拟U盘(基于HAL库)
STM32外挂FLASH模拟U盘(基于HAL库) 1.背景 1.1这篇文章能给你带来什么 1.2根据你要解决的问题,精确快速跳转到相应位置 1.3我在做完这个后还有不明白的地方,希望能有大触解答困惑 ...
- STM32软件学习笔记(一)基于HAL库的STM32F429单片机串口打印程序
|版权声明:本文为博主原创文章,转载请注明出处.https://blog.csdn.net/NeverImagine_/article/details/95517664 目前ST官方有提供两种库文 ...
- STM32F4 HAL库开发 -- 新建基于 HAL 库的工程模板
一.STM32CubeF4 固件包下载 下载:STM32CubeF4 MCU Firmware Package 二.新建基于 HAL 库的工程模板 1.新建文件夹 建立一个文件夹为 Template. ...
- STM32控制步进电机:基于HAL库定时器中断的闭环步进电机驱动+精准控制脉冲数
STM32控制步进电机:基于HAL库定时器中断的闭环步进电机驱动+精准控制脉冲数 一.步进电机闭环驱动器 二.CubeMx配置 1.Clock Configuration 2.脉冲端 定时器配置 3. ...
- STM32F103VET6基于HAL库和标准库下DAC 心形波形输出
STM32F103VET6基于HAL库和标准库下DAC 心形波形输出
- STM32F407学习笔记(1)基于hal库,CubeMX以及keil5的基本使用。
前言:自己是大二的一名通信工程专业的小菜鸡,在实验室培训了四次之后,我们目前使用是stm32f407zgt6这块单片机.在为明年的电赛国赛准备着.实验室目前使用的工具是cubemax和keil这两款软 ...
最新文章
- 【控制】《复杂运动体系统的分布式协同控制与优化》-方浩老师-第11章-意图场模型干预下的人机共享控制
- java 属性访问修饰符_Java中的访问修饰符(二十七)
- 数据挖掘-分类与预测-神经网络算法
- 读书笔记-你不知道的JavaScript(上)
- 谁偷偷删了你的微信?别慌!一篇Python学习教程帮你都揪出来
- Alexa交叉编译(avs-device-sdk)
- 基于51单片机的红外接收
- Java8新特性 方法引用(二)
- macOS Monterey 12.2 (21D49) 正式版 ISO、IPSW、PKG 下载
- 大脑信息编码_编码人大脑的5大小吃
- source and destination ports as seperate columns in wireshark
- python判断word页码
- 一名理想主义的程序员
- 【UOS】如何用Ventoy工具制作UOS启动盘操作
- Mac电脑安装其他系统
- PDF如何设置注释字体大小
- 态度篇·与其混不如滚
- 飞凌imx6dl lvds闪屏问题记录
- 小米路由器提示网络连接问题
- 数据库原理及应用学习笔记
热门文章
- 学计算机要选什么科,新高考省份想学计算机专业怎么选科
- android测试基站,Android iPhone 手机查看基站信息
- android 根据基站测距,无线测距基站
- 超燃动态可视化条形图源码及效果图_HTML5大数据可视化效果(一)彩虹爆炸图...
- 机场精细化管理_王晓鸿:BIM技术在机场工程精细化管理的应用
- 登录注册判断+Mysql
- ❤️1000道《计算机基础知识》汇总上----(建议收藏)❤️
- 服务器输出字符流数据到浏览器 使用println浏览器无显示
- 五个脑叶的功能_人类大脑的生理构造及功能分区
- 程序员是如何下载的视频?