ZARD渐空 发表于 2025-7-15 09:53:16

触摸按键,不隔面板时反应很灵敏,隔了一个1.7mm的亚克力面板后就不灵敏了

<p>1、当前PCB版本是这样的,设计时忘记把触摸按键周围的铜皮挖空了。</p>
<p><img src="data/attachment/forum/202507/15/092652jijemqnjn22jrh99.png" alt="image.png" title="image.png" /></p>
<p>然后实际调试时,电容本来用的是10nF,读取扫描出来的值是2000左右。把电容换成33nF后,读取出来的值在40000-50000之间。</p>
<p>现在的一个现象是,不隔面板时,触摸之后,按键值会降得很大,所以按键效果就很灵敏。</p>
<p>但是隔了一个1.7mm的亚克力面板之后,按键值就只降了2000左右,所以按键效果就不灵敏。</p>
<p><strong>问题一:在不改PCB的情况下,能否通过改变软件来提高灵敏度?</strong></p>
<p>2、按键驱动根据电子琴的代码做了一些修改,麻烦大佬们帮忙看看有没有修改建议</p>
<p>触摸电子琴从无到有心得分享——基于STC8H4K64TL的电子琴<br />
https://www.stcaimcu.com/thread-1380-1-1.html<br />
(出处: 国芯技术交流网站)</p>
<pre><code>volatile u32 TK_cnt={0}; // 键计数值,从触摸中断中保存当前触摸通道的值
u32        xdata TK_Test=0, TK_zero={0};// 0点读数
u32 xdata T_KeyCmp={0};//按键按下后,TK_zero-T_KeyCmp后,判断是否真正按下的阈值
bit        B_ReadKeyOk=0;                          //标志已转换完成4个键
u16 Count={0};        //按键按下后的时间计数
u16 LastState = 0;                                //8位变量         b0=1,代表k0上一次按下过

//========================================================================
// 函数: void TKSU_Interrupt(void)
// 描述: 触摸按键中断。
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2021-02-01
// 备注:
//========================================================================

void TKSU_Interrupt(void) interrupt 13
{
        u8        j;
        j = TSSTA2;
        if(j &amp; 0x40)        //数据溢出, 错误处理(略)
        {
                TSSTA2 |= 0x40;        //写1清零
        }
        if(j &amp; 0x80)        //扫描完成
        {
                j &amp;= 0x0f;
                TSSTA2 |= 0x80;        //写1清零
                switch(j)
                {
                        case 4:
                                TK_cnt = TSDAT;        //保存某个通道的读数
                                break;
                        case 5:
                                TK_cnt = TSDAT;        //保存某个通道的读数
                                break;
                        case 6:
                                TK_cnt = TSDAT;        //保存某个通道的读数
                                break;
                        case 7:
                                TK_cnt = TSDAT;        //保存某个通道的读数
                                break;
                }
                if( j&gt;=7 ){
                        Uart_Test0++;
                        if(Uart_Test0&gt;=65536)
                                Uart_Test0=0;
                        B_ReadKeyOk=1;//读完一次循环
                }
        }   
}



