|
鉴于早期的QTouch Lib都只支持用IAR编译的,官方的程序范例也是IAR的,所以特别针对AVR Studio和论坛上的蜗牛触摸板,教大家在AVR Studio中使用QTouch Lib。由于本人水平有限,难免有错漏之处,欢迎大家指正。
首先,确定你有下面这些东西。
1、爱特梅尔QTouch库用户指南(原名:Atmel QTouch Library User Guide)。这里建议大家使用最新版本。
http://www.atmel.com/dyn/resources/prod_documents/doc8207.pdf
2、AVR Studio 4.17 build 666
http://www.atmel.com/forms/software_download.asp?fn=dl_AvrStudio417Setup.exe(下载前需注册)
3、WinAVR-20090313
http://downloads.sourceforge.net/project/winavr/WinAVR/20090313/WinAVR-20090313-install.exe
4、Atmel QTouch Library 3.0
http://www.atmel.com/forms/software_download.asp?fn=dl_Atmel_QTouch_Libraries_3.0.exe
安装好AVR Studio、WinAVR和Atmel QTouch Library后,运行AVR Studio,新建一个工程,在左方工程名上点击右键,选择“Edit Configuration Options...”。
![]()
在工程名上点击右键 (原文件名:1.JPG)
选择“Include Directories”,再点右上方的按钮“New(Insert)”,添加库里面包含文件的路径。默认安装的话,路径是“C:\Program Files\Atmel\Atmel QTouch Libraries 3.0\include\”。
![]()
设置包含文件目录 (原文件名:2.JPG)
选择“Libraries”,同样点击右上方的按钮“New(Insert)”,添加库文件的路径。默认安装的话,路径是“C:\Program Files\Atmel\Atmel QTouch Libraries 3.0\megaAVR, tinyAVRand XMEGA libray\QTouch library\library files\”。此时,再在“Available Link”里选“libavr4g2-8qt-k-2rs.a”,点击“Add Library --”按钮。下文将解释为什么要选这个。
![]()
加入库 (原文件名:3.JPG)
接下来就可以动手写代码了。
要使用QTouch Lib里的函数,必须包含touch_api.h。这个文件在默认的情况下,在“C:\Program Files\Atmel\Atmel QTouch Libraries 3.0\include\”里面。而上面我们已经把这个目录添加为包含目录了,所以就可以省去冗长的路径,直接写出文件名。
#include"touch_api.h"
然而,在包含这个目录之前,QTouch Lib还要求我们先对几个参数进行定义,定义的方法是预编译。
#define _QTOUCH_
#define QT_NUM_CHANNELS 8
#define QT_MAX_NUM_ROTORS_SLIDERS 2
#define _ROTOR_SLIDER_
#define SNSK B
#define SNS D
#define QT_DELAY_CYCLES 10
_QTOUCH_
使用QTouch技术,而非其它时,需作此声明。
QT_NUM_CHANNELS
这是感应通道的总数。应注意,这个定义必须和所选的库一致,即为8。即使你只使用了1个通道,也务必请你定义成“8”,否则将不能正常工作。
QT_MAX_NUM_ROTORS_SLIDERS 2
这是滑轮和滑条的最大总数目。这里,我们包含的库最多只支持两个,而蜗牛触摸板上也刚好只有一个滑轮和一个滑条,1+1=2。
_ROTOR_SLIDER_
若使用滑轮和滑条,需作此声明。
SNSK
这是感应按键的端口。这一端通过一个电阻连接到按键。
SNS
这是感应端口,通过一个电容后连接到SNSK。
QT_DELAY_CYCLES
延迟的时钟周期个数,需要根据工作频率设定。
再次提醒,包含touch_api.h前,需作上述定义。
定义好以后,就可以包含头文件了。这里我们用到了三个头文件。
#include"avr\io.h"
#include"avr\interrupt.h"
#include"touch_api.h"
要使用QTouch技术的话,I/O口的内部上拉电阻必须禁用。方法是让MCUCR里的PUD置位。
MCUCR|=(1<<PUD);
接下来就可以定义按键、滑轮和滑条了。定义按键,用qt_enable_key()函数。这个函数有四个参数,分别是通道编号、邻键抑制组、重校正门限百分比和滞后百分比。
按照手册上的说法,连接I/O口第1位的,为通道1,以次类推。按照这个说法,A键应为通道6。实际试验下,A键也确实为通道6,B键为通道7。原理图上的标注有误。
我们来定义A键。
qt_enable_key(CHANNEL_6, NO_AKS_GROUP, RECAL_12_5, HYST_25);
如果想同时使用A键和B键,只需在定义完A键后,继续定义B键即可。我们再来定义B键。
qt_enable_key(CHANNEL_7, NO_AKS_GROUP, RECAL_12_5, HYST_25);
定义滑轮和滑条的方法与按键一样,只是所使用的函数和参数不一样,在这里不再展开叙述,请参考官方说明文档。
定义滑轮用qt_enable_rotor()。
qt_enable_rotor(CHANNEL_3, CHANNEL_5, NO_AKS_GROUP, 10u, HYST_50, RES_2_BIT, HYST_50);
滑条用qt_enable_slider()
qt_enable_slider(CHANNEL_0, CHANNEL_2, NO_AKS_GROUP, 10u, HYST_50, RES_2_BIT, HYST_50);
这样,我们的整个定义过程就完成了。
qt_enable_key(CHANNEL_6, NO_AKS_GROUP, RECAL_12_5, HYST_25);
qt_enable_key(CHANNEL_7, NO_AKS_GROUP, RECAL_12_5, HYST_25);
qt_enable_rotor(CHANNEL_3, CHANNEL_5, NO_AKS_GROUP, 10u, HYST_50, RES_2_BIT, HYST_50);
qt_enable_slider(CHANNEL_0, CHANNEL_2, NO_AKS_GROUP, 10u, HYST_50, RES_2_BIT, HYST_50);
定义完成后,需要让各个感应器初始化,方法是使用qt_init_sensing()函数。
qt_init_sensing();
这样,感应器就可以工作了。
QTouch的感应器,是不能产生中断的,所以我们要用查询的方法来检测按键、滑轮等的状态是否发生了变化。并且这个检测过程,也是要我们手动调用函数来进行的。想让QTouch检测各个通道的状态,需要调用qt_measure_sensors()。这个函数有一个参数,是当前系统的运行时间(或曰QTouch被开启的时间,手册上未明确说明)。这个时间是以毫秒为单位的,类型是unsigned int。所以,我们还需要开启一个定时器,来计算时间。在8MHz的时钟频率下,T1采用64分频,记数125次,恰好是1毫秒。于是我们定义一个全局变量hw_time_ms。
volatile unsigned int hw_time_ms;
每次命令QTouch去检测各通道的情况时,我们就用
qt_measure_sensors(hw_time_ms);
调用完这个函数后,可以通过查询sensor_states数组(其元素为unsigned char类型)来获取各感应器当前的状态。对于滑轮和滑条,则需查询数组rotor_slider_values。sensor_states位于结构体qt_touch_status_t里,而qt_touch_status_t则位于另一个结构体qt_touch_lib_measure_data_t当中。并且,qt_touch_lib_measure_data_t不需要我们去定义,touch_api.h已经帮我们做好了这个工作了。touch_api.h里有这样的语句
typedef struct tag_qt_touch_lib_measure_data_t
{
/* measured signal on each channel */
uint16_t channel_signals[QT_NUM_CHANNELS];
/* reference signal for each channel */
uint16_t channel_references[QT_NUM_CHANNELS];
/* state of sensors */
qt_touch_status_t qt_touch_status;
} qt_touch_lib_measure_data_t;
……
extern qt_touch_lib_measure_data_t qt_measure_data;
所以,在实际应用中,我们只需要这样写,便能查询按键A是否被按下了。
qt_measure_data.qt_touch_status.sensor_states[0]&0x01
sensor_states这个数组,第1个元素的第一位,记录着第1个感应器的状态。比如说上面的那条语句,就可以判断第1个感应器是否被触摸了。在这里,第1个感应器是A键。若为1,则表明A键被按下了;若为0,则表明未被按下。为什么第1位就代表按键A(通道6)呢?这涉及到一个顺序问题,将在下面统一讲解。
于是,查询程序可以这样编写。下面这段代码可以实现A键被按下时,A灯亮起;松开时,A灯熄灭。
while(1)
{
qt_measure_sensors(hw_time_ms);
if(qt_measure_data.qt_touch_status.sensor_states[0]&0x01)
PORTC&=~(1<<PC0);//A灯亮
else
PORTC|=(1<<PC0);//A灯灭
}
下面来解释感应器的顺序问题。如上文所述,为什么sensor_states[0]的第1位表示着按键A,即通道6的状态呢?原来我们在定义按键等时,是这样定义的。
qt_enable_key(CHANNEL_6, NO_AKS_GROUP, RECAL_12_5, HYST_25);//A键
qt_enable_key(CHANNEL_7, NO_AKS_GROUP, RECAL_12_5, HYST_25);//B键
qt_enable_rotor(CHANNEL_3, CHANNEL_5, NO_AKS_GROUP,10u,HYST_50, RES_2_BIT, HYST_50);//滑轮
qt_enable_slider(CHANNEL_0, CHANNEL_2, NO_AKS_GROUP,10u,HYST_50, RES_2_BIT, HYST_50);//滑条
我们第1行语句,就定义了通道6,这决定了通道6为感应器1。第2行语句定义了通道7,所以通道7为感应器2。同理,通道3——通道5(整个滑轮)为感应器3,通道0——通道2(整个滑条)为感应器4。而sensor_states[0]是8位的,它的第1位告诉我们感应器1的通道(通道6)有没有被触摸,第2位告诉我们感应器2的通道(通道7)有没有被触摸。被触摸,即为1;未被触摸,当然就是0。而通道3、4、5(即感应器3的各个通道)中的任何一个通道被触摸,都会使sensor_states[0]的第3位为1。同样,通道0、1、2(即感应器4的各个通道)中的任何一个通道被触摸,都会使sensor_states[0]的第4位为1。
所以,在这个定义顺序下,当A键和滑条同时被触摸,而其余未被触摸时,sensor_states[0]的值为0bXXXX1001(“X”表示该位因无感应器而无法判断)。而当B键和滑轮同时被触摸,而其它未然时,sensor_states[0]的值应为0bXXXX0110。
若我们改变定义的顺序,成为下面的情形时,就要发生相应的改变了。
qt_enable_key(CHANNEL_6, NO_AKS_GROUP, RECAL_12_5, HYST_25);//A键
qt_enable_rotor(CHANNEL_3, CHANNEL_5, NO_AKS_GROUP, 10u, HYST_50, RES_2_BIT, HYST_50);//滑轮
qt_enable_key(CHANNEL_7, NO_AKS_GROUP, RECAL_12_5, HYST_25);//B键
qt_enable_slider(CHANNEL_0, CHANNEL_2, NO_AKS_GROUP, 10u, HYST_50, RES_2_BIT, HYST_50);//滑条
A键为感应器1,对应sensor_states[0]的第1位;
滑轮为感应器2,对应sensor_states[0]的第2位;
B键位感应器3,对应sensor_states[0]的第3位;
滑条为感应器4,对应sensor_states[0]的第1位。
另外,若通道超过8个,则第9个通道的被触标志位,会被安排到sensor_states[1]的第1位;第10个通道的被触标志位,会被安排到sensor_states[1]的第2位,以次类推。
滑轮和滑条除了能通过查询sensor_states来判断是否被触摸以外,还能通过查询rotor_slider_values来获取角度、位置信息。对于滑轮,rotor_slider_values中记录的是角度;对于滑条,记录的则是被触点离边缘的距离。
rotor_slider_values是个unsigned int类型的数组。它的第1个元素,是第1个被定义的滑轮(或滑条),以次类推。比如上面的两次定义,都是先定义滑轮,再定义滑条的,所以滑轮对应着rotor_slider_values[0],滑条对应着rotor_slider_values[1]。
应当注意,rotor_slider_values里各感应器的顺序,是独立于sensor_states里的顺序的,即rotor_slider_values与sensor_states无关。如果你先定义了100个按键,再去定义1个滑轮,那么这个滑轮的角度仍然应当用rotor_slider_values[0]去查询。若你想知道这个滑轮是否被碰到了,可以用sensor_states[100]。sensor_states[0]到sensor_states[99]对应的是前面100个按键。
接下来,要讲的是库文件命名规则。我们选的库是libavr4g2-8qt-k-2rs.a。其中,“libavr4g2”是ATmega88这款芯片本身决定的,通过查手册获得。“8qt”表示这个库最多支持8个通道,“2rs”表示最多支持2个滑轮或滑条(两者之和)。
到这里,讲解就全部结束了。其余没有提到的部分(邻键抑制组、重校正门限百分比等)请自行参考手册。最后送上已经调通的代码和HEX文件。工作时钟为8MHz,-0s优化方式,不进行时钟分频。
[相关下载]
代码及HEX文件ourdev_491248.zip(文件大小:6K) (原文件名:Snail Touch.zip) |
阿莫论坛20周年了!感谢大家的支持与爱护!!
“你必须好好活下去,任何时候都不要失去勇气。我们都会走过这个困难的时代。” ——《黄河绝恋》(1999)
|