上次我们使用的那块2.4寸ILI9341芯片LCD是带触摸的,触摸驱动芯片为xpt2046。这次我们来看如何使用stm32获取触摸屏的信息。
XPT2046
xpt2046是一款4线制电阻式触摸屏控制器芯片。它的核心功能是将触摸屏上的物理按压位置转换成数字信号,以便单片机读取和处理。与单片机的接口主要由以下几个引脚
- VCC: 电源正极
- GND: 电源地
- CLK:SPI时钟信号,在我使用的板子上为和LCD的时钟区分,标记为T_CLK
- DIN:SPI数据输入,接受单片机来的控制命令,在我使用的板子上对应的是MOSI
- DOUT: SPI数据输出,向单片机发送转换后的数字数据,在我使用的板子上对应的是MISO
- PEN:接触中断信号
- CS: 片选,低电平有效,我使用板子上为了和LCD的CS区分,标记为T_CS

同样我们使用STM32F103RB,将引脚做以下连接
- T_CLK:PB3
- MISO:PB4
- MOSI:PB5
- T_CS: 连接到PC5
- PEN: 连接到PC0
并且在STM32 CubeMX中做以下操作
- 将SPI1设置为Full Duplex Master模式,对应PB3、PB4、PB5的设置
- 将PC5设置为GPIO_OUTPUT, 并命名为T_CS
- 将PC0设置为GPIO_EXTI0,并且GPIO mode设置为External Interrupt Mode with Falling edge trigger detection(下降沿触发), 并命名为T_PEN_INT
- 在NVIC设置中,确保EXTI line0 interrupt 被勾选上

我们就可以生成代码并实现相关驱动的代码了,部分关键代码如下所示:
#include "touch_xpt2046.h"
#define SAMPLE_TIMES 10 // 采样次数,因为触摸点会漂移,所以我们每次需要采样多次,然后取平均值返回
uint16_t Touch_Read_Data(uint8_t cmd) {
uint8_t txData[3] = {cmd, 0, 0};
uint8_t rxData[3] = {0}; // 这里rxData与rxData用同一个数组也是可以的
__disable_irq(); // 在操作读取数据的时候需要禁止中断
HAL_GPIO_WritePin(TOUCH_CS_PORT, TOUCH_CS_PIN, GPIO_PIN_RESET); // 片选使能
HAL_SPI_TransmitReceive(&TOUCH_SPI_HANDLE, txData, rxData, 3, 1000);
HAL_GPIO_WritePin(TOUCH_CS_PORT, TOUCH_CS_PIN, GPIO_PIN_SET);
__enable_irq();
uint16_t data = (rxData[1] << 8) | rxData[2]; //第一个字节是发送字节,可以丢弃,第二第三个字节需要合并
return data >> 3; // 返回数据是12位的,最后3位无效值,丢弃
}
// 总是长边为x, 短边为y,需要根据实际情况转换为屏幕坐标
void Touch_Get_Pos(uint16_t* x, uint16_t* y) {
uint16_t x_sum = 0, y_sum = 0;
for (int i = 0; i < SAMPLE_TIMES; i++) { // 多次采样
x_sum += Touch_Read_Data(0x90); // 0x90为读取x坐标的命令
y_sum += Touch_Read_Data(0xD0); // 0xD0为读取y坐标的命令
}
*x = x_sum/SAMPLE_TIMES;
*y = y_sum/SAMPLE_TIMES;
}
在实际应用中,还需要根据屏幕的朝向进行坐标转换。
在main.c中,添加全局变量touch_screen_pressed,初始化为false,并且添加中断处理函数
bool touch_screen_pressed = false;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == T_PEN_INT_Pin) {
if (!touch_screen_pressed) {
log_write(LOG_LEVEL_DEBUG, "Touch screen pressed");
touch_screen_pressed = true;
}
}
}
在main.c的主循环中
while (1)
{
if (touch_screen_pressed) {
uint16_t x = 0;
uint16_t y = 0;
Touch_Get_Pos(&x, &y);
if (x == 4095 || y == 0) { // 当x为4905或y为0的时候,表示触摸结束
touch_screen_pressed = false;
log_write(LOG_LEVEL_DEBUG, "touch unpressed");
} else {
log_write(LOG_LEVEL_DEBUG, "X: %d, Y: %d", x, y);
}
}
...
}
启动板子,用串口连接查看数据,就可以看到读取的触摸信息
在实际应用中,我们还需要考虑屏幕四个角坐标的偏移,所以通常需要做一个屏幕坐标与触摸坐标的映射功能,引导用户依次点击屏幕四角和中点的特定位置,即可得到转换的公式。
结合上一篇文章的屏幕显示方法,我们就可以在屏幕上用手绘制图案了
完整项目工程在这里:https://github.com/sqlxx/stm32-workshop/ (lcd_driver分支)
340

被折叠的 条评论
为什么被折叠?