void KEY_TK_Init(void)
{
        u8 i,j;

        TSCHEN1 = 0xF0;        //TK04~TK07
        TSRT = 0x00;                //没有LED分时扫描
        TSCFG1= (7&lt;&lt;4) + 5;        //开关电容工作频率 = fosc/(2*(TSCFG1+1)), 放电时间(系统时钟周期数) 0(125) 1(250) 2(500) 3(1000) 4(2000) 5(2500) 6(5000) 7(7500) 最小3
        TSCFG2= 2;                                        //配置触摸按键控制器的内部参考电压(AVCC的分压比), 0(1/4)1(1/2)2(5/8)3(3/4)
        TSCTRL = 0xA0;                                //开始自动扫描, 无平均, B7: TSGO,B6: SINGLE,B5: TSWAIT, B4: TSWUCS, B3: TSDCEN, B2: TSWUEN, B1 B0: TSSAMP
//        TSCTRL = (1&lt;&lt;7) + (1&lt;&lt;6);        //开始单次扫描, 无平均
//        TSCTRL = (1&lt;&lt;7) + (1&lt;&lt;6)+3;        //开始单次扫描, 4次平均
//        TSCTRL = (1&lt;&lt;7) + (1&lt;&lt;6)+1;        //开始单次扫描, 2次平均
//        TSWUTC = 12;                //100ms唤醒一次
        IE2 |= 0x80;                        //使能触摸中断

        delay_ms(50);

        while(j&lt;=10)//读取10次键值,将此值作为未触摸时的0点, 要求上电时不要触摸按键
        {
                WDT_Clear();   //喂狗
                if( B_ReadKeyOk ){                //每完成一次循环
                        for(i=0;i&lt;TK_Length;i++){
                                TK_zero += TK_cnt;//保存初始化值做为0点
                        }
                        B_ReadKeyOk = 0;
                        j++;
                }
                delay_ms(25);
        }

        for(i=0;i&lt;TK_Length;i++){
                TK_zero=(TK_zero&gt;&gt;3);//取8次读取后的平均值
                TK_zero=TK_zero-TK_zero%10;//取整数值,将此值视为初始化后的0点
                switch(i)
                {
                        case 0:
                                T_KeyCmp=TK_zero/30;
                                break;
                        case 1:
                                T_KeyCmp=TK_zero/30;
                                break;
                        case 2:
                                T_KeyCmp=TK_zero/30;
                                break;
                        case 3:
                                T_KeyCmp=TK_zero/40;
                                break;
                }
       
        }
}

//========================================================================
// 函数名称: KEY_Deal
// 函数功能: 按键状态读取
// 入口参数: 无
// 函数返回: 无
// 当前版本: VER1.0
// 修改日期: 2023 - 1-1
// 当前作者:
// 其他备注:循环读取八个端口的状态,并将按下的时间赋值给 Count 数组,按下的状态赋值给LastState
//========================================================================
void KEY_Deal(void)                        //检查所有的按键状态 10ms执行一次
{
        u8 i = 0;
if(!B_ReadKeyOk){//循环读取完成才进入按键状态读取,12ms左右一个周期
                return;
        }
        B_ReadKeyOk = 0;

//        printf(&quot;(%u,%u,%u)\r\n&quot;,(u16)TK_cnt,(u16)TK_cnt,(u16)TK_cnt);
        for(i=0;i&lt;TK_Length;i++)//循环4次 i的取值范围是0-3代表了P14-P17的状态查询
        {
                if( TK_cnt &lt; (TK_zero-T_KeyCmp))        //当前读取的值与初始值比较,持续按下,变量+1
                {
                        if( Count&lt;60000 )
                                Count ++;        //按键按下,这个计数变量+1
                }
                else                                                        //按键松开了,变量清0
                {
                        if( Count&gt;0 )//如果这个变量是按下过的
                        {
                                LastState |= (1&lt;&lt;i);        //这个变量相应的标志位置位
                        }
                        else
                        {
                                LastState &amp;= ~(1&lt;&lt;i);        //这个变量相应的标志位清0
                        }
                        Count = 0;                                //按键按下,这个计数变量清0
                }
        }
}

//========================================================================
// 函数名称: KEY_ReadState
// 函数功能: 读取指定的按钮的状态
// 入口参数: @keynum : 按钮的端口号 例如P32端口,端口号就是2
// 函数返回: 看其他备注
// 当前版本: VER1.0
// 修改日期: 2023 - 1- 1
// 当前作者:
// 其他备注: 函数返回值如下
//        #define        KEY_NOPRESS                0                //按键未按下
//        #define        KEY_FILCKER                1                //消抖
//        #define        KEY_PRESS                2                //单击
//        #define        KEY_PRESSOVER        3                //单击结束
//        #define        KEY_LONGPRESS        4                //长按3秒
//        #define        KEY_LONGOVER        5                //长按结束
//        #define        KEY_RELAX                6                //按键松开

//========================================================================

