XPT2046是电阻触摸驱动,本质是一个多通道的ADC转换器,通过SPI与单片机连接
应用电路及引脚说明

| 序号 | 引脚 | 说明 |
|---|---|---|
| 1 | VCC | 电源供电,2.7-5.5V,一般接单片机的3.3V,并并联0.1uF电容 |
| 2 | XP | 触摸屏X输入,接XP或XL |
| 3 | YP | 触摸屏Y输入,接YP或YU |
| 4 | XN | 触摸屏X输入,接XN或XR |
| 5 | YN | 触摸屏Y输入,接XP或YD |
| 6 | GND | 接地 |
| 7 | VBAT | 额外的ADC测量通道,内部带1/4分压器,可测电池电压(最高6V) |
| 8 | AUX | 额外的ADC测量通道,不使用空置 |
| 9 | DCLK | 数据时钟,接MCU SPI的SCK |
| 10 | CS# | 数据有效,接MCU的CS,拉低为有效数据 |
| 11 | DIN | 数据输入,接MCU的MOSI(主出从入) |
| 12 | BUSY | 忙标志,ADC采样期间会拉高,可以不用接,程序层面控制延迟 |
| 13 | DOUT | 数据输出,接MCU的MISO(主入从出) |
| 14 | PENIRQ# | 中断标志,在休眠模式下检测是否有触摸按下 (给YN接地,给XP上拉,触摸按下时会拉低XP,触发中断,把PENIRQ#拉低) |
| 15 | IOVDD | IO供电,与电源相同接3.3V |
| 16 | VREF | 外部参考电压,不使用,留空 |
通信过程
基本过程
- 拉低CS,通信开始
- 发送1字节的控制命令
- 略微等待ADC采样
- 读取2字节的转换数据
- 拉高CS,通信完成
控制命令说明
控制命令的长度1字节
| 位 | 定义 | 说明 |
|---|---|---|
| 7(MSB) | S | 起始标志,必须为1 |
| 6,5,4 | A2-A0 | ADC通道选择 |
| 3 | MODE | 下一次ADC分辨率选择,0=12位,1=8位 |
| 2 | SER/DFR | ADC单端/差分选额,测量触摸屏时可以使用差分,其他情况使用单端 0=差分,1=单端 |
| 1 | PD1 | 是否使用内部参考电压,0=关闭(使用外部参考电压),1=打开 |
| 0(LSB) | PD0 | 控制本次通信结束后是否进入休眠,0=休眠,1=不休眠 休眠模式不影响下次采样,而且可以使用PENIRQ |
ADC通道表
| SER/DFR | A2 | A1 | A0 | 说明 |
|---|---|---|---|---|
| 0 | 0 | 0 | 1 | 测量Y坐标(差分) |
| 0 | 0 | 1 | 1 | |
| 0 | 1 | 0 | 0 | |
| 0 | 1 | 0 | 1 | 测量X坐标(差分) |
| 1 | 0 | 0 | 0 | 测量TEMP0 |
| 1 | 0 | 0 | 1 | 测量Y坐标 |
| 1 | 0 | 1 | 0 | 测量VBAT输入 |
| 1 | 0 | 1 | 1 | |
| 1 | 1 | 0 | 0 | |
| 1 | 1 | 0 | 1 | 测量X坐标 |
| 1 | 1 | 1 | 0 | 测量AUX输入 |
| 1 | 1 | 1 | 1 | 测量TEMP1 |
上面留空的是测量Z的(按压屏幕的力度),没有研究
一般情况下,使用差分模式测量X,Y坐标,可额外通过单端模式测量电池电压、芯片温度
读取ADC转换结果
后两个字节是MISO,即XPT2046发出数据,单片机接收
第一位为0,随后12位为ADC转换结果(高位在前),然后3位的0,代码表示如下
uint16_t adc_value = buf[0] << 5 | buf[1] >> 3;多次连续采样
单次采样需要占用3个字节(1发送 2接收),XPT2046支持在上次转换未完成之前就开启下次采样,即在接收第二字节的同时,发起下次采样的命令
SCLK: 第一字节 第二字节 第三字节 第四字节 第五字节
MOSI: 发送第一次命令 空闲 发送第二次采样命令 空闲 发送第三次采样命令 ...
MISO: 无效数据 接收第一次采样高字节 接收第一次采样低字节 接收第二次采样高字节 接收第二次采样低字节 ... 代码示例
初始化
刚上电时,PD0的值是不确定的,先发送一次命令,让其变为0
void XTP2046_init(void) {
gpio_cs_value(0);
spi_transfer_byte(0b10000010); // PD1=1, PD0=0
gpio_cs_value(1);
}采样函数
给定命令,返回对应的ADC值
#define XTP_COMMAND_DIFF_X // 差分X
#define XTP_COMMAND_DIFF_Y // 差分Y
#define XTP_COMMAND_VBAT // 电池电压
#define XTP_COMMAND_TEMP0 // 测温度需要读取两次
#define XTP_COMMAND_TEMP1 // 测温度需要读取两次
uint16_t XTP2046_read_adc(int cmd) {
gpio_cs_value(0);
spi_transfer_byte(cmd);
int high_byte = spi_transfer_byte(cmd);
int low_byte = spi_transfer_byte(cmd);
gpio_cs_value(1);
return high_byte << 5 | low_byte >> 3;
}读取电池电压
内部参考电压为2.5V,测量电阻时内部使用了1/4分压(上电阻7.5k,下电阻2.5k)
uint16_t XTP2046_read_vbat_mv(void) {
uint16_t adc_raw = XTP2046_read_adc(XTP_COMMAND_VBAT);
// return 2500.0 / 4096.0 * adc_raw * 4.0; // 这里使用了浮点数
return (adc_raw * 625) >> 8; // 优化为整数且避免除法
}读取温度
根据文档,使用间接测量法,需要测两次电压
uint16_t XTP2046_read_temperature(void) {
float u1_mv = XTP2046_read_adc(XTP_COMMAND_TEMP0) * 2500 / 4096;
float u2_mv = XTP2046_read_adc(XTP_COMMAND_TEMP1) * 2500 / 4096;
float dv = u2_mv - u1_mv;
temperature = 2.573 * dv - 273;
}读取触摸坐标
电阻触摸屏可以看作是两个滑动变阻器,通过读取中间抽头的电压反推滑动的位置,但是滑动变阻器的两端并不是0欧,而是有一定的电阻,所以电阻屏的使用一定需要先校准,也就是一个映射函数,把ADC的值映射为屏幕坐标
假设触摸屏的电阻分布均匀,则有可以认为坐标与ADC的值为一次函数关系(y = kx + b),即
coord_x = k1 * adc_x + b2
coord_y = k1 * adc_y + b2这样只需提供两个不在同一横线和竖线上的两点,即可解出四个未知变量