打印
[ZLG-ARM]

怎么没事就进中断??

[复制链接]
1117|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
dara8124|  楼主 | 2009-7-21 14:49 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
沙发
dara8124|  楼主 | 2009-7-21 15:04 | 只看该作者

具体

我用的是串口中断,我的中断设置是:U0LCR = 0x83;                        // DLAB = 1    
    bak   = (Fpclk >> 4) /57600;
    U0DLM = bak >> 8;
    U0DLL = bak & 0xFF;
    U0LCR = 0x2b;                         // 设置奇偶校验
    U0FCR = 0x01;                        // 使能FIFO,并设置触发点为1字节
    U0IER = 0x01;                        // 允许RBR中断,即接收中断
    IRQEnable();                        // 使能IRQ中断
    /* 使能UART0中断 */
    VICIntSelect = 0x00000000;            // 设置所有的通道为IRQ中断
    VICVectCntl0 = 0x26;            // UART0分配到IRQ slot0,即最高优先级
    VICVectAddr0 = (int)IRQ_UART0;    // 设置UART0向量地址
    VICIntEnable = 0x40;        // 使能UART0中断


但每次都是在执行最后一句的时候就跑到LDR     PC, [PC, #-0xff0],然后就到了我的中断服务程序去了,请教大家,这是什么原因导致的???谢谢!!

使用特权

评论回复
板凳
zlg_wuwenjie| | 2009-7-22 08:16 | 只看该作者

回复

您好:
    您在中断服务函数中清除中断标志了没有。如果没有清除中断标志则就是说你的串口中断一直在发生当然会一直执行终端服务函数的。

使用特权

评论回复
地板
dara8124|  楼主 | 2009-7-22 08:42 | 只看该作者

回复

呵呵,我清除了,我看了我的程序,他每次都是在执行VICIntEnable = 0x40;        // 使能UART0中断
这一句的时候跑掉了,而且我看了反汇编,好像这句还没执行完呢,而且这句程序是在主程序的开始,还有就是他一会好一会坏,就是一会正常,一会就跑掉了,是不是芯片有问题了,还是编译器。。。。??

使用特权

评论回复
5
armecos| | 2009-7-22 12:05 | 只看该作者

清中断要内外都清,中断源也要清,看万能中断模板,

***********************
* 第三讲 ecos中断操作 *
***********************
    2006/12/30  asdjf@163.com  www.armecos.com

    ecos的中断处理到底比我们自己写的高明在哪里呢?
    如果我们自己写中断处理程序,那么,针对lpc2210、s3c44b0x、2410、2440等具体硬件,每一个片子都要重写ISR。对于不同的体系结构(如ARM、MIPS、X86、POWERPC......)、变种(同体系结构不同型号)、硬件平台(不同公司的开发板),中断机制不尽相同,程序员要花费大量的时间纠缠于细微的差异,心情不会愉快,代码复用性也不强。
    ecos采用了一种通用的中断处理机制,抽象出中断最本质的特点,用硬件抽象层HAL来抹平各种中断硬件体系实现的细节,程序员只要针对ecos抽象出来的通用虚拟中断系统编程就可以了。
    
    那么ecos到底是如何“抽象”中断的呢?暂且不表,先来说说中断响应时间。
    
    如果你写过汇编中断处理程序,或者用过ucos,那么你对ISR的实现方式一定不会陌生,先填好中断向量表,再写好ISR处理程序,一旦中断就通过向量表找到ISR函数地址,跳到那里执行。
    而ecos的虚拟中断系统是ISR+DSR,当然Linux也是ISR+tasklet。很明显,中断分成了两个部分,一部分是中断服务程序ISR,关中断执行,尽可能短,很多内核函数用不了;另一部分是延迟服务程序DSR,不关中断,可以进行比较耗时的处理,比ISR能用更多的内核函数。
    为什么这样做?有什么好处呢?
    我们知道,中断是随时都有可能异步发生的,中断处理程序和被中断程序的“并发”执行,会引起重入、同步、临界资源保护等诸多问题。中断发生时会保存现场,返回时恢复,故不会对被中断的无临界资源的应用线程产生影响,但如果被中断的线程正在执行内核调用,则有可能破坏内核数据,因为内核函数需要原子操作,不能被打断。避免这一问题的方法之一是在处于临界区的内核函数中禁止中断。例如ucos的内核函数就经常开关中断。不过有时这种原子操作的时间可能相当长,对于中断响应时间这个指标有不良影响。改善性能的方法就是不使用开关中断的方法避免冲突,而是使用锁、信号量等方式实现内核数据保护。这样就约定在ISR里限制使用不安全的绝大部分内核API,内核API也不再频繁开关中断,代之以锁、信号量。当我们的ISR可以写得很短,内核API又不会关中断的情况下,中断响应时间就会变小,RTOS的实时性指标就会更好看。因此,采用ISR+DSR的ecos中断响应比ucos快。可能您现在还不太习惯这种把ISR分成两半儿的写法,没关系,我们下面提供一个万能中断处理模板,您只要套用就可以了。把精力集中在中断事件处理上,再不用为搭架子发愁。
    
    想看中断如何被抽象吗?我们结合一个题目边看边讲。
    
    总的思路是:创建中断句柄,挂接中断,使能中断,正常工作后一旦发生中断,先调用ISR,再调用DSR(可选)。其中涉及到中断应答,中断清除、外部中断清除、中断触发方式配置、中断禁止/使能、中断创建/删除、中断挂接/解挂等概念。
    
    题目:用KEY1按键控制蜂鸣器鸣响。低电平有效,按下发声,再按停止,再按又发声,周而复始。(别忘了先装好跳线再实验)
    
    全部源码如下所示。多任务编程第一讲已经讲过了,I/O寄存器读写在第二讲也已经讲过了,现在来看看中断编程。
    
    我在创建taska线程时传递了一个参数CYGNUM_HAL_INTERRUPT_EXT3,这是KEY1对应的外部中断号,没什么特别的目的,只是想用线程参数存放我的私有数据(priv_data),总得找个地儿放我自己的东西不是,得,就放这儿了。CYGNUM_HAL_INTERRUPT_EXT3的意思是符合CYG公司标准的数字---位于硬件抽象层---中断用途---外部中断3号。
    
    线程taska中首先设置引脚,虽然ecos抽象了中断,但中断毕竟还是和硬件紧密相关,有些芯片为了引脚复用或低功耗等目的,多种功能对应在同一个引脚上/多个引脚对应在同一个功能上,使用时必须根据需要选择。例如:SMARTARM2200平台的KEY1使用的是EINT3中断,该中断由P0.9、P0.20、P0.30复用,要屏蔽P0.9(网卡8019中断)和P0.30(ZLG7290中断),以免冲突。感兴趣的读者可以试试允许P0.9和P0.30对应EINT3会发生什么现象。(低电平触发时,中断信号线相与;高电平触发时,中断信号线相或。)
    
    设置BEEP控制引脚方向为输出,禁止刚一开始就BEEP鸣响,上电缺省输出是低电平,导致一开始就鸣叫,此处明确关掉声音。
    
    中断触发方式有4种:高电平、低电平、上升沿、下降沿。ecos使用cyg_interrupt_configure函数设置中断触发方式。
    cyg_interrupt_configure(中断号,电平/边沿选择,上下/高低选择)
    其中:
          电平/边沿选择:0---电平;1---边沿
          上下/高低选择:0---低下;1---高上
    此处设置为(priv_data,0,0),即低电平触发方式。priv_data是线程参数传递过来的EINT3中断号。
    用中断触发配置函数是不是比直接写EMODE,EPOLAR寄存器要方便很多呢!
    边沿触发的中断不用清中断源,但可能会丢中断;电平触发的中断需要清除外部中断源,不会丢失中断指示,而且可以共享同一根中断线。
    
    清除priv_data(即EINT3)中断标志,等效于1<<3。这句要放在cyg_interrupt_configure后面,好象改EMODE和EPOLAR会影响EINT。最好在使用中断前明确清除以前的中断指示,以免发生不能预料的随机事件,这是好习惯。
    
    ecos的虚拟中断体系要求把需要的中断处理函数挂接到对应的中断号上,而不必关心具体的硬件映射细节。中断句柄可以创建多个,但只能挂接其中的一个。
    cyg_interrupt_create(
        中断号,
        中断优先级,
        传递的中断参数,
        ISR函数,
        DSR函数,
        被返回的中断句柄,
        存放与此中断相关的内核数据的变量空间);
    
    cyg_interrupt_attach(中断句柄);
    
    创建了中断句柄并且挂接后,中断服务程序就和中断向量建立了联系,每当中断后,先执行向量服务程序VSR,再调用已注册的中断服务程序ISR,然后执行延迟服务程序DSR。至此,中断已经准备就绪。
    刚挂接的中断是被屏蔽的,要调用解除屏蔽函数,中断才能正式开始工作。这么做是为了在中断注册后,还能有一段时间继续准备其他需要做的工作,而这些工作不希望被中断干扰。如果都准备好了,就可以解除屏蔽,让中断正常工作。
    cyg_interrupt_unmask(中断号);//使能中断
    
    “while(1);”是为了不退出taska线程,你可以试试去掉这一句会发生什么现象。
    
    ISR程序非常短小,也不调用绝大部分内核函数,不过中断应答和返回值是必须的。
    cyg_interrupt_acknowledge(中断号);
    中断应答应该在ISR快结束时执行一次写操作(写入的值一般为0),以便更新优先级硬件。如果不应答,硬件不能正常工作,而且一定要在ISR里应答。
    如果ISR返回CYG_ISR_HANDLED,则不再调用DSR,如果返回CYG_ISR_CALL_DSR,则还要调用DSR。
    此处屏蔽中断的目的是避免重入,因为我不希望中断嵌套,那样会搞乱数据。如果没有冲突的数据,允许嵌套,就可以不用此语句屏蔽中断。注意不要在ISR里用printf函数。
    
    DSR里执行了大部分的处理工作,可以使用更多的内核函数。这段程序的主要作用是取反BEEP控制,让蜂鸣器随着KEY1按键中断控制在响与不响之间切换。解除中断屏蔽是对应ISR里的中断屏蔽,此时中断已经执行完毕,可以使能新的中断。
    需要注意的是:电平中断的处理过程稍微烦琐一些。先撤消外部中断源指示(例如等待KEY1按键抬起/读空8019网卡输出缓冲区),然后撤消内部EINT中断标志位(对应位写1就可以清除)。如果不先撤消外部指示就清除内部标志/不清除内部标志,那么会反复不断地陷入中断,直至出错死机。感兴趣的读者可以试试注释掉“HAL_WRITE_UINT8(LPC2XXX_SYSCON_EINT,1<<(priv_data-CYGNUM_HAL_INTERRUPT_EXT0));”看看中断能否退出,是否死机。去掉while中的KEY1按键抬起判断,看看按下去时的现象有何差别。
    
    以上是面向ecos虚拟中断体系编程的讲解,可以看出,ecos的中断更加抽象,再也不用关心什么VIC,一大堆中断寄存器了,套用这个模板就可以了。
    
    如果你想改成KEY1键高电平触发,只要写成“cyg_interrupt_configure(priv_data,0,1);”即可。此时,一运行就鸣叫,按一下停止,再按一下又鸣叫......结果正确。DSR里同低电平触发时一样等待外部中断源指示失效才退出,此时若信号保持为高电平,中断标志会一直置1。
    
    如果想改成边沿触发,要将以下语句:
    HAL_READ_UINT8(LPC2XXX_SYSCON_EINT,flag);
    while((flag&1<<(priv_data-CYGNUM_HAL_INTERRUPT_EXT0)) != 0)
        {
        HAL_WRITE_UINT8(LPC2XXX_SYSCON_EINT,1<<(priv_data-CYGNUM_HAL_INTERRUPT_EXT0));
        HAL_READ_UINT8(LPC2XXX_SYSCON_EINT,flag);
        }
    改为一句:
    HAL_WRITE_UINT8(LPC2XXX_SYSCON_EINT,1<<(priv_data-CYGNUM_HAL_INTERRUPT_EXT0));
    不用再判断外部中断源的情况,边沿触发不需要清除外部中断源,因为边沿不会一直保持。EINT写“1”清除还是必须的,不然还会反复陷入中断。
    
    写成“cyg_interrupt_configure(priv_data,1,0);”就是下降沿触发,按下KEY1键会响,再按下停止......
    写成“cyg_interrupt_configure(priv_data,1,1);”就是上升沿触发,按下后抬起KEY1键,会响,再按下抬起停止......
    
    套用这个摸板,写基于中断的8019网卡驱动程序十分方便,8019中断也是用的EINT3,高电平触发,感兴趣的读者可以试试。(提示:改引脚分配PINSEL0和PINSEL1,ecos中有dp83902a参考源码)
    
    很多人可能不用ARM或者ecos,那也没有关系,这里介绍的主要是编程思路和中断本质抽象,有了思路,细节的解决只是时间问题,您完全可以把这里的中断处理思路用在2410、X86、MIPS......上。初学者如果一上来就接触大量硬件细节,难免会晕掉,只见树木不见森林,ecos增值软件包使你能集中精力掌握思路,花最少的时间,取得最大的效果。

以下是中断程序源码

#include <cyg/kernel/kapi.h>
#include <cyg/hal/hal_io.h>
#include <cyg/hal/plf_io.h>

#define STACK_SIZE 4096
#define BEEPCON 0x0000080

char stack[2][STACK_SIZE];
static cyg_thread thread_data[2];
static cyg_handle_t thread_handle[2];

// This ISR is called when the ethernet interrupt occurs
static cyg_uint32
yy_isr(cyg_vector_t vector, cyg_addrword_t data)
{
    int priv_data = (int)data;

    cyg_interrupt_mask(priv_data);       //屏蔽中断
    cyg_interrupt_acknowledge(priv_data);//中断应答,应该在ISR快结束时执行一次写操作(写入的值一般为0),以便更新优先级硬件。
    return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR);  // Run the DSR如果ISR返回CYG_ISR_HANDLED,则不再调用DSR,如果返回CYG_ISR_CALL_DSR,则还要调用DSR。
}

