05-笔记:LPC1788-串口

概述

LPC178x/177x 系列 Cortex-M3 具有 5 个符合 16C550 工业标准的异步串行口 UART0、UART1、UART2、UART3 和 UART4。
其中,各串行口的区别是:UART1 比 UART0、2、3 增加了 Modem 接口;
UART4 较 UART0、2、3 增加了 IrDA 接口和符合 ISO7816-3 的智能卡接口,其余都基本相同。
注:默认情况下, UART2 、 UART3 和 UART4 都被关闭,以节省功耗。若需使用 UART2 、 UART3 和 UART4 ,请设置 PCONP 寄存器的相关位。此外,在不启用 Modem 、 IrDA 等模式时, UART1 和 UART4 也可以和 UART0 、2 、 3 一样,当普通 UART 来用。
 

特性

 数据大小为 5、6、7、8 位;
 奇偶发生和校验:奇、偶标记,空格或没有;
 1 个或 2 个停止位;
 16 字节收发 FIFO;
 内置波特率发生器,包括小数波特率分频器用于各种功能;
 支持 DMA 发送和接收;
 自动波特率功能;
 间隔发生和检测;
 多处理器寻址模式;
 UART1 包含标准 Modem 接口信号(CTS、DCD、DTS、DTR、RI、RTS);
 UART4 包含支持红外通信的 IrDA 模式;
 UART0/2/3/4 支持软件流控制;
 UART0/2/3/4 支持 RS-458/EIA-485,其中 UART4 支持 9-位模式和输出使能;
 UART4 支持可选择的同步发送或接收模式;
 UART4 支持可选择的遵循 ISO 7816-3 规范的智能卡接口。
 

引脚描述

[1] 当 UARTn 的两根或两根以上 RXDn 脚启用时,仅引脚编号最小的那根 RXD 脚有效,而成为 UARTn的数据输入引脚。此时,其它被复用为 RXD 的引脚对 UARTn 输入没有影响。例如 RXD3 对应的四根引脚 P0.1 、 P0.3 、 P0.26 、 P4.29 若同时启用为 UART 数据接收脚,那么,仅 P0.1 脚能有效接收数据。
[2] 当 UARTn 的 TXDn 脚全部启用时,所有 TXDn 引脚都会彼此独立地输出相同信号。
只有 UART1 才具有 Modem 接口,因此 UART0、UART2、UART3 和 UART4 不具有 CTS1、
DCD1、DSR1、RI1、DTR1 和 RTS1 引脚。
只有 UART4 支持同步模式,因此 UART0、UART1、UART2 和 UART3 不具有 SCLK 引脚。
 
 
UART0 和 UART1、UART2、UART3、UART4 各有一个独立波特率发生器,它们的功能
都是相同的,我们以 UART0 波特率发生器(U0BRG)为例进行说明。
U0BRG 产生 UART0 发送模块所需的时钟。UART0 波特率发生器时钟源为 APB 时钟
(PCLK)。时钟源与 U0DLL 和 U0DLM 寄存器所确定的除数相除得到 UART0 Tx 模块所需时
钟,该时钟必须为目标波特率的 16 倍。
 
LPC178x/177x 系列 Cortex-M3 UART 部分的寄存器结构如图 5.16 所示。其中,UART1 具
有 Modem 模块,UART4 具有 IrDA 模块和智能卡接口模块,UART0/2/3/4 具有 485 模块。
 
 
LPC178x/177x 系列 Cortex-M3 的五个 UART,具有 16 字节的收发 FIFO,内置波特率发
生器,五个串口具有基本相同的寄存器,其中 UART1 带有完全的调制解调器控制握手接口。
在大多数异步串行通讯的应用中,并不需要完整的 Modem 接口信号(辅助控制信号),而只使用
TXD、RXD 和 GND 信号即可。
 

UART 中断

