jicheng0622

【原创】从零入手Kinetis系统开发(八)之TSI模块

1
阅读(8022)

一周没更新了,今天终于腾出时间再动笔写写了,哈哈。好几天的阴霾过去了,终于迎来了阳光普照的好天气,也许是因为最近好事连连心情大好,也许是因为天气转暖,总之俺感冒终于好转了,哎,不容易,所以继续未完的事业吧,哈哈~

最近好多网友在博客里或者QQ上问我有关K60开发的问题,自己的确有点忙不过来了,呵呵,所以希望有问题的网友尽量在博客里留言,我有空一定会尽量解答(咳咳,如果我不懂的就没办法了,嘿嘿)。还有最近看到fzjz网友也开始开源关于Kineits针对智能车的开发(智能车,对我来说是个怀念的老朋友,真怀念当年用dg128的日子,哈哈。排除掉FSL的市场战略不提,起码我一直觉着飞思卡尔在这一步上走的的确漂亮,相当于每年都有全国最顶级的学校最优秀的学生在给他的片子做测试,尤其是今年DSC,Codefire,Kineits一块上,看看Kinetis片子上的PKxxx就懂了,哈哈,不过这也是双方都获益的事情,我们学生从中的的确确收获了技术和开发经验,起码在我们学校,做过智能车的学生就是多了那么一股子自信,呵呵...所以也小小憧憬一下TI,ADI,ST,NXP也来吧,哈哈),很高兴在开源的路上又多了位战友,也希望有更多的人也参与进来,说不准有一天咱们的队伍就壮大了呢,嘿嘿,高手们参与进来互相进步,初手们更好上手,少走弯路,这就是我的初衷~

题外话又说完了,准备开始切入正题,呵呵。本来在系列八里是打算说手Kinetis中断的使用方法来,不过前几天玩了玩TSI,担心过段时间又生疏了所以就提前说说TSI模块了,下章不出情况的话会说说自己对Kinetis的中断使用的一些心得和技巧,敬请期待哦,哈哈,下面就开始了,enjoy it:

(1)首先说说什么是TSI,我们该知道现在触控技术主要分两种,电阻式触控和电容式触控(当然还有红外式和表面超声波式,这两种卡拉OK点播电视上用的较多,呵呵,要善于观察哈,不过在其他领域应用相对少些),而电容式触控感应技术貌似是从乔大爷的IPOD开始兴盛起来的,以前当然有,不过还是乔大爷发扬光大了,所以说人家是change the world,而咱们就只能know the words了,哈哈。本篇要谈的TSI模块(Touch Sensing Interface)就是飞思卡尔为简化硬件设计人员开发而嵌入到Kinetis架构的电容触控驱动模块,简单的驱动接口,接到一定面积的覆铜(最好设计成圆形或者方形,减少电容特性,呵呵)就OK了,所以大多数硬件工作FSL给咱省了,咱就在软件驱动上多下些功夫吧,到现在也写了Kinetis一部分的驱动了,觉着TSI部分的驱动写起来还是比较顺手的,思路比较清晰,下面揭晓:

(2)背景说完了,再说说原理吧(咳咳,有点写论文的感觉了,呵呵),参考的datasheet部分图片,简单的介绍下电容触摸感应的原理,不多说,上图,呵呵:

(3)还是循循善诱的来,上面既然搞清了电容触摸的原理,即电容值增加了,那就需要测得这个电容值的变化以识别手指的按下,这就是本篇的主角TSI模块要干的活了,哈哈,目前来说我只了解两种方法测量这个电容值的变化,一种是利用电容数字转换,即把电容值的变化反应到AD转换值的变化(貌似Silicon Lab一款片子是这样的),然后第二种就是飞思卡尔的这种,利用三角波电压同时充放电外部电容(可变)和内部参考电容(固定),然后采样内部计数器的计数值来确定电容的变化,第一种就不介绍了可以自己去谷歌一下,本篇就主要介绍下FSL的TSI内部构造,以便于下步的软件编程,其中有几个图非常重要,利于我们脑中建立起一个清晰的编程架构,下面上图:

(4)呵呵,到这里基本原理就该清晰了,如果还不清楚,那....咳咳赶紧恶补下模拟电路和数字电路吧,哈哈,没问题的话就可以喝口水歇歇,然后咱就接着来,呵呵,软件编程部分,也就是最后的收尾部分了,该一步步的操作寄存器初始化TSI和编写触控服务程序了,这里我没法拿出一个一个寄存器来分析了,以前有网友建议过这样,不过这样有点太细了,我精力的确跟不上了,呵呵,所以只能贴出代码来了,注释都有的(这回我换成中文的了,呵呵),根据注释和官方datasheet只要一步一步认真的来肯定没问题的,有问题的话可以下面留言,我解决不了还有广大网友的嘛,群众的力量是伟大的,哈哈。扯远了,下面贴出重要代码(完整代码见附件),也不贴出太多了,代码太多给人一种难以看下去的感觉,是吧,呵呵,每个函数都贴出声明了,估计理解应该没问题:

/********************************************************************************
**Routine: TSI_Start
**Description: TSI启动函数
**Notes:
********************************************************************************/
void TSI_Start(void)
{
TSI_Init(); /* TSI初始化 */
TSI_SelfCalibration(); /* TSI自校准 */
START_SCANNING; /* 打开周期性扫描 */
ENABLE_EOR_INT; /* Init TSI OUT of Range interrupts */
ENABLE_TSI; /* 使能TSI模块 */
}

/********************************************************************************
**Routine: TSI_Init
**Description: 初始化TSI模块,软件触发即写TSI_GENCS_SWTS
**Notes: 这里没有输入参数,所以具体使用哪一个通道可修改第二步及相关第三步的寄存器
********************************************************************************/
void TSI_Init(void)
{
/* First step : Turn on clock to TSI module */
SIM_SCGC5 |= (SIM_SCGC5_TSI_MASK);
/*******************************************************************/
/* Second step : */
PORTA_PCR4 = PORT_PCR_MUX(0); /* Enable ALT0 for portA4 */
PORTB_PCR2 = PORT_PCR_MUX(0); /* Enable ALT0 for portB2 */
PORTB_PCR3 = PORT_PCR_MUX(0); /* Enable ALT0 for portB3 */
PORTB_PCR16 = PORT_PCR_MUX(0); /* Enable ALT0 for portB16 */
/*******************************************************************/
/* Third step : setting the correspongding register */
/* 每个电极连续扫描11次,prescaler分频8,软件触发 */
TSI0_GENCS |= ((TSI_GENCS_NSCN(10))|(TSI_GENCS_PS(3)));
/* 外部充电电流4uA,内部充电电流32uA,600mv delta电压, 连续扫描模式,输入时钟为Busclock/1 */
TSI0_SCANC |= ((TSI_SCANC_EXTCHRG(3))|(TSI_SCANC_REFCHRG(31))|(TSI_SCANC_DELVOL(7))|(TSI_SCANC_SMOD(0))|(TSI_SCANC_AMPSC(0)));
/* 使能5,8, 7,9 TSI通道 */
TSI0_PEN = TSI_CH5_EN_MASK | TSI_CH8_EN_MASK | TSI_CH7_EN_MASK | TSI_CH9_EN_MASK;
TSI0_GENCS |= TSI_GENCS_TSIEN_MASK; /* Enables TSI */
}

