S3C2440串口FIFO模式的中断机制和处理策略

陈海生,郭晓云,邓锐,王峰,陈亮
1. 广东海洋大学 信息学院, 湛江524000; 2. 广东海洋大学 智能工程研究所

摘要:为了研究基于S3C2440的串口FIFO模式的中断机制和相应处理策略,采用实验方法验证了该模式的发送中断的触发时刻是当批量字符从FIFO中移出,直到FIFO中剩下与触发深度设置值相等数目的字符的时候。修正了中文使用手册认为“当批量字符从FIFO中移出,发送中断的触发时刻是当移出字符个数与触发深度设置值相等的时候”的错误。同时通过实验验证了串口FIFO模式的接收中断机制。在两者的中断处理基础上,提出一种实用的处理策略。
关键词: 串口中断;FIFO模式;处理策略;S3C2440 
中图分类号: TP915文献标识码: A

Mechanism and Processing Strategy of UART Interrupt in FIFO Mode Based on S3C2440
Chen Haisheng,Guo Xiaoyun,Deng Rui,Wang Feng,Chen Liang
1. Information College, Guangdong Ocean University, Zhanjiang 524000,China;2. Institute of Intelligent Engineering,Guangdong Ocean University
Abstract: For researching the interrupt mechanism and corresponding treatment strategies of serial FIFO mode based on S3C2440, the experiment verifies that the send interrupt trigger moment of the mode is when the bulk characteres are removed from the FIFO until FIFO remaining trigger depth is set equal to the value of the number of bytes. It fixes Chinese user manual that “when the bulk characters are removed from the FIFO, the transmit interrupt triggering moment is the time that out of the number of characters are equal to the trigger depth settings value”. Meanwhile through experiments it verifies receive interrupt mechanism of the serial FIFO mode. For both interrupt handlings, a practical strategy is proposed. 
Key words: UART interrupt; FIFO mode; processing strategy; S3C2440

引言
  基于S3C2440的串口非FIFO模式的应用文献多有介绍,但少有详细论述FIFO模式的中断机制和应用的。为了深入理解FIFO模式下的串口中断产生条件、中断类型的判断、一批连续的数据发送结束的判断和一批连续数据接收结束的判断等,笔者对比了参考文献〔1〕所示的中文版S3C2440使用手册和参考文献〔2〕所示的英文版S3C2440使用手册中的有关描述,发现两者对发送触发深度的表述有着根本性的矛盾,因此本文将通过实验综合阐述FIFO模式的中断机制和应用要点。 
1 串口内部结构和工作原理 
  S3C2440的每个串口的内部含有发送器和接收器,其中包含了64字节FIFO和数据移位器。将数据写入到FIFO,接着在发送前复制到发送移位器中。随后将在发送数据引脚(TxDn)移出数据。同样从接收数据引脚(RxDn)移入收到的数据,接着从移位器复制到FIFO〔12〕。 
2 FIFO接收中断机制和处理策略 
2.1 接收中断触发深度 
  S3C2440中在UART FIFO 控制寄存器用于设置接收和发送中断的触发深度。若设置Rx FIFO 触发深度为32字节,表示当FIFO中接收到字符达到32个时,就触发中断。如果接收的字符小于32个,在3个周期期间没有收到数据时,就触发接收超时中断。 
2.2 接收中断类型的判断 
  把接收达到触发深度引起的中断称为“接收深度满中断”。由于接收超时中断的处理和接收深度满中断的处理是不同的,有必要对接收中断类型进行判断。 
  对于Phillips公司的LPC2131,可以通过判断其寄存器UxIIR[3:0]中的相应位就可以直接知道串口接收中断类型[3]。而在S3C2440中接收深度满中断和接收超时中断则共用一个中断标志,因此不能直接判断两种类型的中断。 
  S3C2440有3个UART FIFO 状态寄存器 UFSTATn,该寄存器的位[5:0]中的Rx FIFO Count (Rx FIFO计数器)表示当前FIFO中接收到的字符个数。因此,本文采用的方法是当确定是接收中断后,通过读取UFSTATn[5:0],得到当前FIFO中接收字符的个数。若字符个数等于触发深度设置值,则说明该中断是接收深度满中断,否则就是接收超时引起的中断。 
2.3 接收数据的处理策略 
  如果接收的数据长度是随机的,有必要知道一批连续的数据接收结束的时刻,以便进一步处理。假设触发深度设置为32个字符,现有如下两种策略。 
(1) 策略一
  在中断处理的时候,当判定是接收深度满中断后,采用连续读取FIFO数据32个字节;当判定为接收超时中断,采用把FIFO里面的数据读到空为止。 
  若发送方发了33个字符,接收方就会出现两次接收中断。第一个中断是接收深度满中断,接着是接收超时中断,可以利用超时中断的产生来标志一次接收数据的结束。若发送方发了32个或者32的整数倍个字符,接收方触发的都只是接收深度满中断,因此无法确认一批连续数据是否结束。 
(2) 策略二
  在接收深度满中断的处理上,不是连续读取32个字符,而是连续读取31个字符,留1个字符在FIFO中。这样任意长度字符的接收,都会拥有一个接收超时中断,而接收超时中断的发生则认为是一次连续接收数据的结束,本文正是采用此策略。 