LPC178x/177x 系列 Cortex-M3 UART 接口具有中断功能,而且由嵌套向量中断控制器
(NVIC)管理,UART0、UART1、UART2、UART3 和 UART4 中断分别位于 NVIC 中断通道
21、通道 22、通道 23、通道 24 和通道 51。
以 UART0 为例,UART0 接口中断与嵌套向量中断控制器(NVIC)的关系如图 5.24 所示。
UART0 中断占用 NVIC 的通道 21,中断使能寄存器 ISER 用来控制 NIVC 通道的中断使能。
当 ISER0[5]=1 时,通道 21 中断使能,即 UART0 中断使能。
中断优先级寄存器 IPR 用来设定 NIVC 通道中断的优先级。IPR1[15:11]用来设定通道 21
的优先级,即 UART0 中断的优先级。具体的设定方法可参考“嵌套向量中断控制器(NVIC)”
一节。
当 UART0 接口的优先级设定且中断使能后,若触发条件满足时,则会触发中断。当处理
器响应中断后将自动定位到中断向量表,并根据中断号从向量表中找出 UART0 中断处理的入
口地址,然后 PC 指针跳转到该地址处执行中断服务函数。因此,用户需要在中断发生前将
UART0 的中断服务函数地址(UART0_IRQHandler)保存到向量表中。
UART 中断主要分为 5 类:接收中断、发送中断、接收线状态中断、Modem 中断和自动波
特率中断,如图 5.25 所示。其中,接收线状态中断指接收过程中发生了错误,即接收错误中断。
只有 UART1 接口具有 Modem 中断,其它 UART 接口由于没有 Modem 功能,所以没有 Modem
中断。自动波特率中断包括自动波特率结束中断和超时中断。

UARTn 中断标志寄存器(UnIIR,n = 0~4)

UnIIR 提供状态码用于指示挂起中断 [1] 的中断源和优先级,5 个 UART 的 IIR 寄存器之概况如表 5.29
所列。其中,只有 UART1 具有 Modem 中断。[1] “挂起中断”是指产生了但是未被响应的中断请求。
在访问 UnIIR 过程中,中断被冻结。若访问 UnIIR时产生了中断,该中断将被记录,下次访问 UnIIR 时
便可将其读出。UART 中断标志寄存器描述如表 5.30所列。
中断的处理见表 5.31。给定了 UnIIR[3:0]的状态,中断处理程序就能确定中断源以及如何清除激活的中断
(1)UART 接收线状态中断(RLS 中断)
接收线状态(UnIIR[3:1]=011)是最高优先级中断。只要 UARTn 在接收数据时产生下面 4
个错误中的任意一个,UnIIR 将产生相应的中断标志。
 溢出错误(OE);
 奇偶错误(PE);
 帧错误(FE);
 间隔中断(BI)。
具体错误类型可通过查看 UnLSR[4:1]得到。当读取 UnLSR 寄存器时,自动清除该中断标志。
(2)UART 接收数据可用中断(RDA 中断)
接收数据可用中断(UnIIR[3:1]=010)与字符超时中断(UnIIR[3:1]=110)共用第二优先级。
当 UARTn 接收 FIFO 达到 UnFCR[7:6]所定义触发点时,RDA 中断被激活。当 UARTn Rx FIFO
的深度低于触发点时,RDA 中断复位。当接收数据可用中断激活时,CPU 可读出由触发点所
定义长度的数据块。
(3)UART 字符超时中断(CTI 中断)
字符超时中断(UnIIR[3:1]=110)为第二优先级中断。当接收 FIFO 中的有效数据个数少于
触发个数时(至少有一个),如果经过了一段时间 [1] 没有数据到达,将触发字符超时中断,此时
CPU 就认为一个完整的字符串已经结束,然后将接收 FIFO 中的剩余数据。
[1] 这个触发时间为:接收 3.5 到 4.5 个字符的时间。“ 3.5 ~ 4.5 个字符的时间”,其意思是在当前波特率下,发送 3.5 ~ 4.5 个字节所需要的时间。
产生字符超时中断后,对接收 FIFO 的以下操作都会清除该中断标志:
 从接收 FIFO 中读取数据,即,读取 UnRBR 寄存器;
 有新的数据送入接收 FIFO,即,接收到新数据。
需要注意的是:当接收 FIFO 中存在多个数据,从 UnRBR 读取数据,但是没有读完所有数
据,那么在经过 3.5~4.5 个字节的时间后将再次触发字符超时中断;
例如,一个外设向 LPC178x/177x 系列 Cortex-M3 发送 85 个字符,而接收触发值为 8 个字
符,那么前 80 个字符将使 CPU 接收 10 个接收数据可用中断,而剩下的 5 个字符使 CPU 接收
1~5 个字符超时中断(取决于服务程序)。
 
 

接收中断

