UART0串口编程

来源:互联网 发布:java 策略算法 编辑:程序博客网 时间:2024/05/21 22:41
 

本文章针对的是ARM2200环境下编写串口程序,其中设计轮循方式,中断方式,以及在UC/OS-II操作系统下的串口编程。

使用轮循和中断两种方式来实现串口编程。

(当然了,用中断实现串口编程,系统的效率较高。但是难度也较大 。轮循方式效率较低,但是编程比较简单)

UART0串口编程目录:

串口编程之前奏篇


UART0串口编程系列(一)裸机下的轮训方式的串口编程

UART0串口编程系列(二)裸机下的中断方式的串口编程

UART0串口编程系列(三)UC/OS下的串口编程注意的问题

UART0串口编程系列(四)UC/OS下的串口发送任务编程

UART0串口编程系列(五) UC/OS下的串口接收任务编程


一.        串口编程的硬件原理

1.     串口特性:

1>16字节接收FIFO和16字节发送FIFO

2>接收FIFO触发点可设置为1,4,8或14字节。

3>内置波特率发生器。

2.     UART0引脚:
1>RxD0 引脚用于UART0接受数据,接受方式为串行输入。

2>TxD0引脚用于UART0发送数据,发送方式为串行发送数据。

3.     UART0的结构和工作方式

先看图在说明:

 
1>  VPB总线提供CPU与UART0之间得的通信连接

(CPU内核通过VPB接口对UART0的寄存器进行读写访问.)

2>  UART0 接收器模块监视串行输入线RxD0的有效输入。UART0 接收单元的移位寄存器(U0RSR)通过RxD0接收有效的字符。当U0RSR接受到一个有效字符时,它将该字符传送到UART0 接收单元缓冲寄存器FIFO中,等待CPU通过VPB接口进行访问。

3>  UART0发送器模块接收CPU或主机写入的数据并将数据缓存到UART0 的FIFO或U0THR中,UART0发送模块中的移位寄存器(U0TSR)读取U0THR或FIFO中的数据并将数据通过串行输出到引脚TxD0发送。

4>  UART0的接收模块和发送模块的状态信息保存在U0LSR中。

控制信息保存在U0LCR中。

5>  UART0波特率发送器模块产生UART0 发送模块所使用的定时。波特率发生器模块时钟源为VPB时钟(pclk)。主时钟与U0DLL和U0DLM寄存器所定义的除数相除得到UART0 发送器模块使用的时钟,该时钟必须为波特率的16倍。

6>  中断接口包含寄存器U0IER和U0IIR。中断接口接收UART0发送模块和接收模块发出的单时钟宽度的使能信号。

4.     UART0和ARM7 CPU之间的通信过程

1>CPU通过UART0发送模块发送信息给外设

l  CPU发出信息通过AHB总线到AHB-VPB桥

l  通过AHB-VPB桥把信息转换后发送给VPB总线。

l  UART0接收模块接受来自VPB总线的数据。并将数据缓存到U0THR寄存器中。

l  UART0接受模块的移位寄存器U0TSR读取U0THR中的数据 并将数据通过输出引脚TxD0发送


2>外设通过UART0接收模块向ARM7 CPU发送信息

l  UART0移位寄存器(U0RSR)通过引脚RxD0接收有效字符。

l  当UART0接收到一个有效字符后,通过读取U0RBR寄存器可以将FIFO中最早接收到的字节读出,当FIFO中不再包含有效数据时,该寄存器反映接收到的最后一个有效字节数据。接收的数据不足8位时,高位用0填充。

l  VPB总线将缓冲寄存器(U0RBR)中的数据通过AHB-VPB桥传到AHB总线上

l  AHB总线将数据传送给ARM7 CPU

 

二.          轮训方式的串口编程

1.     串口程序都有那几部分组成

看图:       

 

1>  串口初速化

A.    串口初始化的流程

l  设置I/O引脚连接到UART0

l  设置串口波特率

l  设置串口工作模式

B.     串口初始化需要设置的寄存器

l  U0LCR(控制寄存器):设置UART0的通信格式。

l  U0DLL,U0DLM(寄存器):设置UART0的通信波特率。

C.     具体寄存器的设置

(1) U0LCR(线控制寄存器)