2.4 策略实现的验证 
  在实验中,设置串口0为接收FIFO中断模式,接收中断的触发深度为32个字节,串口1设置为非FIFO模式下的查询模式。 
  从外面往串口0送一批连续的数据,若有接收深度满中断的发生就通过串口1发送一个字符 M ;若有接收超时中断的发生就通过串口1发送一个字符 C 。最后,当串口0接收数据结束,把接收到数据全部通过串口1发送出来,对比往串口0发送的数据和串口1发送出来的数据是否相同,从而测试串口0接收中断的处理策略是否正确。 
  串口0中断服务代码中的接收数据策略实现函数UART_receive()如下:
void UART_receive(void){ 
  char temp ;
  char count;
  count=rUFSTAT0&0x3F;
  /*接收超时中断的处理*/
if(count<32){
  while((rUFSTAT0&0x3F)){//读取里面字符,直到FIFO为空
    temp=rURXH0;
    ReceiveBuffer[ReceiveIndex]=temp;
    ReceiveIndex++;
  }
  ReceiveIndex= ReceiveIndex-1; //ReceiveIndex为接收数组的元素位置索引
  flag_Receive_end=1;//flag_Receive_end为接收结束标志,值为1表示一次数据接收结束
  UART1_send_byte( C );//通过串口1发送一个字符 C ,标志接收超时中断
  }
/*接收深度满中断的处理*/
else{
  int i; 
  for(i=0; i<=30; i++){ //先读取31个,留1个
    temp=rURXH0;
    ReceiveBuffer[ReceiveIndex]=temp;
    ReceiveIndex++;
  }
  flag_Receive_end=0; //该值为0表示接收还没有结束UART1_send_byte( M );通过串口1发送一个字符 M ,标志接收深度满中断的产生 
}
/*把串口0接收到的数据通过串口1发送出去*/ 
if(flag_Receive_end==1) {
  UART1_send_string(ReceiveBuffer, ReceiveIndex+1);//调用串口1发送字符串函数
  flag_Receive_end=0;//发送完毕,恢复初值
  ReceiveIndex=0;//发送完毕,恢复初值
  }
}
  往串口0送“b12345678c12345678d12345678f12341 23456789987654321a1234567b12345” 64个字符,得到从串口1送出的数据是:
MMCb12345678c12345678d12345678f12341234567899876 54321a1234567b12345
“MMC”中的“M”表示接收深度满中断标志,“C”表示接收超时中断标志。说明接收64个字符需要三个中断,头两个是接收深度满中断,最后一个是接收超时中断。分别往串口0送27个字符、32个字符、65个字符以及100个字符,测试都能得到预想的结果,证明该处理策略是可靠的。 
3 FIFO发送中断机制和处理策略 
3.1 发送中断的产生讨论 
  参考文献[1]与参考文献[2]给出的发送FIFO中断产生的条件有矛盾。若设置发送中断触发深度为16,参考文献[1]会认为发送中断产生的时刻是从FIFO中移出16个字符的时候,而参考文献[2]则认为是从FIFO里面移出数据直到剩下16个字符的时候。 
3.2 实验方法 
  通过控制寄存器UFCONn来设置发送中断触发深度为16字节。按照笔者对参考文献[2]的理解,编写测试程序。串口0设置为FIFO中断模式,串口1设置为非FIFO查询模式。字符串通过串口0发送出去,观察串口0发送中断产生的时刻。一旦串口0有发送中断发生,则通过串口1发送一个字符 S 作为标志。 
3.3 发送策略与实现 
  由于发送数据是主动的,必然会知道需要发送数据的长度。若需要发送的字符个数大于或者等于48个字符,先把前48个字符一次性写入FIFO。当发送中断发生后,接着对剩下的字符进行发送。若不足48个,批量一次性把剩下的字符写入FIFO,否则把前48个字符一次性写入FIFO;发送中断发生,重复上述过程,直到数据发送完毕。 
  用一个公式来表达数据长度与发送周期和剩下字符数的关系,如下: 
  SendLength(数据长度)=48×Send_Cycle(发送周期)+Send_Remainder(剩下字符数) 
  若要发送100个字符,有SendLength=100,Send_Cycle=2,Send_Remainder=4。即前2次分别发送了48个字符,后一次发送了剩下的4个字符。 
  串口0中断服务程序中的发送中断处理部分关键代码如下:
