CrazyBingo

“君让臣死 臣不得不死 不死也得死”的NIOS II 9.1 SP1中断问题

0
阅读(4163)

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

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

首先NIOS 91用了增强型中断,当然这不是问题的关键,我不详说,看牙缝的博客:http://blog.chinaaet.com/detail/8484.html

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



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惹的祸:


意思是说:

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就是好!


最后解释一个为什么在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

 

.

问题到了现在算是比较清晰了,还以为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!