对于 UART 接口来说,有两种情况可以触发 UART 接收中断:
接收字节数达到接收 FIFO的触发点(RDA)、接收超时(CTI)。
 
接收字节数达到接收 FIFO 中的触发点(RDA)
LPC178x/177x系列Cortex-M3 UART接口具有 16 字节的接收 FIFO,接收触发点可以设
置为 1、4、8、14 字节,当接收到的字节数达到接收触发点时,便会触发中断
如图 5.26 所示,通过 UART FIFO 控制寄存器 UnFCR,将接收触发点设置为“8 字节触
发”。那么当 UART 接收 8 个字节时,便会触发 RDA 中断(注:在接收中断使能的前提下)。
 
接收超时
当接收 FIFO 中的有效数据个数少于触发个数时(注:接收 FIFO 中至少有一个字节),如果长时
间没有数据到达,将触发 CTI 中断。这个时间为:3.5 到 4.5 个字符的时间。

接收线状态中断

在 UART 接收数据时,如果出现溢出错误(OE)、奇偶错误(PE)、帧错误(FE)和间隔中断(BI)中的任意一个错误时,都会触发接收线状态中断。具体的错误标志可以通
过读取 UART 状态寄存器 UnLSR[4:1]得到。当读取 UnLSR寄存器时,会清除该中断标志。
 
 
 
 

初始化

设置 UART 通信波特率,就是设置寄存器 UnDLL 和 UnDLM 的值,UnDLL 和 UnDLM 寄
存器是波特率发生器的除数锁存寄存器,用于设置合适的串口波特率。
上面已经讲过,寄存器
UnDLL 与 UnRBR/UnTHR、UnDLM 与 UnIER 具有同样的地址,如果要访问 UnDLL、UnDLM,
除数访问位 DLAB 必须为 1。在不使用小数分频器时,寄存器 UnDLL 和 UnDLM 的计算如下:
设置 UART 的工作模式,如:字长度选择、停止位个数、奇偶校验位等。此外,还要根据实际情况设置中断。
UART0 初始化示例,程序将串口波特率设置为 UART_BPS(如 115200),8 位数据长度,1 位停止位,无奇偶校验。
#define UART_BPS 115200  /*  定义通讯波特率 */
/**********************************************************************************************
**  函数名称: UART0_Ini
**  函数功能:初始化串口 0 。设置为 8 位数据位, 1 位停止位,无奇偶校验,波特率为 115200
**********************************************************************************************/
void UART0_Ini(void)
{
	uint32_t Fdiv = 0;
	U0LCR = 0x83;  /* DLAB = 1 ,可设置波特率 */
	Fdiv = (Fpclk / 16) / UART_BPS; /*  设置波特率 */
	U0DLM = Fdiv / 256;
	U0DLL = Fdiv % 256; 
	U0LCR = 0x03;  /* 锁定除数访问 */
	U0FCR  = 0x07;  /* 使能并复位 FIFO */
}
 

UART 发送数据

LPC178x/177x 系列 Cortex-M3 含有一个 16 字节的发送 FIFO,在发送数据的过程中,发送
FIFO 是一直使能的,即,UART 发送的数据首先保存到发送 FIFO 中,发送移位寄存器会从发
送 FIFO 中获取数据,并通过 TXD 引脚发送出去,如图 5.36 所示。
上面讲过,寄存器 UnRBR 与 UnTHR 是同一地址,但物理上是分开的,读操作时为 UnRBR,
而写操作时为 UnTHR。
在寄存器 UnLSR 中,有两个位可以用在 UART 发送过程中,UnLSR[5]和 UnLSR[6]
(1)UnLSR[5]——THRE
当发送 FIFO 为空时,THRE 置位。从上面的描述可知,当发送 FIFO 变空时,发送 FIFO
中的数据已经保存到了发送移位寄存器中,因此,移位寄存器此时正开始传输一个新的数据。
当再次向 UnTHR 寄存器中写入数据时,THRE 位会自动清零。
(2)UnLSR[6]——TEMT
当发送 FIFO 和移位寄存器都为空时,TEMT 置位。由于所有发送的数据都是从移位寄存
器中发送出去的,因此,当 TEMT 置位时,表示 UART 数据已经发送完毕,而且,此时发送
FIFO 也已经没有数据了。当再次向 UnTHR 寄存器中写入数据时,TEMT 位会自动清零。
只要 TEMT 位置位,则 THRE 位也一定会置位的。
UART 接口发送操作可以采用两种方式:中断方式和查询方式。如表 5.66 所示。
采用查询方式发送一字节数据
/**********************************************************************************************
**  函数名称: UART0_SendByte
**  函数功能:向串口发送字节数据,并等待发送完毕
**  入口参数: data  要发送的数据
**  出口参数:无
**********************************************************************************************/
void UART0_SendByte(uint8 data)
{
U0THR = data; /*  发送数据 */
while ( (U0LSR&0x40)==0 );  /*  等待数据发送完毕 */
}
 
 
 