void __irq UART0_interrupt(void){  
  /*发送中断处理*/
  if((rSUBSRCPND&0x1<<1)==2){
    UART1_send_byte( S ); //串口1送出字符 S ,表示串口0发送中断的产生
    /*串口0数据发送结束的处理*/
  if(flag_Send_end==1){ //flag_Send_end为数据发//送结束标志,为1表示数据发送结束
    Send_Index=0;//发送的字符在数组中的位置索引,恢复初值为0
    flag_Send_end=0;//恢复初值为0
  }
  /*串口0数据发送还没有结束的处理*/
  else{
    if(Send_Cycle!=0) {
      for(int i=0;i<48;i++){ //连续发送48个字符
        rUTXH0 = P_Send[Send_Index]; 
        Send_Index++;
      }
      Send_Cycle--;
    }
    else{
      for(int i=0;i<Send_Remainder;i++){//连续发送最后剩下的字符
        rUTXH0 = P_Send[Send_Index]; 
        Send_Index++;
      }
      flag_Send_end=1; //给一个数据发送完毕的标志
    }
  }
  /*清除中断标志位*/
  rSUBSRCPND |=0x2;
  rSRCPND |=1<<28 ;
  rINTPND |=1<<28 ;
  }
}
  main.c中其他相关代码如下:
char SendBuffer[2000]="b12345678c12345678d12345678 f1234123456789987654321a1234567b12345p12345678998765432 1a1234567b12345MKL";
char *P_Send;//发送数据指针
U16 SendLength=100;//自定义发送字符个数
U16 Send_Cycle;//48个字符发送的循环周期数
U16 Send_Remainder ;//剩下字符数
U16 Send_Index=0;//发送字符在数组中的索引位置
char flag_Send_end=0;//从0变为1表示一次连续发送结束,处理后重新置0
……
void SendData(char* Data, U16 Length){//串口0发送指定长度的字符串函数
  Send_Index=0;
  flag_Send_end=0;
  P_Send=Data;
  Send_Cycle=Length/48;
  Send_Remainder=Length%48;
  if(Send_Cycle!=0){
    for(int i=0;i<48;i++){
      rUTXH0 = Data[Send_Index]; 
      Send_Index++;
    }
    Send_Cycle--;
  }
  else{
    for(int i=0;i<Send_Remainder;i++){
      rUTXH0 = Data[Send_Index]; 
      Send_Index++;
    }
    flag_Send_end=1;//给一个数据发送完毕的标志
  }
}
int main(void){
  ……
  SendData(SendBuffer,SendLength);//启动串口0发送指定长度字符串
  return 0;
}
3.4 测试结果分析 
  main.c中的SendBuffer[2000]字符数组用于存放测试用的发送数据,根据发送数据长度的需要设置发送长度SendLength的大小。 
  运行程序后,从串口0送出的数据为: 
  b12345678c12345678d12345678f1234123456789987 654321a1234567b12345p123456789987654321a1234567b12 345MKL 
  该结果跟指定发送的字符数组里面的数据一致,说明串口0成功发送了数据。 
  从串口1送出的数据为:SSS。该结果说明发生了3次发送中断,分析如下: 
  先往FIFO里面写入前48个字符。在硬件的机制作用下,一个个字符从FIFO里面移出,FIFO里面剩下16个字符时就触发中断,中断处理时,继续写入剩下数据中的前48个字符。新写入的48个字符不会覆盖FIFO中剩下的16个字符,由于48加上16刚好等于64,而FIFO有64个字节空间,而且写入的时候,剩下的16个字符也会一个个移出,所以不会有新数据把旧数据覆盖的可能。写入48个字符后,退出中断。在内部机制的作用下,随着字符的一个个移出,当重新剩下16个字符时触发中断,中断处理时把最后剩下的4个字符写入FIFO,并给一个数据发送完毕的标志,退出中断服务。随着一个一个字符从FIFO里面移出,当FIFO里面又重新剩下16个字符时就触发中断,不过这次中断在中断处理时,根据判断数据已经有发送完毕的标志,不再作任何处理,退出中断,因此共发生了3次中断。 
  改变程序的SendBuffer[2000]里面的数据和发送长度,分别测试发送64个字符和200个字符,结果与预想分析一致。 
  因此对参考文献[2]中的关于发送FIFO中断触发深度值的正确理解是:若设置的触发深度为N,当FIFO中剩下N个字符的时候,触发中断。而参考文献[1]的中文版使用手册认为是当移出去N个字符的时候,就触发中断,应当给予修正,以免误导开发者。 
结语 
  本文就串口FIFO模式的中断机制进行详细描述,其中修正了中文版使用手册上关于发送中断发生时刻的错误,同时对两者在中断处理上提出了一种实用的处理策略,对正确使用串口的FIFO模式有一定参考价值。 
  编者注:本文为期刊缩略版,全文见本刊网站www.mesnet.com.cn

参考文献
[1] S3C2440全套中文手册[EB/OL]. [20130107].http://wenku.baidu.com/view/009346fc770bf78a6529 54bb.html.
[2] S3C2440A_UserManual_Rev3[EB/OL].[20130107].http://wenku.baidu.com/view/2b88b9607e21af45b307a8dd.html.
[3] 周立功,张华. 深入浅出ARM7——LPC213x/214x(上册)[M].北京:北京航空航天大学出版社,2005:168 .

陈海生(讲师),主要研究方向为嵌入式智能控制系统;郭晓云(博士),主要研究方向为视频编码和处理;邓锐(讲师),主要研究方向为模式识别;王峰(讲师),主要研究方向为嵌入式系统及应用;陈亮(讲师),主要研究方向为嵌入式智能控制系统。

阅读更多
换一批

没有更多推荐了,返回首页