Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1494186
  • 博文数量: 204
  • 博客积分: 4013
  • 博客等级: 中校
  • 技术积分: 4030
  • 用 户 组: 普通用户
  • 注册时间: 2011-12-29 06:34
文章分类

全部博文(204)

文章存档

2012年(204)

分类: 嵌入式

2012-02-17 19:47:42

/**************************************************************
1X6 Key Scan
**************************************************************/
#include "def.h"
#include "option.h"
#include "2440addr.h"
#include "2440lib.h"
#include "2440slib.h"
/******************************************************************************
 1X6 矩阵键盘
六个输入引脚: 
    EINT8 -----( GPG0 )
    EINT11-----( GPG3 )
    EINT13-----( GPG5 )
    EINT14-----( GPG6 )
    EINT15-----( GPG7 )
    EINT19-----( GPG11)
******************************************************************************/
;扫描六个按键所接GPIO口,观察相应GPIO口电平值,键按下:低电平0; 键抬起:高电平1;然后返回当前按下键所对应的键值
U8 Key_Scan( void )  //键盘扫描,返回哪个按键被按下,按下时管脚与地相连
{
 Delay( 80 ) ;                //去除按键抖动
 if( (rGPGDAT&(1<< 0)) == 0 )    //在输出模式下,管脚寄存器与管脚输出状态相匹配,K1键按下
  return 1 ;
 else if( (rGPGDAT&(1<< 3)) == 0 ) //K2键按下
  return 2;
 else if( (rGPGDAT&(1<< 5)) == 0 ) //K3键按下
  return 3 ;
 else if( (rGPGDAT&(1<< 6)) == 0 ) //K4键按下
  return 4 ;
 else if( (rGPGDAT&(1<< 7)) == 0 ) //K5键按下
  return 5 ;
 else if( (rGPGDAT&(1<<11)) == 0 ) //K6键按下
  return 6 ;
 else                     //这里的else就好比switch case的default,不要丢掉
  return 0xff;  
}
//中断处理函数,__irq用来声明通用中断函数,外部中断8~23共用一个中断处理函数
static void __irq Key_ISR(void)
{
 U8 key;
 U32 r;
 EnterCritical(&r);  //中断不能嵌套,进入临界区,保护现场,设置相应cpsr的值 
 if(rINTPND==BIT_EINT8_23) //INTPND是正在执行的或将要执行的中断,如果中断来自于外部中断8~23,那么执行下面的操作
 {
  ClearPending(BIT_EINT8_23); //清除来自于外部中断8~23的中断请求,清空BIT_EINT8_23位
;继续比较EINTPEND寄存器,确定外部中断源,因为公用一个中断函数,所以要通过EINTPEND查看具体是哪一个中断源
if(rEINTPEND&(1<<8))    //EINT8触发中断
  {
  //Uart_Printf("eint11\n");
   rEINTPEND |= 1<< 8;    //清除EINTPEND寄存器中EINT8对应的中断请求,通过写1清零中断请求标志位
  }
  if(rEINTPEND&(1<<11))  //EINT11触发中断
 {
  //Uart_Printf("eint11\n");
   rEINTPEND |= 1<< 11;
  }
  if(rEINTPEND&(1<<13))  //EINT13触发中断
 {
  //Uart_Printf("eint11\n");
   rEINTPEND |= 1<< 13;
  }
  if(rEINTPEND&(1<<14)) //EINT14触发中断
 {
  //Uart_Printf("eint11\n");
   rEINTPEND |= 1<< 14;
  }
  if(rEINTPEND&(1<<15)) //EINT15触发中断
 {
  //Uart_Printf("eint11\n");
   rEINTPEND |= 1<< 15;
  }
  if(rEINTPEND&(1<<19)) //EINT19触发中断
 {
  // Uart_Printf("eint19\n");  
   rEINTPEND |= 1<< 19;
  }
 }
 key=Key_Scan();  //按键扫描,读取按键键值
 if( key == 0xff )
  Uart_Printf( "Interrupt occur... Key is released!\n") ;
 else
  Uart_Printf( "Interrupt occur... K%d is pressed!\n", key) ; //打印信息
 ExitCritical(&r);           //出临界区,恢复现场,恢复相应的cpsr位 
}
//外部中断的初始化工作
void KeyScan_Test(void)
{
 Uart_Printf("\nKey Scan Test, press ESC key to exit !\n");  //串口打印 

 rGPGCON = rGPGCON & (~((3<<22)|(3<<6)|(3<<0)|(3<<10)|(3<<12)|(3<<14))) |      //相应位清零 
       ((2<<22)|(2<<6)|(2<<0)|(2<<10)|(2<<12)|(2<<14)) ;  //GPG11,3 set EINT
 /*以下为设定触发模式*/ 
 rEXTINT1 &= ~(7|(7<<0));  //外部中断8, 即为K1  
 rEXTINT1 |= (0|(0<<0));   //低电平触发,此处可以不加,要此代码是为了满足以后的需要,比如要改为上升沿触发或下降沿触发时就需要了   
 rEXTINT1 &= ~(7<<12);  //外部中断11,即为K2 
 rEXTINT1 |= (0<<12);  //set eint11 low level int 
 
 rEXTINT1 &= ~(7<<20);  //外部中断13,即为K3 
 rEXTINT1 |= (0<<20);  //set eint13 low level int 
 
 rEXTINT1 &= ~(7<<24); //外部中断14,即为K4 
 rEXTINT1 |= (0<<24); //set eint14 low level int
 
 rEXTINT1 &= ~(7<<28); //外部中断15,即为K5  
 rEXTINT1 |= (0<<28); //set eint15 low level int
  
 rEXTINT2 &= ~(0xf<<12); //外部中断19,即为K6 
 rEXTINT2 |= (0<<12); //set eint19 low level int
 
 rEINTPEND |= (1<<8)|(1<<11)|(1<<13)|(1<<14)|(1<<15)|(1<<19);   //clear eint8, 11,13,14,15,19 
 rEINTMASK &= ~((1<<8)|(1<<11)|(1<<13)|(1<<14)|(1<<15)|(1<<19)); //使能6个外部中断,置1为屏蔽,置0表示不屏蔽,enable eint8, 11,13,14,15,19 
 ClearPending(BIT_EINT0|BIT_EINT2|BIT_EINT8_23);                //清除相应中断源的位 
 pISR_EINT0 = pISR_EINT2 = pISR_EINT8_23 = (U32)Key_ISR;         //得到相应中断信息
 EnableIrq(BIT_EINT0|BIT_EINT2|BIT_EINT8_23);               //打开相应中断源eint0,2,8~23
  while( Uart_GetKey() != ESC_KEY ) ; //使能中断后在此等待,直到按键中断产生,跳到Key_ISR(); 
  DisableIrq(BIT_EINT0|BIT_EINT2|BIT_EINT8_23); //关闭相应中断源eint0,2,8~23,中断已发生,禁止再次中断     
}
解析:对ARM的硬件操作无非就是配置寄存器,中断也不例外,需要设置的寄存器有:
      GPGCON :引脚配置寄存器,设置为第二功能,中断引脚;
 
     EINTPEND:中断挂起寄存器,当有中断发生且没有被屏蔽,相应位会自动置1;在进入中断服务程序后必须用软件将其相应位通过再写次1清0以免发生错误中断;参看下图
 
     SRCPND:源挂起寄存器由32位组成,其每一位都涉及一个中断源。如果中断源产生了中断则相应的位被置1并且等待中断服务。此寄存器指示出是哪个中断源正在等待请求服务。

     注意:此寄存器不顾INTMAST的屏蔽位,由硬件自动将相应中断位置1,在进入中断服务程序后必须通过写1清除相应位,以防发生错误中断。

 
    EINTMASK:外部中断屏蔽寄存器,若相应位置1;则相应中断被屏蔽不会产生下一级请求;
 
    INTPND:中断挂起寄存器中32位的每一位都表明了是否相应位未被屏蔽并且正在等待中断服务的中断请求具有最高的优先级。
   
 
 