static void
yy_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    int priv_data = (int)data;
    int i;
    unsigned char flag;
    
    HAL_READ_UINT32(LPC2XXX_GPIO_IO0SET,i);
    
    if((i&BEEPCON) == 0 )
        {
        HAL_WRITE_UINT32(LPC2XXX_GPIO_IO0SET,BEEPCON);
        }
    else
        {
        HAL_WRITE_UINT32(LPC2XXX_GPIO_IO0CLR,BEEPCON);
        }
    
    HAL_READ_UINT8(LPC2XXX_SYSCON_EINT,flag);
    while((flag&1<<(priv_data-CYGNUM_HAL_INTERRUPT_EXT0)) != 0)
        {
        HAL_WRITE_UINT8(LPC2XXX_SYSCON_EINT,1<<(priv_data-CYGNUM_HAL_INTERRUPT_EXT0));
        HAL_READ_UINT8(LPC2XXX_SYSCON_EINT,flag);
        }

    cyg_interrupt_unmask(priv_data);//解除中断屏蔽
}

void taska(cyg_addrword_t data)
{
    int priv_data = (int) data;
    cyg_handle_t handle;
    cyg_interrupt intr;
    
    printf("irq=%d ",priv_data);
    
    //引脚设置,KEY1接EINT3
    HAL_WRITE_UINT32(LPC2XXX_PIN_SEL0,0x55500005);
    HAL_WRITE_UINT32(LPC2XXX_PIN_SEL1,0x00000300);
    
    //设置输出到BEEP
    HAL_WRITE_UINT32(LPC2XXX_GPIO_IO0DIR,BEEPCON);
    
    //禁止刚一开始就BEEP,上电缺省输出是低电平,导致一开始就鸣叫。
    HAL_WRITE_UINT32(LPC2XXX_GPIO_IO0SET,BEEPCON);
    
    //配置中断触发方式,低电平触发
    cyg_interrupt_configure(priv_data,0,0);
    
    //清除priv_data(即EINT3)中断标志,等效于1<<3。这句要放在cyg_interrupt_configure后面,好象改EMODE和EPOLAR会影响EINT。
    HAL_WRITE_UINT8(LPC2XXX_SYSCON_EINT,1<<(priv_data-CYGNUM_HAL_INTERRUPT_EXT0));
    
    //创建句柄
    cyg_interrupt_create(
        priv_data,
        0,                         // Priority - unused
        (cyg_addrword_t)priv_data, // Data item passed to ISR & DSR
        yy_isr,                    // ISR
        yy_dsr,                    // DSR
        &handle,                   // handle to intr obj
        &intr );                   // space for int obj

    //将中断挂接到中断句柄上
    cyg_interrupt_attach(handle);
    
    //使能中断
    cyg_interrupt_unmask(priv_data);
    
    printf("Starting... ");
    
    while(1);
}

