[转载].“君让臣死 臣不得不死 不死也得死”的NIOS II 9.1 SP1中断问题

转自:http://www.cnblogs.com/crazybingo/archive/2011/04/03/2004477.html

    快死了,真的快死了,心死了,哀莫大于心死。。。

     因为调摄像头,其中有个模式是“fps切换”,需要用到PIO外部中断,但是以前没写过,反正当年用过51,应该挺简单的,结果,差点让我执行了想死的冲动。。。

     操蛋的代码一直死在中断初始化那边,一次又一次的压抑自己想死的冲动。

     首先NIOS 91用了增强型中断,当然这不是问题的关键,我不详说,看牙缝的博客:http://www.cnblogs.com/yuphone/archive/2010/05/13/1734712.html

     debug 进了中断n次看细节,无奈真想砸电脑,下面是alt_ic_arq_register() 原型以及内部的int alt_irq_register,我不知道为什么这就是所谓的增强型,听说是为了便于以后升级:

2

int alt_ic_isr_register(alt_u32 ic_id, alt_u32 irq, alt_isr_func isr, 
  void *isr_context, void *flags) 

    return alt_irq_register(irq, isr_context, isr); 

int alt_irq_register (alt_u32 id, 
                      void* context, 
                      alt_isr_func handler) 

  int rc = -EINVAL;  
  alt_irq_context status;

  if (id < ALT_NIRQ) 
  { 
    /* 
     * interrupts are disabled while the handler tables are updated to ensure 
     * that an interrupt doesn't occur while the tables are in an inconsistant 
     * state. 
     */

    status = alt_irq_disable_all ();

    alt_irq[id].handler = handler; 
    alt_irq[id].context = context;

    rc = (handler) ? alt_irq_enable (id): alt_irq_disable (id);

    alt_irq_enable_all(status); 
  } 
  return rc; 
}

     在群里大侠的帮助下,还是NND的不行。。。在与牙缝和o my god的共同协作下,还是不行。。。昏了

     最后不小心试试看是不是多路除了问题,然后单路,竟然可以了,回过头去看SOPC,发现刚好忘了选择Enable bit_clearing。。。这样就行了。

     昏,知其然,不知道其所以然,寻找问题的答案!!!于是和我师父分析为什么???师徒两解决了n久n久,终于解决了师父一年前的问题以后我此刻最难受的郁闷:都是Eanbel bit_clearing惹的祸:

image 

意思是说:

     Bit n 在边沿捕获寄存器中,如果捕获了输入(相应的上升沿,下降沿),相应位的位就会被置1 。一个阿窝龙妹妹外设可以读取边沿捕获寄存器,来决定发生在PIO引脚的边沿变化。 如果选项“Enable bit_clearing for edge capture register”被关闭,写任意的值到边沿捕获寄存器将会清除所有寄存器。反之,写一个1到寄存器中一个特别的位,将会使得边沿捕获失去作用。

     根据我的实际测试,验证了以上的一些理论

(1)Enable bit_clearing 打开的时候:

     因为ds说1 的时候清中断,所以IOWR_ALTERA_AVALON_PIO_EDGE_CAP(KEY_DATA_BASE, 0x03);清了我两个按键的中断,问题成功解决。

(2)Enable bit_clearing 关闭的时候:

     因为ds说任意值写入都将会清除所有的边沿捕获寄存器,所以IOWR_ALTERA_AVALON_PIO_EDGE_CAP(KEY_DATA_BASE, any vaule);都能使得edgecapture清零,实际测试用了“IOWR_ALTERA_AVALON_PIO_EDGE_CAP(KEY_DATA_BASE, 0x00);”和“IOWR_ALTERA_AVALON_PIO_EDGE_CAP(KEY_DATA_BASE, 0x03);”,都达到了效果。当然习惯性的我们会用0来清零,以至于让人再次产生了误解。

可是明摆着这些都能达到目的嘛,altera为啥要搞得那么尴尬呢??有必要吗???

     根据我师父的解释,解释如下:

(1)在一般情况下,外部中断不会同时达到,因此bit_clearing显得没有什么意义,比如我们捕获按键,那个按下就让那个LED亮,同时按下就同时亮,这个完全没问题,直接IOWR_ALTERA_AVALON_PIO_DATA(LED_DATA_BASE, edge_capture);嘛!!!

(2)但是这不否决中断同步捕获的情况。当中断同步捕获的时候,可以用bit_clearing来干活了,也许可以更加的灵活。因为中断同时达到的时候,PIO当然能够同时捕获两个中断信号,但是此时main()函数里面的执行就很尴尬了,两个中断到底听谁的呢?(你爸妈一个让你帮他做饭,一个让你陪他下棋,妈的老子听谁的,不干了继续玩NIOS2!)

      这相当于verilog中的异步,需要同步处理之后才OK)。因此利用bit_clearing来屏蔽同时捕获中断的位,下一次再屏蔽另一个来让CPU执行命令,给CPU完成一个该听谁的机会,这样也能有效防止CPU死机(TMD老子不干了)。

     因为PIO IRS这个在SOPC中指分配了一个IRQ,所以无所谓单片机中的“中断优先级”的说法,所以只能通过bit_clearing来更完美的分配CPU 的任务。

 

     以上说明基本上解释了以下ZLG的翻译是错了,以前我也对着ds发现过别的error,看来原汁原味的dt就是好!