l  作用:设置通信格式(通信字符长度,停止位个数,奇偶校验位

l  长度:8位寄存器

l  各位寄存器的含义:

第[1 ,0]位: 表示字长

00:表示5位字长

01:表示6位字符长度

10:表示7位字符长度

11:表示8位字符长度

第2位: 表示停止位选择

         0:1个停止位

 1:2个停止位

3位:表示奇偶使能

0:禁止奇偶产生和校验

1:使能奇偶产生和校验

注:奇偶使能:控制是否进行奇偶校验。如果使能,发送时将添加一位校验位。

第[5 4]位:表示奇偶选择位

       00:奇数(数据位+校验位=奇数)

       01:偶数(数据位+校验位=偶数)

       10:校验位强制为1

       11:校验位强制为0

注:奇偶选择主要是设置奇偶校验类型。

   第6位:间隔控制

                  0:禁止间隔发送

                  1:使能间隔发送

注:当该位为1时,输出引脚(TxD0)强制为逻辑0,可以引起通信对方产生间隔中断。在一些通信方式中,使用间隔中断作为通信的起始信号(eg:LIN Bus)     

   第7位:除数锁存访问位

        0:禁止访问除数锁存寄存器

        1:始能访问除数锁存寄存器

 

(2) U0DLL,U0DLM(除数锁存寄存器)

l  作用:U0DLL和U0DLM寄存器一起构成一个16位除数。

l  U0DLL和U0DLM都为8位寄存器。

l  U0DLL:存放分频值的低8位

l  U0DLM:存放分频值的高8位。

注:

?  1.使用U0DLL和U0DLM配置波特率之前,必须先计算分频值。

Fdiv=Fpclk/(16*baud)

?  2.使用U0DLL和U0DLM配置波特率之前必须把U0LCR控制寄存器的第8位置为1才能进行配置。配置完后要把U0LCR控制寄存器的第8位置位0。

2>  串口初始化化程序

A方法一:

/**********************************************************

* 作者:tiger-john

* 时间:2011年1月17日

* 名称:UART0_Init()

* 功能:UART0初始化(通讯波特率115200,8位数据位,1位停止

        位,无奇偶校验)

* 入口参数:bps     串口波特率

* 出口参数:无**********************************************************/

void     UART0_Init(uint32 bps)

{

 

    uint16  Fdiv;

    PINSEL0=0x00000005;  //设置串口引脚

    U0LCR=0x83;     //置为除数锁存位,进行配置

    Fdiv=(Fpclk>>4)/UART0_BPS;

    U0DLM=Fdiv>>8;

    U0DLL=Fdiv&0xff;

    U0LCR=0x03;         //清除除数锁存位,并设置工作模式

    }

B.方法二:

/**********************************************************

* 作者:tiger-john

* 时间:2011年1月17日

* 名    称:UART0_Init()

* 功    能:初始化串口0。设置其工作模式及波特率。

* 入口参数:baud                波特率

*          set          模式设置(UARTMODE数据结构)

* 出口参数:返回值为1时表示初化成功,为0表除参数出错********************************************************/

/* 定义串口模式设置数据结构 */

typedef  struct  UartMode

{   uint8 datab;           // 字长度,5/6/7/8

    uint8 stopb;           // 停止位,1/2

    uint8 parity;      // 奇偶校验位,0为无校验,1奇数校验,2为偶数校验

}   UARTMODE;

uint8  UART0_Init(uint32 baud, UARTMODE set)

{   uint32  bak;

  

    /* 参数过滤 */

    if( (0==baud)||(baud>115200) )

    {

        return(0);

    }

    if( (set.datab<5)||(set.datab>8) )

    {

        return(0);

    }

    if( (0==set.stopb)||(set.stopb>2) )

    {

        return(0);

    }

    if( set.parity>4 )

    {

        return(0);

    }

 

    /* 设置串口波特率 */

    U0LCR = 0x80;                    // DLAB位置1

    bak = (Fpclk>>4)/baud;

    U0DLM = bak>>8;

    U0DLL = bak&0xff;

  

    /* 设置串口模式 */

    bak = set.datab-5;                // 设置字长度

    if(2==set.stopb)

    {

       bak |= 0x04;                      // 判断是否为2位停止位 

    }

    if(0!=set.parity)

    {

        set.parity = set.parity-1;

        bak |= 0x08;

    }

    bak |= set.parity<<4;           // 设置奇偶校验

    U0LCR = bak;

 

    return(1);

}

2.     串口接收数据


用轮循方式接收数据

1>CPU通过串口接收数据时各个寄存器之间的关系

 

 
2>串口接受数据的流程:

l  循环检测U0RBR是否有未读取的数据。

l  如果有数据到来,则接收数据。

3>相关寄存器配置

(1) U0LSR(线状态寄存器)

l  作用:只读寄存器,它提供UART0发送和接收模块的状态信息。

l  长度:8位寄存器。

l  各位寄存器的含义:

A.0位:表示接收数据就绪

置0表示U0RBR为空

置1表示U0RBR包含有效数据

注:当U0RBR包含未读的字符时,第0位被置位;当UART0的U0RBR或FIFO为空时,第0位置零。

B.第1位:溢出错误。

置0:溢出错误状态未激活

置1:溢出错误状态激活

注:溢出错误条件在错误发生后立即设置。对U0LSR读操作将清零第1位。当UART0的RSR已经有新的字符就绪,而UART0  RBR或FIFO已满时,第一位置1.此时的UART0 RBR或FIFO不会被覆盖,UART0 的RSR中的字符将丢失。

C.第2位:奇偶错误。

置0:奇偶错误状态未激活

置1:奇偶错误状态激活

注:

2  当接收字符的奇偶位处于错误状态时产生一个奇偶错误。对U0LSR读操作清零该位。

2  奇偶错误检测时间取决于U0FCR的bit0。奇偶错误与UART0 的RBR,FIFO中读出的字符相关。

D.第3位:帧错误

       置0:帧错误状态未激活。

                 置1:帧错误状态激活

                 注:

2  当接收字符的停止位为0时,产生帧错误。对读操作U0LSR清零该位。

2  帧错误检测时间取决于U0FCR的bit0.

2  帧错误与UART0的RBR,FIFO中读出的字符相关。当检测到一个帧错误时,Rx将尝试与数据重新同步并假设错误的停止位实际是一个超前的起始位。但即使没有出现帧错误,它也不能假设下一个接收到的字节是正确的。

E.第四位:间隔中断

置0:间隔中断状态未激活

置1:间隔中断状态状态激活

注:

2  在发送整个字符(起始位,数据,奇偶位和停止位)过程中RXD0如果都保持逻辑0,则产生间隔中断。

2  当检测到中断条件时,接收器立即进入空闲状态直到RXD0变为全1状态。

2  读操作U0LSR清零该状态位。

2  间隔检测的时间取决于U0FCR的bit0.

2  间隔中断与UART0的RBR或FIFO中读出的字符相关。

F.第五位:发送保持寄存器空

      置0:表示U0THR包含有效数据

置1:表示U0THR空

注:

2  当检测到UART0 的THR空时,THRE置位。

2  对U0THR写操作清零该位。

G.第6位:表示发送器空

置0:U0THR和或U0TSR包含有效数据。

置1:U0THR和U0TSR空

注:

2  当U0THR和U0TSR都为空时,该位置1

2  当U0TSR或U0THR包含有效数据时,该位清零。

H第7位:表示Rx FIFO 错误。

置0:U0RBR中没有UART0 Rx错误,或U0FCR的bit为0.

置1:U0RBR包含至少一个UART0 Rx错误。

注:

2  当一个带有Rx错误(例如帧错误,奇偶错误或间隔中断)的字符装入U0RBR时,该位置1.

2  当读取U0LSR寄存器并且UART0的FIFO中不再有错误时,该位置零。

(2) U0RBR(接收器缓冲寄存器)

l  作用:只读寄存器,是UART0 Rx FIFO的最高字节。它包含了最早接收到的字符,可通过总线接口读出。串口接收数据时低位在先,即U0RBR的bit0为最早接收到的数据位。如果接收到的数据小于8位,未使用的MSB填充为0.

l  长度:8位寄存器。

4>串口接收数据程序

/**********************************************************

* 作    者:tiger-john

* 时    间:2011年1月17日

* 名    称:            UART0_RcvByte

* 功    能:            用查询方式接收一字节的数据

* 入口参数:            无

* 出口参数:            data              要接收的数据

**********************************************************/

uint8     UART0_RcvByte(void)

{

uint8 rcv_data ;

while((U0LSR&0X01)==0);         //等待数据到达

        rcv_data = U0RBR;       //从U0RBR中读出接收到的数据

return  rcv_data;               //返回接收到的数据

}

 

3.     串口发送数据

1>  用CPU通过串口发送数据时,各寄存器之间的关系

 

2>  串口发送数据时的流程

l  将要发送的一字节数据写入U0THR

l  等待数据发送完毕

3>  相关寄存器配置

(1)U0THR(发送保持寄存器)

l  最用:只写寄存器。U0THR是UART0 Tx FIFO的最高字节。它包含了Tx FIFO 中最新的字符,可通过总线接口写入。串口发送数据时,低位在先,bit0代表最先发送的位。

l  长度:8位寄存器

(2)U0LSR(线状态寄存器)

在上面已经介绍,在此步再涉及。

4>  串口发送数据程序

/**********************************************************

* 作    者:tiger-john

* 时    间:2011年1月17日

* 名    称:            UART0_SendByte

* 功    能:            向串口发送字节数据,并等待发送完毕。

* 入口参数:            data              要发送的数据

* 出口参数:            无

**********************************************************/

void       UART0_SendByte(uint8 data)

{

U0THR = data;                

while(0 == (U0LSR & 0x40));

 

4.    完整的程序事例:

用轮训方式实现接收上位机数据,并把数据再发送给上位机。

view plaincopy to clipboardprint?
/****************************************Copyright (c)************************************************** 
**                                       西安邮电学院 
**                                      graduate school 
**                                       XNMS实验室 
**                                      Author:冀博 
**                                      Time:2011年1月20日 
**                                 http://blog.csdn.net/tigerjb 
** 
**--------------File Info------------------------------------------------------------------------------- 
** File name:           UART0_while 
** Last modified Date:  2011-01-20 
** Last Version:        1.0 
** Descriptions:        通过上位机给串口发送8字节数据,ARM2200接收到串口数据后,把数据又发送回上位机 
**------------------------------------------------------------------------------------------------------*/ 
 
/****************************************************************************** 
** Modified by:         TIGER0-JOHN 
** Modified date:       2011-1-21 
** Version:             1.1   
** Descriptions:        测试成功 ,在上面上发送16进制数时,每个之间用空格隔开 
                        不加前缀 
****************************************************************************/
 
 
 
 
#include "config.h"  
 
/**************************************************************************** 
* 名    称:   DelayNS() 
* 功    能:   长软件延时 
* 入口参数: dly     延时参数,值越大,延时越久 
* 出口参数: 无 
****************************************************************************/ 
void  DelayNS(uint32  dly)  
{    
    uint32  i;  
      
    for(; dly>0; dly--)   
    {  
        for(i=0; i<5000; i++);  
    }  
 
}  
/********************************************************** 
* 名称:       UART0_Init() 
* 功能:       UART0初始化(通讯波特率115200,8位数据位, 
                1位停止位,无奇偶校验) 
* 入口参数: bps     串口波特率 
* 出口参数: 无 
**********************************************************/ 
void  UART0_Init(uint32 bps)  
{    
    uint16 Fdiv;  
    PINSEL0 = 0x00000005;               //设置串口引脚  
    U0LCR = 0x83;                       //置为除数锁存位,进行配置  
    Fdiv = (Fpclk >> 4) / bps;            // 设置波特率  
    U0DLM = Fdiv >> 8;                              
    U0DLL = Fdiv & 0xff;                          
    U0LCR = 0x03;                      //清除除数锁存位,并设置工作模式  
 
}  
 
/********************************************************** 
* 名    称:            UART0_RcvByte 
* 功    能:            用查询方式接收一字节的数据 
* 入口参数:            无 
* 出口参数:            data              要接收的数据 
**********************************************************/ 
uint8   UART0_RcvByte(void)  
{  
    uint8 rcv_data ;  
    while((U0LSR&0X01)==0);         //等待数据到达  
    rcv_data = U0RBR;               //从U0RBR中读出接收到的数据  
    return  rcv_data;               //返回接收到的数据  
 
}  
/********************************************************** 
* 名    称:            UART0_SendByte 
* 功    能:            向串口发送字节数据,并等待发送完毕。 
* 入口参数:            data              要发送的数据 
* 出口参数:            无 
**********************************************************/ 
void   UART0_SendByte(uint8 data)  
{  
        U0THR = data;                   
        while(0 == (U0LSR & 0x40));  
}  
/********************************************************** 
* 名称:       UART0_RecBuf() 
* 功能:       接收串口发送过来的帧数据,            
* 入口参数: *buffer  存放一帧数据 
* 出口参数: 无 
**********************************************************/ 
void UART0_RecBuf (uint8 *buffer)  
{  
    uint8 *pbuffer;  
    uint8  i;  
    for(pbuffer = buffer, i = 0;i < 8; i++)  
     {  
        *(pbuffer++) = UART0_RcvByte();  
     }  
}  
/********************************************************** 
* 名称:       UART0_SendBuf() 
* 功能:       通过串口发送一帧数据 
* 入口参数: *buffer  存放一帧数据 
* 出口参数: 无 
**********************************************************/ 
void UART0_SendBuf(uint8 *buffer)  
{  
    uint8 *pbuffer;  
    uint8 i;  
    for(pbuffer = buffer,i=0;i < 8; i++)  
    UART0_SendByte(*(pbuffer++));  
}  
 
int main (void)  
{   
    uint8 recver_buffer[8];                                           //定义接收帧缓冲区  
    uint8 send_buffer[8] ={0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27};  //定义发送帧缓冲区  
    UART0_Init(115200);  
    while(1)  
    {  
      UART0_RecBuf(recver_buffer);     
      DelayNS(10);  
     if(0x10 ==recver_buffer[0] && 0x11 == recver_buffer[1])  
     UART0_SendBuf(recver_buffer);  
     else 
     UART0_SendBuf(send_buffer);  
     }      
     return 0;  
}  
/********************************************************************************************************* 
**                            End Of File 
********************************************************************************************************/ 

 

三.        中断方式的串口编程

1.用中断方式编写串口程序由那几部分组成

 

2.硬件上的支持

1>UART0 发送FIFO缓冲区

A.        UART0含有1个16字节的发送FIFO缓冲区

B.        U0THR是UART0发送FIFO的最高字节

C.        UART的发送FIFO是一直使能的

 


2>UART0接收FIFO缓冲区

A.    UART0含有一个16字节的接收FIFO缓冲区。

B.    软件设置接收FIFO缓冲区的触发字节。

 


3>  中断接口:UART0的中断接口包含中断使能寄存器(U0IER)和中断标识寄存器(U0IIR)。

l  U0IIR:提供状态码用于指示一个挂起中断的中断源和优先级。

l  U0IER可以控制UART0的4个中断源。

4>  UART0有4个中断源:

A.    RLS(接收线状态)中断:

(1) 优先级最高

(2) 它在以下条件发生时产生错误

l  帧错误(FE)

l  溢出错误(OE)

l  奇偶错误(PE)

l  间隔中断(BI)

注:

?  可以通过查看U0LSR[4:1]中的值看到产生该中断的错误条件

?  读取U0LSR寄存器时清除该中断。

B.    RDA(接收数据可用)中断:

(1)与CTI中断并列第二优先级。

(2)在以下情况触发中断:

l  当接收的有效数据到达接收FIFO设置寄存器(U0FCR)中设置的触发点时,RDA被激活。当接收FIFO中的有效数据少于触发点时,RDA复位。

l  中断过程:

1>  移位寄存器(U0RSR)从RxD引脚接收串行数据后,送入接收FIFO中

2>  当接收FIFO中的有效数据数量达到预定的触发点时,置位RDA中断。

3>  从U0RBR寄存器中读取FIFO中最早到达的数据,当FIFO中的有效数据小于触发点时,清零RDA中断。

C.    CTI(字符超时指示)中断

(1) 优先级为2.

(2) 在以下情况发生中断:

l  当接收FIFO中的有效数据少于预定的触发点数量时,如果在一定时间内仍没有接收到新的数据,那将触发该中断。

(3) 上面的时间指的是:3.5~4.5个字节所需要的时间。

(4)  对接收FIFO的任何操作都会清零该中断标志。

(5) 中断过程:

l  移位寄存器(U0RSR)从RxD0引脚接收串行数据后,送入接收FIFO中。

l  当接收FIFO中的有效数据少于触发个数,但如果长时间没有数据到达,则触发CTI中断。

l  从U0RBR中读取接收FIFO中的数据,或者有新的数据送入接收FIFO,都将清零CTI中断。

注:

?  3.5~4.5个字节的时间:指在串口当前的波特率下,发送3.5~4.5个字节所需要的时间。

?  当接收FIFO中存放多个数据,从U0RBR读取数据,但是没有读完所有数据,那么在经过3.5~4.5个字节的时间后触发CTI中断。

D.   THRE(发送)中断

(1) 优先级为第三级优先级。

(2) 当FIFO为空并且在以下情况触发中断:

l  系统启动时,虽然发送FIFO为空,但不会产生THRE中断。

l  在上一次发生THRE中断后,向发送FIFO中写入1个字节数据,将在一个字节加上一个停止位后发生THRE中断

(because:如果发送移位寄存器为空,那么写入发送FIFO的数据将直接进入发送移位寄存器。此时发送FIFO仍然为空,如果立即产生THRE中断,就会影响紧接着写入发送FIFO的数据。

所以在发送完该一个字节以及一个停止位后,才产生THRE中断。

l  如果在发送FIFO中有过两个字节以上的数据,但是现在发送FIFO为空时,将立即触发THRE中断。

l  当THRE中断为当前有效的最高优先级中断时,往U0THR写数或者对U0IIR的读操作,将使THRE中断复位

 我们来看看这些中断源与存储器之间的关系:

 


注:

?  由上图可知:UART0有4个中断源:分别是RLS(线状态)中断,RDA(接收数据)中断,CTI(字符超时)中断,THRE(发送数据)中断。

?  4个中断源的优先级如下图所示:

 

3.串口中断接收初始化

 

1>串口中断接收初始化流程

l  设置I/O引脚连接到UART0

l  置位除数锁存位,配置UART0帧数据格式

l  根据波特率计算分频值

l  设置波特率

l  清除除数锁存位,并设置工作模式

l  使能FIFO,并设置触发点

l  允许RBR中断

 

注:我们可以发现与轮训方式相比,中断方式只是增加了使能FIFO,并设置中断触发点和允许RBR中断两步。


2>中断串口初始化需要配置的寄存器

     (与轮循方式配置方法相同的寄存器在此处不在涉及)

l  U0FCR(FIFO控制寄存器):U0FCR控制UART0 Rx和Tx FIFO的操作。

l  U0IER(中断使能寄存器):U0IER用于使能4个UART0中断源。

3>具体寄存器的配置

(1) U0FCR

A作用:控制UART0 Rx和Tx的操作。

B长度:8位寄存器。

C:每一位的含义:

l  第0位:表示FIFO使能

置1:表示使能对UART0 Rx和Tx 的FIFO以及U0FCR[7:1]的访问。

置0:表示不能使用Rx和Tx的FIFO以及步能对U0FCR[7:1]的访问。

注:该位的任何变化都将使UART0 FIFO清空。

l  第1位:表示Rx FIFO的复位。

置1:会清零UART0 RxFIFO中的所有字节并复位指针逻辑。该位自动清零。

l  第2位:表示Tx FIFO的复位。

置1:会清零UART0 TxFIFO中的所有字节并复位指针逻辑。改位自动清零。

l  第[5:3]位:保留位,用户不能对其进行操作。从保留位读出的值未被定义。

l  第[7:6]位:表示Rx 触发选择

00:触发点为0(默认为1字节)

01:触发点为1(默认为4字节)

10:触发点为2(默认为8字节)

11: 触发点为3(默认为14字节)

注:这两个位决定在激活中断之前,接收UART0 FIFO必须写入个字符。

(2) U0IER(中断使能寄存器)

A 作用:U0IER用于使能4个UART0中断源

B 长度:8位寄存器

C每一位的含义:

l  第0位:表示RBR中断使能

置1:使能RDA中断

置0:禁止RDA中断

注:U0IEER 第零位使能UART0接收数据可用中断。它还控制(CTI)字符接收超时中断。

l  第1位:表示THRE中断使能

置1:使能THRE中断

置0:禁止THRE中断

l  第2位:表示Rx线状态中断使能

置1:使能Rx线状态中断

置0:禁止RX线状态中断

注:U0IER第二位使能UART0 Rx线状态中断。该中断的状态可从U0LSR[4:1]读出

l  第[7:3]位:是保留位

注:用户不能向其写入1.

4>串口初始化程序:

***********************************************************

* 作者:          tiger-john

* 时间:          2011年1月18日

* 名称:                    UART0_Init

* 功能:                    UART0初始化 通讯波特率115200,8位数据位,      1位停止位,无奇偶校验

*                               接收FIFO触发点为8字节,使能RDR(接收数据)中断

* 入口参数:            bps           串口波特率

* 出口参数:            无

****************************************************************************/

void      UART0_Init(uint32 bps)

{

 

    uint16  Fdiv;

    PINSEL0 = (PINSEL0 & ~(0xf) | 0x05) ;  //设置UART0的引脚

    U0LCR = 0x83;                  //置为除数锁存位,进行配置

    Fdiv = (Fpclk>>4)/bps;

    U0DLM = Fdiv>>8;

    U0DLL = Fdiv%256;

    U0LCR = 0x03;            //清除除数锁存位,并设置工作模式

    U0FCR = 0x81;           // 使能FIFO,并设置触发点为8字节

    U0IER = 0x01;           // 允许RBR中断,即接收中断

  

}

5.    中断初始化

先来看一下UART0和VIC之间的关系:

 


1>中断初始化流程:

l  选择中断为向量中断或快速中断

l  分配中断通道

l  设置UART0向量地址

l  使能UART0中断

2>关于ARM中断编程方法和寄存器的使用在此不做涉及(如果想知道的话,可以看这篇文章http://blog.csdn.net/tigerjb/archive/2010/11/30/6045409.aspx

3>中断初始化程序:

/**********************************************************

* 作者:          tiger-john

* 时间:          2011年1月18日

* 名称:                Interrupt_Init

* 功能:                初始化串口中断,给串口中断选择为向量中断,     分配向量通道号1给串口                        

* 入口参数:           无

* 出口参数:            无

**********************************************************/

void Interrupt_Init (void)

{

   VICIntSelect = 0x00000000;      // 设置所有通道为IRQ中断    

   VICVectCntl1 = (0x20 | 6);   // UART0中断通道分配到IRQ slot 1

   VICVectAddr1 = (uint32)UART0_Exception; // 设置UART0向量地址         

   VICIntEnable = 1 << 6;                      //使能串口中断

   

           

}

6.    用中断编写接收函数


1>  中断服务函数流程

l  清除串口中断标识寄存器(U0IIR)

l  清除中断标志

l  读取接收FIFO中的数据

2>  中断函数服务函数中需要配置的寄存器

(1) U0IIR中断标识寄存器。

l  第0位:表示中断挂起

置1:表示没有挂起的中断

置0:表示至少有一个中断挂起

l  第[3:1]位:中断标识,这三位表示了对应UART0 Rx FIFO的中断。

001:表示发送中断(THRE)

010:  表示接收数据可用中断(RDA)

011:  表示接收线状态中断(RLS)

110:  表示字符超时中断(CTI)

l  第[7:4]:是保留位

注:

1.    U0IIR提供的状态码可用于指示一个挂起中断的中断源和优先级。

2.    在访问U0IIR过程中,中断被冻结

3.    如果在访问U0IIR时,产生了中断,该中断被记录。在下次访问U0IIR时可以读出,避免中断的丢失。

3>  中断服务函数程序:

/******************************************************  * * 名    称:IRQ_UART0()

* 功    能:串口UART0中断接收8个字节的数据

* 入口参数:无

* 出口参数:无

**********************************************************/

uint8  rcv_buf[8];          // UART0数据接收缓冲区

 

void   __irq IRQ_UART0(void)

{   uint8  i;

    uint32 data;

    data = U0IIR;                 //清除中断表示寄存器标志

    VICVectAddr = 0;             //清除中断

    for(i=0; i<8; i++)

    {

        rcv_buf[i] = U0RBR;      // 读取FIFO的数据

    }

}  

 


7.    看一个总程序:

 


view plaincopy to clipboardprint?
/****************************************Copyright (c)************************************************** 
**                                       西安邮电学院 
**                                      graduate school 
**                                       XNMS实验室 
**                                      Author:冀博 
**                                      Time:2011年1月20日 
**                                 http://blog.csdn.net/tigerjb 
** 
**--------------File Info------------------------------------------------------------------------------- 
** File name:           UART0_while 
** Last modified Date:  2011-01-20 
** Last Version:        1.0 
** Descriptions:        通过上位机给串口发送8字节数据,ARM2200接收到串口数据后,把数据又发送回上位机 
**------------------------------------------------------------------------------------------------------*/ 
 
/********************************************************************************** 
** Modified by:         TIGER0-JOHN 
** Modified date:       2011-1-21 
** Version:             2.0   
** Descriptions:        测试成功 
                        在用中断的时候在Startup.S文件中的InitStack子程序中,修改设置 
                        系统模式堆栈处的代码为"MSR CPSR_c,#0x5f" 
                        测试成功 ,在上面上发送16进制数时,每个之间用空格隔开 
                        不加前缀 
********************************************************************************/
 
 
#include "config.h"  
uint8 recver_buffer[8];      
uint8 rcv_new;       
/********************************************************** 
* 名称:       UART0_Init() 
* 功能:       UART0初始化(通讯波特率115200,8位数据位, 
                1位停止位,无奇偶校验) 
* 入口参数: bps     串口波特率 
* 出口参数: 无 
**********************************************************/ 
void  UART0_Init(uint32 bps)  
{    
    uint16 Fdiv;  
    PINSEL0 = 0x00000005;               //设置串口引脚  
    U0LCR = 0x83;                       //置为除数锁存位,进行配置  
    Fdiv = (Fpclk >> 4) / bps;            // 设置波特率  
    U0DLM = Fdiv >> 8;                              
    U0DLL = Fdiv & 0xff;                          
    U0LCR = 0x03;                      //清除除数锁存位,并设置工作模式模式  
    U0FCR = 0x81;                     // 使能FIFO,并设置触发点为8字节  
    U0IER = 0x01;                     // 允许RBR中断,即接收中断  
 
}  
/*********************************************************   
* 名    称:    IRQ_UART0 
* 功    能:   串口UART0中断接收8个字节的数据 
* 入口参数: 无 
* 出口参数: 无 
**********************************************************/   
void   __irq IRQ_UART0(void)  
{   uint8  i;  
    if( 0x04 == (U0IIR&0x0F) )   
    {  
     rcv_new = 1;   // 设置接收到新的数据标志,并清除中断标志位  
     for(i=0; i<8; i++)  
        {  
            recver_buffer[i] = U0RBR;       // 读取FIFO的数据  
        }  
          VICVectAddr = 0;  
     }                            //清除中断  
}    
/********************************************************** 
* 名称:          Interrupt_Init 
* 功能:          初始化串口中断,给串口中断选择为向量中断, 
*                 分配向量通道号1给串口                         
* 入口参数:      无 
* 出口参数:      无 
**********************************************************/ 
void Interrupt_Init (void)  
{  
    VICIntSelect = 0x00000000;              // 设置所有通道为IRQ中断  
    VICVectCntl0 = 0x26;                    // UART0中断通道分配到IRQ slot 0,即优先级最高  
    VICVectAddr0 = (int)IRQ_UART0;          // 设置UART0向量地址  
    VICIntEnable = 0x00000040;              // 使能UART0中断  
                 
}  
/********************************************************** 
* 名    称:            UART0_SendByte 
* 功    能:            向串口发送字节数据,并等待发送完毕。 
* 入口参数:            data              要发送的数据 
* 出口参数:            无 
**********************************************************/ 
void   UART0_SendByte(uint8 data)  
{  
        U0THR = data;                   
        while(0 == (U0LSR & 0x40));  
}  
/********************************************************** 
* 名称:       UART0_SendBuf() 
* 功能:       通过串口发送一帧数据 
* 入口参数: *buffer  存放一帧数据 
* 出口参数: 无 
**********************************************************/ 
void UART0_SendBuf(uint8 *buffer)  
{  
    uint8 *pbuffer;  
    uint8 i;  
    for(pbuffer = buffer,i = 0;i < 8; i++)  
    UART0_SendByte(*(pbuffer++));  
}  
 
/********************************************************** 
* 名称:       main()函数 
* 功能:       上位机接收的数据开头两个字符为0x10,0x11, 
*               则原样输出,否次输出0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27 
* 入口参数: *buffer  存放一帧数据 
* 出口参数: 无 
**********************************************************/ 
 
int main (void)  
{   
                                      
    uint8 send_buffer[8] ={0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27};  //定义发送帧缓冲区  
    UART0_Init(115200);  
    Interrupt_Init();  
    while(1)  
        {  
            if(1 == rcv_new)                    // 是否已经接收到8 Bytes的数据  
                {    
                    rcv_new = 0;                    // 清除标志  
                    if(0x10 ==recver_buffer[0] && 0x11 == recver_buffer[1])  
                        {  
                            UART0_SendBuf(send_buffer);  
                        }  
                    else 
                        {  
                            UART0_SendBuf(recver_buffer);  
                        }  
                }   
        }          
     return 0;  
}  
/********************************************************************************************************* 
**                            End Of File 
********************************************************************************************************/ 
/****************************************Copyright (c)**************************************************
**                                       西安邮电学院
**                                      graduate school
**           XNMS实验室
**                                      Author:冀博
**          Time:2011年1月20日
**                                 http://blog.csdn.net/tigerjb
**
**--------------File Info-------------------------------------------------------------------------------
** File name:   UART0_while
** Last modified Date:  2011-01-20
** Last Version:  1.0
** Descriptions:  通过上位机给串口发送8字节数据,ARM2200接收到串口数据后,把数据又发送回上位机
**------------------------------------------------------------------------------------------------------*/

/**********************************************************************************
** Modified by:         TIGER0-JOHN
** Modified date:       2011-1-21
** Version:             2.0 
** Descriptions:        测试成功
      在用中断的时候在Startup.S文件中的InitStack子程序中,修改设置
      系统模式堆栈处的代码为"MSR CPSR_c,#0x5f"
                        测试成功 ,在上面上发送16进制数时,每个之间用空格隔开
      不加前缀
********************************************************************************/


#include "config.h"
uint8 recver_buffer[8];   
uint8 rcv_new;    
/**********************************************************
* 名称:  UART0_Init()
* 功能:  UART0初始化(通讯波特率115200,8位数据位,
    1位停止位,无奇偶校验)
* 入口参数: bps     串口波特率
* 出口参数: 无
**********************************************************/
void  UART0_Init(uint32 bps)

    uint16 Fdiv;
    PINSEL0 = 0x00000005;               //设置串口引脚
    U0LCR = 0x83;      //置为除数锁存位,进行配置
    Fdiv = (Fpclk >> 4) / bps;      // 设置波特率
    U0DLM = Fdiv >> 8;      
    U0DLL = Fdiv & 0xff;     
    U0LCR = 0x03;                      //清除除数锁存位,并设置工作模式模式
    U0FCR = 0x81;                     // 使能FIFO,并设置触发点为8字节
    U0IER = 0x01;                     // 允许RBR中断,即接收中断

}
/********************************************************* 
* 名    称:    IRQ_UART0
* 功    能: 串口UART0中断接收8个字节的数据
* 入口参数: 无
* 出口参数: 无
**********************************************************/
void   __irq IRQ_UART0(void)
{   uint8  i;
    if( 0x04 == (U0IIR&0x0F) )
    {
     rcv_new = 1; // 设置接收到新的数据标志,并清除中断标志位
     for(i=0; i<8; i++)
     {
         recver_buffer[i] = U0RBR;       // 读取FIFO的数据
     }
       VICVectAddr = 0;
     }                            //清除中断

/**********************************************************
* 名称:          Interrupt_Init
* 功能:          初始化串口中断,给串口中断选择为向量中断,
*                 分配向量通道号1给串口                       
* 入口参数:      无
* 出口参数:      无
**********************************************************/
void Interrupt_Init (void)
{
    VICIntSelect = 0x00000000;            // 设置所有通道为IRQ中断
    VICVectCntl0 = 0x26;           // UART0中断通道分配到IRQ slot 0,即优先级最高
    VICVectAddr0 = (int)IRQ_UART0;        // 设置UART0向量地址
    VICIntEnable = 0x00000040;            // 使能UART0中断
              
}
/**********************************************************
* 名    称:            UART0_SendByte
* 功    能:            向串口发送字节数据,并等待发送完毕。
* 入口参数:            data              要发送的数据
* 出口参数:            无
**********************************************************/
void   UART0_SendByte(uint8 data)
{
  U0THR = data;                
  while(0 == (U0LSR & 0x40));
}
/**********************************************************
* 名称:  UART0_SendBuf()
* 功能:  通过串口发送一帧数据
* 入口参数: *buffer  存放一帧数据
* 出口参数: 无
**********************************************************/
void UART0_SendBuf(uint8 *buffer)
{
 uint8 *pbuffer;
    uint8 i;
 for(pbuffer = buffer,i = 0;i < 8; i++)
 UART0_SendByte(*(pbuffer++));
}

/**********************************************************
* 名称:  main()函数
* 功能:  上位机接收的数据开头两个字符为0x10,0x11,
*    则原样输出,否次输出0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27
* 入口参数: *buffer  存放一帧数据
* 出口参数: 无
**********************************************************/

int main (void)
{
                                   
    uint8 send_buffer[8] ={0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27};  //定义发送帧缓冲区
    UART0_Init(115200);
    Interrupt_Init();
    while(1)
     {
        if(1 == rcv_new)       // 是否已经接收到8 Bytes的数据
         { 
              rcv_new = 0;           // 清除标志
         if(0x10 ==recver_buffer[0] && 0x11 == recver_buffer[1])
          {
           UART0_SendBuf(send_buffer);
          }
            else
          {
           UART0_SendBuf(recver_buffer);
          }
            }
      }       
     return 0;
}
/*********************************************************************************************************
**                            End Of File
********************************************************************************************************/
 

 

四.用中断编写发送函数

1.中断初始化

同上和用中断接收函数时的中断初始化是一样的

2.串口初始化

 1>串口初始化流程:

l  设置I/O引脚连接到UART0

l   置位除数锁存位,配置UART0帧格式

l  根据波特率计算分频值

l  设置波特率

l  清除除数锁存位,并设置工作模式

l  使能FIFO,并使TxFIFO复位

l  使能THRE中断

  2>串口初始化函数:

    /**********************************************************

* 名称:                    UART0_Init

* 功能:                    UART0初始化 通讯波特率115200,8位数据位,1位停止位,无奇偶校验

*                                  使能TxFIFO,和THRE中断

* 入口参数:            bps        串口波特率

* 出口参数:            无

**********************************************************/

void      UART0_Init(uint32 bps)

{

 

    uint16  Fdiv;

    PINSEL0 = (PINSEL0 & ~(0xf) | 0x05) ;  //设置UART0的引脚

    U0LCR = 0x83;         //置位除数锁存位,配置UART0帧格式

    Fdiv = (Fpclk>>4)/bps;  //根据波特率计算分频值

    U0DLM = Fdiv>>8;     //设置波特率

    U0DLL = Fdiv%256;

    U0LCR = 0x03;        // 清除除数锁存位,并设置工作模式

    U0FCR = 0x05;       // 使能TxFIFO,并使TxFIFO复位

    U0IER = 0x02;       // 使能THRE中断

  

}

3中断服务函数:

  1>中断服务函数流程:

l  清除串口中断标识寄存器

l  清除中断控制标识寄存器

  2>中断发送服务函数程序:

/**********************************************************

* 名    称:            UART0_Exception

* 功    能:            串口发送中断

* 入口参数:            无

* 出口参数:            data              发送的数据

**********************************************************/

void   __irq IRQ_UART0(void)

{  

  

uint32 data;

data = U0IIR;                                                 //清除中断表示寄存器标志

VICVectAddr = 0;                                              //清除中断

4.   用串口中断发送函数的总程序:


view plaincopy to clipboardprint?
/****************************************Copyright (c)************************************************** 
**                                       西安邮电学院 
**                                      graduate school 
**                                       XNMS实验室 
**                                      Author:冀博 
**                                      Time:2011年1月20日 
**                                 http://blog.csdn.net/tigerjb 
** 
**--------------File Info------------------------------------------------------------------------------- 
** File name:           UART0_while 
** Last modified Date:  2011-01-20 
** Last Version:        1.0 
** Descriptions:        通过上位机给串口发送8字节数据,ARM2200接收到串口数据后,用中断方式把数据又发送回上位机 
**------------------------------------------------------------------------------------------------------*/ 
 
/********************************************************************************** 
** Modified by:         TIGER0-JOHN 
** Modified date:       2011-1-21 
** Version:             2.0   
** Descriptions:        测试成功 
                        在用中断的时候在Startup.S文件中的InitStack子程序中,修改设置 
                        系统模式堆栈处的代码为"MSR CPSR_c,#0x5f" 
                        测试成功 ,在上面上发送16进制数时,每个之间用空格隔开 
                        不加前缀 
********************************************************************************/
 
 
#include "config.h"  
uint8 recver_buffer[8];      
uint8 rcv_new;       
/**************************************************************************** 
* 名称:           UART0_Init 
* 功能:           UART0初始化 通讯波特率115200,8位数据位,1位停止位,无奇偶校验 
*                   使能TxFIFO,和THRE中断 
* 入口参数:     bps     串口波特率 
* 出口参数:     无 
****************************************************************************/ 
void    UART0_Init(uint32 bps)  
{  
   
    uint16  Fdiv;  
    PINSEL0 = (PINSEL0 & ~(0xf) | 0x05) ;        //设置UART0的引脚  
    U0LCR = 0x83;                                //置位除数锁存位,配置UART0帧格式  
    Fdiv = (Fpclk>>4)/bps;                       //根据波特率计算分频值  
    U0DLM = Fdiv>>8;                            //设置波特率  
    U0DLL = Fdiv%256;  
    U0LCR = 0x03;                               //  清除除数锁存位,并设置工作模式  
    U0FCR = 0x05;                               // 使能TxFIFO,并使TxFIFO复位  
    U0IER = 0x02;                               // 使能THRE中断  
     
}  
/**************************************************************************** 
* 名    称:       UART0_Exception 
* 功    能:       串口发送中断 
* 入口参数:     无 
* 出口参数:     data        发送的数据 
****************************************************************************/ 
void   __irq IRQ_UART0(void)  
{     
     
    uint32 data;  
    data = U0IIR;                                   //清除中断表示寄存器标志  
    VICVectAddr = 0;                                //清除中断  
      
    
}    
/********************************************************** 
* 名称:          Interrupt_Init 
* 功能:          初始化串口中断,给串口中断选择为向量中断, 
*                 分配向量通道号1给串口                         
* 入口参数:      无 
* 出口参数:      无 
**********************************************************/ 
void Interrupt_Init (void)  
{  
    VICIntSelect = 0x00000000;              // 设置所有通道为IRQ中断  
    VICVectCntl0 = 0x26;                    // UART0中断通道分配到IRQ slot 0,即优先级最高  
    VICVectAddr0 = (int)IRQ_UART0;          // 设置UART0向量地址  
    VICIntEnable = 0x00000040;              // 使能UART0中断  
                 
}  
/********************************************************** 
* 名    称:            UART0_RcvByte 
* 功    能:            用查询方式接收一字节的数据 
* 入口参数:            无 
* 出口参数:            data              要接收的数据 
**********************************************************/ 
uint8   UART0_RcvByte(void)  
{  
    uint8 rcv_data ;  
    while((U0LSR&0X01)==0);         //等待数据到达  
    rcv_data = U0RBR;               //从U0RBR中读出接收到的数据  
    return  rcv_data;               //返回接收到的数据  
 
}  
/********************************************************** 
* 名称:       UART0_RecBuf() 
* 功能:       接收串口发送过来的帧数据,            
* 入口参数: *buffer  存放一帧数据 
* 出口参数: 无 
**********************************************************/ 
void UART0_RecBuf (uint8 *buffer)  
{  
    uint8 *pbuffer;  
    uint8  i;  
    for(pbuffer = buffer, i = 0;i < 8; i++)  
     {  
        *(pbuffer++) = UART0_RcvByte();  
     }  
}  
/********************************************************** 
* 名称:       main()函数 
* 功能:       上位机接收的数据开头两个字符为0x10,0x11, 
*               则原样输出,否次输出0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27 
* 入口参数: *buffer  存放一帧数据 
* 出口参数: 无 
**********************************************************/ 
 
int main (void)  
{   
    uint8 i;                                  
    uint8 send_buffer[8] ={0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27};  //定义发送帧缓冲区  
    UART0_Init(115200);  
    Interrupt_Init();  
    while(1)  
        {  
            UART0_RecBuf(recver_buffer);                //接收8字节数据  
            if(0x10 ==recver_buffer[0] && 0x11 == recver_buffer[1])  
                {   
                    for(i = 0;i < 8 ;i++)  
                    {  
                        U0THR = send_buffer[i];         //用中断发送每次8字节  
                    }     
                }  
            else 
                {  
                    for(i = 0;i < 8;i++)                //用中断发送每次8字节  
                    {  
                        U0THR = recver_buffer[i];  
                    }  
                }   
        }          
     return 0;  
}  
/********************************************************************************************************* 
**                            End Of File 
********************************************************************************************************/ 

一.潜在的危机

1.在uc/os操作系统中设计串口编程时,由于ISR和多个任务并发执行,情况比较复杂。尤其是接收状态为被动状态时,只能靠串行口中断来接收数据。

2.在进行串行通信时,双方遵循相同的通信协议。由于波特率不变,因此相邻两次串口中断的间隔时间基本固定。

3.在以下两种情况时会使接收过程出现错误:

?  第一种情况是系统关中断的最长时间大于相邻两次串行接收中断的间隔时间,这时将可能导致遗漏一次中断,造成数据丢失。


2  实时操作系统内核的关中断的最长时间是已知的,通常很短,它不是问题关键。

2  系统关中断的最长时间往往是由用户软件造成的,例如:我们编写的中断服务函数过于复杂,导致系统为了处理中断服务函数而导致关中断时间过长。

?  第二种情况是在串口程序正在运行期间有一个比它优先级更高的中断程序中断了串口程序。从而造成数据丢失。

2  在这里提一个概念:把不能响应串口接收中断的这段时间称为“死区”。

2  因此解决问题的关键是:死区时间不能比相邻两次串口中断的间隔时间长。

二.如何解决危机

l  任务在访问比较耗时的共享资源时不要采用关中断的方式(改成互斥信号量)。

l  ISR要尽可能简短,将可以剥离的工作转交关联任务去完成。

(此处的设计方式和Linux中把中断分为上半部分,和下半部分的原理有着同工异曲的含义)

采用上面的方法来缩短死区时间。

另一中方法是:

加长相邻两次串口接收中断的间隔时间。

l  方法一:降低波特率,这个方法简单,但因此也导致通信效率的下将。其次,一般在进行串口编程时,波特率一般是固定的。因此此方法一般不太适用。

l  方法二:在波特率不变的情况下减少中断次数,达到加长相邻两次串口接收中断间隔时间的效果。

ARM芯片的串口具有16字节的缓冲区,可以设置每接收1,4,8,14字节产生一次中断。如果设置每接收8字节中断一次,则比1字节中断一次要延长8倍的中断间隔时间。

Tiger-John说明:

l  在使用有数据缓冲功能的串口编程后,比较容易满足相邻两次串口接收中断的间隔时间大于死区时间的条件,但仍然存在潜在的危险。

想要可靠的避免这场危机:必须要满足以下条件

2  相邻两次串口接收中断的间隔时间必须大于系统死区时间

2  接收缓冲区的空闲时间必须足够存放在“死区”时间内接收到的新数据。

< 若设置每接收8字节中断一次,则空闲空间也为8字节。由于死区时间比中断间隔时间短,故接收的新数据必然少于8字节,才不会出现数据丢失现象。

即在满足中断间隔时间大于“死区”时间的前提下,将中断条件设置为接收缓冲区的1/2,则死区时间接近中断间隔时间,接收过程是可靠的。

 

一.在UC/OS中设计串口程序所要考虑的问题

1.     串口通信数据以帧为单位进行处理,如果需要接收大量数据,则帧缓冲区规模必然很大;如果需要发送大量数据,则可将原始数据快看作缓冲区,不需要另外再创建帧缓冲区。

2.     帧缓冲区是全局数据结构,通常为共享资源,需要考虑互斥访问问题(如在任务中关中断)。但是此时系统的效率就会下降。我们可以通过合理设计通信任务,将对帧缓冲区进行读/写操作的双方封装到一个任务里,是帧缓冲区称为这个任务的私有数据结构,不再是共享资源, 次时就不需要互斥信号量了。


3.     在UC/OS操作系统中串口发送和串口接收函数都被设计成了任务。

Tiger-john说明:

在UC/OS上编写串口编程时,主要是考虑以上问题。其它的方面就是UART0的配置以及如何编写中断程序这些方面在前面已经提过,再此不再涉及。

如果你还不是很明白的话请看我《UART0串口编程系列》文章的前半部分。


二.UC/OS串口编程

通过一个程序来分析UC/OS串口编程设计和实现:

程序实现的目标:

通过按键来控制串口发送任务80字节的帧,每次发送8字节,分25次发送完。

1.在UC/OS串口编程中由那几个任务组成

1>启动任务

2>按键任务(此任务可以根据不同的程序设计内容来改变)

3>发送任务

4>串口发送中断

2.各个任务之间的关系

 

3.启动任务流程:

l  定义各种通信工具(例如:信号量)

l  系统硬件初始化

l  初始化UART0

l  创建各个任务

l  创建各种通信工具

l  删除自己

 

图3启动任务流程图

 


程序:

/**********************************************************

* 名称:                    UART0_Init

* 功能:                    UART0初始化 通讯波特率115200,8位数据位,1位停止位,无奇偶校验,使能TxFIFO,和THRE中断

* 入口参数:            bps        串口波特率

* 出口参数:            无

**********************************************************/

void      UART0_Init(uint32 bps)

{

 

    uint16  Fdiv;

    PINSEL0 = (PINSEL0 & ~(0xf) | 0x05) ;      //设置UART0的引脚

    U0LCR = 0x83;            

    Fdiv = (Fpclk>>4)/bps;

    U0DLM = Fdiv>>8;

    U0DLL = Fdiv%256;

    U0LCR = 0x03;  

    U0FCR = 0x05;                             // 使能TxFIFO

    U0IER = 0x02;                            //使能THRE中断

  

}

 

/**********************************************************

* 作者:tiger-john

* 时间:2011年1月21

* 名称:Task0 启动任务

* 功能:初始化硬件,创建其他任务,

* 入口参数:pdata

**********************************************************/

void Task0     (void *pdata)

{

pdata = pdata;              

//硬件初始化

TargetInit ();

//初始化串口

UART0_Init(115200);

//创建按键信号量                                              

Sem_ButtonFlag =  OSSemCreate(0);  

//创建发送信号量                               

Sem_SendFlg =  OSSemCreate(0);

//创建按键任务                                    

OSTaskCreate(Task1,(void *)0, &TaskStk1[TaskStkLengh - 1],10);     

OSTaskCreate(Task2,(void *)0, &TaskStk2[1000 - 1],6);               //创建发送任务

OSTaskDel(OS_PRIO_SELF);                                            //删除自己

       

}

4.按键任务流程:

l  等待开始信号量

l  制造模拟数据

l  按键按下发送信号量,否则延迟等待

程序:

/**********************************************************

**                            Task1(按键任务)

**********************************************************/

void Task1     (void *pdata)

{

uint8 i;

uint8 err;

pdata = pdata;

while(1)

        {

               OSSemPend(Sem_StartFlag,0,&err); //等带开始信号量

               for(i = 0;i < 80;i++)

                      {

                             send_buf[i] = i ;             //制造模拟数据。

                      }     

            while((IO0PIN & KEY ) != 0);     //等带按键

            OSSemPost(Sem_ButtonFlag);   //发送按键信号量

           

        }

}

5.发送任务流程:

l  等待按键信号量

l  打开串口中断

l  发送10次

l  等待发送信号量

l  发送8字节

l  关发送串口中断

l  发送开始信号量

 

 
 

程序:

/************************************************************                            Task2(发送任务)

**********************************************************/

void Task2     (void *pdata)

{

uint8 i,j,err;

pdata = pdata;


while(1)

{

        OSSemPend(Sem_ButtonFlag,0,&err);  //等待按键信号量

        U0IER = U0IER | 0x02;               //打开串口发送中断

        for(i = 0;i < 10;i++)                     //发送10次

               {

                    for(j = 0;j < 8;j++)                //每次8字节

                      {

                             U0THR = send_buf[i*8+j];

                      }     

               OSSemPend(Sem_SendFlag,0,&err); //等待发送信号量

                     

         }

               U0IER = U0IER & ~0x02;              //关掉串口中断

               OSSemPost(Sem_StartFlag);           //发送开始信号量

}            

      

}

6.串口发送中断流程:

l  关中断

l  清除中断控制标志位

l  清除串口中断标志位

l  开中断

l  发送发送信号量

 

/**********************************************************

* 名    称:            UART0_Exception

* 功    能:            串口发送中断

* 入口参数:            无

* 出口参数:            data              发送的数据

**********************************************************/

void  UART0_Exception(void)

{


uint32 data;

OS_ENTER_CRITICAL();             //关中断

VICVectAddr = 0;                  //清除中断

data = U0IIR;                            //清除中断表示寄存器标志

OS_EXIT_CRITICAL();               //开中断

OSSemPost(Sem_SendFlag);        //发出发送信号量

 }
7.总的程序如下:

 

 


view plaincopy to clipboardprint?
/****************************************Copyright (c)************************************************** 
**                                       西安邮电学院 
**                                      graduate school 
**                                       XNMS实验室 
**                                      Author:冀博 
**                                      Time:2011年1月21日 
**                                 http://blog.csdn.net/tigerjb 
** 
**--------------File Info------------------------------------------------------------------------------- 
** File name:           UART0_while 
** Last modified Date:  2011-01-20 
** Last Version:        1.0 
** Descriptions:        当按键按下后,串口发送中断给上位机发送80字节数据 
**----------------------------------------------------------------------------------------------------- 
 
 
/****************************************************************************** 
** Modified by:         TIGER0-JOHN 
** Modified date:       2011-1-21 
** Version:             1.1   
** Descriptions:        测试成功 
****************************************************************************/
#include "config.h" 
#include "stdlib.h" 
 
#define KEY             1<<20        //设置为触发按键 
#define TaskStkLengh    64          //Define the Task0(启动任务) 堆栈长度 
#define TaskStkLengh    64          //Define the Task1(按键任务)  堆栈长度  
#define TaskStkLengh    64          //Define the Task2((发送任务) 堆栈长度  
 
 
OS_STK  TaskStk0[TaskStkLengh];     //Define the Task0 stack  定义启动任务堆栈   
OS_STK  TaskStk1[TaskStkLengh];     //Define the Task1 stack  定义按键任务堆栈  
OS_STK  TaskStk2[TaskStkLengh];     //Define the Task2 stack  定义发送任务堆栈  
 
 
void    Task0(void *pdata);         //Task0 启动任务  
void    Task1(void *pdata);         //Task1 按键任务  
void    Task2(void *pdata);         //Task2 发送任务  
 
uint8   send_buf[80];  
OS_EVENT        *Sem_ButtonFlag;    //定义按键信号量  
OS_EVENT        *Sem_SendFlag;      //定义发送信号量  
OS_EVENT        *Sem_StartFlag;      //定义开始信号量  
 
 
/**************************************************************************** 
* 名称:           UART0_Init 
* 功能:           UART0初始化 通讯波特率115200,8位数据位,1位停止位,无奇偶校验 
*                   使能TxFIFO,和THRE中断 
* 入口参数:     bps     串口波特率 
* 出口参数:     无 
****************************************************************************/ 
void    UART0_Init(uint32 bps)  
{  
   
    uint16  Fdiv;  
    PINSEL0 = (PINSEL0 & ~(0xf) | 0x05) ;       //设置UART0的引脚  
    U0LCR = 0x83;               
    Fdiv = (Fpclk>>4)/bps;  
    U0DLM = Fdiv>>8;  
    U0DLL = Fdiv%256;  
    U0LCR = 0x03;     
    U0FCR = 0x05;                               // 使能TxFIFO  
    U0IER = 0x02;                               // 使能THRE中断  
     
}  
/**************************************************************************** 
* 名    称:       UART0_Exception 
* 功    能:       串口发送中断 
* 入口参数:     无 
* 出口参数:     data        发送的数据 
****************************************************************************/ 
void  UART0_Exception(void)  
{  
      
    uint32 data;  
    OS_ENTER_CRITICAL();                            //关中断  
    VICVectAddr = 0;                                //清除中断  
    data = U0IIR;                                   //清除中断表示寄存器标志  
    OS_EXIT_CRITICAL();                             //开中断  
    OSSemPost(Sem_SendFlag);                        //发出发送信号量  
 }  
/********************************************************************************************************* 
**                              main()函数 
********************************************************************************************************/ 
 
int main (void)  
{  
    OSInit ();                                                          //初始化操作系统                                                 
    OSTaskCreate (Task0,(void *)0, &TaskStk0[TaskStkLengh - 1], 2);     //创建启动任务  
    OSStart ();                                                         //启动操作系统,开始对任务进行调度管理  
    return 0;                                                             
}  
/********************************************************************************************************* 
**                            Task0(启动任务) 
********************************************************************************************************/ 
 
void Task0  (void *pdata)  
{  
    pdata = pdata;                 
    TargetInit ();                                                     //硬件初始化                                                  
    UART0_Init(115200);                                                //初始化串口  
    Sem_ButtonFlag =  OSSemCreate(0);                                  //创建按键信号量  
    Sem_SendFlag   =  OSSemCreate(0);                                     //创建发送信号量  
    Sem_StartFlag  =  OSSemCreate(1);   
    OSTaskCreate(Task1,(void *)0, &TaskStk1[TaskStkLengh - 1],5);      //创建按键任务  
    OSTaskCreate(Task2,(void *)0, &TaskStk2[TaskStkLengh - 1],4);               //创建发送任务  
    OSTaskDel(OS_PRIO_SELF);                                           //删除自己  
          
}  
/********************************************************************************************************* 
**                            Task1(按键任务) 
********************************************************************************************************/ 
void Task1  (void *pdata)  
{  
    uint8 i;  
    uint8 err;  
    pdata = pdata;   
    while(1)  
        {  
            OSSemPend(Sem_StartFlag,0,&err);           //等带开始信号量  
            for(i = 0;i < 80;i++)  
                {  
                    send_buf[i] = i ;           //制造模拟数据。  
                }     
            while((IO0PIN & KEY ) != 0);        //等带按键  
            OSSemPost(Sem_ButtonFlag);          //发送按键信号量  
              
        }  
}  
/********************************************************************************************************* 
**                            Task2(发送任务) 
********************************************************************************************************/ 
void Task2  (void *pdata)  
{  
    uint8 i,j,err;  
    pdata = pdata;  
      
    while(1)  
    {  
        OSSemPend(Sem_ButtonFlag,0,&err);       //等待按键信号量  
        U0IER = U0IER | 0x02;                   //打开串口发送中断  
        for(i = 0;i < 10;i++)                    //发送10次  
            {  
                for(j = 0;j < 8;j++)             //每次8字节  
                    {  
                        U0THR = send_buf[i*8+j];  
                    }     
                OSSemPend(Sem_SendFlag,0,&err);    //等待发送信号量  
                      
            }  
            U0IER = U0IER & ~0x02;              //关掉串口中断  
            OSSemPost(Sem_StartFlag);           //发送开始信号量  
    }         
           
}  
    
/********************************************************************************************************* 
**                            End Of File 
********************************************************************************************************/

一.串口接收数据在UC/OS设计中应注意的问题

1.    串口通信的数据接收过程:

1>  UART 接收FIFO接收到预定字节后触发中断

2>  ISR读取接收到的内容并保存

3>  经过一次或若干次ISR完成一个通信帧的接收(拼装通信帧)

 

4>  处理和解释通信内容

5>  根据处理结果触发其他任务

2.    串口数据接收程序设计时,应该考虑的问题:

1>即使以上的操作过程很简单,也最好不要把它全部安排在ISR中完成,如果放在一起的话,就会给UART0通信带来危机(此处具体请看前面的文章)。

2>所以要安排一个与ISR关联的“串口接收”任务来完成后面的工作。再创建一个帧缓冲区。在接收的过程中,将接收到的内容写入帧缓冲区。接收完一帧后,处理和解释过程需要读帧缓冲区的内容。

3>将写帧缓冲区的操作安排在ISR中完成,读帧缓冲去的操作安排在串口接收任务中完成。

4>由于ISR和串口接收任务是并发程序单元,存在资源同步问题,故需要对帧缓冲区进行互斥访问。

二.设计ISR与串口接收任务之间的通信方法:

1. ISR的主要功能是响应异步事件,该异步事件将触发一系列操作。ISR设计的基本原则是:尽可能简短。

2.ISR与关联任务的通信方式有两种类型:信号型和数据型。

1>当使用信号量进行通信时,ISR只完成发送信号量的工作,表示事件已经发生,通过信号量的同步功能触发关联任务。

2>当使用数据进行通信时,ISR需要完成对异步事件的信息进行采集工作,然后使用消息邮箱(或消息队列)将数据发送给关联任务,由关联任务完成后续数据处理工作。

3>做项目时常见的三种情况:

?  触发ISR的事件不包含数据:不需要对事件进行信息采集。此时,ISR使用信号量与关联任务进行通信。

?  触发ISR的事件是包含数据的低频事件:将数据采集的工作放在关联任务中完成,(产生的时刻延迟与采样周期相比可以忽略不计,对采集数据的质量没有影响。此时,ISR使用信号量与关联任务进行通信,从而简化了ISR。

?  触发ISR的事件是包含数据的中高频事件:数据采集的工作放在关联任务中完成时,产生的时延与采样周期相比不能忽略不计时,对采样数据的质量有影响。此时,关联任务从消息邮箱中得到消息的数据,并完成后续处理工作。

?  触发ISR的事件是包含数据的非周期高频率事件:对于非周期高频事件,其最短事件间隔可能小于一个事件数据处理的耗时,如果使用消息邮箱进行通信,就可能会出现数据丢失现象。此时,数据采集的工作应该在ISR中完成,由ISR使用具有数据缓冲功能的消息队列与关联任务进行通信。关联任务从消息队列中得到消息的数据,并完成后续处理工作。

Tiger-John说明:

具体采用那一种方式来实现ISR与串口接收任务之间的通信要视具体情况而定。

以下用信号量和消息队列两种方式来实现串口接收编程

三.   UC/OS串口接收数据编程

通过一个程序来分析UC/OS串口接收数据设计和实现

程序设计目标:

用串口中断接收上位机发送的8字节数据,再把它们传送给上位机。

u 用信号量的方式

1.系统有那些任务组成

1>  启动任务

2>  接收任务

3>  接收中断服务例程

4>  发送任务

2.各任务之间的关系

 

3.启动任务流程:

l  定义各种通信工具(例如:信号量)

l  系统硬件初始化

l  初始化UART0

l  创建各个任务

l  创建各种通信工具

l  删除自己

 

程序:

/********************************************************************

**                            Task0(启动任务)

********************************************************************/

void Task0    (void *pdata)

{

       pdata = pdata;              

       TargetInit();                         //硬件初始化                                               

       UART0_Init(115200);                 //初始化串口

       Sem_SendFlag   =  OSSemCreate(0);  //创建发送信号量

       Sem_StartFlag  =  OSSemCreate(1);   //创建开始信号量

       OSTaskCreate(Task1,(void *)0, &TaskStk1[TaskStkLengh - 1],4);  //创建接收任务

       OSTaskCreate(Task2,(void *)0, &TaskStk2[TaskStkLengh - 1],5);  //创建发送任务

       OSTaskDel(OS_PRIO_SELF);           //删除自己

      

}

4.接收任务流程

l  等待开始信号量

l  处理和解释通信内容(本程序较简单,不涉及)

 

程序:

/********************************************************************

                           Task1(接收任务)

********************************************************************/

void Task1     (void *pdata)

{

uint8 err;

pdata = pdata;

while(1)

        {

               OSSemPend(Sem_StartFlag,0,&err);  //等带开始信号量

               //以下可以根据具体业务来编写处理和解释通信内容

                      

        }

}

5.串口中断接收流程:

l  关中断

l  清除串口中断标志位

l  清除中断控制寄存器

l  接收数据放入缓冲区

l  开中断

l  发送发送信号量

 

程序:

/**********************************************************

* 名    称:           UART0_Exception

* 功    能:           串口接收中断

* 入口参数:           无

* 出口参数:        无

**********************************************************/

void  UART0_Exception(void)

{

       uint8 i;

       uint32 data;

       OS_ENTER_CRITICAL();

       data = U0IIR;                            //清除中断表示寄存器标志

       VICVectAddr = 0;                         //清除中断

        for(i=0; i<8; i++)

          {

             rcv_buf[i] = U0RBR;        // 读取FIFO的数据

          }

   OS_EXIT_CRITICAL();

    OSSemPost(Sem_SendFlag);          //发送发送信号量

}

6.发送任务流程

l  等待发送信号量

l  发送数据

l  发送开始信号量

 

程序:

/**********************************************************

**                            Task2(发送任务)

**********************************************************/

void Task2    (void *pdata)

{

       uint8 i,err;

       pdata = pdata;

       while(1)

       {

              OSSemPend(Sem_SendFlag,0,&err);       //等待发送信号量

              for(i = 0;i < 8; i++)

              UART0_SendByte(rcv_buf[i]);    //通过轮训方式来发送串口数据

              OSSemPost(Sem_StartFlag);           //发送开始信号量

       }                  

}

 

发送数据函数:

 

/**********************************************************

* 名    称:            UART0_SendByte

* 功    能:            向串口发送字节数据,并等待发送完毕。

* 入口参数:            data              要发送的数据

* 出口参数:            无

**********************************************************/

void   UART0_SendByte(uint8 data)

{

              U0THR = data;                

              while(0 == (U0LSR & 0x40));

}

u 用消息队列接收数据的方式

1.    系统有那些任务组成

1>启动任务

2>接收任务(中调用一个接受处理函数)

3>接收中断服务例程

4>发送任务

2.    各任务之间的关系

 

3.启动任务流程:

l  定义各种通信工具(例如:信号量)

l  系统硬件初始化

l  初始化UART0

l  创建各个任务

l  创建各种通信工具

l  删除自己

 

程序:

/********************************************************************

                           Task0(启动任务)

********************************************************************/

void Task0    (void *pdata)

{

pdata = pdata;              

TargetInit();                            //硬件初始化                                               

UART0_Init(115200);                    //初始化串口

Sem_SendFlag   =  OSSemCreate(0);     //创建发送信号量

Sem_StartFlag  =  OSSemCreate(1);      //创建开始信号量

ReMsg_Qeue = OSQCreate(&MsgGrp_Buf[0],10);    //创建消息队列

OSTaskCreate(Task1,(void *)0, &TaskStk1[TaskStkLengh - 1],4); //创建接收任务

OSTaskCreate(Task2,(void *)0, &TaskStk2[TaskStkLengh - 1],5); //创建发送任务

OSTaskDel(OS_PRIO_SELF);                           //删除自己

      

}

4.接收任务流程

l  等待开始信号量

l  处理接收数据

l  发送发送信号量

 

程序:

/********************************************************************

**                            Task1(接收任务)

********************************************************************/

void Task1    (void *pdata)

{

       uint8 err;

       pdata = pdata;

       while(1)

              {

                     OSSemPend(Sem_StartFlag,0,&err);  //等待开始信号量

                  UART0_RcvData(rcv_buf,2);        //接收数据

                  OSSemPost(Sem_SendFlag);         //发送发送信号量

              }

}

处理接收数据函数

/********************************************************************

* 名    称:           Rcv_Data

* 功    能:           rcv_buf:接收中断返回后的数据   count :控制中断次数

* 入口参数:           无

* 出口参数:           无

********************************************************************/

void UART0_RcvData(uint8 *rcv_buf,uint8 count)

{

       uint8 i;

       uint8 j;

       uint32 rcv_data;

       uint8 err;

       for(j = 0;j < count;j++)

              {

                     //等待消息队列

                     rcv_data = (uint32)(uint32 *)OSQPend(ReMsg_Qeue,0,&err);

                     if(0x11223344 == rcv_data)                     

                            {

                                   rcv_data = 0x12345678;

                            }     

                     //将每条消息分解为4字节,存入帧缓冲区

                     for(i = 0;i < 4;i++)

                            {

                                   rcv_buf[4*j+3-i] = (uint8)(rcv_data&0xff);

                                    rcv_data >>= 8;

                            }

      

             }

5.串口中断接收流程:

l  关中断

l  清除串口中断标志位

l  清除中断控制寄存器

l  接收4字节数据拼装成32为地址

l  发送消息邮箱

l  开中断

 

程序:

/********************************************************************

* 名    称:           UART0_Exception

* 功    能:           串口接收中断

* 入口参数:           无

* 出口参数:      无

********************************************************************/

void  UART0_Exception(void)

{

       uint8 i;

       uint32 data;

       OS_ENTER_CRITICAL();

       data = U0IIR;                                                 //清除中断表示寄存器标志

       VICVectAddr = 0;                                       //清除中断

    for(i = 0;i < 4;i++)

       {

              data = (data << 8) | U0RBR;        //将接受四字节数据拼装成32位地址

       }

   if(0x00000000 == data)

   {

           data = 0x11223344;                 //防止00000000地址不能发送

   }

   OSQPost(ReMsg_Qeue,(void *)data);          //发送该地址到消息邮箱

   OS_EXIT_CRITICAL();

 }

6.发送任务流程

l  等待发送信号量

l  发送数据

l  发送开始信号量

程序:

/**********************************************************

**                            Task2(发送任务)

**********************************************************/

void Task2    (void *pdata)

{

       uint8 i,err;

       pdata = pdata;

       while(1)

       {

              OSSemPend(Sem_SendFlag,0,&err);       //等待发送信号量

              for(i = 0;i < 8; i++)

              UART0_SendByte(rcv_buf[i]);    //通过轮训方式来发送串口数据

              OSSemPost(Sem_StartFlag);           //发送开始信号量

       }                  

}

 

发送数据函数:

/**********************************************************

* 名    称:            UART0_SendByte

* 功    能:            向串口发送字节数据,并等待发送完毕。

* 入口参数:            data              要发送的数据

* 出口参数:            无

**********************************************************/

void   UART0_SendByte(uint8 data)

{

              U0THR = data;                

              while(0 == (U0LSR & 0x40));

}


本篇文章来源于 Linux公社网站(www.linuxidc.com)  原文链接:http://www.linuxidc.com/Linux/2011-02/32124p5.htm