专注电子技术学习与研究
当前位置:单片机教程网 >> Arduino >> 浏览文章

扩展NDS掌机连接Arduino (3)--硬件中断设计

作者:c_gao   来源:转自c_gao   点击数:  更新时间:2014年07月12日   【字体:
上一篇文章:扩展NDS掌机连接Arduino (2)--NDS端SPI通信协议解析较详细分析了SPI协议原理,以及NDS端SPI相关寄存器和使用方法。本篇介绍Arduino与NDS之间的硬件中断设计,即一方如何触发另一方的硬件中断。

由于第一篇文章介绍的方案中,我们将NDS作为SPI通信中的主机(Master),将Arduino作为从机(Slave)。在SPI通信中,主机向从机发送数据很容易:将SS线打低电平,然后就可以发送数据到MOSI线上。而从机向主机发送数据则比较困难,因为SPI从机不能主动向主机发送数据。为了我们最后实现的方案中使从机能主动向主机发送数据,我们采用从机向主机触发一个IRQ中断,让主机知道从机有数据需要发送,然后由主机引发SPI通信,并使从机的数据发送到主机。

一、硬件中断设计

这里最核心的部分,即如何让从机(Arduino)去触发一个主机(NDS)的IRQ中断呢?这可以通过以下两步完成:

(1)硬件上:连接Atmega 168/328 CPU的 PB6 引脚到NDS Slot 1接口的第7引脚,见图1;
(2)软件上:首先配置NDS端开启CARD LINE IRQ中断,然后让Atmega CPU 在 PB6 引脚上发生一个从低电平到高电平的上升沿,或从高电平到低电平的下降沿,来触发NDS的IRQ中断。


图1. Atmega 168/328 与NDS Slot 1的连接,红色线为IRQ硬件连线。

说明:NDS Slot 1的第7引脚对应Card Line IRQ,可接收Slot 1的外部中断到NDS内部,并进行处理。Atmega 168/328的 PB6 (即PORT B 6)和很多其它引脚一样,是个多功能复用的引脚。它有3个功能,除了可以和PB7一起连接外部晶振等功能外,也可以作为external interrupt source,即外部中断源。(注:不过因为NDS作为SPI主机和Atmega通信不需要再通过触发Atmega的外部硬件中断来告诉Atmega需要接收NDS即将发的数据,而NDS可以直接将SS拉低后发送,因此这里只用于Atmega去触发NDS的外部中断。所以也可以连接到Atmega的其它空余的数字引脚。)

二、NDS端对外部IRQ硬件中断的软件设置

步骤如下:
(1)首先按上篇教程先完成对NDS端的SPI初始化工作;
(2)然后编写CARD INE IRQ中断服务函数代码,并设置该中断调用的函数到相应的中断向量表中。使用libnds的库函数,可如下操作实现:

irqSet(IRQ_CARD_LINE, card_line_irq);

其中,参数IRQ_CARD_LINE为中断号,该宏对应Slot 1卡带的CARD LINE IRQ中断号,值为IRQ_CARD_LINE=(1<<20)。参数card_line_irq为中断服务函数,其函数原型为:

static void card_line_irq();

说明:NDS的各中断可参考REG_IE/REG_IF寄存器,如图2所示:


图2. REG_IE/REG_IF寄存器。红色部分为第20位,CARD LINE IRQ中断标记位。

(3)中断开启NDS的CARD LINE IRQ中断使能,可通过设置REG_IE寄存器第20位为1实现:
int oldIME = REG_IME;
REG_IME = 0;
REG_IE |= (1<<20);
REG_IME = oldIME;
这段代码首先将NDS的中断总使能寄存器REG_IME置0, 然后对REG_IE第20位置1,最后恢复REG_IME的原有值。这是因为REG_IE寄存器属于临界资源,也不允许操作REG_IE寄存器时发生别的中断而转去别的中断处理代码。也就是说对REG_IE的操作是原子操作。

如果使用libnds库函数则比较方便,直接调用如下函数便可

irqEnable(IRQ_CARD_LINE);


至此,NDS端对Slot 1接口的CARD LINE IRQ的中断处理工作就准备就绪了,一旦Arduino向NDS端发送一个电平波动引发NDS的CARD LINE IRQ中断后,card_line_irq()函数便会自动执行。因此可以在这个函数里向Arduino发送一个dummy字节,引发SPI通信,使得Arduino可以在收到这个dummy字节后随随后把数据赋给SPDR寄存器,使该数据传输给NDS主机。

三、Arduino端如何触发NDS端CARD LINE IRQ硬件中断

上文已述,需要Arduino在PB6接口向NDS端发送一个电平波动。一般需要跟据NDS端对CARD LINE IRQ中断响应的配置进行发送电平波动,但由于找不到文档关于NDS端对CARD LINE IRQ中断响应的配置信息,所以不知道是该发送一个低电平到高电平的上升沿过去,还是发送一个从高电平到低电平的下降沿过去。参考DS brut中Atmega端的代码,是这么做的:
void do_irq()
{
  // raise a Card Line hardware interrupt (PB6)
  DDRB |= (1 << 6);
  PORTB &= ~(1 << 6);    // low
  PORTB |= (1 << 6);     // high
  PORTB &= ~(1 << 6);    // low  
}

分析该函数:

1. DDRBPORT B引脚对应的数据传输方向寄存器 (Port B Data Direction Register),DDRB |= (1 << 6); 的意思是将DDRB第6位置1,查看DataSheet,1代表输出,0代表输入。所以这里将PB6引脚配置为输出,类似于调于Arduino的库函数:pinMode (PB6, OUTPUT); 
2. PORTB为PORT B引脚对应的数据传输寄存器,相应的位如果置1代表电平为HIGH置0代表为LOW。因此上述代码先产生了一个由低电平到高电平的上升沿,再产生一个高电平到低电平的下降沿。这样一来,肯定能触发NDS的CARD LINE IRQ中断了。

相关文章