image

最后解释一个为什么在Enable bit_clearing 打开的时候,IOWR_ALTERA_AVALON_PIO_EDGE_CAP(KEY_DATA_BASE,0x00)为什么会死机,程序卡死在初始化这句话中的问题:

     根据altera的dt(我以上分析的(2)),讲到了当打开Enable bit_cleraing的时候,要给相应位写1,才能清除标志位。(网上很多人都是给0清零的,这可能是因为他们没有打开Enbale bit_clearing),但是我们给了0 来清楚edagcapture,这样根本不能清除中断标志,因此一次死在里面(所谓君让臣死,臣不得不死,不死也得死)。只有清除了才可以,根据我的验证,此时写1来清除相应的bit,很好的解决了问题。

     这同时也反驳了牙缝的博客,诱惑了我的致命问题(当然我很感谢本人,因为他对我的帮助远胜过这两天的痛苦,可能人家NC了失误了哈哈):Enable bit_clearing打开了,而清除边沿寄存器的时候却给了0:http://www.cnblogs.com/yuphone/archive/2010/11/25/1887621.html

image image .

     问题到了现在算是比较清晰了,还以为ALtera自己神经病,最后发现不是软件问题,也不是altera神经病,而是我们没有好好啃ds。

     师父说:“altara是奥托拉,是奥特曼的弟弟,可牛逼了;MOTO是摩托罗拉,是摩托也要用骡来拉,可戳了。。”(师父真是个神!!!)因此不能一直埋怨altera,他们做的事情一般都是有哥根据的。

     下面贴上代码,呵呵,给自己留个底:

一般情况下,不管你Enable bit_clearing怎么配置,给1 清零永远不会错!!!

/* 
* sys_main.c 

*  Created on: 2011-4-1 
*      Author: CrazyBingo 
*/

#include <stdio.h> 
#include "unistd.h" 
#include "system.h" 
#include "alt_types.h" 
#include "sys/alt_irq.h" 
//#include "io.h" 
#include "altera_avalon_pio_regs.h"

//#include "../inc/my_sopc.h" 
//#include "../inc/key_scan.h"

void key_interrupts(void * key_isr_context); 
void key_interrupts_init(void);

// 定义全局变量以储存边沿捕获值 
volatile int edge_capture;

int main(void) 

    key_interrupts_init();

    while(1) 
    { 
        IOWR_ALTERA_AVALON_PIO_DATA(LED_DATA_BASE, edge_capture); 
    } 
    return 0; 
}

//* 按键中断初始化 */ 
void key_interrupts_init(void) 

    /**//* Recast the edge_capture pointer to match the alt_irq_register() function 
     * prototype. */ 
    void* edge_capture_ptr = (void*) &edge_capture; 
    /**//* Enable all 2 button interrupts. */ 
    IOWR_ALTERA_AVALON_PIO_IRQ_MASK(KEY_DATA_BASE, 0x03); 
    /**//* Reset the edge capture register.Enable bit_clearing turn0 off, write any vaule will take effect */ 
    IOWR_ALTERA_AVALON_PIO_EDGE_CAP(KEY_DATA_BASE, 0x03); //active High always take effect 
    /**//* Register the interrupt handler. */ 
    alt_ic_isr_register 
    ( 
            KEY_DATA_IRQ_INTERRUPT_CONTROLLER_ID,    // 中断控制器标号,从system.h复制 
            KEY_DATA_IRQ,                             // 硬件中断号,从system.h复制 
            key_interrupts,                         // 中断服务子函数 
            edge_capture_ptr,                          // 指向与设备驱动实例相关的数据结构体 
            NULL                                    // flags,保留未用 
    ); 
}

//按键中断服务程序(Interrupt service routime),每次下降沿进入中断执行一次 
void key_interrupts(void * key_isr_context) 

    /**//* Cast context to edge_capture's type. It is important that this be 
     * declared volatile to avoid unwanted compiler optimization. 
     */ 
   volatile int* edge_capture_ptr = (volatile int*) key_isr_context; 
    /**//* Store the value in the Button's edge capture register in *context. */ 
    *edge_capture_ptr = IORD_ALTERA_AVALON_PIO_EDGE_CAP(KEY_DATA_BASE); 
    /**//* Reset the edge capture register. */ 
    IOWR_ALTERA_AVALON_PIO_EDGE_CAP(KEY_DATA_BASE,0x03); 
}

    终于可以进行我下一步了。。。柳暗花明----有时候,解决为什么比解决怎么做更加的痛苦,但此时解决了师父和牙缝的的失误,同时,我终于可以放松一下了,这阵子因为这个中断,因为Cyclone III PCB,快让我over了,不过终于解决了问题,累着并且开心着。

     听师父的,Go on!

posted @ 2011-04-03 21:18  _安德鲁  阅读(1573)  评论(0编辑  收藏  举报