|
本帖最后由 慕名而来 于 2023-6-19 21:08 编辑
STC8H***T、ST8H***T**型号的单片机内部带有触摸按键功能,经过一段时间的玩耍感觉挺有趣的,对此感兴趣的新手朋友可以弄个板子玩一玩,我在刚刚上手这个功能时也曾经在这里分享过一些不成熟的经验,
STC8H单片机硬件触摸按键的简单实现(1)——官方例程与配置软件
https://www.mydigit.cn/forum.php?mod=viewthread&tid=353279&fromuid=1505079
(出处: 数码之家)
STC8H单片机硬件触摸按键的简单实现(2)——代码分享
https://www.mydigit.cn/forum.php?mod=viewthread&tid=353464&fromuid=1505079
(出处: 数码之家)
--------------文归正题------------
单片机硬件触摸按键的实现就配置寄存器而言对于玩51单片机的人来说都是毫无难度的,无外乎就是按键对应通道的使能、按键电容充电频率的设置、放电时间的设置、参考比较电压的配置,至于工作原理此不赘述可详见数据手册或百度。
触摸按键的实现就程序而言最关键应该就是键值的获取,它由两个值组成即按键没有按下时的键值和按键按下时的键值,而这两个键值受PCB布局、按键面板材质以及最终制成品结构影响需要在整机装配完成的情况下进行测试,在上述链接的贴(1)中曾经提到了官方提供的按键参数测试软件,但因为当时我没有找到合适的配套例程所以没能玩明白,这里给出这个例程,你可以到STC新的官网参照下图按图索骥就能获得了:
这个软件包里既有配套的上位机软件也有测试用的代码,这个代码在使用时需要注意以下几点,一是烧录到你的芯片的时候一定要将时钟(新型STC单片机基本上都淘汰了外部晶振)设置为22.1184MHz。二是打开上位机软件时一定要将串口号码设置成与STC-ISP烧录软件中的串口号码一致,波特率要设置为115200。另外,软件包里还有一个上位机软件的使用说明,打开看一下就很容易操作了,烧录好了软件后打开上位机串口并点击软件中的“触摸数据”标签并设置好按键通道号并点击读“取数据”,此时如果软件下方表格中开始出现红色波形图了就说明一切可以正常操作了,接下来的操作就请详读说明书好了。
至此,就可以获取触摸键值了,如果你使用STC的例程中的按键判断模式来改编自己的代码的话,以上这些就够用了,但是本文如果到此结束则就有了抄书的嫌疑了,下面将我习惯使用的方法分享给大家,它是我用另一个官方例程实现的,没有使用官方的上位机软件,而是通过STC-ISP软件中的串口助手实现的,就是指定某个按键后在串口助手中发送改该按键的号码(16个按键编号为0-9-A-F不分大小写)就可以同时收到该键的键值,如果不触摸按键发送键号则得到是不触摸的键值,如果触摸按键并同时发送键号则得到的是触摸后的键值,每种状态发送10次就可以得到各自10个数据,各取平均值后就得到了一个按键的两个键值,如果只是DIY制作,我觉得这个方法看得见摸得着比较巴适。
用到的测试代码如下:使用时请详细阅读代码中的“功能说明”
- #include <STC8H.h>
- #define MAIN_Fosc 24000000L //定义主时钟 串口波特率 300 ~ 4800
- /************* 功能说明 **************
- //代码源自STC官方例程
- 工程文件:TouchKey-V10-中断读取.c
- 读取16个触摸按键
- 串口是用来读取触摸按键值, 不是必须的, 与触摸按键无关.
- 串口设置: 115200, 8, n, 1.
- 发送区以文本模式发送0~9与A~F(或a~f)键号
- 接收区以文本模式接收返回按键0~15(0~F)的键值.
- 注:
- 串口助手设置波特率为115200
- 1.串口助手“打开串口”后发送一个键号即可看到返回的该键未触摸的键值。
- 2.按住某个按键并发送键号后即可看到返回的该键触摸后的键值。
-
- 比如按键0, 不触摸时发送字符0, 返回不触摸的值, 然后触摸按键0时发送字符1, 返回触摸时的值, 两者差值就是下面表格T_KeyPress[]里的值(大约即可), 用于判断按键按下还是释放.
- K00=20143 未触摸读数
- K00=18527 已触摸读数
- 则20143-18527=1616, 此值就是T_KeyPress[0]的值1600, 接近即可.
- 所以, 用户将产品安装好之后, 读出每个键的未触摸与已触摸的读数, 计算出差值写于T_KeyPress[]中即可.
- 差值是未触摸的值的5%以上为合格, 达到10%以上为非常优秀. 占比小则灵敏度太低, 请重新审视硬件设计.
- 按键定义:
- P3.5 --> OLED SCL, P3.7 --> OLED SDA, P3.0 --> RXD, P3.1 --> TXD.
- P1.0-->TK0, P1.1-->TK1, P5.4-->TK2, P1.3-->TK3, P1.4-->TK4, P1.5-->TK5, P1.6-->TK6, P1.7-->TK7,
- P5.0-->TK8, P5.1-->TK9, P5.2-->TK1, P5.3-->TK1, P0.0-->TK1, P0.1-->TK1, P0.2-->TK1, P0.3-->TK1,
- ******************************************/
- /************* 本地变量声明 **************/
- u16 xdata TK_cnt[16]; // 按键计数值, 16位
- u16 xdata TK_zero[16]; // 按键0点值, 16位
- u16 KeyState; // 按键状态, 每个bit对应一个键, 1为按下, 0为释放
- u8 read_cnt; // 读次数
- bit B_TK_Lowpass; // 低通允许, 1允许, 0禁止
- u8 cmd; //串口命令
- bit B_TX1_Busy; // 串口发送忙标志
- /************* 本地函数声明 **************/
- void DisplayRTC(void);
- void delay_ms(u8 ms);
- void UART1_config(u32 brt, u8 timer, u8 io); // brt: 通信波特率, timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1, =1: 切换到P3.6 P3.7, =2: 切换到P1.6 P1.7, =3: 切换到P4.3 P4.4
- void UART1_PrintString(u8 *puts);
- void UART1_TxByte(u8 dat);
- void ReturnValue(u8 channel, u16 value);
- /**************** 外部函数声明和外部变量声明 *****************/
- u16 code T_KeyState[16]={0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,0x0100,0x0200,0x0400,0x0800,0x1000,0x2000,0x4000,0x8000};
- u16 code T_KeyPress[16]={1600,2200,1100,1200,1100,1400,1300,1600,1200,1500,1200,1500,900,1100,1300,1800};
- /********************* 主函数回调函数 *************************/
- void main(void)
- {
- u8 i;
- u16 j;
- P1n_pure_input(0xff); //设置所有触摸键的O为高阻
- P5n_pure_input(0x0f);
- P0n_pure_input(0x0f);
- P_SW2 |= 0x80; //允许访问扩展寄存器xsfr
- EA = 1;
- UART1_config(115200UL, 1, 0); // brt: 通信波特率, timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1, =1: 切换到P3.6 P3.7, =2: 切换到P1.6 P1.7, =3: 切换到P4.3 P4.4
- UART1_PrintString("STC8H系列触摸按键测试程序!\r\n");
- TSCHEN1 = 0xff; //TK0~TK7
- TSCHEN2 = 0xff; //TK8~TK15
- TSCFG1 = (7<<4) + 6; //B6~B4:开关电容工作频率 = fosc/(2*(TSCFG1[6:4]+1)), B2~B0:放电时间(系统时钟周期数) 0(125) 1(250) 2(500) 3(1000) 4(2000) 5(2500) 6(5000) 7(7500) 最好大于等于3(1000)
- TSCFG2 = 1; //B1~B0:配置触摸按键控制器的内部参考电压(AVCC的分压比), 0(1/4) 1(1/2) 2(5/8) 3(3/4)
- // TSCTRL = (1<<7) + (1<<6) +3; //开始扫描, B7: TSGO, B6: SINGLE, B5: TSWAIT, B4: TSWUCS, B3: TSDCEN, B2: TSWUEN, B1 B0: TSSAMP
- TSRT = 0x00; //没有LED分时扫描
- IE2 |= 0x80; //允许触摸按键中断
-
-
- delay_ms(50); //延时一下
- B_TK_Lowpass = 0; //禁止低通滤波
- for(read_cnt=0; read_cnt<10; read_cnt++) //扫描10次键, 将此值作为未触摸时的0点, 要求上电时不要触摸按键
- {
- // TSCTRL = (1<<7) + (1<<6) +3; //开始扫描, 4次平均
- TSCTRL = (1<<7) + (1<<6); //开始扫描, 无平均
- delay_ms(50); //延时一下, 等待扫描完成
- }
- for(i=0; i<16; i++)
- TK_zero[i] = TK_cnt[i]; //保存0点
- B_TK_Lowpass = 1; //允许低通滤波
- KeyState = 0;
- read_cnt = 0;
- while (1)
- {
- TSCTRL = (1<<7) + (1<<6); //开始扫描, 无平均
- delay_ms(20); //延时一下, 根据实际扫描速度调整
-
- if(++read_cnt >= 3)
- {
- read_cnt = 0;
- j = KeyState;
- for(i=0; i<16; i++)
- {
- // if((TK_zero[i] >= TK_cnt[i]) && ((TK_zero[i] - TK_cnt[i]) >= 300)) ShowValue(i,TK_zero[i] - TK_cnt[i]); //6ms 有键按下则显示差值
- // else ShowValue(i,TK_cnt[i]); //无键按下则显示读数
- if(TK_zero[i] > TK_cnt[i]) //0点值比当前值大, 则键按下, 缓慢0点跟踪
- {
- TK_zero[i]--; //缓慢0点跟随
- if((TK_zero[i] - TK_cnt[i]) >= T_KeyPress[i]/2)
- KeyState |= T_KeyState[i]; //变化值大于最大变化值的1/2, 则判断键已触摸
- else if((TK_zero[i] - TK_cnt[i]) <= T_KeyPress[i]/3)
- KeyState &= ~T_KeyState[i]; //变化值小于最大变化值的1/3, 则判断键已释放
- }
- else //0点值比当前值小, 则键释放
- {
- KeyState &= ~T_KeyState[i]; //键状态: 释放
- // TK_zero[i] = TK_cnt[i]; //快速0点回归(比如按着键上电)
- // TK_zero[i] += 50; //比较快速0点回归(比如按着键上电)
- if((TK_cnt[i] - TK_zero[i]) > 100) TK_zero[i] += 100; //差别很大, 则快速回0点
- else TK_zero[i] += 10; //差别不大, 则慢速回0点
- }
- }
- }
-
- if(cmd != 0)
- {
- if((cmd >= '0') && (cmd <= '9')) ReturnValue(cmd-'0', TK_cnt[cmd-'0']);
- if((cmd >= 'a') && (cmd <= 'z')) cmd = cmd -'a'+'A'; //小写转大写
- if((cmd >= 'A') && (cmd <= 'F')) ReturnValue(cmd-'A'+10, TK_cnt[cmd-'A'+10]);
- cmd = 0;
- }
-
- }
- }
- /**********************************************/
- //========================================================================
- // 函数: void ReturnValue(u8 channel, u16 value)
- // 描述: 返回按键值函数。
- // 参数: none.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-12-23
- // 备注:
- //========================================================================
- void ReturnValue(u8 channel, u16 value)
- {
- // value = value / 100; //缩小为1/100, 数值小一些比较容易观察
- UART1_TxByte('K');
- UART1_TxByte(channel/10+'0');
- UART1_TxByte(channel%10+'0');
- UART1_TxByte('=');
- UART1_TxByte(value / 10000 + '0');
- UART1_TxByte((value % 10000)/1000 + '0');
- UART1_TxByte((value % 1000)/100 + '0');
- UART1_TxByte((value % 100)/10 + '0');
- UART1_TxByte(value % 10 + '0');
- UART1_TxByte(0x0d);
- UART1_TxByte(0x0a);
- }
- //========================================================================
- // 函数: void delay_ms(u8 ms)
- // 描述: 延时函数。
- // 参数: ms,要延时的ms数, 这里只支持1~255ms. 自动适应主时钟.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2013-4-1
- // 备注:
- //========================================================================
- void delay_ms(u8 ms)
- {
- u16 i;
- do
- {
- i = MAIN_Fosc / 10000;
- while(--i) ;
- }while(--ms);
- }
-
- /**********************************************/
- u8 isr_index;
- //========================================================================
- // 函数: void AUXR_ISR(void) interrupt 13
- // 描述: 辅助中断函数
- // 参数: none.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2018-4-2
- // 备注: B_TK_Lowpass;// 低通允许, 1允许, 0禁止
- //========================================================================
- void AUXR_ISR(void) interrupt 35 //用户中断处理代码
- {
- u8 j;
- j = TSSTA2;
-
- if(j & 0x40) //数据溢出, 错误处理(略)
- {
- TSSTA2 |= 0x40; //写1清零
- }
- if(j & 0x80) //扫描完成
- {
- j &= 0x0f;//TSSTA2的低4位数据对应0-15个按键通道号码
- TSSTA2 |= 0x80; //写1清零
- if(!B_TK_Lowpass) TK_cnt[j] = TSDAT; //保存某个通道的读数, 无低通滤波
- else TK_cnt[j] = (TK_cnt[j] >> 1) + (TSDAT >> 1); //保存某个通道的读数 低通滤波 1/2
- }
- if(TK_cnt[4]<3000) P20=0;else P20=1;
- }
- //========================================================================
- // 函数: SetTimer2Baudraye(u16 dat)
- // 描述: 设置Timer2做波特率发生器。
- // 参数: dat: Timer2的重装值.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2018-4-2
- // 备注:
- //========================================================================
- void SetTimer2Baudraye(u16 dat) // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
- {
- AUXR &= ~(1<<4); //Timer stop
- AUXR &= ~(1<<3); //Timer2 set As Timer
- AUXR |= (1<<2); //Timer2 set as 1T mode
- TH2 = (u8)(dat >> 8);
- TL2 = (u8)dat;
- IE2 &= ~(1<<2); //禁止中断
- AUXR |= (1<<4); //Timer run enable
- }
- //========================================================================
- // 函数: void UART1_config(u32 brt, u8 timer, u8 io)
- // 描述: UART1初始化函数。
- // 参数: brt: 通信波特率.
- // timer: 波特率使用的定时器, timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率.
- // io: 串口1切换到的IO, io=1: 串口1切换到P3.0 P3.1, =1: 切换到P3.6 P3.7, =2: 切换到P1.6 P1.7, =3: 切换到P4.3 P4.4
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2018-4-2
- // 备注:
- //========================================================================
- void UART1_config(u32 brt, u8 timer, u8 io) // brt: 通信波特率, timer=2: 波特率使用定时器2, 其它值: 使用Timer1做波特率. io=0: 串口1切换到P3.0 P3.1, =1: 切换到P3.6 P3.7, =3: 切换到P4.3 P4.4
- {
- brt = 65536UL - (MAIN_Fosc / 4) / brt;
- if(timer == 2) //波特率使用定时器2
- {
- AUXR |= 0x01; //S1 BRT Use Timer2;
- SetTimer2Baudraye((u16)brt);
- }
- else //波特率使用定时器1
- {
- TR1 = 0;
- AUXR &= ~0x01; //S1 BRT Use Timer1;
- AUXR |= (1<<6); //Timer1 set as 1T mode
- TMOD &= ~(1<<6); //Timer1 set As Timer
- TMOD &= ~0x30; //Timer1_16bitAutoReload;
- TH1 = (u8)(brt >> 8);
- TL1 = (u8)brt;
- ET1 = 0; // 禁止Timer1中断
- INT_CLKO &= ~0x02; // Timer1不输出高速时钟
- TR1 = 1; // 运行Timer1
- }
- if(io == 1) {S1_USE_P36P37(); P3n_standard(0xc0);} //切换到 P3.6 P3.7
- else if(io == 2) {S1_USE_P16P17(); P1n_standard(0xc0);} //切换到 P1.6 P1.7
- else if(io == 3) {S1_USE_P43P44(); P4n_standard(0x18);} //切换到 P4.3 P4.4
- else {S1_USE_P30P31(); P3n_standard(0x03);} //切换到 P3.0 P3.1
- SCON = (SCON & 0x3f) | (1<<6); // 8位数据, 1位起始位, 1位停止位, 无校验
- // PS = 1; //高优先级中断
- ES = 1; //允许中断
- REN = 1; //允许接收
- }
- //========================================================================
- // 函数: void UART1_TxByte(u8 dat)
- // 描述: 串口1发送一个字节函数
- // 参数: dat: 要发送的单字节数据.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2018-4-2
- // 备注:
- //========================================================================
- void UART1_TxByte(u8 dat)
- {
- B_TX1_Busy = 1; //标志发送忙
- SBUF = dat; //发一个字节
- while(B_TX1_Busy); //等待发送完成
- }
- //========================================================================
- // 函数: void UART1_PrintString(u8 *puts)
- // 描述: 串口1字符串打印函数
- // 参数: puts: 字符串指针.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2018-4-2
- // 备注:
- //========================================================================
- void UART1_PrintString(u8 *puts)
- {
- for (; *puts != 0; puts++)
- {
- UART1_TxByte(*puts);
- }
- }
- //========================================================================
- // 函数: void UART1_ISR (void) interrupt UART1_VECTOR
- // 描述: 串口1中断函数
- // 参数: none.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2018-4-2
- // 备注:
- //========================================================================
- void UART1_ISR (void) interrupt UART1_VECTOR
- {
- if(RI)
- {
- RI = 0;
- cmd = SBUF;
- }
- if(TI)
- {
- TI = 0;
- B_TX1_Busy = 0;
- }
- }
复制代码
这段代码烧录到你的单片机中就可以获取键值了,需要注意的是烧录时需要设置时钟为24MHz,串口助手中的串口号要与左侧烧录用的串口好一致,波特率为115200。
演示入下图:
获取键值无论是用前一种标准方法还后一种变通方法,使用例程时只需要按要求烧录到你的单片机中就可以了,可以不用理会代码中写了什么,等到写你自己的代码时过来借用一下按键初始化、按键中断函数就可以了。
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|