注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
liongt的个人空间 https://home.eeworld.com.cn/space-uid-53096.html [收藏] [复制] [分享] [RSS]
日志

LM3开发笔记_3.中断学习

已有 879 次阅读2010-11-10 11:52 |个人分类:LM3S|

     对于“裸奔”的系统而言,使用中断是无法避免的,Stellaris外设驱动库的中断操作函数都封装在相应外设的.c/h文件和interrupt.c/h文件中,使用起来非常方便。我比较支持使用固件库,固件库出错的可能性绝对要比自己去直接操作寄存器要小的多,而且使用固件库可以将驱动层和应用层相分离,符合软件分层开发的思想。将固件库的维护交给厂商去做,我们坐享其成,何乐而不为呢。

     利用《Stellaris 外设驱动库》编写一个中断程序的基本方法如下: 

     1. 使能相关片内外设,并进行基本的配置

     对于中断源所涉及的片内外设必须要首先使能,使能的方法是调用头文件<sysctl.h>中的函数SysCtlPeripheralEnable( )。使能该片内外设以后,还要进行必要的基本配置。

     2. 设置具体中断的类型或触发方式

     不同片内外设具体中断的类型或触发方式也各不相同。在使能中断之前,必须对其进行正确的设置。以GPIO 为例,分为边沿触发、电平触发两大类,共 5 种,这要通过调用函数GPIOIntTypeSet( )来进行设置。

     3. 使能中断

     对于 Stellaris 系列ARM,使能一个片内外设的具体中断,通常要采取分 3 步走的方法: 
       a.调用片内外设具体中断的使能函数 
       b.调用函数IntEnable( ),使能片内外设的总中断 
       c.调用函数IntMasterEnable( ),使能处理器总中断

     4. 编写中断服务函数

     中断服务函数从形式上跟普通函数类似,但在命名及具体的处理上有所不同。中断服务函数的名称可以由程序员自行指定,但是为了提高程序的可移植性,我们还是建议采用标准的中断服务函数名称。

     a.中断状态查询 

     一个具体的片内外设可能存在多个子中断源,但是都共用同一个中断向量。例如GPIOA 有8个管脚,每个管脚都可以产生中断,但是都共用同一个中断向量号16,任一管脚发生中断时都会进入同一个中断服务函数。为了能够准确区分每一个子中断源,就需要利用中断状态查询函数,例如GPIO 的中断状态查询函数是 GPIOPinIntStatus() 。
    
b.中断清除

     对于 Stellaris 系列ARM 的所有片内外设,在进入其中断服务函数后,中断状态并不能自动清除,而必须采用软件清除。如果中断未被及时清除,则在退出中断服务函数时会立即再次触发中断而造成混乱。清除中断的方法是调用相应片内外设的中断清除函数。例如,GPIO 端口的中断清除函数是GPIOPinIntClear( ) 。
 
     5. 注册中断服务函数
     中断服务函数虽然已经编写完成,但是当中断事件产生时程序还无法找到它,因为还缺少最后一个步骤:注册中断服务函数。注册方法有两种,一是直接利用中断注册函数,操作上跟C程序中的回掉函数一样。这种方法的好处是操作简单、可移植性好。

     void IntRegister(unsigned long ulInterrupt, void (*pfnHandler)(void)) //注册一个中断出现时被调用的函数;
     void IntUnregister(unsigned long ulInterrupt) //注销一个中断出现时被调用的函数;

     论坛上还有人用另一种方法,通过修改启动文件来注册中断服务函数。以Port_F中断为例,在Keil 开发环境下的启动文件“Startup.s”里找到“Vectors”表格,在其前面插入声明EXTERN GPIO_Port_F_ISR, 再根据“Vectors”表格的注释内容找到外设GPIO_Port_F的位置,把相应的“IntDefaultHandler”替换为“GPIO_Port_F”。 个人认为这种方法比较繁琐,移植性不好。

     在上述几个步骤完成后,就可以等待中断事件的到来了。当中断事件产生时,程序就会自动跳转到对应的中断服务函数去处理。

     举个简单的例子,用lm3s8962评估板上的SELECT按键产生中断,控制LED的状态,每次按下按键会改变LED的点亮/熄灭状态。其它外设的中断照猫画虎即可。

//main
int main(void)
{
    SysCtlClockSet(SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |  SYSCTL_XTAL_8MHZ | SCTL_SYSDIV_4 ); //50MHz

    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); //这里是给F口时钟
    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, LED_PIN); //设F0为输出
    //设F1为输入
    GPIODirModeSet(GPIO_PORTF_BASE, KEY_SELECT, GPIO_DIR_MODE_IN); 
    //推挽弱上拉,2mA
    GPIOPadConfigSet(GPIO_PORTF_BASE, KEY_SELECT, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
    GPIOPortIntRegister(GPIO_PORTF_BASE,GPIO_Port_F_ISR);    //注册中断函数

    GPIOIntTypeSet(GPIO_PORTF_BASE, KEY_SELECT, GPIO_LOW_LEVEL);  //低电平触发中断 
    GPIOPinIntEnable(GPIO_PORTF_BASE, KEY_SELECT);      //使能KEY中断
    IntEnable(INT_GPIOF);            //使能PF中断
    IntMasterEnable();             //使能处理器中断
    //等待中断发生
    while(1)
    {
    }
}

//PortE中断服务
void GPIO_Port_F_ISR(void)
{
    unsigned char ucVal;
    unsigned long ulStatus;
 
    ulStatus = GPIOPinIntStatus(GPIO_PORTF_BASE, true);  //读取中断状态
    GPIOPinIntClear(GPIO_PORTF_BASE, ulStatus);    //清除中断状态

    if (ulStatus & KEY_SELECT)                              //如果KEY的中断状态有效
    {
     ucVal = GPIOPinRead(GPIO_PORTF_BASE, LED_PIN);      
     GPIOPinWrite(GPIO_PORTF_BASE, LED_PIN, ~ucVal);     //翻转LED

     Delay_nMS(5);                     //延时约5ms,消除按键抖动

     while (GPIOPinRead(GPIO_PORTF_BASE, KEY_SELECT) == 0x00);     //等待KEY抬起

     Delay_nMS(5);                     //延时约5ms,消除松键抖动
     }
}

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章