u8 KEY_ReadState(u8 keynum)        //读取指定的按键的状态 10ms执行一次
{
        if( Count&gt;0 )                                        //按键是按下的各种状态返回
        {
                if( Count&lt;5 )                                //按下&lt;50ms 返回消抖状态
                {
                        return KEY_FILCKER;
                }
                else if( Count==5 )                        //按下正好50ms 返回单击
                {
                        return KEY_PRESS;
                }
                else if( Count&lt;150 )                //按下不到3000ms 返回单击结束
                {
                        return KEY_PRESSOVER;
                }       
                else if( Count==150 )                //按下正好3000ms 返回长按状态
                {
                        return KEY_LONGPRESS;
                }
                else                                                                //长按结束
                {
                        return KEY_LONGOVER;
                }
        }
        else                                                                        //按键已经松开了,返回各种状态
        {
                if( LastState &amp;( 1&lt;&lt;keynum ) )                //按键之前按下过
                {
                        return KEY_RELAX;       
                }
                else                                                                //按键之前没有按下
                {
                        return KEY_NOPRESS;
                }
        }
}
</code></pre>
<p><strong>问题二:由于我这里只用了4个按键(TK4-TK7),那么在中断扫描通道时,是否还是16个全部扫描?有没有办法从TK4扫描到TK7时,直接回到TK4重新扫描?</strong></p>
<p><strong>问题三:在KEY_Deal函数中,查询当前按键值的for循环,能否改成非阻塞式的查询?像普通按键一样用按键状态值做按键判断?</strong></p>
<p>3、根据手册的触摸按键PCB设计建议,做了以下修改,不知修改得对不对,麻烦大佬们指点一下。</p>
<p><img src="data/attachment/forum/202507/15/094511z7vt02tjy2cxzcj7.png" alt="image.png" title="image.png" /></p>
<p><strong>问题四:本来想在触摸弹簧下放置一个实心感应盘的,但是现在在触摸弹簧下加了一个LED,这个实心感应盘还能放吗?如果还能放的话,那么这个感应盘应该怎么设计?</strong></p>
<p><strong>问题五:铜皮是否要改成45°网格状?</strong></p>
<p>以上问题,有大佬看到的话,麻烦帮忙解答一下,谢谢🙏🙏</p>

DebugLab 发表于 2025-7-15 10:04:26

必须改PCB
触摸按键区域,顶层铜挖空,外扩至少1mm
所有触摸按键网络走线添加规则,距离其他铜至少0.5mm
附近至少5mm,网状铺铜

ZARD渐空 发表于 2025-7-15 10:15:45

DebugLab 发表于 2025-7-15 10:04
必须改PCB
触摸按键区域,顶层铜挖空,外扩至少1mm
所有触摸按键网络走线添加规则,距离其他铜至少0.5mm


意思是可以不用放实心感应盘?底层也要挖空吧?

DebugLab 发表于 2025-7-15 10:19:32

ZARD渐空 发表于 2025-7-15 10:15
意思是可以不用放实心感应盘?底层也要挖空吧?
只有和手指感应的面才允许有大面积连接触摸网络的导体
用弹簧,PCB上不能有大面积连接触摸网络的铜,两个小焊点就可以了
比如弹簧直径10mm,PCB顶层铜要挖空至少12mm直径一个圆,顶层和底层20mm直径圆形范围内网状铺铜或挖空
外壳或附近三维结构有电磁屏蔽措施或电磁干扰小的应用环境或PCB底部距离非金属外壳较远时,底层才可挖空
如果底层挖空且PCB背面安装在不同介电常数或电导率的物体上或背面有人体接近,触摸按键值可能漂移
附近接地的铺铜和触摸按键网络之间的分布电容越小,触摸按键越容易受外部环境影响(所以背面要加一点网状铺铜,稍微给一点参考平面,但耦合度又不能太大),反之会降低灵敏度

ZARD渐空 发表于 2025-7-15 10:27:25

DebugLab 发表于 2025-7-15 10:19
只有和手指感应的面才允许有大面积连接触摸网络的导体
用弹簧,PCB上不能有大面积连接触摸网络的铜,两个 ...

