完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980
本章教程为大家讲解LTDC应用之LCD电阻触摸芯片STMPE811的4点和2点触摸校准和电容触摸芯片FT5X06、GT911和GT811的使用。
目录
第54章 STM32H7的LTDC应用之LCD电阻触摸和电容触摸
54.1 初学者重要提示
54.2 电阻触摸和电容触摸相关知识
54.3 电阻屏硬件设计
54.4 电容屏硬件设计
54.5 电阻触摸驱动设计
54.5.1 STMPE811的驱动实现
54.5.2 电阻触摸扫描函数TOUCH_Scan
54.5.3 电阻屏触摸校准原理(2点)
54.5.4 电阻屏触摸校准原理(4点)
54.5.5 电阻屏触摸校准的实现
54.5.6 电阻屏触摸ADC值转物理坐标
54.5.7 电阻触摸的使用方法
54.6 电容触摸驱动设计
54.6.1 电容屏触摸IC---FT5X06
54.6.2 电容屏触摸IC---GT911
54.6.3 电容触摸的使用方法
54.7 不同触摸IC的识别
54.8 LCD触摸移植和使用
54.9 实验例程设计框架
54.10 实验例程说明(MDK)
54.11 实验例程说明(IAR)
54.12 总结
这部分知识点在第52章的2.2小节有详细说明,必看。
电阻触摸STMPE811的原理图如下:
通过STMPE811的原理图要了解以下几点:
电容触摸主要有三种:FT5X06,GT911和GT811,其中GT811已经停产。下面是FT5X06和GT911触摸板效果(触摸板和触摸芯片是一体的):
触摸芯片已经集成到柔性PCB上,且已经校准好。用户使用的话,直接通过I2C方式读取数据即可。下面是电容触摸板引出的引脚:
注意I2C_SDK和I2C_SCL的上拉电阻在V7主板上。
下面将电阻触摸程序设计中的相关问题逐一为大家做个说明。
电阻触摸要比电容触摸麻烦很多,因为电阻触摸要做校准,还要做滤波,否则采集回来的触摸值会抖动或者出现飞点,出现这种情况的主要原因是电阻触摸板的线性度不够好。开发板电阻屏使用的触摸芯片是STMPE811,这个芯片其实就是12位分辨率的ADC,用来采集电阻触摸板的X轴ADC值和Y轴ADC值,然后按照一定的线性关系将ADC值转换为实际的坐标值。其中这个线性关系是通过触摸校准建立起来的,每次采集的X轴和Y轴ADC就可以代入这个线性关系,从而获得实际的坐标值。
总的来说,STMPE811的驱动不难实现,可以结合STMPE811的数据手册:http://bbs.armfly.com/read.php?tid=23306 研究开发板提供的驱动配置。配置好后仅需要提供读取的X轴,Y轴的ADC值以及触摸按下状态(判断STMPE811的中断输出引脚就可以了,如果有触摸,这个引脚输出低电平,反之,输出高电平。通过判断这个引脚就可以选择是否读取X轴,Y轴的ADC值,避免不必要的操作)。这些函数在bsp_ts_stmpe811.c文件实现。而触摸值滤波,触摸扫描和触摸校准是在bsp_touch.c文件里面实现。
下面是清除触摸中断标志函数和X轴,Y轴的ADC值读取函数,这些函数被bsp_touch.c文件所调用,而函数TOUCH_PenInt是在bsp_touch.c文件,这里也贴出来。
1. /*
2. ******************************************************************************************************
3. * 函 数 名: TOUCH_PenInt
4. * 功能说明: 判断触摸按下
5. * 形 参: 无
6. * 返 回 值: 0表示无触笔按下,1表示有触笔按下
7. ******************************************************************************************************
8. */
9. uint8_t TOUCH_PenInt(void)
10. {
11. if ((TP_INT_GPIO_PORT->IDR & TP_INT_PIN) == 0)
12. {
13. return 1;
14. }
15. return 0;
16. }
17.
18. /*
19. ******************************************************************************************************
20. * 函 数 名: STMPE811_ClearInt
21. * 功能说明: 清楚触笔中断
22. * 形 参: 无
23. * 返 回 值: 无
24. ******************************************************************************************************
25. */
26. void STMPE811_ClearInt(void)
27. {
28. STMPE811_WriteReg1(REG811_INT_STA, 0xFF);
29. }
30.
31. /*
32. ******************************************************************************************************
33. * 函 数 名: STMPE811_ReadX
34. * 功能说明: 读取X坐标adc
35. * 形 参: 无
36. * 返 回 值: X坐标值adc
37. ******************************************************************************************************
38. */
39. uint16_t STMPE811_ReadX(void)
40. {
41. /* 按照 XY 读取模式,连续读取3字节数据,然后分解出X,Y
42. | byte0 | byte1 | byte2 |
43. | X[11:4], | X[3:0],Y[11:8] | Y[7:0] |
44. */
45. uint8_t buf[3];
46.
47. #if 0
48. STMPE811_ReadBytes(buf, REG811_TSC_DATA1, 3);
49.
50. s_AdcX = ((uint16_t)buf[0] << 4) | (buf[1] >> 4);
51. s_AdcY = ((uint16_t)(buf[1] & 0xF) << 8) | buf[2];
52. #else
53. if (STMPE811_ReadReg1(REG811_TSC_CTRL) & 0x80)
54. {
55. STMPE811_ReadBytes(buf, REG811_TSC_DATA1, 3);
56.
57. s_AdcX = ((uint16_t)buf[0] << 4) | (buf[1] >> 4);
58. s_AdcY = ((uint16_t)(buf[1] & 0xF) << 8) | buf[2];
59.
60. #if 0
61. /* for debug */
62. {
63. static int32_t s_t1 = 0;
64. int32_t tt;
65.
66. tt = bsp_GetRunTime();
67. if (tt - s_t1 > 1000)
68. {
69. printf("\r\n");
70. s_t1 = tt;
71. }
72. printf("(%7d) %5d %5d\r\n", tt, s_AdcX, s_AdcY);
73. }
74. #endif
75. }
76. else
77. {
78. s_AdcX = 0;
79. s_AdcY = 0;
80. }
81. #endif
82.
83. return s_AdcX;
84. }
85.
86. /*
87. ******************************************************************************************************
88. * 函 数 名: STMPE811_ReadX
89. * 功能说明: 读取Y坐标adc
90. * 形 参: 无
91. * 返 回 值: Y坐标值adc
92. ******************************************************************************************************
93. */
94. uint16_t STMPE811_ReadY(void)
95. {
96. return s_AdcY;
97. }
下面将程序设计中的关键地方做个阐释:
接下来再来看bsp_touch.c文件中STMPE811触摸扫描函数TOUCH_Scan的实现:
1. /*
2. ******************************************************************************************************
3. * 函 数 名: TOUCH_Scan
4. * 功能说明: 触摸板事件检测程序。该函数被周期性调用,每ms调用1次. 见 bsp_Timer.c
5. * 形 参: 无
6. * 返 回 值: 无
7. ******************************************************************************************************
8. */
9. void TOUCH_Scan(void)
10. {
11. uint16_t usAdcX;
12. uint16_t usAdcY;
13. static uint16_t s_usXBuf[SAMPLE_COUNT];
14. static uint16_t s_usYBuf[SAMPLE_COUNT];
15. static uint8_t s_ucPos = 0;
16. static uint8_t s_count = 0;
17. static uint8_t s_down = 0;
18. static uint16_t s_usSaveAdcX, s_usSaveAdcY; /* 用于触笔抬起事件,保存按下和移动的最后采样值 */
19. static uint8_t s_ms = 0;
20.
21. if (g_GT811.Enable == 1)
22. {
23. GT811_Timer1ms(); /* 电容触摸屏程序计数器 */
24. return;
25. }
26.
27. if (g_GT911.Enable == 1)
28. {
29. GT911_Timer1ms(); /* 电容触摸屏程序计数器 */
30. return;
31. }
32.
33. if (g_tFT5X06.Enable == 1)
34. {
35. FT5X06_Timer1ms(); /* 电容触摸屏程序计数器 */
36. return;
37. }
38.
39. /* 下面用于电阻触摸 */
40.
41. if (g_tTP.Enable == 0)
42. {
43. return;
44. }
45.
46. if (++s_ms >= 2)
47. {
48. return;
49. }
50.
51. /* 2ms进入一次 */
52. s_ms = 0;
53.
54. /* 触笔中断发生 */
55. if (TOUCH_PenInt())
56. {
57. /* 获得原始的ADC值,未滤波 */
58. usAdcX = STMPE811_ReadX();
59. usAdcY = STMPE811_ReadY();
60.
61. if (TOUCH_PressValid(usAdcX, usAdcY))
62. {
63. /* 按压30ms之后才开始采集数据 */
64. if (s_count >= DOWN_VALID / 2)
65. {
66. s_usXBuf[s_ucPos] = usAdcX;
67. s_usYBuf[s_ucPos] = usAdcY;
68.
69. /* 采集20ms数据进行滤波 */
70. if (++s_ucPos >= SAMPLE_COUNT / 2)
71. {
72. s_ucPos = 0;
73.
74. /* 对ADC采样值进行软件滤波 */
75. g_tTP.usAdcNowX = TOUCH_DataFilter(s_usXBuf, SAMPLE_COUNT / 2);
76. g_tTP.usAdcNowY = TOUCH_DataFilter(s_usYBuf, SAMPLE_COUNT / 2);
77.
78. if (s_down == 0)
79. {
80. s_down = 1;
81. /* 触摸按下事件 */
82. TOUCH_PutKey(TOUCH_DOWN, g_tTP.usAdcNowX, g_tTP.usAdcNowY);
83.
84. s_usSaveAdcX = g_tTP.usAdcNowX;
85. s_usSaveAdcY = g_tTP.usAdcNowY;
86. }
87. else
88. {
89. if (TOUCH_MoveValid(s_usSaveAdcX, s_usSaveAdcY, g_tTP.usAdcNowX, g_tTP.usAdcNowY))
90. {
91. /* 触摸移动事件 */
92. TOUCH_PutKey(TOUCH_MOVE, g_tTP.usAdcNowX, g_tTP.usAdcNowY);
93.
94. s_usSaveAdcX = g_tTP.usAdcNowX;
95. s_usSaveAdcY = g_tTP.usAdcNowY;
96. }
97. else
98. {
99. g_tTP.usAdcNowX = 0; /* for debug stop */
100. }
101. }
102. }
103. }
104. else
105. {
106. s_count++;
107. }
108. }
109. else
110. {
111. if (s_count > 0)
112. {
113. if (--s_count == 0)
114. {
115. /* 触摸释放事件 */
116. //TOUCH_PutKey(TOUCH_RELEASE, g_tTP.usAdcNowX, g_tTP.usAdcNowY);
117. TOUCH_PutKey(TOUCH_RELEASE, s_usSaveAdcX, s_usSaveAdcY);
118.
119. g_tTP.usAdcNowX = 0;
120. g_tTP.usAdcNowY = 0;
121.
122. s_count = 0;
123. s_down = 0;
124.
125. STMPE811_ClearInt(); /* 清触笔中断标志 */
126. }
127. }
128. s_ucPos = 0;
129. }
130. }
131. }
下面将程序设计中的关键地方做个阐释:
函数TOUCH_PressValid的具体实现如下,其中全局变量g_tTP.usMaxAdc = 4095,因为电阻触摸芯片STMPE811是12位ADC,最大触摸值就是2^12 – 1 = 4095。
/* 有效ADC值的判断门限. 太接近ADC临界值的坐标认为无效 */
#define ADC_VALID_OFFSET 2
/*
*********************************************************************************************************
* 函 数 名: TOUCH_PressValid
* 功能说明: 判断按压是否有效,根据X, Y的ADC值进行大致判断
* 形 参: 无
* 返 回 值: 1 表示有效; 0 表示无效
*********************************************************************************************************
*/
static uint8_t TOUCH_PressValid(uint16_t _usX, uint16_t _usY)
{
if ((_usX <= ADC_VALID_OFFSET) || (_usY <= ADC_VALID_OFFSET)
|| (_usX >= g_tTP.usMaxAdc - ADC_VALID_OFFSET)
|| (_usY >= g_tTP.usMaxAdc - ADC_VALID_OFFSET))
{
return 0;
}
else
{
return 1;
}
}
#define DOWN_VALID 30
由于是每2ms进行一次检测,这里就表示延迟30ms后进行触摸数据采集。延迟30ms是为了消除触摸抖动。
#define SAMPLE_COUNT 20
由于是每2ms进行一次检测,这里就表示采集够10组数据,即20ms后进行下一步操作。
由于不同电阻触摸板的线性度参差不齐,不能直接采用比例关系将电阻触摸芯片STMPE811的返回
值转换成实际的坐标。比如我们操作的显示屏分辨率是800*480,电阻触摸芯片采用STMPE811(12位ADC,触摸值范围0-4095),获得当前的触摸值是(1024, 2048),按照比例关系转换成坐标值就是(1024*800/4096,2048*800/4096),即(200,400)。采用这种方法效果不好,容易出现点击不准确的问题。
鉴于此原因,需要通过触摸校准在ADC数值和显示屏分辨率之间建立一个新的线性关系,简单的说就是由比例关系y = ax升级为y = ax + b。如果有了新的触摸ADC数值,代入这个线性关系里面就可以得到当前实际的坐标值,触摸校准的作用就在这里了。
具体实现原理图如下:
在左上角和右下角分别设置两个坐标点(LcdX0, LcdY0)和(LcdX1, LcdY1),然后让用户去点击,会得到两组ADC数值(AdcX0,AdcY0)和(AdcX1, AdcY1)。
根据这四个坐标点,可以建立两组方程,一个X轴的,一个Y轴。
y = (x - AdcX0)*(LcdX1 - LcdX0)/(AdcX1 - AdcX0) + LcdX0
y = (x - AdcY0)*(LcdY1 - LcdY0)/(AdcY1 - AdcX0) + LcdY0
后面采集到的ADC数值直接代入上面公式就可以得到校准后的物理坐标值(实际的分辨率坐标)。
4点触摸校准实现,略复杂,实现原理如下(如果理解起来麻烦的话,会用就行,一般情况下2点校准就行):
在LCD的左上角,右上角,左下角和右下角分别标坐标点(LcdX1, LcdY1),(LcdX4, LcdY4),(LcdX3, LcdY3)和(LcdX2, LcdY2)。然后让用户去点击,会得到四组ADC数值(AdcX1, AdcY1),(AdcX4, AdcY4),(AdcX3, AdcY3)和(AdcX2, AdcY2)。
计算X轴:
y = (x - AdcX1)*(AdcY2- AdcY1)/(AdcX2- AdcX1) + AdcY1
y = (x - AdcX1)*(AdcY2- AdcY1)/(AdcX3- AdcX1) + AdcY1。
y = (x - AdcX4)*(AdcY2- AdcY1)/(AdcX3- AdcX4) + AdcY1
那么将采集的X值代入上面两个方程会得到两个数值,假设数值是x1和x2。
y = (x - x1)*(LcdX2- LcdX1)/(x2- x1) + LcdX1
将采集的X轴ADC数值再次代入这个方程就得到了最终的物理坐标(实际的分辨率坐标)。
计算Y轴:
y = (x - AdcX1)*(AdcY2- AdcY1)/(AdcX2- AdcX1) + AdcY1
y = (x - AdcX1)*(AdcY4- AdcY1)/(AdcX2- AdcX1) + AdcY1
y = (x - AdcX3)*(AdcY2- AdcY1)/(AdcX2- AdcX3) + AdcY1
那么将采集的X值代入上面两个方程会得到两个数值,假设数值是x1和x2。
y = (x - x1)*(LcdY2- LcdY1)/(x2- x1) + LcdY1
将采集的Y轴ADC数值再次代入这个方程就得到了最终的物理坐标(实际的分辨率坐标)。
对2点和4点触摸校准原理有所了解后,再看代码部分就比较好理解了:
1. /*
2. ******************************************************************************************************
3. * 函 数 名: TOUCH_Calibration
4. * 功能说明: 触摸屏校准
5. * 形 参: _PointCount : 校准点数,2 或 4.
6. * 返 回 值: 无
7. ******************************************************************************************************
8. */
9. void TOUCH_Calibration(uint8_t _PointCount)
10. {
11. uint16_t usAdcX;
12. uint16_t usAdcY;
13. uint8_t usCount;
14. uint8_t I;
15. uint32_t n;
16.
17. /* 校准点数,2点或4点 */
18. if (_PointCount == 4)
19. {
20. g_tTPParam.CalibPointCount = 4;
21. }
22. else
23. {
24. g_tTPParam.CalibPointCount = 2;
25. }
26.
27. TOUCH_CelarFIFO(); /* 清除无效的触摸事件 */
28.
29. for (I = 0; I < g_tTPParam.CalibPointCount; i++)
30. {
31. TOUCH_DispPoint(i); /* 显示校准点 */
32.
33. TOUCH_WaitRelease(); /* 等待触笔释放 */
34.
35. usCount = 0;
36. for (n = 0; n < 500; n++)
37. {
38. usAdcX = TOUCH_ReadAdcX();
39. usAdcY = TOUCH_ReadAdcY();
40.
41. if (TOUCH_PressValid(usAdcX, usAdcY))
42. {
43. if (++usCount > 5)
44. {
45. /* 按压有效, 保存校准点ADC采样值 */
46. if (I == 0)
47. {
48. g_tTPParam.usAdcX1 = usAdcX;
49. g_tTPParam.usAdcY1 = usAdcY;
50. }
51. else if (I == 1)
52. {
53. g_tTPParam.usAdcX2 = usAdcX;
54. g_tTPParam.usAdcY2 = usAdcY;
55. }
56. else if (I == 2)
57. {
58. g_tTPParam.usAdcX3 = usAdcX;
59. g_tTPParam.usAdcY3 = usAdcY;
60. }
61. else
62. {
63. g_tTPParam.usAdcX4 = usAdcX;
64. g_tTPParam.usAdcY4 = usAdcY;
65. }
66. break;
67. }
68. }
69. else
70. {
71. usCount = 0;
72. }
73. bsp_DelayMS(10);
74. }
75. if (n == 500)
76. {
77. return;
78. }
79. }
80.
81. TOUCH_WaitRelease(); /* 等待触笔释放 */
82.
83. /* 识别触摸的 X, Y 和 显示面板的 X,Y 是否需要交换 */
84. g_tTPParam.XYChange = 0; /* 1表示X Y需要交换 */
85. if (LCD_GetHeight() < LCD_GetWidth())
86. {
87. if (TOUCH_Abs(g_tTPParam.usAdcX1 – g_tTPParam.usAdcX2) <
88. TOUCH_Abs(g_tTPParam.usAdcY1 – g_tTPParam.usAdcY2))
89. {
90. g_tTPParam.XYChange = 1;
91. }
92. }
93. else
94. {
95. if (TOUCH_Abs(g_tTPParam.usAdcX1 – g_tTPParam.usAdcX2) >
96. TOUCH_Abs(g_tTPParam.usAdcY1 – g_tTPParam.usAdcY2))
97. {
98. g_tTPParam.XYChange = 1;
99. }
100. }
101.
102. g_tTPParam.usLcdX1 = TP_X1;
103. g_tTPParam.usLcdY1 = TP_Y1;
104. g_tTPParam.usLcdX2 = TP_X2;
105. g_tTPParam.usLcdY2 = TP_Y2;
106. g_tTPParam.usLcdX3 = TP_X3;
107. g_tTPParam.usLcdY3 = TP_Y3;
108. g_tTPParam.usLcdX4 = TP_X4;
109. g_tTPParam.usLcdY4 = TP_Y4;
110.
111. /* 在最后一步,将校准参数保存入Flash 或者EEPROM */
112. TOUCH_SaveParam();
113. }
下面将程序设置中的关键地方做个阐释:
第41行检测到有按下的话,会连续读取5次,确保已经按下了,然后标记本次按下得到的ADC数值。根据执行的是4点触摸校准还是2点触摸校准,这个操作会执行4次或者2次。
电阻屏触摸ADC值转物理坐标的公式就是由前面5.4和5.6小节而来。
1. /*
2. ******************************************************************************************************
3. * 函 数 名: CalTwoPoint
4. * 功能说明: 根据2点直线方程,计算Y值
5. * 形 参: 2个点的坐标和x输入量
6. * 返 回 值: x对应的y值
7. ******************************************************************************************************
8. */
9. static int32_t CalTwoPoint(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x)
10. {
11. return y1 + ((int32_t)(y2 - y1) * (x - x1)) / (x2 - x1);
12. }
13.
14. /*
15. ******************************************************************************************************
16. * 函 数 名: TOUCH_TransX
17. * 功能说明: 将触摸ADC值转换为像素坐标
18. * 形 参: 无
19. * 返 回 值: X 坐标值,允许负值
20. ******************************************************************************************************
21. */
22. static int16_t TOUCH_TransX(uint16_t _usAdcX, uint16_t _usAdcY)
23. {
24. if (g_tTPParam.CalibPointCount == 2)
25. {
26. uint16_t x;
27. int32_t y;
28.
29. if (g_tTPParam.XYChange == 0)
30. {
31. x = _usAdcX;
32. if (x == 0)
33. {
34. y = 0;
35. }
36. else
37. {
38. //y = CalTwoPoint(g_tTPParam.usAdcX1, TP_X1, g_tTPParam.usAdcX2, TP_X2, x);
39. y = CalTwoPoint(g_tTPParam.usAdcX1, g_tTPParam.usLcdX1, g_tTPParam.usAdcX2,
40. g_tTPParam.usLcdX2, x);
41. }
42. }
43. else
44. {
45. x = _usAdcY;
46. if (x == 0)
47. {
48. y = 0;
49. }
50. else
51. {
52. //y = CalTwoPoint(g_tTPParam.usAdcY1, TP_X1, g_tTPParam.usAdcY2, TP_X2, x);
53. y = CalTwoPoint(g_tTPParam.usAdcY1, g_tTPParam.usLcdX1, g_tTPParam.usAdcY2,
54. g_tTPParam.usLcdX2, x);
55. }
56. }
57. return y;
58. }
59. else /* 4点校准 */
60. {
61. uint16_t x, x1, x2;
62. int32_t y;
63.
64. if (g_tTPParam.XYChange == 0) /* X Y 坐标不交换 */
65. {
66. x = _usAdcX;
67.
68. /* 根据 Y ADC 实时计算直线方程的参考点x1, x2
69. if _usAdcY = usAdcY1 then 取点 = (AdcX1, TP_X1, AdcX4, TP_X4, _usAdcY)
70. if _usAdcY = usAdcY2 then 取点 = (AdcX3, TP_X3, AdcX2, TP_X2, _usAdcY)
71.
72. 其中 TP_X1 = TP_X3; TP_X4 = TP_X1 , 这是程序设定的校准位置的像素坐标, 是固定的。
73. 我们仅需要动态计算对第1个和第3个参数。同样采用2点直线方程计算。
74. */
75. x1 = CalTwoPoint(g_tTPParam.usAdcY1, g_tTPParam.usAdcX1, g_tTPParam.usAdcY2,
76. g_tTPParam.usAdcX3, _usAdcY);
77. x2 = CalTwoPoint(g_tTPParam.usAdcY1, g_tTPParam.usAdcX4, g_tTPParam.usAdcY2,
78. g_tTPParam.usAdcX2, _usAdcY);
79. }
80. else /* X Y 坐标交换 */
81. {
82. x = _usAdcY;
83.
84. /* 根据 X ADC 实时计算直线方程的参考点x1, x2
85. if _usAdcX = usAdcX1 then 取点 = (AdcY1, TP_X1, AdcY4, TP_X4, _usAdcX)
86. if _usAdcX = usAdcX2 then 取点 = (AdcY3, TP_X3, AdcY2, TP_X2, _usAdcX)
87.
88. 其中 TP_X1 = TP_X3; TP_X4 = TP_X1 , 这是程序设定的校准位置的像素坐标, 是固定的。
89. 我们仅需要动态计算对第1个和第3个参数。同样采用2点直线方程计算。
90. */
91. x1 = CalTwoPoint(g_tTPParam.usAdcX1, g_tTPParam.usAdcY1, g_tTPParam.usAdcX2,
92. g_tTPParam.usAdcY3, _usAdcX);
93. x2 = CalTwoPoint(g_tTPParam.usAdcX1, g_tTPParam.usAdcY4, g_tTPParam.usAdcX2,
94. g_tTPParam.usAdcY2, _usAdcX);
95. }
96.
97. if (x == 0)
98. {
99. y = 0;
100. }
101. else
102. {
103. /* 根据2点直线方程,计算坐标 */
104. //y = CalTwoPoint(x1, TP_X1, x2, TP_X2, x);
105. y = CalTwoPoint(x1, g_tTPParam.usLcdX1, x2, g_tTPParam.usLcdX2, x);
106. }
107. return y;
108. }
109. }
110.
111. /*
112. ******************************************************************************************************
113. * 函 数 名: TOUCH_TransY
114. * 功能说明: 将触摸ADC值转换为像素坐标
115. * 形 参: 无
116. * 返 回 值: Y 坐标值,允许负值
117. ******************************************************************************************************
118. */
119. static int16_t TOUCH_TransY(uint16_t _usAdcX, uint16_t _usAdcY)
120. {
121. if (g_tTPParam.CalibPointCount == 2) /* 2点校准 */
122. {
123. /* 类似函数TOUCH_TransX,省略未贴出 */
124. }
125. else /* 4点校准 */
126. {
127. /* 类似函数TOUCH_TransX,省略未贴出 */
128. }
129. }
下面将程序设计中几个关键地方做个阐释:
这里注意g_tTPParam.XYChange = 1情况的处理,之所以会有这种情况,详情看此贴:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=93300 。
电阻触摸的使用主要分为三步:
/*
*********************************************************************************************************
* 函 数 名: bsp_Init
* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
/* 省略未写 */
bsp_InitI2C(); /* 初始化I2C总线 */
TOUCH_InitHard(); /* 初始化触摸芯片,LCD面板型号的检查也在此函数,所以要在函数LCD_InitHard前调用 */
LCD_InitHard(); /* 初始化LCD */
}
/*
*********************************************************************************************************
* 函 数 名: bsp_RunPer1ms
* 功能说明: 该函数每隔1ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些需要周期性处理
* 的事务可以放在此函数。比如:触摸坐标扫描。
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_RunPer1ms(void)
{
TOUCH_Scan(); /* 触摸屏 */
}
TOUCH_DOWN表示按下消息。
TOUCH_MOVE表示触摸移动消息。
TOUCH_RELEASE表示触摸释放消息。
根据这几个消息,用户可以在程序里面判断当前获取的物理坐标值是否在设置的区域内来执行触摸操作。
int16_t tpX, tpY;
uint8_t ucTouch; /* 触摸事件 */
ucTouch = TOUCH_GetKey(&tpX, &tpY); /* 读取触摸事件 */
if (ucTouch != TOUCH_NONE)
{
switch (ucTouch)
{
case TOUCH_DOWN: /* 触笔按下事件 */
/* 在触笔所在位置显示一个小圈 */
if ((tpX > 0) && (tpY > 0))
{
}
break;
case TOUCH_MOVE: /* 触笔移动事件 */
/* 实时刷新触摸ADC采样值和转换后的坐标 */
{
/* 在触笔所在位置显示一个小圈 */
if ((tpX > 0) && (tpY > 0))
{
}
}
break;
case TOUCH_RELEASE: /* 触笔释放事件 */
/* 在触笔所在位置显示一个小圈 */
if ((tpX > 0) && (tpY > 0))
{
}
break;
}
}
电容触摸相比电阻触摸就要简单很多了,因为电容触摸不需要做触摸校准,而且用的是触摸板和触摸芯片一体的,也不需要做寄存器初始化配置,上电后直接读取参数即可。
由于GT811已经停产,这里重点把GT911和FT5X06做个说明。
电容触摸IC是支持多点触摸的,FT5X06支持多达10点触摸同时按下,并提供了I2C和SPI两种
通信接口方式,开发板使用的是I2C通信接口。更多相关知识学习可以在这里下载FT5X06数据手册和应用手册:http://bbs.armfly.com/read.php?tid=16461 。
注意,这个芯片返回的就是实际的坐标值,比如显示屏的分辨率是800*480,那么返回的就是在这个分辨率范围内的实际坐标,然后通过函数TOUCH_PutKey将FT5X06读出的实际坐标值存储到FIFO中即可,具体实现代码如下:
1. /*
2. ******************************************************************************************************
3. * 函 数 名: FT5X06_Scan
4. * 功能说明: 读取触摸数据。读取全部的数据。放在主程序 bsp_Idle()中执行
5. * 形 参: 无
6. * 返 回 值: 无
7. ******************************************************************************************************
8. */
9. void FT5X06_Scan(void)
10. {
11. uint8_t buf[CFG_POINT_READ_BUF];
12. uint8_t i;
13. static uint8_t s_tp_down = 0;
14. uint16_t x, y;
15. static uint16_t x_save, y_save;
16. static uint8_t s_count = 0;
17.
18. if (g_tFT5X06.Enable == 0)
19. {
20. return;
21. }
22.
23. /* 10ms 执行一次 */
24. if (g_tFT5X06.TimerCount < 10)
25. {
26. return;
27. }
28.
29. g_tFT5X06.TimerCount = 0;
30.
31. #if 1 /* 方案1: 检测INT引脚电平. */
32. if (TOUCH_PenInt() == 0)
33. {
34. #else /* 方案2:不用INT引脚,读状态寄存器 */
35. FT5X06_ReadReg(2, buf, 1);
36. if ((buf[0] & 0x07) == 0)
37. {
38. #endif
39. /* 持续按下时,INT电平是脉冲信号。每隔18ms出现1个宽度4ms的高电平。 */
40. if (s_tp_down == 1)
41. {
42. if (++s_count > 2)
43. {
44. s_count = 0;
45. s_tp_down = 0;
46. TOUCH_PutKey(TOUCH_RELEASE, x_save, y_save);
47. }
48. }
49. return;
50. }
51. s_count = 0;
52.
53. /* 有触摸,读取完整的数据 */
54. FT5X06_ReadReg(0, buf, CFG_POINT_READ_BUF);
55.
56. g_tFT5X06.Count = buf[2] & 0x07;
57. if (g_tFT5X06.Count > FT5X06_TOUCH_POINTS)
58. {
59. g_tFT5X06.Count = FT5X06_TOUCH_POINTS;
60. }
61.
62. g_tFT5X06.Count = 0;
63. for (i = 0; i < FT5X06_TOUCH_POINTS; i++)
64. {
65. uint8_t pointid;
66.
67. pointid = (buf[5 + 6*i]) >> 4;
68. if (pointid >= 0x0f)
69. {
70. break;
71. }
72. else
73. {
74. g_tFT5X06.Count++;
75. g_tFT5X06.X[i] = (int16_t)(buf[3 + 6*i] & 0x0F)<<8 | (int16_t)buf[4 + 6*i];
76. g_tFT5X06.Y[i] = (int16_t)(buf[5 + 6*i] & 0x0F)<<8 | (int16_t)buf[6 + 6*i];
77. g_tFT5X06.Event[i] = buf[0x3 + 6*i] >> 6;
78. g_tFT5X06.id[i] = (buf[5 + 6*i])>>4;
79. }
80. }
81.
82. /* 检测按下 */
83. {
84. if (g_tFT5X06.ChipID == 0x55) /* 4.3寸 480 * 272 */
85. {
86. x = g_tFT5X06.Y[0];
87. y = g_tFT5X06.X[0];
88.
89. /* 判断值域 */
90. if (x > 479)
91. {
92. x = 479;
93. }
94.
95. if (y > 271)
96. {
97. y = 271;
98. }
99. }
100. else if (g_tFT5X06.ChipID == 0x0A) /* 5.0寸 800 * 480 */
101. {
102. x = g_tFT5X06.X[0];
103. y = g_tFT5X06.Y[0];
104.
105. /* 判断值域 */
106. if (x > 799)
107. {
108. x = 799;
109. }
110. if (y > 479)
111. {
112. y = 479;
113. }
114. }
115. else /* id == 0x06 表示7寸电容屏(FT芯片) */
116. {
117. x = g_tFT5X06.X[0];
118. y = g_tFT5X06.Y[0];
119.
120. /* 判断值域 */
121. if (x > 799)
122. {
123. x = 799;
124. }
125. if (y > 479)
126. {
127. y = 479;
128. }
129. }
130. }
131.
132. if (s_tp_down == 0)
133. {
134. s_tp_down = 1;
135.
136. TOUCH_PutKey(TOUCH_DOWN, x, y);
137. }
138. else
139. {
140. TOUCH_PutKey(TOUCH_MOVE, x, y);
141. }
142. x_save = x; /* 保存坐标,用于释放事件 */
143. y_save = y;
144.
145. #if 0
146. for (i = 0; i < CFG_POINT_READ_BUF; i++)
147. {
148. printf("%02X ", buf[i]);
149. }
150. printf("\r\n");
151. #endif
152.
153. #if 0 /* 打印5个坐标点数据 */
154. printf("(%5d,%5d,%3d,%3d) ", g_tFT5X06.X[0], g_tFT5X06.Y[0], g_tFT5X06.Event[0],
155. g_tFT5X06.id[0]);
156. printf("(%5d,%5d,%3d,%3d) ", g_tFT5X06.X[1], g_tFT5X06.Y[1], g_tFT5X06.Event[1],
157. g_tFT5X06.id[1]);
158. printf("(%5d,%5d,%3d,%3d) ", g_tFT5X06.X[2], g_tFT5X06.Y[2], g_tFT5X06.Event[2],
159. g_tFT5X06.id[2]);
160. printf("(%5d,%5d,%3d,%3d) ", g_tFT5X06.X[3], g_tFT5X06.Y[3], g_tFT5X06.Event[3],
161. g_tFT5X06.id[3]);
162. printf("(%5d,%5d,%3d,%3d) ", g_tFT5X06.X[4], g_tFT5X06.Y[4], g_tFT5X06.Event[4],
163. g_tFT5X06.id[4]);
164. printf("\r\n");
165. #endif
166. }
下面将程序设计中几个关键地方做个阐释:
GT911支持多达5点触摸同时按下,并提供了I2C通信接口方式,更多相关知识学习可以在这里下载GT911数据手册:http://www.armbbs.cn/forum.php?mod=viewthread&tid=87205 。
GT911的使用基本跟FT5X06是一样(注意,芯片GT911返回的就是实际的坐标值,比如显示屏的分辨率是800*480,那么返回的就是在这个分辨率范围内的实际坐标,跟FT5X06也是一样的),具体实现代码如下(这里没有使用多点触摸功能,仅存了一个触摸值,一般情况下已经够用):
1. /*
2. ******************************************************************************************************
3. * 函 数 名: GT911_Scan
4. * 功能说明: 读取GT911触摸数据。读取全部的数据,需要 720us左右。放在 bsp_Idle()中执行
5. * 形 参: 无
6. * 返 回 值: 无
7. ******************************************************************************************************
8. */
9. void GT911_Scan(void)
10. {
11. uint8_t buf[48];
12. static uint8_t s_tp_down = 0;
13. uint16_t x, y;
14. static uint16_t x_save, y_save;
15. uint8_t clear_flag = 0;
16. static uint8_t s_count = 0;
17.
18. if (g_GT911.Enable == 0)
19. {
20. return;
21. }
22.
23. /* 10ms 执行一次 */
24. if (g_GT911.TimerCount < 10)
25. {
26. return;
27. }
28.
29. g_GT911.TimerCount = 0;
30.
31. #if 1 /* 方案1: 检测INT引脚电平. */
32. if (TOUCH_PenInt() == 0)
33. {
34. #else /* 方案2:不用INT引脚,读状态寄存器 */
35. GT911_ReadReg(GT911_READ_XY_REG, buf, 1);
36. if (buf[0] == 0)
37. {
38. #endif
39. if (s_tp_down == 1)
40. {
41. if (++s_count > 1)
42. {
43. s_count = 0;
44. s_tp_down = 0;
45. TOUCH_PutKey(TOUCH_RELEASE, x_save, y_save);
46. }
47. }
48. return;
49. }
50. s_count = 0;
51.
52. #if 1 /* 一般应用只读1点 */
53. GT911_ReadReg(GT911_READ_XY_REG, buf, 8);
54. #else /* 读5个触摸点 */
55. GT911_ReadReg(GT911_READ_XY_REG, buf, 40);
56. #endif
57.
58. GT911_WriteReg(GT911_READ_XY_REG, &clear_flag, 1); /* 读完坐标后必须写0清除 */
59.
60. /*
61. 0x814E R/W Bufferstatus Large_Detect number of touch points
62. Bit7: Buffer status,1表示坐标(或按键)已经准备好,主控可以读取;0表示未就绪,数据无效。
63. 当主控读取完坐标后,必须通过I2C将此标志(或整个字节)写为0。
64. Bit4: HaveKey, 1表示有按键,0表示无按键(已经松键)。
65. Bit3~0: Number of touch points, 屏上的坐标点个数
66.
67. 0x814F R Point1 track id
68. 0x8150 R Point1Xl 触摸点 1,X 坐标低 8 位
69. 0x8151 R Point1Xh 触摸点 1,X 坐标高 8 位
70. 0x8152 R Point1Yl 触摸点 1,Y 坐标低 8 位
71. 0x8153 R Point1Yh 触摸点 1,Y 坐标高 8 位
72. 0x8154 R Point1 触摸点 1,触摸面积低 8 位
73. 0x8155 R Point1 触摸点 1,触摸面积高 8 位
74. 0x8156 ----
75.
76. 0x8157 R Point2 track id
77. 0x8158 R Point2Xl 触摸点 2,X 坐标低 8 位
78. 0x8159 R Point2Xh 触摸点 2,X 坐标高 8 位
79. 0x815A R Point2Yl 触摸点 2,Y 坐标低 8 位
80. 0x815B R Point2Yh 触摸点 2,Y 坐标高 8 位
81. 0x815C R Point2 触摸点 2,触摸面积低 8 位
82. 0x815D R Point2 触摸点 2,触摸面积高 8 位
83. 0x815E ----
84.
85. 0x815F R Point3 track id
86. 0x8160 R Point3Xl 触摸点 3,X 坐标低 8 位
87. 0x8161 R Point3Xh 触摸点 3,X 坐标高 8 位
88. 0x8162 R Point3Yl 触摸点 3,Y 坐标低 8 位
89. 0x8163 R Point3Yh 触摸点 3,Y 坐标高 8 位
90. 0x8164 R Point3 触摸点 3,触摸面积低 8 位
91. 0x8165 R Point3 触摸点 3,触摸面积高 8 位
92. 0x8166 ----
93.
94. 0x8167 R Point4 track id
95. 0x8168 R Point4Xl 触摸点 4,X 坐标低 8 位
96. 0x8169 R Point4Xh 触摸点 4,X 坐标高 8 位
97. 0x816A R Point4Yl 触摸点 4,Y 坐标低 8 位
98. 0x816B R Point4Yh 触摸点 4,Y 坐标高 8 位
99. 0x816C R Point4 触摸点 4,触摸面积低 8 位
100. 0x816D R Point4 触摸点 4,触摸面积高 8 位
101. 0x816E ----
102.
103. 0x816F R Point5 track id
104. 0x8170 R Point5Xl 触摸点 5,X 坐标低 8 位
105. 0x8171 R Point5Xh 触摸点 5,X 坐标高 8 位
106. 0x8172 R Point5Yl 触摸点 5,Y 坐标低 8 位
107. 0x8173 R Point5Yh 触摸点 5,Y 坐标高 8 位
108. 0x8174 R Point5 触摸点 5,触摸面积低 8 位
109. 0x8175 R Point5 触摸点 5,触摸面积高 8 位
110. 0x8176 --
111.
112. */
113. g_GT911.TouchpointFlag = buf[0];
114. g_GT911.Touchkeystate = buf[1];
115.
116. g_GT911.X0 = ((uint16_t)buf[3] << 8) + buf[2];
117. g_GT911.Y0 = ((uint16_t)buf[5] << 8) + buf[4];
118. g_GT911.P0 = ((uint16_t)buf[7] << 8) + buf[6];
119.
120. #if 0 /* 其余4点一般不用 */
121. g_GT911.X1 = ((uint16_t)buf[9] << 8) + buf[10];
122. g_GT911.Y1 = ((uint16_t)buf[11] << 8) + buf[12];
123. g_GT911.P1 = ((uint16_t)buf[13] << 8) + buf[14];
124.
125. g_GT911.X2 = ((uint16_t)buf[17] << 8) + buf[16];
126. g_GT911.Y2 = ((uint16_t)buf[19] << 8) + buf[18];
127. g_GT911.P2 = ((uint16_t)buf[21] << 8) + buf[20];
128.
129. g_GT911.X3 = ((uint16_t)buf[24] << 8) + buf[23];
130. g_GT911.Y3 = ((uint16_t)buf[26] << 8) + buf[25];
131. g_GT911.P3 = ((uint16_t)buf[28] << 8) + buf[27];
132.
133. g_GT911.X4 = ((uint16_t)buf[31] << 8) + buf[30];
134. g_GT911.Y4 = ((uint16_t)buf[33] << 8) + buf[32];
135. g_GT911.P4 = ((uint16_t)buf[35] << 8) + buf[34];
136. #endif
137.
138. /* 检测按下 */
139. {
140. /* 坐标转换 :
141. 电容触摸板左下角是 (0,0); 右上角是 (479,799)
142. 需要转到LCD的像素坐标 (左上角是 (0,0), 右下角是 (799,479)
143. */
144. {
145. x = g_GT911.X0;
146. y = g_GT911.Y0;
147.
148. if (x > 799)
149. {
150. x = 799;
151. }
152.
153. if (y > 479)
154. {
155. y = 479;
156. }
157. }
158. }
159.
160. if (s_tp_down == 0)
161. {
162. s_tp_down = 1;
163.
164. TOUCH_PutKey(TOUCH_DOWN, x, y);
165. }
166. else
167. {
168. TOUCH_PutKey(TOUCH_MOVE, x, y);
169. }
170. x_save = x; /* 保存坐标,用于释放事件 */
171. y_save = y;
172.
173. #if 0
174. {
175. uint8_t i;
176.
177. for (i = 0; i < 34; i++)
178. {
179. printf("%02X ", buf[i]);
180. }
181. printf("\r\n");
182.
183. printf("(%5d,%5d,%3d) ", g_GT911.X0, g_GT911.Y0, g_GT911.P0);
184. printf("(%5d,%5d,%3d) ", g_GT911.X1, g_GT911.Y1, g_GT911.P1);
185. printf("(%5d,%5d,%3d) ", g_GT911.X2, g_GT911.Y2, g_GT911.P2);
186. printf("(%5d,%5d,%3d) ", g_GT911.X3, g_GT911.Y3, g_GT911.P3);
187. printf("(%5d,%5d,%3d) ", x, y, g_GT911.P4);
188. printf("\r\n");
189. }
190. #endif
191. }
下面将程序设计中几个关键地方做个阐释:
电容触摸的使用主要分为三步:
/*
*********************************************************************************************************
* 函 数 名: bsp_Init
* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
/* 省略未写 */
bsp_InitI2C(); /* 初始化I2C总线 */
TOUCH_InitHard(); /* 初始化触摸芯片,LCD面板型号的检查也在此函数,所以要在函数LCD_InitHard前调用 */
LCD_InitHard(); /* 初始化LCD */
}
/*
*********************************************************************************************************
* 函 数 名: bsp_RunPer1ms
* 功能说明: 该函数每隔1ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些需要周期性处理
* 的事务可以放在此函数。比如:触摸坐标扫描。
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_RunPer1ms(void)
{
TOUCH_Scan(); /* 触摸屏 */
}
/*
*********************************************************************************************************
* 函 数 名: bsp_Idle
* 功能说明: 空闲时执行的函数。一般主程序在for和while循环程序体中需要插入 CPU_IDLE() 宏来调用本函数。
* 本函数缺省为空操作。用户可以添加喂狗、设置CPU进入休眠模式的功能。
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_Idle(void)
{
/* --- 喂狗 */
/* --- 让CPU进入休眠,由Systick定时中断唤醒或者其他中断唤醒 */
/* 例如 emWin 图形库,可以插入图形库需要的轮询函数 */
//GUI_Exec();
/* 例如 uIP 协议,可以插入uip轮询函数 */
TOUCH_CapScan();
}
TOUCH_DOWN表示按下消息。
TOUCH_MOVE表示触摸移动消息。
TOUCH_RELEASE表示触摸释放消息。
根据这几个消息,用户可以在程序里面判断当前获取的物理坐标值是否在设置的区域内来执行触摸操作。
int16_t tpX, tpY;
uint8_t ucTouch; /* 触摸事件 */
ucTouch = TOUCH_GetKey(&tpX, &tpY); /* 读取触摸事件 */
if (ucTouch != TOUCH_NONE)
{
switch (ucTouch)
{
case TOUCH_DOWN: /* 触笔按下事件 */
/* 在触笔所在位置显示一个小圈 */
if ((tpX > 0) && (tpY > 0))
{
}
break;
case TOUCH_MOVE: /* 触笔移动事件 */
/* 实时刷新触摸ADC采样值和转换后的坐标 */
{
/* 在触笔所在位置显示一个小圈 */
if ((tpX > 0) && (tpY > 0))
{
}
}
break;
case TOUCH_RELEASE: /* 触笔释放事件 */
/* 在触笔所在位置显示一个小圈 */
if ((tpX > 0) && (tpY > 0))
{
}
break;
}
}
由于V7开发板要做不同显示屏的自适应,所以关联了多个文件。自适应方法也比较简单,因为触摸芯片GT911,GT811,FT5X06和STMPE811都是I2C接口,通过I2C地址就区分开了,具体代码如下:
1. /*
2. ******************************************************************************************************
3. * 函 数 名: bsp_DetectLcdType
4. * 功能说明: 通过I2C触摸芯片,识别LCD模组类型。结果存放在全局变量 g_LcdType 和 g_TouchType
5. * 形 参: 无
6. * 返 回 值: 无
7. ******************************************************************************************************
8. */
9. void bsp_DetectLcdType(void)
10. {
11. uint8_t i;
12.
13. g_TouchType = 0xFF;
14. g_LcdType = 0xFF;
15.
16. bsp_DelayUS(5000);
17.
18. touch_printf("正在识别触摸屏型号\r\n");
19.
20. /* 50ms,等待GT811复位就绪,才能探测GT811芯片 ID */
21. for (i = 0; i < 5; i++)
22. {
23. /*
24. GT811电容触摸板和GT911的I2C地址相同
25. 一般就 0x28 、 0xBA 两种。
26. 通过读取触摸IC的芯片ID来识别。
27. */
28. if (i2c_CheckDevice(0x28) == 0)
29. {
30. uint32_t id;
31.
32. bsp_DelayUS(500);
33. g_GT911.i2c_addr = 0x28;
34. id = GT911_ReadID();
35. if (id == 0x00313139)
36. {
37. g_GT911.i2c_addr = 0x28;
38. g_TouchType = CT_GT911;
39. g_LcdType = LCD_70_800X480;
40. touch_printf("检测到7.0寸电容触摸屏GT911(0x28) 800x480\r\n");
41. }
42. else /* GT811 */
43. {
44. g_GT811.i2c_addr = 0x28;
45. g_TouchType = CT_GT811;
46. g_LcdType = LCD_70_800X480;
47. touch_printf("检测到7.0寸电容触摸屏GT811(0x28) 800x480\r\n");
48. }
49. break;
50. }
51.
52. if (i2c_CheckDevice(0xBA) == 0)
53. {
54. uint32_t id;
55.
56. bsp_DelayUS(500);
57. g_GT911.i2c_addr = 0xBA;
58. id = GT911_ReadID();
59. if (id == 0x00313139)
60. {
61. g_GT911.i2c_addr = 0xBA;
62. g_TouchType = CT_GT911;
63. g_LcdType = LCD_70_800X480;
64. touch_printf("检测到7.0寸电容触摸屏GT911(0xBA) 800x480\r\n");
65. }
66. else /* GT811 */
67. {
68. g_GT811.i2c_addr = 0xBA;
69. g_TouchType = CT_GT811;
70. g_LcdType = LCD_70_800X480;
71. touch_printf("检测到7.0寸电容触摸屏GT811(0xBA) 800x480\r\n");
72. }
73. break;
74. }
75.
76. /* FT系列电容触摸: 4.3寸id = 0x55 5.0寸id = 0x0A 7.0寸id = 0x06 */
77. if (i2c_CheckDevice(FT5X06_I2C_ADDR) == 0)
78. {
79. uint8_t id;
80.
81. bsp_DelayUS(50000); /* 延迟50ms */
82. id = FT5X06_ReadID();
83. if (id == 0x55)
84. {
85. g_TouchType = CT_FT5X06;
86. g_LcdType = LCD_43_480X272;
87. touch_printf("检测到4.3寸电容触摸屏\r\n");
88. }
89. else if (id == 0x0A)
90. {
91. g_TouchType = CT_FT5X06;
92. g_LcdType = LCD_50_800X480;
93. touch_printf("检测到5.0寸电容触摸屏\r\n");
94. }
95. else /* id == 0x06 表示7寸电容屏(FT芯片) */
96. {
97. g_TouchType = CT_FT5X06;
98. g_LcdType = LCD_70_800X480;
99. touch_printf("检测到7.0寸电容触摸屏FT\r\n");
100. }
101. break;
102. }
103.
104. /* 电阻触摸板 */
105. if (i2c_CheckDevice(STMPE811_I2C_ADDRESS) == 0)
106. {
107. /*
108. 0 = 4.3寸屏(480X272)
109. 1 = 5.0寸屏(480X272)
110. 2 = 5.0寸屏(800X480)
111. 3 = 7.0寸屏(800X480)
112. 4 = 7.0寸屏(1024X600)
113. 5 = 3.5寸屏(480X320)
114. */
115. uint8_t id;
116.
117. g_TouchType = CT_STMPE811; /* 触摸类型 */
118.
119. STMPE811_InitHard(); /* 必须先配置才能读取ID */
120.
121. id = STMPE811_ReadIO(); /* 识别LCD硬件类型 */
122.
123. touch_printf("检测到电阻触摸屏, id = %d\r\n", id);
124. switch (id)
125. {
126. case 0:
127. g_LcdType = LCD_43_480X272;
128. break;
129.
130. case 1:
131. g_LcdType = LCD_50_480X272;
132. break;
133.
134. case 2:
135. g_LcdType = LCD_50_800X480;
136. break;
137.
138. case 3:
139. g_LcdType = LCD_70_800X480;
140. break;
141.
142. case 4:
143. g_LcdType = LCD_70_1024X600;
144. break;
145.
146. case 5:
147. g_LcdType = LCD_35_480X320;
148. break;
149.
150. default:
151. g_LcdType = LCD_35_480X320;
152. break;
153. }
154. break;
155. }
156.
157. bsp_DelayMS(10);
158. }
159.
160. if (i == 5)
161. {
162. touch_printf("未识别出显示模块\r\n");
163. }
164. }
下面将程序设计中几个关键地方做个阐释:
LCD驱动的移植与第51章51.7小节相同,这里重点说下触摸部分的移植。
通过前面的讲解,移植触摸驱动到自己的板子上,最简单的办法是将开发板上与触摸相关的文件全部移植过来,然后在这些文件的基础上进行修改。下面分两种情况进行说明:
当然,如果大家不想用开发板实现的方案,想自己重新实现一个,也是没问题的。
通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:
第1阶段,上电启动阶段:
第2阶段,进入main函数:
配套例子:
V7-034_LCD的电阻触摸和电容触摸(电阻触摸支持2点和4点校准)
实验目的:
实验内容:
LCD界面显示效果如下:
上电后串口打印的信息:
波特率 115200,数据位 8,奇偶校验位无,停止位 1
程序设计:
系统栈大小分配:
RAM空间用的DTCM:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/*
*********************************************************************************************************
* 函 数 名: bsp_Init
* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
/* 配置MPU */
MPU_Config();
/* 使能L1 Cache */
CPU_CACHE_Enable();
/*
STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
- 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
- 设置NVIV优先级分组为4。
*/
HAL_Init();
/*
配置系统时钟到400MHz
- 切换使用HSE。
- 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
*/
SystemClock_Config();
/*
Event Recorder:
- 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
- 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章
*/
#if Enable_EventRecorder == 1
/* 初始化EventRecorder并开启 */
EventRecorderInitialize(EventRecordAll, 1U);
EventRecorderStart();
#endif
bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
bsp_InitTimer(); /* 初始化滴答定时器 */
bsp_InitUart(); /* 初始化串口 */
bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */
bsp_InitLed(); /* 初始化LED */
bsp_InitI2C(); /* 初始化I2C总线 */
TOUCH_InitHard(); /* 初始化触摸芯片,LCD面板型号的检查也在此函数,所以要在函数LCD_InitHard前调用 */
LCD_InitHard(); /* 初始化LCD */
}
MPU配置和Cache配置:
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区和SDRAM。由于SDRAM要用于LCD的显存,方便起见,直接将其配置为WT模式。
/*
*********************************************************************************************************
* 函 数 名: MPU_Config
* 功能说明: 配置MPU
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void MPU_Config( void )
{
MPU_Region_InitTypeDef MPU_InitStruct;
/* 禁止 MPU */
HAL_MPU_Disable();
/* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x24000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x60000000;
MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* 配置SDRAM的MPU属性为Write through, read allocate,no write allocate */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0xC0000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_32MB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER2;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/*使能 MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
/*
*********************************************************************************************************
* 函 数 名: CPU_CACHE_Enable
* 功能说明: 使能L1 Cache
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
/* 使能 I-Cache */
SCB_EnableICache();
/* 使能 D-Cache */
SCB_EnableDCache();
}
主功能:
主程序实现如下操作:
/*
*********************************************************************************************************
* 函 数 名: main
* 功能说明: c程序入口
* 形 参: 无
* 返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{
uint16_t ucBright; /* 背光亮度(0-255) */
uint8_t ucKeyCode; /* 按键代码 */
uint8_t fRefresh; /* 刷屏请求标志,1表示需要刷新 */
FONT_T tFont; /* 定义一个字体结构体变量,用于设置字体参数 */
char buf[64];
uint16_t usAdcX, usAdcY;
int16_t tpX, tpY;
uint8_t ucTouch; /* 触摸事件 */
/* 设置字体参数 */
{
tFont.FontCode = FC_ST_16; /* 字体代码 16点阵 */
tFont.FrontColor = CL_WHITE; /* 字体颜色 */
tFont.BackColor = CL_BLUE; /* 文字背景颜色 */
tFont.Space = 0; /* 文字间距,单位 = 像素 */
}
bsp_Init(); /* 硬件初始化 */
PrintfLogo(); /* 打印例程名称和版本等信息 */
PrintfHelp(); /* 打印操作提示 */
/* 延迟200ms再点亮背光,避免瞬间高亮 */
bsp_DelayMS(200);
DispFirstPage(); /* 显示第1页 */
/* 界面整体显示完毕后,再打开背光,设置为缺省亮度 */
bsp_DelayMS(100);
ucBright = BRIGHT_DEFAULT;
LCD_SetBackLight(ucBright);
bsp_StartAutoTimer(0, 200); /* 启动1个200ms的自动重装的定时器,软件定时器0 */
/* 进入主程序循环体 */
fRefresh = 1;
while (1)
{
bsp_Idle();
/* 判断软件定时器0是否超时 */
if(bsp_CheckTimer(0))
{
/* 每隔200ms 进来一次 */
bsp_LedToggle(2);
}
if (fRefresh)
{
fRefresh = 0;
/* 实时刷新触摸ADC采样值和转换后的坐标 */
{
/* 读取并显示当前X轴和Y轴的ADC采样值 */
usAdcX = TOUCH_ReadAdcX();
usAdcY = TOUCH_ReadAdcY();
sprintf(buf, "触摸ADC值 X = %4d, Y = %4d ", usAdcX, usAdcY);
LCD_DispStr(5, 40, buf, &tFont);
/* 读取并显示当前触摸坐标 */
tpX = TOUCH_GetX();
tpY = TOUCH_GetY();
sprintf(buf, "触摸坐标 X = %4d, Y = %4d ", tpX, tpY);
LCD_DispStr(5, 60, buf, &tFont);
/* 在触笔所在位置显示一个小圈 */
if ((tpX > 0) && (tpY > 0))
{
LCD_DrawCircle(tpX, tpY, 2, CL_YELLOW);
}
}
/* 在屏幕边沿绘制2个矩形框(用于检测面板边缘像素是否正常) */
LCD_DrawRect(0, 0, LCD_GetHeight(), LCD_GetWidth(), CL_WHITE);
LCD_DrawRect(2, 2, LCD_GetHeight() - 4, LCD_GetWidth() - 4, CL_YELLOW);
/* 显示背光值 */
sprintf(buf, "当前背光亮度: %3d", ucBright);
LCD_DispStr(5, 80, buf, &tFont);
}
ucTouch = TOUCH_GetKey(&tpX, &tpY); /* 读取触摸事件 */
if (ucTouch != TOUCH_NONE)
{
switch (ucTouch)
{
case TOUCH_DOWN: /* 触笔按下事件 */
/* 在触笔所在位置显示一个小圈 */
if ((tpX > 0) && (tpY > 0))
{
LCD_DrawCircle(tpX, tpY, 3, CL_RED);
}
break;
case TOUCH_MOVE: /* 触笔移动事件 */
/* 实时刷新触摸ADC采样值和转换后的坐标 */
{
/* 读取并显示当前X轴和Y轴的ADC采样值 */
usAdcX = TOUCH_ReadAdcX();
usAdcY = TOUCH_ReadAdcY();
sprintf(buf, "触摸ADC值 X = %4d, Y = %4d ", usAdcX, usAdcY);
LCD_DispStr(5, 40, buf, &tFont);
/* 读取并显示当前触摸坐标 */
//tpX = TOUCH_GetX();
//tpY = TOUCH_GetY();
sprintf(buf, "触摸坐标 X = %4d, Y = %4d ", tpX, tpY);
LCD_DispStr(5, 60, buf, &tFont);
/* 在触笔所在位置显示一个小圈 */
if ((tpX > 0) && (tpY > 0))
{
LCD_DrawCircle(tpX, tpY, 2, CL_YELLOW);
}
}
break;
case TOUCH_RELEASE: /* 触笔释放事件 */
/* 在触笔所在位置显示一个小圈 */
if ((tpX > 0) && (tpY > 0))
{
LCD_DrawCircle(tpX, tpY, 4, CL_WHITE);
}
break;
}
}
ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
if (ucKeyCode != KEY_NONE)
{
/* 有键按下 */
switch (ucKeyCode)
{
case JOY_DOWN_L: /* 摇杆LEFT键按下, 2点触摸校准 */
TOUCH_Calibration(2);
DispFirstPage();
fRefresh = 1; /* 请求刷新LCD */
break;
case JOY_DOWN_OK: /* 摇杆OK键,清屏 */
DispFirstPage();
fRefresh = 1; /* 请求刷新LCD */
break;
case JOY_DOWN_U: /* 摇杆UP键按下 */
ucBright += BRIGHT_STEP;
if (ucBright > BRIGHT_MAX)
{
ucBright = BRIGHT_MAX;
}
LCD_SetBackLight(ucBright);
fRefresh = 1; /* 请求刷新LCD */
break;
case JOY_DOWN_D: /* 摇杆DOWN键按下 */
if (ucBright < BRIGHT_STEP)
{
ucBright = 0;
}
else
{
ucBright -= BRIGHT_STEP;
}
LCD_SetBackLight(ucBright);
fRefresh = 1; /* 请求刷新LCD */
break;
default:
break;
}
}
}
}
LCD主界面显示内容如下:
/*
*********************************************************************************************************
* 函 数 名: DispFirstPage
* 功能说明: 显示操作提示
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
static void DispFirstPage(void)
{
FONT_T tFont; /* 定义一个字体结构体变量,用于设置字体参数 */
uint16_t y; /* Y坐标 */
uint16_t usLineCap; /* 行高 */
char buf[50];
LCD_ClrScr(CL_BLUE); /* 清屏,背景蓝色 */
/* 设置字体属性 */
tFont.FontCode = FC_ST_16; /* 字体选择宋体16点阵,高16x宽15) */
tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */
tFont.BackColor = CL_MASK; /* 文字背景颜色,透明 */
tFont.Space = 0; /* 字符水平间距, 单位 = 像素 */
y = 4;
usLineCap = 18; /* 行间距 */
LCD_DispStr(5, y, "安富莱STM32-V7开发板 www.armfly.com", &tFont);
y += usLineCap;
LCD_DispStr(5, y, "电阻屏和电容屏实验,电容屏无需校准,电阻需要校准", &tFont);
y += 5 * usLineCap;
LCD_DispStr(30, y, "操作提示:", &tFont);
y += usLineCap;
LCD_DispStr(50, y, "摇杆上键 = 增加背光亮度", &tFont);
y += usLineCap;
LCD_DispStr(50, y, "摇杆下键 = 降低背光亮度", &tFont);
y += usLineCap;
LCD_DispStr(50, y, "摇杆左键 = 电阻屏触摸校准", &tFont);
y += usLineCap;
LCD_DispStr(50, y, "摇杆OK键 = 清屏", &tFont);
y += 2 * usLineCap;
/* 显示TFT控制器型号和屏幕分辨率 */
//LCD_GetChipDescribe(buf); /* 读取TFT驱动芯片型号 */
if (g_TouchType == CT_GT811)
{
strcpy(buf, "STM32H743 + GT811");
}
else if (g_TouchType == CT_GT911)
{
strcpy(buf, "STM32H743 + GT911");
}
else if (g_TouchType == CT_FT5X06)
{
strcpy(buf, "STM32H743 + FT5X06");
}
else if (g_TouchType == CT_STMPE811)
{
strcpy(buf, "STM32H743 + STMPE811");
}
else
{
strcpy(buf, "STM32H743 + NoTouch");
}
sprintf(&buf[strlen(buf)], " %d x %d", LCD_GetWidth(), LCD_GetHeight());
LCD_DispStr(5, y, (char *)buf, &tFont);
}
配套例子:
V7-034_LCD的电阻触摸和电容触摸(电阻触摸支持2点和4点校准)
实验目的:
实验内容:
LCD界面显示效果如下:
上电后串口打印的信息:
波特率 115200,数据位 8,奇偶校验位无,停止位 1
程序设计:
系统栈大小分配:
RAM空间用的DTCM:
硬件外设初始化
硬件外设的初始化是在 bsp.c 文件实现:
/*
*********************************************************************************************************
* 函 数 名: bsp_Init
* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
/* 配置MPU */
MPU_Config();
/* 使能L1 Cache */
CPU_CACHE_Enable();
/*
STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
- 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
- 设置NVIV优先级分组为4。
*/
HAL_Init();
/*
配置系统时钟到400MHz
- 切换使用HSE。
- 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
*/
SystemClock_Config();
/*
Event Recorder:
- 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
- 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章
*/
#if Enable_EventRecorder == 1
/* 初始化EventRecorder并开启 */
EventRecorderInitialize(EventRecordAll, 1U);
EventRecorderStart();
#endif
bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
bsp_InitTimer(); /* 初始化滴答定时器 */
bsp_InitUart(); /* 初始化串口 */
bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */
bsp_InitLed(); /* 初始化LED */
bsp_InitI2C(); /* 初始化I2C总线 */
TOUCH_InitHard(); /* 初始化触摸芯片,LCD面板型号的检查也在此函数,所以要在函数LCD_InitHard前调用 */
LCD_InitHard(); /* 初始化LCD */
}
MPU配置和Cache配置:
数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区和SDRAM。由于SDRAM要用于LCD的显存,方便起见,直接将其配置为WT模式。
/*
*********************************************************************************************************
* 函 数 名: MPU_Config
* 功能说明: 配置MPU
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void MPU_Config( void )
{
MPU_Region_InitTypeDef MPU_InitStruct;
/* 禁止 MPU */
HAL_MPU_Disable();
/* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x24000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x60000000;
MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* 配置SDRAM的MPU属性为Write through, read allocate,no write allocate */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0xC0000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_32MB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER2;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/*使能 MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
/*
*********************************************************************************************************
* 函 数 名: CPU_CACHE_Enable
* 功能说明: 使能L1 Cache
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
/* 使能 I-Cache */
SCB_EnableICache();
/* 使能 D-Cache */
SCB_EnableDCache();
}
主功能:
主程序实现如下操作:
/*
*********************************************************************************************************
* 函 数 名: main
* 功能说明: c程序入口
* 形 参: 无
* 返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{
uint16_t ucBright; /* 背光亮度(0-255) */
uint8_t ucKeyCode; /* 按键代码 */
uint8_t fRefresh; /* 刷屏请求标志,1表示需要刷新 */
FONT_T tFont; /* 定义一个字体结构体变量,用于设置字体参数 */
char buf[64];
uint16_t usAdcX, usAdcY;
int16_t tpX, tpY;
uint8_t ucTouch; /* 触摸事件 */
/* 设置字体参数 */
{
tFont.FontCode = FC_ST_16; /* 字体代码 16点阵 */
tFont.FrontColor = CL_WHITE; /* 字体颜色 */
tFont.BackColor = CL_BLUE; /* 文字背景颜色 */
tFont.Space = 0; /* 文字间距,单位 = 像素 */
}
bsp_Init(); /* 硬件初始化 */
PrintfLogo(); /* 打印例程名称和版本等信息 */
PrintfHelp(); /* 打印操作提示 */
/* 延迟200ms再点亮背光,避免瞬间高亮 */
bsp_DelayMS(200);
DispFirstPage(); /* 显示第1页 */
/* 界面整体显示完毕后,再打开背光,设置为缺省亮度 */
bsp_DelayMS(100);
ucBright = BRIGHT_DEFAULT;
LCD_SetBackLight(ucBright);
bsp_StartAutoTimer(0, 200); /* 启动1个200ms的自动重装的定时器,软件定时器0 */
/* 进入主程序循环体 */
fRefresh = 1;
while (1)
{
bsp_Idle();
/* 判断软件定时器0是否超时 */
if(bsp_CheckTimer(0))
{
/* 每隔200ms 进来一次 */
bsp_LedToggle(2);
}
if (fRefresh)
{
fRefresh = 0;
/* 实时刷新触摸ADC采样值和转换后的坐标 */
{
/* 读取并显示当前X轴和Y轴的ADC采样值 */
usAdcX = TOUCH_ReadAdcX();
usAdcY = TOUCH_ReadAdcY();
sprintf(buf, "触摸ADC值 X = %4d, Y = %4d ", usAdcX, usAdcY);
LCD_DispStr(5, 40, buf, &tFont);
/* 读取并显示当前触摸坐标 */
tpX = TOUCH_GetX();
tpY = TOUCH_GetY();
sprintf(buf, "触摸坐标 X = %4d, Y = %4d ", tpX, tpY);
LCD_DispStr(5, 60, buf, &tFont);
/* 在触笔所在位置显示一个小圈 */
if ((tpX > 0) && (tpY > 0))
{
LCD_DrawCircle(tpX, tpY, 2, CL_YELLOW);
}
}
/* 在屏幕边沿绘制2个矩形框(用于检测面板边缘像素是否正常) */
LCD_DrawRect(0, 0, LCD_GetHeight(), LCD_GetWidth(), CL_WHITE);
LCD_DrawRect(2, 2, LCD_GetHeight() - 4, LCD_GetWidth() - 4, CL_YELLOW);
/* 显示背光值 */
sprintf(buf, "当前背光亮度: %3d", ucBright);
LCD_DispStr(5, 80, buf, &tFont);
}
ucTouch = TOUCH_GetKey(&tpX, &tpY); /* 读取触摸事件 */
if (ucTouch != TOUCH_NONE)
{
switch (ucTouch)
{
case TOUCH_DOWN: /* 触笔按下事件 */
/* 在触笔所在位置显示一个小圈 */
if ((tpX > 0) && (tpY > 0))
{
LCD_DrawCircle(tpX, tpY, 3, CL_RED);
}
break;
case TOUCH_MOVE: /* 触笔移动事件 */
/* 实时刷新触摸ADC采样值和转换后的坐标 */
{
/* 读取并显示当前X轴和Y轴的ADC采样值 */
usAdcX = TOUCH_ReadAdcX();
usAdcY = TOUCH_ReadAdcY();
sprintf(buf, "触摸ADC值 X = %4d, Y = %4d ", usAdcX, usAdcY);
LCD_DispStr(5, 40, buf, &tFont);
/* 读取并显示当前触摸坐标 */
//tpX = TOUCH_GetX();
//tpY = TOUCH_GetY();
sprintf(buf, "触摸坐标 X = %4d, Y = %4d ", tpX, tpY);
LCD_DispStr(5, 60, buf, &tFont);
/* 在触笔所在位置显示一个小圈 */
if ((tpX > 0) && (tpY > 0))
{
LCD_DrawCircle(tpX, tpY, 2, CL_YELLOW);
}
}
break;
case TOUCH_RELEASE: /* 触笔释放事件 */
/* 在触笔所在位置显示一个小圈 */
if ((tpX > 0) && (tpY > 0))
{
LCD_DrawCircle(tpX, tpY, 4, CL_WHITE);
}
break;
}
}
ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
if (ucKeyCode != KEY_NONE)
{
/* 有键按下 */
switch (ucKeyCode)
{
case JOY_DOWN_L: /* 摇杆LEFT键按下, 2点触摸校准 */
TOUCH_Calibration(2);
DispFirstPage();
fRefresh = 1; /* 请求刷新LCD */
break;
case JOY_DOWN_OK: /* 摇杆OK键,清屏 */
DispFirstPage();
fRefresh = 1; /* 请求刷新LCD */
break;
case JOY_DOWN_U: /* 摇杆UP键按下 */
ucBright += BRIGHT_STEP;
if (ucBright > BRIGHT_MAX)
{
ucBright = BRIGHT_MAX;
}
LCD_SetBackLight(ucBright);
fRefresh = 1; /* 请求刷新LCD */
break;
case JOY_DOWN_D: /* 摇杆DOWN键按下 */
if (ucBright < BRIGHT_STEP)
{
ucBright = 0;
}
else
{
ucBright -= BRIGHT_STEP;
}
LCD_SetBackLight(ucBright);
fRefresh = 1; /* 请求刷新LCD */
break;
default:
break;
}
}
}
}
LCD主界面显示内容如下:
/*
*********************************************************************************************************
* 函 数 名: DispFirstPage
* 功能说明: 显示操作提示
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
static void DispFirstPage(void)
{
FONT_T tFont; /* 定义一个字体结构体变量,用于设置字体参数 */
uint16_t y; /* Y坐标 */
uint16_t usLineCap; /* 行高 */
char buf[50];
LCD_ClrScr(CL_BLUE); /* 清屏,背景蓝色 */
/* 设置字体属性 */
tFont.FontCode = FC_ST_16; /* 字体选择宋体16点阵,高16x宽15) */
tFont.FrontColor = CL_WHITE; /* 字体颜色设置为白色 */
tFont.BackColor = CL_MASK; /* 文字背景颜色,透明 */
tFont.Space = 0; /* 字符水平间距, 单位 = 像素 */
y = 4;
usLineCap = 18; /* 行间距 */
LCD_DispStr(5, y, "安富莱STM32-V7开发板 www.armfly.com", &tFont);
y += usLineCap;
LCD_DispStr(5, y, "电阻屏和电容屏实验,电容屏无需校准,电阻需要校准", &tFont);
y += 5 * usLineCap;
LCD_DispStr(30, y, "操作提示:", &tFont);
y += usLineCap;
LCD_DispStr(50, y, "摇杆上键 = 增加背光亮度", &tFont);
y += usLineCap;
LCD_DispStr(50, y, "摇杆下键 = 降低背光亮度", &tFont);
y += usLineCap;
LCD_DispStr(50, y, "摇杆左键 = 电阻屏触摸校准", &tFont);
y += usLineCap;
LCD_DispStr(50, y, "摇杆OK键 = 清屏", &tFont);
y += 2 * usLineCap;
/* 显示TFT控制器型号和屏幕分辨率 */
//LCD_GetChipDescribe(buf); /* 读取TFT驱动芯片型号 */
if (g_TouchType == CT_GT811)
{
strcpy(buf, "STM32H743 + GT811");
}
else if (g_TouchType == CT_GT911)
{
strcpy(buf, "STM32H743 + GT911");
}
else if (g_TouchType == CT_FT5X06)
{
strcpy(buf, "STM32H743 + FT5X06");
}
else if (g_TouchType == CT_STMPE811)
{
strcpy(buf, "STM32H743 + STMPE811");
}
else
{
strcpy(buf, "STM32H743 + NoTouch");
}
sprintf(&buf[strlen(buf)], " %d x %d", LCD_GetWidth(), LCD_GetHeight());
LCD_DispStr(5, y, (char *)buf, &tFont);
}
本章节为大家讲解的电阻触摸方案和电容触摸方案都是经过实战总结的,实际项目中比较实用。