TCC89x外部中断使用详解

//=====================================================================
//TITLE:
// TCC89x外部中断使用详解
//AUTHOR:
// norains
//DATE:
// Wednesday 03-November-2010
//Environment:
// Windows CE 6.0
// Telechips TCC8900
//=====================================================================

在嵌入式设备中,外部中断的使用很频繁,最简单的例子,用一个GPIO来检测耳机的插入或拔出,从而依此决定是否对喇叭静音。如果是采用Windows CE 系统,流程基本都相同,流程图如下:


接下来的我们要做的事情,就是在TCC8900这颗CPU上,看看如何将这流程具现为代码吧。有目的才有动力,我们就自己给自己找个题目,题目内容就是将GPIO_D05作为输入,当外部电平分别为高或低时,就产生一个中断,然后我们打印出相应的信息。

依照流程图,我们首先要做的是,设置中断寄存器。查看TCC8900的DataSheet可以发现,该CPU一共有64个中断源,其中有12个外部中断,如图:


在这12个外部输入中断中,是不是可以随便选呢?我觉得最好不要,因为BSP已经用了其中一部分,为了不和现有的驱动相冲突,我们应该选择闲置的外部中断号。已经使用和闲置的外部中断如下表所示:

外部中断号

BSP使用

IRQ_EI0

IRQ_EI1

IRQ_EI2

Touch2

IRQ_EI3

SPI

IRQ_EI4

SianoSpi

Touch1

IRQ_EI5

IRQ_EI6

GpioExp

IRQ_EI7

PwrBtn

IRQ_EI8

IRQ_EI9

IRQ_EI10

IRQ_EI11

Nand_Dll


在接下来的代码里,我们选择的是IRQ_EI9。那么,我们又如何将这IRQ_EI9和GPIO_D05相联系起来?这时候,就需要设置EINTSEL寄存器了。对于该功能寄存器,一共有三个,如图:


每个中断源占了6bit,可以表示0~63的范围。而这些数值,每个对应于一个输入源,如图:


从图中可以获知,如果要IRQ_EI9和GPIO_D05相联系,需要做的就是EINTSEL2寄存器的8~13bit设置为16即可。


准备工作就绪,就是开始干活,设置中断寄存器吧:

PGPIO g_pGPIO = (PGPIO)tcc_allocbaseaddress((unsigned int)&HwGPIO_BASE); //将GPIO_D05设置为输入模式 BITCLR(g_pGPIO->GPDFN0, Hw20|Hw21|Hw22|Hw23); BITCLR(g_pGPIO->GPDEN, Hw5); //设置外部中断源9为GPIO_D05 BITCSET(g_pGPIO->EINTSEL2, Hw14-Hw8, Hw12); PPIC g_pPPIC = (PPIC)tcc_allocbaseaddress((unsigned int)&HwPIC_BASE); //设置中断模式为边缘触发 BITCLR(g_pPPIC->MODE0, Hw12); //设置中断模式为上下边缘 BITSET(g_pPPIC->MODEA0, Hw12); //同步设置 BITSET(g_pPPIC->SYNC0, Hw12);


采用Telechips提供的宏定义,寥寥几行代码就将相关寄存器设置完毕。接下来,就是将调用KernelIoControl函数将IRQ_EI9和某个系统中断号相连接:

DWORD dwHwIrq = IRQ_EI9; DWORD dwSysIrq = 0; if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &dwHwIrq, sizeof(dwHwIrq), &dwSysIrq, sizeof(dwSysIrq), NULL)) { RETAILMSG(TRUE, (TEXT("[WAVEDEV]ERROR: Failed to request sysintr value for detect headphone interrupt./r/n"))); return 0x01; }

当KernelIoControl函数调用成功后,dwSysIrq存储的则是申请的和IRQ_EI9联系起来的系统中断号。成功获取之后,就是将这系统中断号和一个事件相联系起来:

//创建通知事件 HANDLE hIntrEvent = CreateEvent(NULL, FALSE, FALSE, NULL); //将通知事件和中断号相联系 if (!(InterruptInitialize(dwSysIrq, hIntrEvent, 0, 0))) { RETAILMSG(TRUE, (TEXT("[WAVEDEV]ERROR: Interrupt initialize failed./r/n"))); return 0x02; }

当这一切都准备就绪之后,我们就可以调用WaitForSingleObject来等待事件了。因为系统会在发生中断的时候,通过将事件置为有效状态来进行通知。一般在实际使用中,我们都是通过循环来监视中断,如:

while(TRUE) { //等待中断事件 WaitForSingleObject(hIntrEvent, dwWaitTime); //获取输入的电平 BOOL bHigh = ((g_pGPIO->GPDDAT & Hw5) != 0); if(bHigh == FALSE) { RETAILMSG(TRUE, (TEXT("Input is low./r/n"))); } else { RETAILMSG(TRUE, (TEXT("Input is high./r/n"))); } //清除本次中断 InterruptDone(dwSysIrq); }

不过这里可能会有一个抖动的问题存在,就是在外部电平改变时,会有上下波动,具体到打印信息可能会输出如下序列:

Input is low
Input is low
Input is low
Input is high
Input is high
Input is low
Input is high
Input is low

虽然最终状态还是Low,但在变换的过程中,却突变出现了三次high。要解决这个问题其实也很简单,当持续有中断发生时,我们不做任何处理;等待一段时间之后,没有了中断,才进行相应处理。根据此思想,上面的代码我们可以变换为:

DWORD dwWaitTime = INFINITE; while(TRUE) { //等待中断事件 DWORD dwReturnVal = WaitForSingleObject(hIntrEvent, dwWaitTime); if(dwReturnVal == WAIT_OBJECT_0) { dwWaitTime = 300; //clear Interrupt InterruptDone(dwSysIrq); continue; } //获取输入的电平 BOOL bHigh = ((g_pGPIO->GPDDAT & Hw5) != 0); if(bHigh == FALSE) { RETAILMSG(TRUE, (TEXT("Input is low./r/n"))); } else { RETAILMSG(TRUE, (TEXT("Input is high./r/n"))); } //清除本次中断 InterruptDone(dwSysIrq); dwWaitTime = INFINITE; }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值