<p>那改成这样可不可以?底层要不要一起网状铺铜?</p>
<p><img src="data/attachment/forum/202507/15/102716l8ec2nn9iccccw1m.png" alt="image.png" title="image.png" /></p>

ZARD渐空 发表于 2025-7-15 10:27:58

DebugLab 发表于 2025-7-15 10:19
只有和手指感应的面才允许有大面积连接触摸网络的导体
用弹簧,PCB上不能有大面积连接触摸网络的铜,两个 ...

理解了,谢谢

DebugLab 发表于 2025-7-15 10:36:17

ZARD渐空 发表于 2025-7-15 10:27
那改成这样可不可以?底层要不要一起网状铺铜?
5楼有修改请再次仔细阅读
此PCB图:
弹簧和LED走线交叉的地方,实心丝印,绝缘加厚,避免阻焊被磨破短路,直接画一个和弹簧直径相同宽1mm的圆就行
以下两种情况需要背面网状铺铜
1、如果背面距离非金属外壳较近且背面会安装在介电常数或电导率不同的物体上,或背面可能有人体接近,为了避免背面电磁环境影响触摸按键,需要背面网状铺铜
2、电磁干扰较严重应用场合,尤其是来自背面的电磁干扰,需要背面网状铺铜

ZARD渐空 发表于 2025-7-15 10:41:08

DebugLab 发表于 2025-7-15 10:36
5楼有修改请再次仔细阅读
此PCB图:
以下两种情况需要背面网状铺铜


好的,看到了,谢谢大佬

梁工 发表于 2025-7-15 11:31:35

做触摸按键基本要求:尽量减小分布电容,触摸感应面积尽量大,这样触摸后读数的变化率尽量大,能得到很好的效果,一般要求不触摸时读数为10000~20000左右,触摸后读数变化5%以上,能达到10%以上会非常好。
1、铺铜时,铜箔离按键线间隔不要小于0.5mm,触摸线要细、短,以此减小分布电容。
2、网格铺铜。
3、触摸线附近不要有高速变化的信号线,避免干扰。
4、尽量加大触摸感应面积。
5、相邻触摸线可以并走一条细地线隔开,避免两键相互影响。
6、一定要做0点跟踪。

回答你的问题:
问题一:在不改PCB的情况下,能否通过改变软件来提高灵敏度?
答:不能!灵敏度由分布电容和触摸改变的电容量决定,软件无法改变的。
比如分布电容为10pF,触摸后增大0.8pF,则读数改变率为0.8/10=0.08=8%,这个变化率就比较好,灵敏度高,使用可靠。
比如分布电容为10pF,触摸后增大0.3pF,则读数改变率为0.3/10=0.03=3%,这个变化率就比较差,灵敏度低,使用不可靠。

问题二:由于我这里只用了4个按键(TK4-TK7),那么在中断扫描通道时,是否还是16个全部扫描?
答:允许几个键就扫描几个,没有允许的不扫描。

问题三:在KEY_Deal函数中,查询当前按键值的for循环,能否改成非阻塞式的查询?
答:使用中断,扫描完成,进入中断读取,几乎不耗CPU时间。

问题四:本来想在触摸弹簧下放置一个实心感应盘的,但是现在在触摸弹簧下加了一个LED,这个实心感应盘还能放吗?
答:有感应弹簧,就不要再放实心感应盘,会加大分布电容,降低灵敏度的。

问题五:铜皮是否要改成45°网格状?
答:不用45度,一般的横竖网格就可以了。网格铺铜就是为了减小分布电容,又能抑制干扰。

ZARD渐空 发表于 2025-7-17 13:48:13

梁工 发表于 2025-7-15 11:31
做触摸按键基本要求:尽量减小分布电容,触摸感应面积尽量大,这样触摸后读数的变化率尽量大,能得到很好的 ...

谢谢梁工解答
页: [1] 2
查看完整版本: 触摸按键,不隔面板时反应很灵敏,隔了一个1.7mm的亚克力面板后就不灵敏了