UART 接收数据

寄存器 UnRBR 与 UnTHR 是同一地址,但物理上是分开的,读操作时为 UnRBR,
而写操作时为 UnTHR。
LPC178x/177x 系列 Cortex-M3 的四个 UART,各含有一个 16 字节的 FIFO,用来作为接收
缓冲区,缓冲区中的数据只能够通过寄存器 UnRBR 来获取。UnRBR 是 UARTn 接收 FIFO 的
最高字节,它包含了最早接收到的字符。每读取一次 UnRBR,接收 FIFO 便丢掉一个字符。
可见,只要接收 FIFO 中含有数据,则寄存器 UnRBR 便不会为空,就会包含有效数据,即,
UnLSR[0] = 1。
UART 接收数据时,可以使用查询方式接收,也可以使用中断方式接收,如表 5.67 所示。
采用查询方式接收一字节数据
/**********************************************************************************************
**  函数名称: UART0_RcvByte
**  函数功能:从串口接收一个字节的数据。使用查询方式
**  入口参数:无
**  出口参数:返回接收到的数据
**********************************************************************************************/
uint8 UART0_RcvByte(void)
{
uint8 rcv_data;
while ((U0LSR&0x01) == 0); /*  查询数据是否接收完毕 */
rcv_data = U0RBR;
return (rcv_data);
}
 
 
使用中断方式接收数据时,如果发生 RDA 中断,则循环从 UnRBR 中读取数据即可。如果
发生了字符超时中断——CTI,可以通过 UnLSR[0]来判断 FIFO 中是否含有有效数据,如图 5.43
所示。
/**********************************************************************************************
**  函数名称: UART_Exception
**  函数功能:串口中断服务程序
**********************************************************************************************/
void UART_Exception(void)
{
……
switch(U0IIR & 0x0f)
{
case 0x04 : /*  发生 RDA 中断 */
/*
**  从接收 FIFO 中读取数据
*/
break;
case 0x0c : /*  发生字符超时中断 ——CTI  */
while((U0LSR & 0x01) == 1) { 
/*
**  如果接收 FIFO 中含有有效数据,就读取 UnRBR 寄存器
*/
RcvData[i++] = U0RBR;
break;
    ……
default :
break;
}
……
}
 
 
注:彻底清除 UART 中断标志后才可退出中断服务程序,否则会导致处理器反复陷入中断。
 
综上所述,UARTn 的基本操作方法:
 设置 I/O 连接到 UARTn;
 设置串口波特率(UnDLM、UnDLL);
 设置串口工作模式(UnLCR、UnFCR);
 发送或接收数据(UnTHR、UnRBR);
 检查串口状态字或等待串口中断(UnLSR)。
 
 
 
 
#ifndef __DEBUGSERIAL_H_
#define __DEBUGSERIAL_H_

#include "sys.h"
#include "stdio.h"

extern u8 serialBuffer[256];
extern u16 serialStatus;

void Debug_Serial_Init(u32 baud);
void Debug_Serial_Send_Byte(u8 dat);
void Debug_Serial_Send_Buffer(u8 length,u8* buffer);

#endif

 

 
 
#include "debugSerial.h"

//加入printf支持
#pragma import(__use_no_semihosting)                            
struct __FILE
{
    int handle;
    /* Whatever you require here. If the only file you are using is */
    /* standard output using printf() for debugging, no file handling */
    /* is required. */
};

FILE __stdout;      

_sys_exit(int x)
{
    x = x;
} 

int fputc(int ch, FILE *f)
{     
    while(!((LPC_UART0->LSR) & 0x20));      //等待判断LSR[5](即THRE)是否是1,1时表示THR中为空     
    LPC_UART0->THR = (u8)ch;                    //发送数据    
    return ch;
}



//定义一个256字节的缓冲区用于存放接收到的串口数据信息
//定义一个16位数据同时保存接收数据长度以及接收数据的状态
u8 serialBuffer[256] = {0};
u16 serialStatus = 0;

//16字节的状态
//低八位为当前存储的有效数据长度
//15位为接收完成等待处理标志
//8位表示当前已经接受到回车符
//第9到十四位表示在等待处理期间系统冗余发送的数据量
//用于后期通讯系统的负载自适应
 
void TransSerialsCommand(u8 res)
{
    u8 lostCount;
    u8 receiveCount;

    //接收数据处理
    if(serialStatus & (1<<15))//已经接收完成,这个数据被抛弃
    {
        lostCount = ((u8)(serialStatus>>9))&0x3f;//漏掉的数据计数
        if(lostCount < 0x3f)lostCount++;
        serialStatus &= ~(0x3f<<9);
        serialStatus |= (lostCount<<9);
    }
    else//上一个命令没有接收完
    {
        if(serialStatus & (1<<8))//接收到
        {
            //等待接收N
            if(res == '
')
            {
                //接收完成
                serialStatus |= 0x8000;
            }
            else//不是
,这一次命令作废
            {
                serialStatus = 0;
            }
        }
        else//没收到
        {
            if(res == '
')
            {
                serialStatus |= 0x0100;
            }
            else
            {
                receiveCount = (u8)(serialStatus&0xff);
                if(receiveCount < 255)
                {
                    serialBuffer[receiveCount] = res;
                    receiveCount++;
                    serialStatus &= 0xff00;
                    serialStatus |= receiveCount;
                }
                else
                {
                    //数据溢出,清空
                    serialStatus = 0;
                }
            }
        }
    }
}


void UART0_IRQHandler(void)
{
    u8 status = 0;
    u8 res = 0;
	
    //清除串口中断挂起
    NVIC_ClearPendingIRQ(GPIO_IRQn);
    //清除串口接收中断
    if(!(LPC_UART0->IIR & 0x01))//确认有中断发生
    {
        status = LPC_UART0->IIR & 0x0e;
        if(status == 0x04)//确认是RDA中断
        {
            //读取串口接收值
            res = (LPC_UART0->RBR&0xff);
            //处理串口接收值
            TransSerialsCommand(res);
        }
    }
}


void Debug_Serial_Init(u32 baud)
{
    LPC_SC->PCONP |= (1<<3)|(1<<15);                //打开时钟
	
    //配置io口
    LPC_IOCON->P0_2 = 0x00;                         //选择TXD功能,禁止迟滞 不反向 正常推挽
    LPC_IOCON->P0_2 |= (1<<0)|(2<<3);               //上拉
    LPC_IOCON->P0_3 = 0x00;                         //选择RXD功能,禁止迟滞 不反向 正常推挽
    LPC_IOCON->P0_3 |= (1<<0)|(2<<3);               //上拉
    LPC_UART0->LCR = 0x83;                          //设置串口数据格式,8位字符长度,1个停止位,无校验,使能除数访问
    LPC_UART0->DLM = ((ApbClock/16)/baud) / 256;    //除数高八位  , 没有小数情况
    LPC_UART0->DLL = ((ApbClock/16)/baud) % 256;    //除数第八位
    LPC_UART0->LCR = 0x03;                          //禁止访问除数锁存器,锁定波特率
    LPC_UART0->FCR  = 0x00;                         //禁止FIFO
    LPC_UART0->IER = 0x01;                          //使能接收中断RDA

    NVIC_EnableIRQ(UART0_IRQn);                     //打开IRQ中断
}

void Debug_Serial_Send_Byte(u8 dat)
{
    //当检测到UARTn THR已空时,THRE就会立即被设置。写UnTHR会清零THRE
    //0  -  UnTHR包含有效字符
    //1  -  UnTHR为空
    while(!((LPC_UART0->LSR) & 0x20));      //等待判断LSR[5](即THRE)是否是1,1时表示THR中为空     
    LPC_UART0->THR = dat;                   //发送数据
}

void Debug_Serial_Send_Buffer(u8 length,u8* buffer)
{
    u8 i = 0;
	
    for(i = 0; i < length; i++)
    {
        Debug_Serial_Send_Byte(buffer[i]);
    }
    printf("
");
}




原文地址:https://www.cnblogs.com/bog-box/p/LPC1788-UART.html