下图将说明中断如何被通知到CPU:
 
  
   如果中断源产生了中断则SRCPND和INTPND相应的位被置1,前提是SUBMASK和MASK相应位不使能(即中断开启),MODE决定是IRQ还是FIQ,PRIORITY决定中断优先级。
   编程流程如下:
1.具体执行中断之前,要初始化好要用的中断。2440的外部中断引脚EINT与通用IO引脚F和G复用,要想使用中断功能,就要把相应的引脚配置成中断模式。
    rGPGCON = rGPGCON & (~((3<<22)|(3<<6)|(3<<0)|(3<<10)|(3<<12)|(3<<14))) |    //相应位清零  

 

            ((2<<22)|(2<<6)|(2<<0)|(2<<10)|(2<<12)|(2<<14)) ;       //GPG11,3,0,5,6,7set EINT第二功能  

    配置完引脚后,还需要配置具体的中断功能。我们要打开某一中断的屏蔽,这样才能响应该中断,相对应的寄存器为INTMSK;如:

   rEINTMASK &= ~((1<<8)|(1<<11)|(1<<13)|(1<<14)|(1<<15)|(1<<19)); //enable eint8, 11,13,14,15,19  

       继续,紧接着设置外部中断的触发方式,如低电平、高电平、上升沿、下降沿等,相对应的寄存器为EXTINT

     rEXTINT1 &= ~(7|(7<<0));//clear  3 bits     

     rEXTINT1 |= (0|(0<<0));   //set eint8 low level int  

          另外由于EINT4~EINT7共用一个中断向量,EINT8~EINT23也共用一个中断向量,而INTMSK只负责总的中断向量的屏蔽,要具体打开某一具体的中断屏蔽,还需要设置EINTMASK。如:

        rEINTMASK &= ~((1<<8)|(1<<11)|(1<<13)|(1<<14)|(1<<15)|(1<<19)); //enable eint8, 11,13,14,15,19  

       上面介绍的是最基本的初始化,当然还有一些其他的配置,如当需要用到快速中断时,要使用INTMOD,当需要配置中断优先级时,要使用PRIORITY等。