/********************************************************************************
**Routine: TSI_SelfCalibration
**Description: TSI模块自校准,采样无按键情况下的电容值(即相应的计数值)作为校准值。
**Notes: 注意下,执行这个函数的时候是采用软件触发的,自校准之后采用周期性扫描
********************************************************************************/
void TSI_SelfCalibration(void)
{
uint16 counter_temp;
TSI0_GENCS |= TSI_GENCS_SWTS_MASK; /* 开启TSI周期扫描 */
while(!(TSI0_GENCS&TSI_GENCS_EOSF_MASK)){}; /* 等待扫描结束 */
for(uint16 i=0; i<5000;i++) /* 延时一段时间 */
{
asm ("NOP");
}
/********************以下为具体校验过程(有哪些通道根据实际情况)********************************/
counter_temp = ELECTRODE5_COUNT; /* 采样无按键情况下电容值 */
if(counter_temp > ELECTRODE_Shake) /* 限幅 */
TSI0_THRESHLD5 = TSI_THRESHLD_HTHH(counter_temp + ELECTRODE_Shake) | TSI_THRESHLD_LTHH(counter_temp - ELECTRODE_Shake);
else
TSI0_THRESHLD5 = TSI_THRESHLD_HTHH(counter_temp + ELECTRODE_Shake) | TSI_THRESHLD_LTHH(counter_temp);
counter_temp = ELECTRODE8_COUNT; /* 采样无按键情况下电容值 */
if(counter_temp > ELECTRODE_Shake) /* 限幅 */
TSI0_THRESHLD8 = TSI_THRESHLD_HTHH(counter_temp + ELECTRODE_Shake) | TSI_THRESHLD_LTHH(counter_temp - ELECTRODE_Shake);
else
TSI0_THRESHLD8 = TSI_THRESHLD_HTHH(counter_temp + ELECTRODE_Shake) | TSI_THRESHLD_LTHH(counter_temp);
counter_temp = ELECTRODE7_COUNT; /* 采样无按键情况下电容值 */
if(counter_temp > ELECTRODE_Shake) /* 限幅 */
TSI0_THRESHLD7 = TSI_THRESHLD_HTHH(counter_temp + ELECTRODE_Shake) | TSI_THRESHLD_LTHH(counter_temp - ELECTRODE_Shake);
else
TSI0_THRESHLD7 = TSI_THRESHLD_HTHH(counter_temp + ELECTRODE_Shake) | TSI_THRESHLD_LTHH(counter_temp);
counter_temp = ELECTRODE9_COUNT; /* 采样无按键情况下电容值 */
if(counter_temp > ELECTRODE_Shake) /* 限幅 */
TSI0_THRESHLD9 = TSI_THRESHLD_HTHH(counter_temp + ELECTRODE_Shake) | TSI_THRESHLD_LTHH(counter_temp - ELECTRODE_Shake);
else
TSI0_THRESHLD9 = TSI_THRESHLD_HTHH(counter_temp + ELECTRODE_Shake) | TSI_THRESHLD_LTHH(counter_temp);
DISABLE_TSI; /* 先禁止,等待在TSI_START里再使能 */
}
/********************************************************************************
**Routine: TSI_isr
**Description: TSI模块,out of Range 中断服务程序,中断服务号为99,IRQ为83
**Notes:
********************************************************************************/
void TSI_isr(void)
{
uint16 Status = 0;
TSI0_GENCS |= TSI_GENCS_OUTRGF_MASK;
TSI0_GENCS |= TSI_GENCS_EOSF_MASK;
Status = (uint16) (TSI0_STATUS & 0x0000FFFF);
if(Status != 0xFFFF) /* 确定是OUT OF Range中断(排除掉ERROR中断) */
Debounce_Counter--;
else
Debounce_Counter = DBOUNCE_COUNTS; /* 复位去抖动系数 */
if(!Debounce_Counter)
{
Debounce_Counter = DBOUNCE_COUNTS; /* 复位去抖动系数 */
switch(Status)
{
case TSI_STATUS_ORNGF0_MASK:
break;
case TSI_STATUS_ORNGF1_MASK:
break;
case TSI_STATUS_ORNGF2_MASK:
break;
case TSI_STATUS_ORNGF3_MASK:
break;
case TSI_STATUS_ORNGF4_MASK:
break;
case TSI_STATUS_ORNGF5_MASK:
GPIO_Toggle(PORTA,11);
break;
case TSI_STATUS_ORNGF6_MASK:
break;
case TSI_STATUS_ORNGF7_MASK:
GPIO_Toggle(PORTA,29);
break;
case TSI_STATUS_ORNGF8_MASK:
GPIO_Toggle(PORTA,28);
break;
case TSI_STATUS_ORNGF9_MASK:
GPIO_Toggle(PORTA,10);
break;
case TSI_STATUS_ORNGF10_MASK:
break;
case TSI_STATUS_ORNGF11_MASK:
break;
case TSI_STATUS_ORNGF12_MASK:
break;
case TSI_STATUS_ORNGF13_MASK:
break;
case TSI_STATUS_ORNGF14_MASK:
break;
case TSI_STATUS_ORNGF15_MASK:
break;
default:
break;
}
}
TSI0_STATUS = 0xFFFFFFFF; /* 清零标志 */
}
上面是TSI的驱动部分,具体调用可能有些人不是很明白,这里就再贴出主函数的调用方法,汗,没办法,我是担心不全说明白,估计我又该被问的忙不过来了,呵呵
/**********************************************************************************
**File Name: test.c
**Description: project test file, used to test some modules function of k60
**Editor: jicheng at Shandong University, all copyrights are reserved.
**Date: 2012.3.x
**History: V1.0
***********************************************************************************/
#include "system.h"
#include "common.h"
#include "gpio.h"
#include "tsi.h"
void main(void)
{
//---------------insert your code in the following--------------
GPIO_Init(PORTA,11,1,1);
GPIO_Init(PORTA,28,1,0);
GPIO_Init(PORTA,29,1,1);
GPIO_Init(PORTA,10,1,0);
TSI_Start();
enable_irq(INT_TSI0-16); /* 使能TSI的IRQ中断 */
EnableInterrupts;
while(1)
{
}
}
需要说明一下,上一篇我给出的MQX例程里面的TSI驱动是有问题的(汗,飞思卡尔的官方例程啊,伤不起),而且它使用的中断方式是End of Scan,我的例程里使用的是Out of Range,其实后来我想了想,这两种方式各有优点,前者缺点是中断占用时间多(每次扫描完都要中断)不过这种方式的优点是可以加入很多算法处理更丰富的触摸动作(例如滑动,旋转等等),而我的方法呢只有手指按下才触发中断(省CPU),不过对一些复杂动作的话要处理的话可能会麻烦些,呵呵,这算是我的一点心得体会吧,在这里分享给大家,更多技巧还待我们去开发,呵呵,如果有好的想法欢迎留言咱们一块探讨下,我觉着这个TSI还是挺好玩的,哈哈。
呼呼,接口热水喝,这篇写的有点多,够累的,其实本该在昨晚周日写完的,结果内容有点多还是写到现在,哈哈,新鲜出炉,希望觉着可以的可以帮着顶一顶,让更多人获益,也算是对俺的劳动的支持,另外还需要再强调一下,本系列首发ChinaAET,转载请注明出处和保留俺原作者的信息,尊重下俺,呵呵,老话,未完待续~
附件为完整的TSI模块驱动C文件和头文件(其实直接加到我的那个开发框架里就能用,自己用的话可以稍微设置一下,很简单)。
Baidu
map