概述
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位表示當前已經接受到回車符\r
//第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))//接收到\r
{
//等待接收\N
if(res == '\n')
{
//接收完成
serialStatus |= 0x8000;
}
else//不是\n,這一次命令作廢
{
serialStatus = 0;
}
}
else//沒收到\r
{
if(res == '\r')
{
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("\r\n");
}
