中断处理程序的设计

在嵌入式系统的设计中,与外设交互,基本是两种模式:轮询和中断。考虑到CPU与外设之间的速度差,如果涉及到与外设通信,一般采用中断的方式:只有当外设完成了数据发送或者接收,才中断CPU,获得处理,这样可以保证CPU的处理效率最高。

与数据收发相关的中断,中断处理程序与外部程序(操作系统和应用程序)之间的数据交换方式的设计是中断处理程序设计的一个关键部分,直接影响系统的工作效率,甚至是一些偶发性故障的原因。

数据交换的主要考虑的因素有:数据完整性(保证数据不丢失)、交换的效率(尽量减少CPU的参与)、执行效率(中断处理程序的执行应尽量保证时间短)、异常数据丢弃的效率(尽可能在离入口近的位置丢弃异常数据)。其中数据完整性经常会被忽视,中断处理程序的不恰当导致的数据丢失经常会与通信干扰导致的数据丢失相混淆,从而掩盖了真正的问题。

本文主要探讨中断处理程序与外部程序之间的数据交互的方式及其优缺点。

FIFO结构缓存

这是一种最简单直观的数据交互方式,采用循环Buffer作为缓存,主要用于数据接收。其结构如下:

中断处理程序的设计

循环Buffer有一个起始位置Start和一个结束位置End,当指针移动到End以后,自动回到Start位置。关于循环Buffer的具体实现,网上有很多详细资料,此处就不赘述。

应用到中断的接收处理中,中断处理程序作为该FIFO的数据生产者,操作写指针WritePointer,将接收到的数据写入该缓存;外部程序作为该FIFO的数据消费者,操作读指针ReadPointer,将数据从缓存中读出,进行处理。外部程序必须保证读指针不超越写指针,以防止读出不正确的数据。简单的处理伪代码如下:

Interrupt Handler:

{

  ...

  FifoBuffer[WritePointer] = DataRegister;

  WriterPointer++;

  if(WriterPointer > End) WriterPointer = Start;

  ...

}

Application:

{

  ...

  AppBuffer[bufPointer] = FifoBuffer[ReadPointer];

  bufPointer++;

  ReadPointer++;

  if(ReadPointer > End) ReadPointer = Start;

  Lock_CPU();

  if(ReadPointer == WritePointer) {

    unLock_CPU();

    Stop;

  }

  unLock_CPU();

  ...

}

在采用该方法时,实际上隐含着一个假设:写指针永远不会追上读指针,也就是不会发生循环Buffer溢出的情况。为了满足这个要求,一方面外部程序必须保证读处理应该以合适的频度得到执行;另外,在设计时,循环Buffer保证有足够的长度。

从上面的实现可以看出,中断处理程序只负责将接收到的数据放入FIFO中,处理简单,占用时间少;但外部程序必须频繁地检查FIFO以确保新收到的数据得到及时处理;数据包的识别、数据的检查和拷贝也都需要外部程序负责,处理负担较重。同时为了保证数据不丢失,就必须满足上述隐含条件。

 

消息传递

在嵌入式系统中,不同任务之间,经常采用一种消息机制来进行数据传输。所有的嵌入式操作系统,都提供了相应的机制,如RTX的MailBox。基本的思路就是将需要发送的数据放入公共的内存中(在嵌入式系统中,实际上就是全局变量),然后通过邮箱将指针传递给接收方。

在中断处理程序和外部程序之间,也可以采用这种机制传递接收到的数据。为了提高传递的效率,一般需要中断处理程序识别出一个完整的数据包,以一次传递一个数据包的形式进行。简单的处理伪代码如下所示:

 

Interrupt Handler:

{

 ...

msgSendBuffer = Get_Message_Buffer();

CopyDataToBuffer(InternalBuffer, msgSendBuffer);

Send_MailBox(DestinationMailBox, msgSendBuffer);

...

}

 

Application:

{

...

Receive_MailBox(WaitMailBox, msgRcvBuffer);

...

}

 

采用这种方式,可以简化外部处理程序的实现,以一种统一的处理消息的方式来实现对来自中断和其他任务的数据。对接收到的数据的处理,中断处理程序侧需要识别出一个完整的数据包,并将它拷贝到消息缓存中,对数据包合法性的检查可以由中断执行,也可以由外部程序执行。因此,中断处理程序的任务较多,而外部Task则相对简单。

在这种方式中,同样有一个隐含的条件:必须保证消息缓存肯定能申请到,否则将导致整个数据包的丢失。另外,如果消息缓存与外部任务用的缓存采用同一套机制的话,外部任务的缓存申请可能延迟中断的缓存申请,影响中断处理的效率。为了解决这个问题,常常为中断与外部传输准备专用的缓存。

 

缓存区交换

在中断处理程序中的数据拷贝是一件比较耗时的工作,往往会阻塞其他中断处理程序和应用程序,严重时导致异常发生。因此在中断处理中避免拷贝,是提高效率的捷径。如果能够同时与DMA等的使用相结合,能够显著解放CPU的负担,提高整个系统的运行效率。

缓存区交换是对消息传递的一种改进,中断处理程序与外部应用程序之间还是采用消息传递的机制,但在中断处理程序中不再需要申请缓存,而是直接使用外部应用程序预先申请好的缓存;而预先申请缓存,在中断需要使用缓存时保证已经准备好则是外部应用程序的责任。

基本处理流程如下所示:

中断处理程序的设计

该方法如果与DMA结合使用,接收数据的动作将由硬件自动完成,大大减少对CPU的中断次数,可以大大提供处理效率。