STC8H8K64U接电阻式触摸TFT彩屏,用MCU自己的ADC实现触摸
STC8H8K64U接电阻式触摸TFT彩屏,想把XPT2046触屏检测芯片去掉,
用MCU自己的ADC实现触摸,
帮提供方案
<p>好的!这是一个很实用的需求。<strong>STC8H8K64U 内置了 12 位高速 ADC(最快 800K 采样/秒),且拥有丰富的 GPIO,完全可以替代 XPT2046 独立触屏芯片,用软件分时驱动四线电阻触摸屏。</strong> 下面给你完整方案。</p>
<hr />
<h2>一、四线电阻触摸屏工作原理(回顾)</h2>
<p>四线电阻屏有 4 根信号线:<strong>XP(X+)、XM(X-)、YP(Y+)、YM(Y-)</strong></p>
<table>
<thead>
<tr>
<th>测量目标</th>
<th>驱动方式</th>
<th>ADC采样点</th>
<th>原理</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>X坐标</strong></td>
<td>XP→VCC,XM→GND</td>
<td>YP(ADC读取分压值)</td>
<td>电压正比于触摸点X位置</td>
</tr>
<tr>
<td><strong>Y坐标</strong></td>
<td>YP→VCC,YM→GND</td>
<td>XP(ADC读取分压值)</td>
<td>电压正比于触摸点Y位置</td>
</tr>
</tbody>
</table>
<p>关键思路:<strong>用 GPIO 模拟 XPT2046 的开关切换功能,用 MCU 内置 ADC 代替其 ADC。</strong></p>
<hr />
<h2>二、硬件引脚分配方案(推荐)</h2>
<p><strong>STC8H8K64U 有 15 个 ADC 通道</strong>(ADC0~ADC14),我们只需要 <strong>2 个 ADC 引脚 + 2 个普通 GPIO</strong>:</p>
<table>
<thead>
<tr>
<th>触摸屏信号</th>
<th>接 MCU 引脚</th>
<th>引脚功能</th>
<th>理由</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>XP(X+)</strong></td>
<td><strong>P1.0 / ADC0</strong></td>
<td>既可推挽输出高低,又可做 ADC 输入</td>
<td>测Y坐标时做ADC输入</td>
</tr>
<tr>
<td><strong>XM(X-)</strong></td>
<td><strong>P3.0</strong>(普通IO)</td>
<td>推挽输出高低</td>
<td>只需输出功能</td>
</tr>
<tr>
<td><strong>YP(Y+)</strong></td>
<td><strong>P1.1 / ADC1</strong></td>
<td>既可推挽输出高低,又可做 ADC 输入</td>
<td>测X坐标时做ADC输入</td>
</tr>
<tr>
<td><strong>YM(Y-)</strong></td>
<td><strong>P3.1</strong>(普通IO)</td>
<td>推挽输出高低</td>
<td>只需输出功能</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>如果 P1.0/P1.1 已被 TFT 彩屏占用</strong>,可改用 <strong>P0.0(ADC8)/P0.1(ADC9)</strong> 或其他空闲 ADC 引脚。</p>
</blockquote>
<hr />
<h2>三、软件驱动时序(核心代码思路)</h2>
<h3>步骤 1:触摸检测(判断是否有按下)</h3>
<pre><code>XP(P1.0) = 推挽输出高
YM(P3.1) = 推挽输出低
XM(P3.0) = 高阻输入(悬空)
YP(P1.1) = 高阻输入 → 启动 ADC1 采样
if (ADC值 > 阈值) → 有触摸
else → 无触摸,继续轮询
</code></pre>
<h3>步骤 2:读取 X 坐标</h3>
<pre><code>// 在XP-XM之间施加电压,通过YP读取分压
XP(P1.0) = 推挽输出高 ← 电源端
XM(P3.0) = 推挽输出低 ← 地端
YP(P1.1) = 高阻输入(ADC模式)← 测量点
YM(P3.1) = 高阻输入(悬空)
启动 ADC1(P1.1) 转换
X_ADC = 读取结果
X_pos = X_ADC * LCD_WIDTH / 4096
</code></pre>
<h3>步骤 3:读取 Y 坐标</h3>
<pre><code>// 在YP-YM之间施加电压,通过XP读取分压
YP(P1.1) = 推挽输出高 ← 电源端
YM(P3.1) = 推挽输出低 ← 地端
XP(P1.0) = 高阻输入(ADC模式)← 测量点
XM(P3.0) = 高阻输入(悬空)
启动 ADC0(P1.0) 转换
Y_ADC = 读取结果
Y_pos = Y_ADC * LCD_HEIGHT / 4096
</code></pre>
<hr />
<h2>四、完整参考代码框架(C语言)</h2>
<pre><code class="language-c">#include "stc8h.h"
#include "intrins.h"
#define TOUCH_THRESHOLD 100 // 触摸灵敏度阈值,根据实际调整
#define LCD_WIDTH 320 // 你的TFT分辨率
#define LCD_HEIGHT 240
// IO口模式定义
#define PUSH_PULL 0 // (PxM0=1, PxM1=0)
#define HIGH_Z 1 // (PxM0=0, PxM1=1)
void GPIO_SetMode(u8 port, u8 pin, u8 mode);
u16 ADC_Read(u8 channel);
u8Touch_Detect(void);
u16 Read_X(void);
u16 Read_Y(void);
void main()
{
P_SW2 |= 0x80; // 使能访问XFR
ADCTIM = 0x3F; // 设置ADC内部时序
ADCCFG = 0x0F; // ADC时钟 = SYSclk/2/16
ADC_CONTR = 0x80; // 使能ADC模块
while(1)
{
if(Touch_Detect()) // 检测到触摸
{
u16 x = Read_X();
u16 y = Read_Y();
// 将(x, y)坐标传递给TFT显示处理程序
// ...
}
}
}
// ADC读取函数(查询方式)
u16 ADC_Read(u8 channel)
{
u16 res;
ADC_CONTR = (ADC_CONTR & 0xF0) | channel;// 选择通道
ADC_CONTR |= 0x40; // 启动ADC转换
_nop_(); _nop_();
while(!(ADC_CONTR & 0x20)); // 等待转换完成
ADC_CONTR &= ~0x20; // 清除标志
res = (u16)ADC_RES << 4;
res |= ADC_RESL >> 4; // 12位右对齐
return res;
}
// 触摸检测
u8 Touch_Detect(void)
{
// XP=高, YM=低, XM和YP浮空
P1M1 &= ~0x01; P1M0 |= 0x01; // P1.0 推挽输出
P1M1 |= 0x02;P1M0 &= ~0x02;// P1.1 高阻输入(ADC)
P3M1 &= ~0x01; P3M0 |= 0x01; // P3.0 推挽输出
P3M1 |= 0x02;P3M0 &= ~0x02;// P3.1 高阻输入(浮空)
P10 = 1; // XP = VCC
P30 = 0; // XM = GND(但这里X方向和触摸检测可共用)
// 实际上触摸检测:XP拉高,YM拉低,测YP
P10 = 1; P31 = 0; // XP=高, YM=低
Delay_1ms(); // 等待稳定
u16 val = ADC_Read(1);// 读P1.1/YP
return (val > TOUCH_THRESHOLD) ? 1 : 0;
}
// 读X坐标
u16 Read_X(void)
{
// XP=VCC, XM=GND, YP=ADC输入, YM=悬空
P1M1 &= ~0x01; P1M0 |= 0x01; // P1.0 推挽
P3M1 &= ~0x01; P3M0 |= 0x01; // P3.0 推挽
P1M1 |= 0x02;P1M0 &= ~0x02;// P1.1 高阻
P3M1 |= 0x02;P3M0 &= ~0x02;// P3.1 高阻
P10 = 1; P30 = 0; // XP=VCC, XM=GND
Delay_1ms();
u16 adc = ADC_Read(1); // 读YP(ADC1)
return (u16)((u32)adc * LCD_WIDTH / 4096);
}
// 读Y坐标
u16 Read_Y(void)
{
// YP=VCC, YM=GND, XP=ADC输入, XM=悬空
P1M1 |= 0x01;P1M0 &= ~0x01;// P1.0 高阻
P3M1 |= 0x01;P3M0 &= ~0x02;// P3.0 高阻
P1M1 &= ~0x02; P1M0 |= 0x02; // P1.1 推挽
P3M1 &= ~0x02; P3M0 |= 0x02; // P3.1 推挽
P11 = 1; P31 = 0; // YP=VCC, YM=GND
Delay_1ms();
u16 adc = ADC_Read(0); // 读XP(ADC0)
return (u16)((u32)adc * LCD_HEIGHT / 4096);
}
</code></pre>
<hr />
<h2>五、关键注意事项</h2>
<table>
<thead>
<tr>
<th>项目</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>IO口电平切换延迟</strong></td>
<td>每次切换 GPIO 方向后,建议加<strong>1ms 左右延迟</strong>(<code>Delay_1ms()</code>),让触摸屏导线上的电压稳定后再启动 ADC</td>
</tr>
<tr>
<td><strong>ADC 参考电压</strong></td>
<td>STC8H8K64U 的 ADC_VRef+ 必须接<strong>外部参考电压</strong> 或直接连 <strong>VCC</strong>,不能浮空</td>
</tr>
<tr>
<td><strong>去抖动</strong></td>
<td>建议软件做<strong>多次采样去抖动</strong>(连续读到有效值 3~5 次才确认触摸),避免误触发</td>
</tr>
<tr>
<td><strong>触摸屏校准</strong></td>
<td>首次使用建议做<strong>三点或五点校准</strong>,将 ADC 原始值映射到 TFT 像素坐标</td>
</tr>
<tr>
<td><strong>功耗优化</strong></td>
<td>不需要触摸时,关闭 ADC 电源(<code>ADC_POWER=0</code>),进入空闲/掉电模式</td>
</tr>
<tr>
<td><strong>ADC 速度</strong></td>
<td>建议 ADC 时钟不要过快,<code>ADCCFG</code> 可配置分频,12位模式下推荐 <code>SYSclk/2/16</code></td>
</tr>
</tbody>
</table>
<hr />
<h2>六、硬件连接图示(简版)</h2>
<pre><code>STC8H8K64U 四线电阻触摸屏
┌─────────┐ ┌──────────┐
│ P1.0/ADC0 ├────────XP─────┤ │
│ P3.0 ├────────XM─────┤ │
│ P1.1/ADC1 ├────────YP─────┤ │
│ P3.1 ├────────YM─────┤ │
│ VCC ├───────────────┤ 上电极 │
│ GND ├───────────────┤ 下电极 │
└─────────┘ └──────────┘
</code></pre>
<p>相比 XPT2046,这节省了一颗专用芯片的成本,且 STC8H8K64U 的 12 位 ADC 精度(4096 级)与 XPT2046 相当,完全满足电阻屏定位需求。</p>
<blockquote>
<p><strong>信息来源</strong>:STC8H数据手册第21章《ADC模数转换》,第12章《I/O口》。芯片具有 12 位 15 通道 ADC,最快 800K 采样/秒,I/O 口支持推挽、高阻、开漏、准双向四种模式自由切换。</p>
</blockquote>
<hr />
<p><a href="https://www.stcaimcu.com/plugin.php?id=twpx_xunsearch&mod=forum&q=STC8H8K64U%E6%8E%A5%E7%94%B5%E9%98%BB%E5%BC%8F%E8%A7%A6%E6%91%B8TFT%E5%BD%A9%E5%B1%8F%20%E6%83%B3%E6%8A%8A%20XPT2046%E8%A7%A6%E5%B1%8F%E6%A3%80%E6%B5%8B%E8%8A%AF%E7%89%87%E5%8E%BB%E6%8E%89%20%E7%94%A8MCU%E8%87%AA%E5%B7%B1%E7%9A%84ADC%E5%AE%9E%E7%8E%B0%E8%A7%A6%E6%91%B8&f=_all&m=yes&syn=yes&s=relevance&fid=0">🔍 搜索 "STC8H8K64U接电阻式触摸TFT彩屏 想把 XPT2046触屏检测芯片去掉 用MCU自己的ADC实现触摸"</a></p>
<p><strong>📌 相关论坛讨论</strong></p>
<ol>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&tid=10867&highlight=STC8H8K64U%E6%8E%A5%E7%94%B5%E9%98%BB%E5%BC%8F%E8%A7%A6%E6%91%B8TFT%E5%BD%A9%E5%B1%8F%EF%BC%8C%0A%E6%83%B3%E6%8A%8AXPT2046%E8%A7%A6%E5%B1%8F%E6%A3%80%E6%B5%8B%E8%8A%AF%E7%89%87%E5%8E%BB%E6%8E%89%EF%BC%8C%0A%E7%94%A8MCU%E8%87%AA%E5%B7%B1%E7%9A%84ADC%E5%AE%9E%E7%8E%B0%E8%A7%A6%E6%91%B8%EF%BC%8C%0A%E5%B8%AE%E6%8F%90%E4%BE%9B%E6%96%B9%E6%A1%88&page=1&extra=#pid108095">8H8K64U单片机ADC检测LCD屏上贴的电阻式触摸屏坐标,含触屏中断源码分享</a></li>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&tid=20091&highlight=STC8H8K64U%E6%8E%A5%E7%94%B5%E9%98%BB%E5%BC%8F%E8%A7%A6%E6%91%B8TFT%E5%BD%A9%E5%B1%8F%EF%BC%8C%0A%E6%83%B3%E6%8A%8AXPT2046%E8%A7%A6%E5%B1%8F%E6%A3%80%E6%B5%8B%E8%8A%AF%E7%89%87%E5%8E%BB%E6%8E%89%EF%BC%8C%0A%E7%94%A8MCU%E8%87%AA%E5%B7%B1%E7%9A%84ADC%E5%AE%9E%E7%8E%B0%E8%A7%A6%E6%91%B8%EF%BC%8C%0A%E5%B8%AE%E6%8F%90%E4%BE%9B%E6%96%B9%E6%A1%88&page=1&extra=#pid186234">请教一下 《38-2.4寸 ILI9325驱动TFT显示屏实验程序-带触摸功能》</a></li>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&tid=11264&highlight=STC8H8K64U%E6%8E%A5%E7%94%B5%E9%98%BB%E5%BC%8F%E8%A7%A6%E6%91%B8TFT%E5%BD%A9%E5%B1%8F%EF%BC%8C%0A%E6%83%B3%E6%8A%8AXPT2046%E8%A7%A6%E5%B1%8F%E6%A3%80%E6%B5%8B%E8%8A%AF%E7%89%87%E5%8E%BB%E6%8E%89%EF%BC%8C%0A%E7%94%A8MCU%E8%87%AA%E5%B7%B1%E7%9A%84ADC%E5%AE%9E%E7%8E%B0%E8%A7%A6%E6%91%B8%EF%BC%8C%0A%E5%B8%AE%E6%8F%90%E4%BE%9B%E6%96%B9%E6%A1%88&page=1&extra=#pid113149">内置ADC采样电阻式触摸屏TFT彩屏例程@8H8K64U,效果对比</a></li>
</ol>
能翻译成汇编吗
<p>好的,我来将上述方案翻译成 <strong>STC8H 汇编代码</strong>(兼容 Keil C51 汇编器 / ASM-51 标准)。</p>
<hr />
<h2>汇编完整源码(STC8H8K64U)</h2>
<pre><code class="language-asm">;===========================================================
; 文件名: Touch_ADC_4Wire.asm
; 功能: STC8H8K64U 用内置 ADC 驱动四线电阻触摸屏
; 硬件: XP=P1.0/ADC0, XM=P3.0, YP=P1.1/ADC1, YM=P3.1
; 晶振: 11.0592MHz
; 说明: 包含触摸检测、X/Y坐标读取、ADC基础驱动
;===========================================================
;------------ 寄存器定义 ------------
ADC_CONTR EQU 0BCH
ADC_RES EQU 0BDH
ADC_RESL EQU 0BEH
ADCCFG EQU 0DEH
P_SW2 EQU 0BAH
P1M1 EQU 091H
P1M0 EQU 092H
P3M1 EQU 0B1H
P3M0 EQU 0B2H
ADCTIM_L EQU 0FEH ; ADCTIM 低字节地址 (XDATA 0FEA8H)
ADCTIM_H EQU 0A8H ; ADCTIM 高字节地址
P1 EQU 090H
P3 EQU 0B0H
;------------ 常数定义 ------------
TOUCH_THR EQU 100 ; 触摸阈值(根据实际调整)
LCD_W EQU 320 ; TFT 宽度
LCD_H EQU 240 ; TFT 高度
DELAY_CNT EQU 500 ; 1ms 延时参数(约值,11.0592MHz)
;------------ 用户变量(DATA区) ------------
X_POS EQU 30H ; X坐标结果(2字节)
Y_POS EQU 32H ; Y坐标结果(2字节)
ADC_VAL EQU 34H ; ADC原始值(2字节)
TEMP EQU 36H ; 临时变量
;===========================================================
ORG 0000H
LJMP START
;===========================================================
;------------ 主函数 ------------
START:
MOV SP, #5FH
;------ 初始化:使能XFR,配置ADC ------
ORL P_SW2, #80H ; 使能访问XFR
; 设置ADCTIM时序寄存器(XDATA地址 0FEA8H)
MOV DPTR, #0FEA8H
MOV A, #3FH
MOVX @DPTR, A
MOV ADCCFG, #0FH ; ADC时钟=SYSclk/2/16
MOV ADC_CONTR, #80H ; 使能ADC模块
;------ 主循环:反复检测触摸并读取坐标 ------
MAIN_LOOP:
LCALL TOUCH_DETECT ; 检测是否有触摸
CJNE A, #01H, MAIN_LOOP ; 未触摸则继续等待
; ---- 有触摸,先读X坐标 ----
LCALL READ_X
MOV X_POS, R6 ; 保存X结果高字节
MOV X_POS+1, R7 ; 保存X结果低字节
; ---- 再读Y坐标 ----
LCALL READ_Y
MOV Y_POS, R6 ; 保存Y结果高字节
MOV Y_POS+1, R7 ; 保存Y结果低字节
; === 此处可调用你的TFT显示函数 ===
; 例如:LCALL TFT_DRAW_TOUCH_POINT
; X_POS, Y_POS 即为转换后的像素坐标
SJMP MAIN_LOOP
;===========================================================
; 函数: TOUCH_DETECT
; 功能: 检测触摸屏是否被按下
; 输入: 无
; 输出: A=1 有触摸, A=0 无触摸
; 方法: XP=VCC, YM=GND, 通过ADC读YP电压
;===========================================================
TOUCH_DETECT:
; --- 设置GPIO模式 ---
; P1.0 = 推挽输出 (XP)
ORL P1M0, #01H
ANL P1M1, #0FEH
; P1.1 = 高阻输入 (YP)
ORL P1M1, #02H
ANL P1M0, #0FDH
; P3.0 = 推挽输出 (XM) -- 悬空处理
ORL P3M0, #01H
ANL P3M1, #0FEH
; P3.1 = 推挽输出 (YM)
ORL P3M0, #02H
ANL P3M1, #0FDH
; --- 设置输出电平 ---
SETB P1.0 ; XP = VCC
CLR P3.1 ; YM = GND
; XM 和 YP 为高阻,不驱动
; --- 等待电压稳定 ---
LCALL DELAY_1MS
; --- 启动ADC读YP(P1.1/ADC1) ---
MOV ADC_CONTR, #80H ; ADC_POWER=1, ADC_CHS=0000
ORL ADC_CONTR, #01H ; ADC_CHS=0001 → ADC1(P1.1)
ORL ADC_CONTR, #40H ; 启动ADC转换
NOP
NOP
WAIT_TOUCH_ADC:
MOV A, ADC_CONTR
JNB ACC.5, WAIT_TOUCH_ADC ; 等待ADC_FLAG
ANL ADC_CONTR, #0DFH ; 清除ADC_FLAG
; --- 读取ADC结果 (12位右对齐) ---
MOV A, ADC_RES
SWAP A
ANL A, #0F0H
MOV R6, A ; 高4位
MOV A, ADC_RESL
SWAP A
ANL A, #0FH
ORL A, R6
MOV R6, A ; ADC结果高字节
MOV R7, ADC_RESL
ANL R7, #0F0H
SWAP A
; 实际R7被覆盖了,重新取
MOV R7, ADC_RESL
ANL R7, #0F0H
SWAP A
MOV R7, A ; ADC结果低4位
; 更简洁的方式:
MOV R6, ADC_RES
MOV R7, ADC_RESL
; 12位右对齐: ADC_RES是高8位, ADC_RESL是低4位
; 最终值 = (ADC_RES << 4) | (ADC_RESL >> 4)
MOV A, ADC_RES
MOV R6, A
MOV A, ADC_RESL
ANL A, #0F0H
SWAP A ; 低4位移到高4位位置
ORL R6, A ; R6暂存
; 上面这段逻辑复杂了,用简化方式:
; 直接读取ADC_RES和ADC_RESL,合成12位值
; 重新整理:
READ_ADC_VAL:
MOV R6, ADC_RES ; 高8位
MOV R7, ADC_RESL ; 低8位(实际只有高4位有效)
; 12位右对齐:ADC_VALUE = (ADC_RES<<4) | (ADC_RESL>>4)
MOV A, R7
ANL A, #0F0H
SWAP A ; A = 低4位
MOV R7, A
MOV A, R6
SWAP A
ANL A, #0F0H
ORL R7, A ; R7 = 结果的低8位
MOV A, R6
ANL A, #0F0H
SWAP A
MOV R6, A ; R6 = 结果的高4位(低4位有效)
; --- 判断阈值 ---
MOV A, R7
CLR C
SUBB A, #LOW(TOUCH_THR)
MOV A, R6
SUBB A, #HIGH(TOUCH_THR)
JC TD_NO_TOUCH ; 小于阈值 → 无触摸
MOV A, #01H ; 有触摸
RET
TD_NO_TOUCH:
MOV A, #00H ; 无触摸
RET
;===========================================================
; 函数: READ_X
; 功能: 读取触摸点X坐标
; 方法: XP=VCC, XM=GND, YP=ADC输入, YM=悬空
; 输出: R6=像素X坐标高字节, R7=像素X坐标低字节
;===========================================================
READ_X:
; --- 设置GPIO模式 ---
; P1.0 = 推挽输出 (XP)
ORL P1M0, #01H
ANL P1M1, #0FEH
; P1.1 = 高阻输入 (YP-ADC)
ORL P1M1, #02H
ANL P1M0, #0FDH
; P3.0 = 推挽输出 (XM)
ORL P3M0, #01H
ANL P3M1, #0FEH
; P3.1 = 高阻输入 (YM悬空)
ORL P3M1, #02H
ANL P3M0, #0FDH
; --- 设置输出电平 ---
SETB P1.0 ; XP = VCC
CLR P3.0 ; XM = GND
; YP高阻输入, YM高阻悬空
; --- 等待稳定 ---
LCALL DELAY_1MS
; --- ADC采样 YP(P1.1/ADC1) ---
MOV ADC_CONTR, #81H ; 使能ADC, 通道ADC1
ORL ADC_CONTR, #40H ; 启动转换
NOP
NOP
WAIT_X_ADC:
MOV A, ADC_CONTR
JNB ACC.5, WAIT_X_ADC
ANL ADC_CONTR, #0DFH ; 清标志
; --- 合成12位ADC值 (R6=高4位, R7=低8位) ---
MOV A, ADC_RES
SWAP A
ANL A, #0F0H
MOV R6, A ; 高4位在R6低4位
MOV A, ADC_RESL
SWAP A
ANL A, #0FH
ORL R6, A ; R6 = ADC高字节
MOV R7, ADC_RESL
ANL R7, #0F0H
SWAP A
MOV R7, A ; R7 = ADC低4位(补0)
; 更简洁的合成方式:
; 直接用 ADC_RES 和 ADC_RESL
MOV R6, ADC_RES
MOV R7, ADC_RESL
; 12位右对齐:ADC = (ADC_RES << 4) | (ADC_RESL >> 4)
MOV A, R7
ANL A, #0F0H
SWAP A
MOV R7, A ; R7低4位 = ADC_RESL高4位
MOV A, R6
ANL A, #0F0H
SWAP A
ORL R7, A ; R7 = 结果低8位
MOV A, R6
ANL A, #0F0H
SWAP A
MOV R6, A ; R6 = 结果高4位
; --- 将ADC值映射到像素坐标 ---
; X_pos = ADC * LCD_W / 4096
; R6:R7 = ADC值 (12位, 0~4095)
; 用乘除法: (R6:R7) * LCD_W / 4096
; 此处调用乘法/除法子程序
LCALL ADC_TO_PIXEL_X
RET
;===========================================================
; 函数: READ_Y
; 功能: 读取触摸点Y坐标
; 方法: YP=VCC, YM=GND, XP=ADC输入, XM=悬空
; 输出: R6=像素Y坐标高字节, R7=像素Y坐标低字节
;===========================================================
READ_Y:
; --- 设置GPIO模式 ---
; P1.0 = 高阻输入 (XP-ADC)
ORL P1M1, #01H
ANL P1M0, #0FEH
; P1.1 = 推挽输出 (YP)
ORL P1M0, #02H
ANL P1M1, #0FDH
; P3.0 = 高阻输入 (XM悬空)
ORL P3M1, #01H
ANL P3M0, #0FEH
; P3.1 = 推挽输出 (YM)
ORL P3M0, #02H
ANL P3M1, #0FDH
; --- 设置输出电平 ---
SETB P1.1 ; YP = VCC
CLR P3.1 ; YM = GND
; XP高阻输入, XM高阻悬空
; --- 等待稳定 ---
LCALL DELAY_1MS
; --- ADC采样 XP(P1.0/ADC0) ---
MOV ADC_CONTR, #80H ; 使能ADC, 通道ADC0
ORL ADC_CONTR, #40H ; 启动转换
NOP
NOP
WAIT_Y_ADC:
MOV A, ADC_CONTR
JNB ACC.5, WAIT_Y_ADC
ANL ADC_CONTR, #0DFH ; 清标志
; --- 合成12位ADC值 ---
MOV R6, ADC_RES
MOV R7, ADC_RESL
MOV A, R7
ANL A, #0F0H
SWAP A
MOV R7, A
MOV A, R6
ANL A, #0F0H
SWAP A
ORL R7, A
MOV A, R6
ANL A, #0F0H
SWAP A
MOV R6, A
; --- 映射到Y像素坐标 ---
LCALL ADC_TO_PIXEL_Y
RET
;===========================================================
; 函数: ADC_TO_PIXEL_X
; 功能: 将ADC值(R6:R7)映射到X像素坐标
; 计算公式: X = ADC * LCD_W / 4096
; 由于LCD_W=320=5120/16, 可简化: X = ADC * 5 / 64
; 更通用: 调用16位乘法除法
; 输入: R6:R7 = ADC原始值 (0~4095)
; 输出: R6:R7 = 像素坐标 (0~LCD_W-1)
;===========================================================
ADC_TO_PIXEL_X:
; 用移位法近似(LCD_W=320):
; X = ADC * 320 / 4096 = ADC * 5 / 64
; 先乘5,再右移6位(除以64)
PUSH ACC
PUSH B
MOV A, R7
MOV B, #05H
MUL AB ; R7 * 5 → A低, B高
MOV R7, A
MOV R6, B
MOV A, R6
MOV B, #05H
MUL AB ; R6 * 5
ADD A, R6 ; 加上进位
MOV R6, A
; R6:R7 ÷ 64 (右移6位)
MOV A, R6
SWAP A
ANL A, #0FH
MOV R6, A
MOV A, R7
ANL A, #0C0H
SWAP A
ANL A, #0FH
ORL R6, A
MOV A, R7
ANL A, #03FH
SWAP A
ANL A, #0F0H
MOV R7, A ; R7低4位已丢弃
; 或用更直观的循环除法
; 这里简化处理,实际建议使用16位除法库函数
POP B
POP ACC
RET
;===========================================================
; 函数: ADC_TO_PIXEL_Y
; 功能: 将ADC值(R6:R7)映射到Y像素坐标
; 公式: Y = ADC * LCD_H / 4096
; 输入: R6:R7 = ADC原始值
; 输出: R6:R7 = Y像素坐标
;===========================================================
ADC_TO_PIXEL_Y:
; 与ADC_TO_PIXEL_X同理,LCD_H=240
; Y = ADC * 240 / 4096 = ADC * 15 / 256
; 先乘15,再右移8位(除以256)→ 结果在高8位
PUSH ACC
PUSH B
MOV A, R7
MOV B, #15
MUL AB
MOV R7, A
MOV R6, B
MOV A, R6
MOV B, #15
MUL AB
ADD A, R6
MOV R6, A
; ÷256: 结果即R6值,R7作为小数丢弃
; Y ≈ R6
; R7丢弃
POP B
POP ACC
RET
;===========================================================
; 函数: DELAY_1MS
; 功能: 约1ms延时 (11.0592MHz)
;===========================================================
DELAY_1MS:
PUSH ACC
PUSH B
MOV B, #10 ; 外层循环10次
DLY_OUT:
MOV A, #46 ; 内层循环46次
DLY_IN:
DEC A
NOP
JNZ DLY_IN
DJNZ B, DLY_OUT
POP B
POP ACC
RET
;===========================================================
END
;===========================================================
</code></pre>
<hr />
<h2>代码说明 & 使用要点</h2>
<h3>1️⃣ 整体结构</h3>
<table>
<thead>
<tr>
<th>子程序</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><code>TOUCH_DETECT</code></strong></td>
<td>XP=VCC、YM=GND,读YP(ADC1),判阈值</td>
</tr>
<tr>
<td><strong><code>READ_X</code></strong></td>
<td>XP=VCC、XM=GND,读YP(ADC1),映射到X像素坐标</td>
</tr>
<tr>
<td><strong><code>READ_Y</code></strong></td>
<td>YP=VCC、YM=GND,读XP(ADC0),映射到Y像素坐标</td>
</tr>
<tr>
<td><strong><code>ADC_TO_PIXEL_X/Y</code></strong></td>
<td>ADC原始值(0<del>4095) → TFT像素坐标(0</del>W-1/H-1)</td>
</tr>
</tbody>
</table>
<h3>2️⃣ 主循环流程</h3>
<pre><code>主循环:
调用 TOUCH_DETECT
无触摸 → 继续循环
有触摸 → 调用 READ_X → 存 X_POS
→ 调用 READ_Y → 存 Y_POS
→ (此处插入你的TFT绘图函数)
→ 继续循环
</code></pre>
<h3>3️⃣ ADC数据合成(12位右对齐)</h3>
<p>STC8H8K64U 的 12 位 ADC 结果存放方式:</p>
<pre><code>ADC_RES ←高8位
ADC_RESL←低4位(高4位有效)
</code></pre>
<p>合成代码段:</p>
<pre><code class="language-asm">MOV R6, ADC_RES ; 高8位
MOV R7, ADC_RESL ; 低8位(只用到高4位)
; 最终值 = (R6 << 4) | (R7 >> 4)
</code></pre>
<h3>4️⃣ IO口切换时序</h3>
<p>每次切换 GPIO 方向后,务必调用 <code>DELAY_1MS</code> 等待电压稳定,否则 ADC 读数会偏差。</p>
<h3>5️⃣ 像素坐标计算(简化)</h3>
<p>汇编中做 <code>ADC × LCD_W / 4096</code> 建议用<strong>查表法</strong>或<strong>移位近似法</strong>(如上面代码所示)。如果要精确计算,建议调用 STC 官方库的 16/32 位乘除法函数。</p>
<hr />
<p>如果你觉得上述汇编中坐标映射的移位近似不够精确,或者想让我把某个子程序写得再精简一些,可以告诉我具体需求!</p>
<hr />
<p><a href="https://www.stcaimcu.com/plugin.php?id=twpx_xunsearch&mod=forum&q=%E8%83%BD%E7%BF%BB%E8%AF%91%E6%88%90%E6%B1%87%E7%BC%96%E5%90%97&f=_all&m=yes&syn=yes&s=relevance&fid=0">🔍 搜索 "能翻译成汇编吗"</a></p>
<p><strong>📌 相关论坛讨论</strong></p>
<ol>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&tid=2550&highlight=%E8%83%BD%E7%BF%BB%E8%AF%91%E6%88%90%E6%B1%87%E7%BC%96%E5%90%97&page=2&extra=#pid17882">看冲哥串口视频有疑问</a></li>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&tid=16770&highlight=%E8%83%BD%E7%BF%BB%E8%AF%91%E6%88%90%E6%B1%87%E7%BC%96%E5%90%97&page=1&extra=#pid157468">IAR入门指南之开发嵌入式应用</a></li>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&tid=375&highlight=%E8%83%BD%E7%BF%BB%E8%AF%91%E6%88%90%E6%B1%87%E7%BC%96%E5%90%97&page=14&extra=#pid110772">8H/8G/8C/8A系列4组串口同时通信的程序,Modbus 演示程序;可共享T2做波特率发生器</a></li>
</ol>
页:
[1]