2. 中断处理函数负责执行具体的中断指令,除此以外还需要把SRCPND和INTPND中的相应的位清零(通过置1来清零),因为当中断发生时,2440会自动把这两个寄存器中相对应的位置1,以表示某一中断发生,如果不在中断处理函数内把它们清零,系统会一直执行该中断函数。如:

#define BIT_EINT0 (0x1)

#define BIT_EINT2 (0x1<<2)

#define BIT_EINT8_23 (0x1<<5)

ClearPending(BIT_EINT0|BIT_EINT2|BIT_EINT8_23);            

__inline  void  ClearPending(int bit)  )     // 输入参数是相应位为1的值

{  

 register i;               // 定义一个寄存器变量

 rSRCPND = bit;   // 向相应位置写1清除源挂起寄存器

 rINTPND = bit;     // 向相应位置写1清除中断挂起寄存器

 i = rINTPND;        // 没有该语句也能正常运行,可能是保证寄存器能清除的,重读寄存器,保证被清零

}

另外还是由于前面介绍过的,有一些中断是共用一个中断向量的,而一个中断向量只能有一个中断执行函数,因此具体是哪个外部中断,还需要EINTPEND来判断,并同样还要通过置1的方式把相应的位清零。

rEINTPEND |= (1<<8)|(1<<11)|(1<<13)|(1<<14)|(1<<15)|(1<<19);        //clear eint8, 11,13,14,15,19  

参考网址:http://blog.csdn.net/okliujieko/article/details/7035369

                   

阅读(2386) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~