1.触摸屏简介* H, x# h7 @( j9 }7 U3 k! `3 A# }
1.1 电阻式触摸屏; F# V) g3 {' U: U/ h7 x' y
电阻式的触摸屏结构如下图示,它主要由表面硬涂层、两个ITO层、间隔点以及玻璃底层构成,这些结构层都是透明的,整个触摸屏覆盖在液晶面板上,透过触摸屏可看到液晶面板。表面涂层起到保护作用,玻璃底层起承载的作用,而两个ITO层是触摸屏的关键结构,它们是涂有铟锡金属氧化物的导电层。两个ITO层之间使用间隔点使两层分开,当触摸屏表面受到压力时,表面弯曲使得上层ITO与下层ITO接触,在触点处连通电路。+ M6 l6 @* ]- Q
7 C3 `' ]# [2 \6 E
* n4 l0 N/ ~" h0 t+ R
+ ?1 y1 j3 C5 y6 \
2 m, }2 y t2 K& f8 H
& r# p$ W0 i/ K1 \
两个ITO涂层的两端分别引出X-、X+、Y-、Y+四个电极,这是电阻屏最常见的四线结构,通过这些电极,外部电路向这两个涂层可以施加匀强电场或检测电压。
# g* ~, c. Y% ~' T- u9 {7 q
' v9 x3 C0 W& e$ B/ S# e; f, a: N7 f: N& l& l
" U0 Y" l/ C4 b% r! S' L
" L9 l8 r" W/ l% d0 S
+ m- N# `5 {/ M电阻触摸屏的校准:确定位置和对应的电压值之间的比例关系% \( f* G1 z- Q* G3 h* q
X0 = Xfac * ADC_X + Xoff
. T8 H7 p5 z8 i. ]+ Z8 c0 VY0 = Yfac * ADC_Y + Yoff
7 k: J% b, N9 \
3 F# Z5 e6 x2 h% T+ r电阻触摸屏的优缺点3 m- R' ], j7 E: P
优点:精度高、价格便宜、抗干扰能力强、稳定性好
4 F9 Z% I# b+ S缺点:容易被划伤、透光性不太好、不支持多点触控
" l* F/ q N$ F: e& y* t& ]7 o
. }& d- [7 K$ w/ S触摸屏都需要一个AD转换器来将电压变化读取出来,供主机算出触摸的位置。本例程中的TFTLCD模块使用的是四线电阻式触摸屏,触摸屏控制芯片为XPT2046,XPT2046是一款4导线制触摸屏控制器,采用SPI模式进行通讯,内含12位分辨率125KHz转换速率逐步逼近型A/D转换器。XPT2046引脚图(SOP-16封装)及引脚说明如下图示
Z9 `5 ?1 V! a* ]( O( o W
! y, k" |! |1 d) L6 F. }9 y
/ V0 P' R( U" K% }4 H3 c8 D3 D
) a' ~4 t" P q4 F) s- N+ y; s( h7 [3 m6 \1 |
1.2 电容式触摸屏+ j1 I, a; u, ?8 R
与电阻式触摸屏不同,电容式触摸屏不需要通过压力使触点变形。它的基本原理是利用充电时间检测电容大小,若手指触摸屏幕,会影响触摸点附近两个电极之间的耦合,从而改变两个电极之间的电容量,若检测到某电容的电容量发生了改变,即可获知该电容处有触摸动作从而通过检测出电容值的变化来获知触摸信号。" b/ p4 S6 P! A* D# U' k( t
1 V% @; }7 l2 w3 X" Z9 X4 `2 s, _
( E2 r( W' N" X
2 a4 p" m+ I4 g- z) Q由于本例程使用的是电阻式触摸屏,这里电容式触摸屏不做详细介绍
. A# l: _9 y& w5 o J% J* `6 q% X8 J1 w
2.硬件设计9 s) \' k* u. x ?, w1 w3 @7 n; ~
D1指示灯用来提示系统运行状态,K_UP按键用来强制校准电阻触摸屏(电容屏无需校准),AT24C02用来存储电阻触摸屏校准数据,TFTLCD模块用来显示触摸
$ r! m5 }' p% F! C* B$ L0 B指示灯D13 h/ t3 e! ~1 x2 M, k P/ {0 V% S3 [
USART1串口, |6 q+ w9 R9 x
FSMC
' t; N- ~- @' n" KTFTLCD' s) D' m' G6 c# ?( p
AT24C02" [! E. A3 G( `5 m; M4 O: r
K_UP按键
! _" W/ I7 |& A3 ?
8 O [( D% T6 s0 S
: z' _" O1 t: Y/ z
; i- Q& T3 i: W, J3 J( [, |% B3.软件设计1 o2 |5 p" Z {: M: P( `/ v
3.1 STM32CubeMX设置
/ B m& p2 z' z( R4 s➡️ RCC设置外接HSE,时钟设置为72M
- g& A" ?6 ~9 ?# Z5 S5 \+ y3 g➡️ PC0设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平
4 E1 K- r: O6 ^4 A) p, Q3 r➡️ PA0设置为GPIO输入模式、下拉模式
8 j: w9 H( K; {1 A➡️ PD6设置为GPIO输出模式(TOUCH_CS);PD7设置为GPIO输入模式(TOUCH_PEN)$ f. W4 _' h! ~, O4 Y( t
➡️ USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位
2 q% M. U( X. O3 j5 ^' A4 \1 g# K" U➡️ 激活I2C2,选择标准传输模式,选择7位寻址地址,其余默认设置
+ {& Y6 [1 [' `0 D' n➡️ 激活FSMC,详细请参考TFTLCD显示章节的设置
% }" G& J2 D, a" l5 P➡️ 激活SPI1,不开启NSS,数据长度8位,MSB先输出,分频因子16,CPOL为HIGH,CPHA为第二个边沿,不开启CRC检验,NSS为软件控制0 O% {# W# L2 n
" a E- r# R) ~1 T3 I
+ T. ^ w! ~2 q- d2 ?
: m8 L2 g/ L3 k% i# b➡️输入工程名,选择路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码
$ k- ~; c4 i6 Y+ k% @. Q' a) F
* Q; l/ H, _- H/ C1 w: L3 h; c+ m3.2 MDK-ARM软件编程; K( k- E f3 Z% C# [7 @8 K
➡️ 创建按键驱动文件key.c和key.h,驱动代码参考按键输入例程
4 f' X, V3 v1 Y. I2 Q➡️ 创建AT24C02驱动文件24cxx.c和24cxx.h,驱动代码参考I2C总线例程
* N1 N$ n/ t" y$ v5 |! o➡️ 创建LCD驱动文件tftlcd.c 和tftlcd.h,驱动代码参考TFTLCD显示例程
; t+ s$ [2 A- ~➡️ 创建XPT2046触摸芯片驱动文件touch.c和touch.h# q# D ~0 ]: T4 ^9 d# w
$ l. V3 f: B0 t" l7 L, J9 r( I8 S7 ~
/ U( f1 N8 W0 W' c+ V两个重要的结构体:" ]# M, _9 d1 J. z+ d# C: W
- typedef struct{ /*存储触点读取到的数据*/ p& R. }1 i2 N: v7 p/ U( e$ O# U
- uint16_t x; //x轴物理坐标值$ ]3 Y7 d# z4 `/ h7 }! x
- uint16_t y; //y轴物理坐标值
T0 f$ N2 @/ R" c - uint16_t lcdx; //x轴彩屏坐标值: [/ o0 M# z* T3 Z
- uint16_t lcdy; //y轴彩屏坐标值
+ d8 M) v0 s; g- ]! E7 e - }TouchTypeDef;# r; J3 {; C4 `, w+ T
- ' u: n) F7 N: g/ }% l7 a
- typedef struct{ /*保存校正因素*/! p& e, G1 U/ S4 E
- uint8_t posState; //校正参数标志$ J. |8 s" m2 f/ P" E' ]* E
- int16_t xOffset; //x轴偏移量
% b2 \( m9 |* }. [; Q9 ~ - int16_t yOffset; //y轴偏移量
/ N, T. V; `9 N5 Y% P - float xFactor; //x轴比例因数
, K# o b$ Y& q2 T0 v - float yFactor; //y轴比例因数
2 Y6 J1 f+ o$ W3 D3 r - }PosTypeDef;
复制代码 7 v3 G/ B8 k9 P% ?6 J
触摸屏初始化及读取物理坐标(ADC值)函数:. m! t' e1 T9 w
) [# x4 ?, Z9 W
p B" t" ]+ u. I5 Z" e- q- void TOUCH_Init(void){
+ J: d8 N4 Q( m% X6 N" K% w - //读取AT24C02的TOUCH_ADJ_ADDR地址处的值( e, J8 y$ T( X2 K) ]. T/ f- s
- HAL_I2C_Mem_Read(&hi2c2,ADDR_24CXX_READ,TOUCH_ADJ_ADDR,I2C_MEMADD_SIZE_8BIT,&TouchAdj.posState,sizeof(TouchAdj),0xff);
: i, L4 k+ g3 |! L; C, a - if(TouchAdj.posState != TOUCH_ADJ_OK){ //检查是否有校正数据
- {% k& C: p, ^( q0 h4 e - TOUCH_Adjust(); //如果没有校正数据,则启动校正函数
* [' k& l5 A6 |2 r& C6 d3 x \4 _ - }$ u" G0 j. Y& D
- }% ]. c. o# I0 F7 k
- /*封装SPI读取数据函数*/
8 T5 E( B; Z: t+ }6 {- N - uint8_t WR_Cmd(uint8_t cmd){
% m) x+ e K$ M- e- B3 k2 t4 P - uint8_t Tx_DATA = cmd;$ F' C6 V# ]+ O0 o, {: g
- uint8_t Rx_DATA = 0;
/ Z8 @% D% E1 v - HAL_SPI_TransmitReceive(&hspi1,&Tx_DATA,&Rx_DATA,1,500);5 _) A5 T: V! a
- return Rx_DATA;
5 b; l( b1 O7 `# M! j - }' p- y+ Q9 n% M7 e3 s# }$ ?
- /*读取X轴或Y轴的ADC值,并进行滤波处理*/: s1 p+ |% X) f8 \
- uint16_t TOUCH_Read_AD(uint8_t cmd){
3 M, ]2 r9 E( g4 _' f2 F - uint8_t i, j;. @+ ^ S2 j$ B; }4 y, P
- uint16_t NUMH,NUML;3 C1 L+ W! ^. j$ n2 y, G4 Z: a3 F
- uint16_t NUM[TOUCH_READ_TIMES] = {0};
2 R9 _* h Z! b' x! o0 \& i1 b# R0 u - uint16_t temp,value;8 ]$ Z, E9 \! f4 x4 U
- uint32_t totalValue; //注意数据类型1 o- u0 R e- \2 ~; K$ y. |/ }& m
-
& u- a6 j% P4 ~" S' |# T - for(i=0; i<TOUCH_READ_TIMES; i++){
7 A% J$ m$ C) P+ y4 H j3 c - TOUCH_CS_LOW(); //开始SPI通讯
& D' q) h$ C i" M0 Y% W& ~1 ^& I - WR_Cmd(cmd);
) H5 o. w0 ?2 O5 j - NUMH = WR_Cmd(0XFF);
9 B0 t: X7 J4 i* K3 Y7 o - NUML = WR_Cmd(0XFF);
* l, T9 I; c- b8 U! N - NUM[i] = (NUMH<<8)+ NUML;3 d: Z- G/ l; D( @6 v2 I
- NUM[i] >>= 3; //最低三位无用
: m ?3 v0 F3 ~ - TOUCH_CS_HIGH(); //结束SPI通讯
2 _- b: A$ w9 ?) X5 k - }& ]. w1 Z* G. u$ ^6 h) J
- //滤波处理:1.从大到小排序+ i% G7 [# y# m2 ?& r7 a) [$ I3 b
- for(i=0; i<(TOUCH_READ_TIMES - 1); i++){$ \# j" L7 q& {- Q
- for(j=i+1; j<TOUCH_READ_TIMES; j++){0 |; s) y/ \5 O6 O: c
- if(NUM[i] < NUM[j]){
* q3 v- E* D# {+ d% c# R - temp = NUM[i];
+ _0 M- e3 S1 E k: _( } - NUM[i] = NUM[j];
( ^: p/ h; ^- B: y8 {# ?$ P - NUM[j] = temp;
: M: c' Y$ @1 E& C' D" F - } ) v1 r3 Y* R& ~* P9 v$ j. U) c2 x% s
- } 3 ?: G7 I" K0 C5 _* \( p, M0 c
- }
6 a# F+ p* {/ A/ o/ [ - //滤波处理:2.去掉最大值和最小值,求余下的平均值5 n$ t+ a& g# K- q& W
- j = TOUCH_READ_TIMES - 1;' O2 a6 K: s/ e4 S2 V& H
- totalValue = 0;6 o7 W0 c3 B: U0 ~1 G, X4 A8 J" |
- for(i=1; i<j; i++){* x7 F0 i+ M0 x% s" z+ j% b2 q
- totalValue += NUM[i];8 e9 f2 Z1 a( s9 J' ?5 \
- }
7 k Y$ _& M1 ~3 r9 H - value = totalValue/(TOUCH_READ_TIMES - 2);
( D$ H8 ^7 D" E }# A - return value;% b+ W. F- D+ N
- }: S6 Q( X& n {: b! w
- /*读取X轴和Y轴的ADC值,再次进行滤波*/
, N, A6 n& n9 v1 J% k; D6 i - uint8_t TOUCH_ReadXY(uint16_t *xValue, uint16_t *yValue){
, b- }8 H9 g9 L4 D3 P; H - uint16_t xValue1, yValue1, xValue2, yValue2;
1 c7 L! |* q) e' T3 ~/ X0 z7 U# R - xValue1 = TOUCH_Read_AD(TOUCH_X_CMD);
. Z( E' ]. j8 g5 n8 Y7 T" O - yValue1 = TOUCH_Read_AD(TOUCH_Y_CMD);
7 D1 D; I8 k0 e& @5 N2 r9 ` - xValue2 = TOUCH_Read_AD(TOUCH_X_CMD);7 i; V2 ?: ~; g" T6 d
- yValue2 = TOUCH_Read_AD(TOUCH_Y_CMD);
! Q% O+ G" g t: M" K7 A - //计算两点之间的采样差值
7 i# g: K1 c# j4 N - if(xValue1 > xValue2)
& ~; H; k! j; M. W S f - *xValue = xValue1 - xValue2;
9 c* s6 S p; ?7 i/ w* D - else$ J8 }8 \1 }5 b( a! d. }5 K2 c, O
- *xValue = xValue2 - xValue1;+ H1 |8 y @- @' `) H3 G
- , s0 Q/ U5 Y& m
- if(yValue1 > yValue2)
) v! X9 `7 _$ A9 _. ~5 b5 u" H y9 G - *yValue = yValue1 - yValue2;
6 C2 E# U4 w# @& }: S, U - else
0 I: n* b# n( Q/ c! D - *yValue = yValue2 - yValue1;
2 e/ r5 `9 U/ R0 S% m, s- h0 u - //判断采样差值是否在可控范围内+ T. M" X. P" J5 |; H; P$ s _" l
- if((*xValue > TOUCH_MAX+0) || (*yValue > TOUCH_MAX+0)) ! e( u9 z# V8 u% f( g
- return 0xFF;& a' M6 c! s6 g+ l; x
- //求平均值
7 S8 m. F0 ]! {, R) H! y8 m' y% a4 v - *xValue = (xValue1 + xValue2) / 2;) o7 D$ d# m/ W% _# ^
- *yValue = (yValue1 + yValue2) / 2;
2 E9 A. b6 W( n' ~ - //判断得到的值,是否在取指范围内,避免出现飞点现象 6 P5 D n' V: |2 w
- if((*xValue > TOUCH_X_MAX+0) || (*xValue < TOUCH_X_MIN) || (*yValue > TOUCH_Y_MAX+0) || (*yValue < TOUCH_Y_MIN))
- }1 ]# q- K T' F/ L2 v' a, o - return 0xFF;
$ f& R* f0 l9 Q5 j( s+ q9 p0 a- ~& a -
# j2 n% V- @/ \ T; w( C; {* L- _ - return 0; ! {/ I* B# ^- I6 _ d' a( e
- }
复制代码 ( C, e# _. f# a5 ?
触摸屏扫描函数:
$ v; N( S% s, [5 o; Z; F- uint8_t TOUCH_Scan(void){
* J6 D2 `6 q0 ^( x3 W4 z - if(TOUCH_ReadXY(&TouchData.x, &TouchData.y)) / d- }$ H; T9 l% Z {& q
- return 0xFF; //没有触摸,直接返回0xFF$ @" Y% q3 x7 I. G! `
- //根据物理坐标值,计算出彩屏坐标值
: D. E) [/ b5 k. ]8 ?" m - TouchData.lcdx = TouchData.x * TouchAdj.xFactor + TouchAdj.xOffset;, T5 e: b& v! h3 `
- TouchData.lcdy = TouchData.y * TouchAdj.yFactor + TouchAdj.yOffset;
5 |9 P, {" ^9 P( T* Q# O( b - //查看彩屏坐标值是否超过彩屏大小 {2 N6 ]- e$ z1 `" ~* n# V
- if(TouchData.lcdx > tftlcd_data.width)0 N+ o& N7 ~. |
- TouchData.lcdx = tftlcd_data.width;+ i& A" L# W6 @- p7 B7 M8 i5 ]
- if(TouchData.lcdy > tftlcd_data.height)
# C, s: d0 n$ {2 h2 U( d - TouchData.lcdy = tftlcd_data.height;$ Z, N. ^: K$ G* c: \7 u7 k
- return 0;
d* `" H, u# G% G9 V7 q - }
复制代码
4 g7 h' ]% P, q: c3 g! d% K3 l B➡️ 在main.c文件下编写触摸屏测试代码
' X3 ?! z- ~2 G5 G- int main(void){- H" a$ {" Y+ K5 Y- u9 F
- uint8_t key;$ I3 R6 K4 }2 p8 W: @
- uint16_t penColor = BLUE;$ x5 ]3 T/ L. u' j, C3 g1 Y
- HAL_Init();; n" x* ]0 e1 v% L6 V5 F
- SystemClock_Config();+ |) u' f$ F$ _+ i9 R5 e
- MX_GPIO_Init();7 ?; N5 A& v Y: Q
- MX_FSMC_Init();# {/ Q( V% B+ r% s9 P; A
- MX_I2C2_Init();/ ~6 K" y' N4 t$ E+ \
- MX_SPI1_Init();5 b6 z9 d% J! _2 T
- MX_USART1_UART_Init();
$ g# c/ P2 d2 }$ F - /* USER CODE BEGIN 2 */; q! d. c' L5 I8 W3 ~
- TFTLCD_Init();, k6 i, |/ C6 l% u0 p
- AT24CXX_Init();1 ^& x* k$ P$ E$ G5 ?9 i
- HAL_Delay(2000);
- I/ D1 H+ Z- E, ~ R" B# S - LCD_Clear(WHITE);
- o5 H- d- X' m* j0 E! r' w - TOUCH_Init();! ?# @, b9 \# N+ H- e# X7 O6 U
- /* USER CODE END 2 */
0 I' d0 b" E# P1 a0 y - while (1){
4 e8 z( d5 O9 {7 ]" i: q1 X8 O1 a0 e - key=KEY_Scan(0);
7 [9 V ~4 E; n y a - if(key==KEY_UP_PRES){//按下KEY_UP键进入校准函数# H8 T6 Z: h* |9 p p7 o
- TOUCH_Adjust(); 5 |( w. Y7 V; R) i
- }
2 p8 w. N$ V+ F) {' V N -
5 ?- s! T+ A: S# Q - if(TOUCH_Scan() == 0){ j) D+ E/ y0 l. u3 n2 q8 T2 `
- if(TouchData.lcdy > tftlcd_data.height - 18){
: L% N o# q8 W" p% U- n - //选择画笔颜色
; C1 {6 [5 Y: i - if(TouchData.lcdx>220)
+ n& N' ^* k1 R( I# M- O; s- n - penColor = YELLOW;9 Q$ F3 w. `4 |. k) g: @) D
- else if(TouchData.lcdx>200)% H7 n" g. k7 I( i$ p: i9 G/ V' G% k
- penColor = CYAN;
' A* c$ v# G* Z- v - else if(TouchData.lcdx>180)
6 |! ^) @& g3 c' i - penColor = GREEN;
8 I0 U" l9 p+ i - else if(TouchData.lcdx>160)9 l. J9 t% j: @1 R; f
- penColor = MAGENTA;
7 ^& E; x6 u, c$ N - else if(TouchData.lcdx>140)2 Q9 o% E+ h: C5 N$ Z
- penColor = RED;
( t) [. l3 t/ f4 a - else if(TouchData.lcdx>120)( |$ |% S1 W' ~( _9 q( h) @9 O7 M
- penColor = BLUE;
+ X4 l1 \1 y5 ^9 H+ Q2 Y - }else{ //画点; x! r& M& W& A$ n: c
- LCD_Fill(TouchData.lcdx-1, TouchData.lcdy-1, TouchData.lcdx+2,* H7 z) E# D% g! @9 @9 p9 ]# q4 k
- TouchData.lcdy+2, penColor);
5 K9 {/ v, w* k/ G - }
! d) x: Y8 E4 A* ^$ [! j - //清屏
! T- |6 e* k1 F" _' z! A5 U - if ((TouchData.lcdx > tftlcd_data.width-8*4) && (TouchData.lcdy < 16))//215 = TFT_XMAX - 24{
5 @+ i; C Y( R5 E. C - LCD_Fill(0, 0, tftlcd_data.width,tftlcd_data.height-16, BACK_COLOR);# H- N' j( C0 C T- ~% o5 R# D
- LCD_ShowString(tftlcd_data.width-8*4,0,tftlcd_data.width,tftlcd_data.height,16,(uint8_t *)"RST");
' Q! m8 P n. v, M; L n& { - } " p1 b( C" b, w1 Y% p3 @# e P
- }9 r9 R: I8 c8 |. [1 y
-
) i3 E \' @5 s3 s( ~& @2 R. T - HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);
, k8 v* L' m( v+ v - HAL_Delay(10);- C% T4 ?/ |( S$ m/ o
- }
1 A3 l& }+ M9 P3 v$ Z- e& c - }
复制代码 / {2 q. [0 r1 h6 ^+ ~5 v$ q
4.下载验证+ i; I" K. E* }! q1 o6 A
编译无误下载到开发板后,可以看到D1指示灯不断闪烁,可在触摸屏上写字,显示界面如下图示3 O! ?0 U# L& t7 I# K
( V2 S4 c. U# `8 n* z% ]7 I, `8 Q! ?( x& b6 ~8 i. A: C
9 q7 m$ J5 y$ Y! a$ `& |
% [& s6 r( M! ~5 {+ d) p+ t如有侵权请联系删除" \5 `8 r" w4 J1 `
( z% ?# o. w' w/ U! d% P
转载自: 嵌入式攻城狮5 D! k; W9 ]6 M: V8 u7 [+ L/ s& n
$ R& E4 S; M6 z5 L+ v1 H6 a* e8 b
( K3 Q6 o+ W/ ?0 I8 u$ Q* b& L1 u
/ m* V+ A+ n ^3 f: ^: w J7 a
|
看着开发简单多了,有机会试用一下
这个正是我需要的,好资料啊。电阻触摸校准老是不稳,学**这个电阻触摸屏校准算法。