触摸电子琴从无到有心得分享——基于STC8H4K64TL的电子琴
八、触摸程序编写
上面的章节我们已经测试过触摸没有问题了,并且也找到了一个比较好用的触摸程序,那么现在话不多说先把他的触摸程序移植过来,他的就是我的,我的也还是我的!
首先打开上个章节用过的那个代码,
前面几行是变量,IO等的初始化,这些都一样,这里先不管他,我们把他最终要的触摸的功能(红色框)复制到我们的代码里,绿色框他是串口的,我们也可以复制过来帮助我们调试,蓝色框他是触摸变量用到的初始,他这个功能比较强大,还加了零点追踪的功能,这里先给大家用最简单的办法,先不考虑这么复杂的功能,先开始移植
第一步,先开始移植串口
-
- //========================================================================
- // 函数: void UART1_config(u32 clk, u32 brt)
- // 描述: UART1初始化函数。
- // 参数: clk: 系统主频.
- // brt: 通信波特率.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2021-7-29
- // 备注:
- //========================================================================
- void UART1_config(u32 clk, u32 brt)
- {
- brt = 65536UL - (clk / 4) / brt;
- 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中断
- INTCLKO &= ~0x02; // Timer1不输出高速时钟
- TR1 = 1; // 运行Timer1
-
- P_SW1 &= 0x3f;
- P_SW1 |= 0x00; //UART1 switch to, 0x00: P3.0 P3.1, 0x40: P3.6 P3.7, 0x80: P1.6 P1.7, 0xC0: P4.3 P4.4
-
- SCON = (SCON & 0x3f) | (1<<6); // 8位数据, 1位起始位, 1位停止位, 无校验
- // PS = 1; //高优先级中断
- ES = 1; //允许中断
- REN = 1; //允许接收
-
- B_TX1_Busy = 0;
- RX1_Cnt = 0;
- }
-
- //========================================================================
- // 函数: 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_int (void) interrupt UART1_VECTOR
- // 描述: 串口1中断函数
- // 参数: none.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2018-4-2
- // 备注:
- //========================================================================
- void UART1_int (void) interrupt UART1_VECTOR
- {
- if(RI)
- {
- RI = 0;
- if(RX1_Cnt >= CMD_LEN) RX1_Cnt = 0;
- RX1_Buffer[RX1_Cnt] = SBUF;
- RX1_Cnt++;
- RX1_TimeOut = 5;
- }
-
- if(TI)
- {
- TI = 0;
- B_TX1_Busy = 0;
- }
- }
复制代码
先把这几个串口初始化的代码全都复制到我们的代码里(上面的代码随便找个MCU的串口初始化代码都能用),然后现在main函数里调用初始化函数UART1_config,第一个入口参数是主时钟,第二个波特率,这里可以通过软件自己计算参数,这里我们就直接定死波特率115200好了。最后我们的程序里的串口初始化就是这么一行: - UART1_config(MAIN_Fosc,115200);
复制代码
这边串口接收函数我们也暂时不用,所以串口中断里接收的部分我们也给他全都屏蔽掉,如下蓝色部分屏蔽掉, - //========================================================================
- // 函数: void UART1_int (void) interrupt UART1_VECTOR
- // 描述: 串口1中断函数
- // 参数: none.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2018-4-2
- // 备注:
- //========================================================================
- void UART1_int (void) interrupt UART1_VECTOR
- {
- if(RI)
- {
- RI = 0;
- <font color="#00bfff">// if(RX1_Cnt >= CMD_LEN) RX1_Cnt = 0;
- // RX1_Buffer[RX1_Cnt] = SBUF;
- // RX1_Cnt++;
- // RX1_TimeOut = 5;</font>
- }
-
- if(TI)
- {
- TI = 0;
- B_TX1_Busy = 0;
- }
- }
复制代码
最后在为了能更加方便的使用我们的串口打印调试信息,这里我们增加一个printf的函数的支持,首先调用头文件 然后重定向一下串口的函数,也就是添加如下的代码 - char putchar(char dat)
- {
- UART1_TxByte(dat);
- return dat;
- }
复制代码
这样我们的工程就可以使用printf函数了,最后我们在main函数初始化串口完成之后加上下面这句,使能一下总中断 最后我们在while函数之前编写如下代码就完成了: - printf("基于STC8H4K64TL的触摸电子琴\r\n");
复制代码
我们来看一下测试效果: 可以看到这边串口打印出来了我们想要的数据,那这个函数成功了之后,后面我们就可以做很多的功能了。
第二步触摸初始化代码移植 - P1n_pure_input(0xff); //Touch Key设置为高阻
- P5n_pure_input(0x0f);
- P0n_pure_input(0x0f);
复制代码
可以看到我们原来的代码里有这么三行,这个就是为了初始化引脚的,我们上一张内容用来测试的代码写法好像没有这个好,那我们就还在这个基础上就好就好了,我们P1.6-P1.7因为后面要作为RTC时钟的晶振引脚,这里就不配置了,所以我们最后改好的初始化代码是这个样子的 - P1n_pure_input(0x3f); //Touch Key设置为高阻
- P5n_pure_input(0x0f);
- P0n_pure_input(0x0f);
复制代码
然后我们把上节内容里的触摸初始化的代码全都复制过来:放在我们之前的LED驱动代码之后,EA=1;之前。 - TSRT = 0x00; //没有LED分时扫描
- TSCHEN1 = 0xff; //TK00~TK07
- TSCHEN2 = 0xff; //TK08~TK15
- TSCFG1 = (7<<4) + 3; //开关电容工作频率 = fosc/(2*(TSCFG1[6:4]+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<<7) + (1<<6); //开始单次扫描, 无平均
- // TSCTRL = (1<<7) + (1<<6)+3; //开始单次扫描, 4次平均
- // TSCTRL = (1<<7) + (1<<6)+1; //开始单次扫描, 2次平均
- // TSWUTC = 12; //100ms唤醒一次
- IE2 |= 0x80; //使能触摸中断
复制代码
来一个个看下这里提到的寄存器。
第一个TSRT寄存器,说这个接了触摸的IO口如果又要接触摸,又要接指示灯可以用这个功能,不过这里我们不需要,那这个寄存器就和他说的这样直接写0就好了。
这里TSCHENn是用来配置触摸是否需要,总共16个触摸,要用的通道对应的位置写1就好,这里的话,我们除了P16,P17别的都选中,所以这里我们要把它写成
- TSCHEN1 = 0x3f; //TK00~TK05
- TSCHEN2 = 0xff; //TK08~TK15
复制代码
在下一个寄存器是
这里就是一些系统时钟和参考电源的设置,当然我们历程能用,且用的好好地我们就不需要再去修改它了。
最后一个就是这里的TSCTRL寄存器了,这个寄存器具体描述可以看手册的这部分描述,这里的话我们需要按键一直检测,所以我们直接把它配置为启动模式,重复扫描,并且完成一轮采集后需要手动清除TSIF的标志位才能继续,这样也能减轻CPU的开销,毕竟不会频繁的进入中断。
这就是整个触摸检测流程的介绍了,说白了这些配置就是为了帮助我们设置一下触摸的参数,整套触摸的配置流程其实就这下面的这个图而已,手册写的还是很清晰的!
第三触摸参数的读取 - //========================================================================
- // 函数: void TKSU_Interrupt(void)
- // 描述: 触摸按键中断。
- // 参数: none.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2021-02-01
- // 备注:
- //========================================================================
- void TKSU_Interrupt(void) interrupt 13
- {
- u8 j;
- j = TSSTA2;
- if(j & 0x40) //数据溢出, 错误处理(略)
- {
- TSSTA2 |= 0x40; //写1清零
- }
- if(j & 0x80) //扫描完成
- {
- j &= 0x0f;
- TK_cnt[j] = TSDAT; //保存某个通道的读数
- TSSTA2 |= 0x80; //写1清零
- read_cnt++; //读次数+1, 用于延时或读键计数
- TK_TimeOut = 0;
- }
- }
复制代码
可以看到我们之前测试的那个代码里的数据采集全部在中断里实现,首先读取TSSTA2寄存器判断是哪个中断源,是溢出中断不处理,是扫描完成中断的话把数据保存下来。TSSTA2的最高位是扫描完成的中断,次高位是溢出,最低四位是哪个通道,所以判断到最高位是1直接读取这个数据保存到这个通道的数组里就完成了。
这个中断采集的函数我们给他稍稍的改写一下,变成这个样子: - //========================================================================
- // 函数: void TKSU_Interrupt(void)
- // 描述: 触摸按键中断。
- // 参数: none.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2021-02-01
- // 备注:
- //========================================================================
- void TKSU_Interrupt(void) interrupt 13
- {
- u8 j;
- j = TSSTA2;
- if(j & 0x40) //数据溢出, 错误处理(略)
- {
- TSSTA2 |= 0x40; //写1清零
- }
- if(j & 0x80) //扫描完成
- {
- j &= 0x0f;
- TSSTA2 |= 0x80; //写1清零
- TK_cnt[j] = TSDAT; //保存某个通道的读数
- if( j==15 )
- B_ReadKeyOk=1;
- }
- }
复制代码
这里增加了一个B_ReadKeyOk标志位,这个标志位是原来的工程里的,表示16个通道全部检测完成了,把这个标志位置1,看起来有用就把他带上了,这样我们就可以判断当前一轮循环结束了没有。其次这里读取完一次一定要记得手动清除TSSTA2 这个里的标志位!!当然这里每个通道的数据最终都会保存到TK_cnt的数组里。数组第几个元素就是第几个通道的ADC,当然这里用到了一个空中断的小技巧,可以看到这里中断向量号是13,至于为什么大家可以去看手册的这一张,里面有详细解读: 上面一步理论来说我们已经能能够正确的读取到了触摸的数据,但是这个数据对不对我们是不是可以通过串口打印出来看下,上面我们已经调通了串口,调通的adc,那这里我们直接在while函数里把触摸的数据打印出来看看就好了,当然也可以吧之前的那个数码管显示的代码先屏蔽掉,直接册数adc,我们直接编写如下代码: - if( B_ReadKeyOk )
- {
- B_ReadKeyOk = 0;
- printf("(%u,%u)\r\n",TK_cnt[0],TK_cnt[1]);
- }
- delay_ms(20);
复制代码
这里我们就简单的打印两路数据就好了,因为是循环采集的,两路数据没问题那别的应该也没有问题的,上面的代码写入主函数,然后我们开始编译下载:
这里我们就已经可以打印出当触摸通道0和触摸通道1的ADC数据了,但是这样看数据不是特别的直观,我们用绘图软件去看一下, 这里我用的RCOM,大家也可以用自己喜欢的,我用的这个软件的数据个数就是"(通道0数据,通道1数据)"就可以接收到数据了,我们先打开这个软件配置一下数据:
可以看到我们这个曲线图里面,红色的是我们通道0的数据,绿色的是通道1的数据,无论通道1或者通道2按下都会有明显的数值变化,数值基本和我们上一节测试的差值差不多,说明我们这一章的代码都是可以的,不过这里能看得出来通道0按下的时候,通道1的数据也会变小一点点。通道1按下的时候通道0的数值也会干扰,这个就是走线的时候,触摸的线和焊盘考的太近了的缘故,这个下一个板子可以修改一下,当然了从数值上分析误差只有几百,我们可以忽略不计,不影响我们对他的整个功能的调试~触摸和串口的驱动到这里就完美的结束了~
|