[转载].“君让臣死 臣不得不死 不死也得死”的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,我不知道为什么这就是所谓的增强型,听说是为了便于以后升级:
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!