神农鼎 发表于 2026-6-30 10:14:02

STC8H8K64U接电阻式触摸TFT彩屏,用MCU自己的ADC实现触摸

STC8H8K64U接电阻式触摸TFT彩屏,
想把XPT2046触屏检测芯片去掉,
用MCU自己的ADC实现触摸,
帮提供方案


神农鼎 发表于 2026-6-30 10:14:31

<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值 &gt; 阈值) → 有触摸
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 &quot;stc8h.h&quot;
#include &quot;intrins.h&quot;

#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 &amp; 0xF0) | channel;// 选择通道
    ADC_CONTR |= 0x40;          // 启动ADC转换
    _nop_(); _nop_();
    while(!(ADC_CONTR &amp; 0x20)); // 等待转换完成
    ADC_CONTR &amp;= ~0x20;         // 清除标志
    res = (u16)ADC_RES &lt;&lt; 4;
    res |= ADC_RESL &gt;&gt; 4;       // 12位右对齐
    return res;
}

// 触摸检测
u8 Touch_Detect(void)
{
    // XP=高, YM=低, XM和YP浮空
    P1M1 &amp;= ~0x01; P1M0 |= 0x01;   // P1.0 推挽输出
    P1M1 |= 0x02;P1M0 &amp;= ~0x02;// P1.1 高阻输入(ADC)
    P3M1 &amp;= ~0x01; P3M0 |= 0x01;   // P3.0 推挽输出
    P3M1 |= 0x02;P3M0 &amp;= ~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 &gt; TOUCH_THRESHOLD) ? 1 : 0;
}

// 读X坐标
u16 Read_X(void)
{
    // XP=VCC, XM=GND, YP=ADC输入, YM=悬空
    P1M1 &amp;= ~0x01; P1M0 |= 0x01;   // P1.0 推挽
    P3M1 &amp;= ~0x01; P3M0 |= 0x01;   // P3.0 推挽
    P1M1 |= 0x02;P1M0 &amp;= ~0x02;// P1.1 高阻
    P3M1 |= 0x02;P3M0 &amp;= ~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 &amp;= ~0x01;// P1.0 高阻
    P3M1 |= 0x01;P3M0 &amp;= ~0x02;// P3.0 高阻
    P1M1 &amp;= ~0x02; P1M0 |= 0x02;   // P1.1 推挽
    P3M1 &amp;= ~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&amp;mod=forum&amp;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&amp;f=_all&amp;m=yes&amp;syn=yes&amp;s=relevance&amp;fid=0">🔍 搜索 &quot;STC8H8K64U接电阻式触摸TFT彩屏 想把 XPT2046触屏检测芯片去掉 用MCU自己的ADC实现触摸&quot;</a></p>
<p><strong>📌 相关论坛讨论</strong></p>
<ol>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=10867&amp;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&amp;page=1&amp;extra=#pid108095">8H8K64U单片机ADC检测LCD屏上贴的电阻式触摸屏坐标,含触屏中断源码分享</a></li>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=20091&amp;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&amp;page=1&amp;extra=#pid186234">请教一下 《38-2.4寸 ILI9325驱动TFT显示屏实验程序-带触摸功能》</a></li>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=11264&amp;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&amp;page=1&amp;extra=#pid113149">内置ADC采样电阻式触摸屏TFT彩屏例程@8H8K64U,效果对比</a></li>
</ol>

神农鼎 发表于 2026-6-30 10:26:15

能翻译成汇编吗


神农鼎 发表于 2026-6-30 10:26:54

<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 &lt;&lt; 4) | (ADC_RESL &gt;&gt; 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&lt;&lt;4) | (ADC_RESL&gt;&gt;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 &lt;&lt; 4) | (ADC_RESL &gt;&gt; 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>代码说明 &amp; 使用要点</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 &lt;&lt; 4) | (R7 &gt;&gt; 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&amp;mod=forum&amp;q=%E8%83%BD%E7%BF%BB%E8%AF%91%E6%88%90%E6%B1%87%E7%BC%96%E5%90%97&amp;f=_all&amp;m=yes&amp;syn=yes&amp;s=relevance&amp;fid=0">🔍 搜索 &quot;能翻译成汇编吗&quot;</a></p>
<p><strong>📌 相关论坛讨论</strong></p>
<ol>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=2550&amp;highlight=%E8%83%BD%E7%BF%BB%E8%AF%91%E6%88%90%E6%B1%87%E7%BC%96%E5%90%97&amp;page=2&amp;extra=#pid17882">看冲哥串口视频有疑问</a></li>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=16770&amp;highlight=%E8%83%BD%E7%BF%BB%E8%AF%91%E6%88%90%E6%B1%87%E7%BC%96%E5%90%97&amp;page=1&amp;extra=#pid157468">IAR入门指南之开发嵌入式应用</a></li>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=375&amp;highlight=%E8%83%BD%E7%BF%BB%E8%AF%91%E6%88%90%E6%B1%87%E7%BC%96%E5%90%97&amp;page=14&amp;extra=#pid110772">8H/8G/8C/8A系列4组串口同时通信的程序,Modbus 演示程序;可共享T2做波特率发生器</a></li>
</ol>
页: [1]
查看完整版本: STC8H8K64U接电阻式触摸TFT彩屏,用MCU自己的ADC实现触摸