void
test(cyg_addrword_t data)
{
    printf(" ");
    printf("     ******************************* ");
    printf("     *     Hello! The world.       * ");
    printf("     ******************************* ");

    cyg_thread_create(10,                // Priority - just a number
                      taska,             // entry
                      CYGNUM_HAL_INTERRUPT_EXT3, // entry parameter
                      "taska",           // Name
                      &stack[1],         // Stack
                      STACK_SIZE,        // Size
                      &thread_handle[1], // Handle
                      &thread_data[1]    // Thread data structure
            );
    cyg_thread_resume(thread_handle[1]); // Start it
}

void
cyg_start(void)
{
    cyg_thread_create(10,                // Priority - just a number
                      test,              // entry
                      0,                 // entry parameter
                      "test",            // Name
                      &stack[0],         // Stack
                      STACK_SIZE,        // Size
                      &thread_handle[0], // Handle
                      &thread_data[0]    // Thread data structure
            );
    cyg_thread_resume(thread_handle[0]); // Start it
    cyg_scheduler_start();
}

使用特权

评论回复
6
dara8124|  楼主 | 2009-7-22 16:57 | 只看该作者

回复

谢谢!

使用特权

评论回复
7
armqt| | 2009-7-24 17:22 | 只看该作者

引起这个问题的原因很多

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

5

主题

9

帖子

1

粉丝