ARM的串行口實驗
一、 實驗目的
1.掌握ARM 的串行口工作原理。
2.學習編程實現ARM 的UART 通訊。
3.掌握CPU 利用串口通訊的方法。
二、 實驗內容
學習串行通訊原理,了解串行通訊控制器,閱讀ARM 芯片文檔,掌握ARM 的UART
相關寄存器的功能,熟悉ARM 系統硬件的UART 相關接口。編程實現ARM 和計算機之間
串行通訊:
ARM 監視串行口,將接收到的字符在液晶屏上顯示出來。(計算機向串口發送數據是通
過鍵盤來實現的)
三、 預備知識
1、用ARM SDT 2.5 集成開發環境,編寫和調試程序的基本過程。
2、ARM 應用程序的框架結構。
3、會使用Source Insight 3 編輯C 語言源程序。
4、了解串行總線
四、 實驗設備及工具
硬件:ARM 嵌入式開發板、用于ARM7TDMI 的JTAG 仿真器、PC 機Pentumn100 以
上、串口線
軟件:PC 機操作系統win98、ARM SDT 2.51 集成開發環境、仿真器驅動程序、Source
Insight 3.0
五、 實驗原理及說明
1.異步串行I/O
異步串行方式是將傳輸數據的每個字符一位接一位(例如先低位、后高位)地傳送。數據
的各不同位可以分時使用同一傳輸通道,因此串行I/O 可以減少信號連線,最少用一對線
即可進行。接收方對于同一根線上一連串的數字信號,首先要分割成位,再按位組成字符。
為了恢復發送的信息,雙方必須協調工作。在微型計算機中大量使用異步串行I/O 方式,
雙方使用各自的時鐘信號,而且允許時鐘頻率有一定誤差,因此實現較容易。但是由于每個
字符都要獨立確定起始和結束(即每個字符都要重新同步),字符和字符間還可能有長度不定
的空閑時間,因此效率較低。
圖3-1 串行通信字符格式
圖3-1l 給出異步串行通信中一個字符的傳送格式。開始前,線路處于空閑狀態,送出連
續“1”。傳送開始時首先發一個“0”作為起始位,然后出現在通信線上的是字符的二進制
編碼數據。每個字符的數據位長可以約定為5 位、6 位、7 位或8 位,一般采用ASCII 編碼。
后面是奇偶校驗位,根據約定,用奇偶校驗位將所傳字符中為“1”的位數湊成奇數個或偶數個。也可以約定不要奇偶校驗,這樣就取消奇偶校驗位。最后是表示停止位的“1”信號,
這個停止位可以約定持續1 位、1.5 位或2 位的時間寬度。至此一個字符傳送完畢,線路又
進入空閑,持續為“1”。經過一段隨機的時間后,下一個字符開始傳送才又發出起始位。
每一個數據位的寬度等于傳送波特率的倒數。微機異步串行通信中,常用的波特率為
50,95,110,150,300,600,1200,2400,4800,9600 等。
接收方按約定的格式接收數據,并進行檢查,可以查出以下三種錯誤:
1.奇偶錯:在約定奇偶檢查的情況下,接收到的字符奇偶狀態和約定不符。
2.幀格式錯:一個字符從起始位到停止位的總位數不對。
3.溢出錯:若先接收的字符尚未被微機讀取,后面的字符又傳送過來,則產生溢出錯。
每一種錯誤都會給出相應的出錯信息,提示用戶處理。
2.串行接口的物理層標準
通用的串行I/O 接口有許多種,現僅就最常見的兩種標準作簡單介紹。
(—)EIA RS—232C
這是美國電子工業協會推薦的一種標準(Electronic industries Association Recoil-mended
Standard)。它在一種25 針接插件(DB—25)上定義了串行通信的有關信號。這個標準后來被
世界各國所接受并使用到計算機的I/O 接口中。
⑴ 信號連線
在實際異步串行通信中,并不要求用全部的RS—232C 信號,許多PC/XT 兼容機僅用
15 針接插件(DB—15)來引出其異步串行I/O 信號,而PC 中更是大量采用9 針接插件(DB
—9)來擔當此任,因此這里也不打算就RS—232C 的全部信號作詳細解釋。圖5.6.2 給出兩
臺微機利用RS—232C 接口通信的連線(無MODEM),我們按DB—25 的引腳號標注各個信
號。
下面對圖5.6.2 中幾個主要信號作簡要說明。
保護地 通信線兩端所接設備的金屬外殼通過此線相聯。當通信電纜使用屏蔽線時,常
利用其外皮金屬屏蔽網來實現。由于各設備往往已通過電源線接通保護地,因此,通信線中
不必重復接此地線(圖中用虛線表示)。例如使用9 針插頭(DB—9)的異步串行I/O 接口就沒
有引出保護地信號。
TXD/RXD 是一對數據線,TXD 稱發送數據輸出,RXD 稱接收數據輸入。當兩臺微
機以全雙工方式直接通信(無MODEM 方式)時,雙方的這兩根線應交叉聯接(扭接)。
信號地 所有的信號都要通過信號地線構成耦合回路。通信線有以上三條(TXD、RXD
和信號地)就能工作了。其余信號主要用于雙方設備通信過程中的聯絡(握手信號),而且有些
信號僅用于和MODEM 的聯絡。若采取微型機對微型機直接通信,且雙方可直接對異步串
行通信電路芯片編程,若設置成不要任何聯絡信號,則其它線都可不接。有時在通信線的同
一端將相關信號短接以“自握手”方式滿足聯絡要求。這就是如圖3-2(a)所示的情況。
RTS/CTS 請求發送信號RTS 是發送器輸出的準備好信號。接收方準備好后送回清除
發送信號CTS 后,發送數據開始進行,在同一端將這兩個信號短接就意味著只要發送器準
備好即可發送。
DCD 載波檢測(又稱接收線路信號檢測)。本意是MODEM 檢測到線路中的載波信號
后,通知終端準備接收數據的信號,在沒有接MODEM 的情況下,也可以和RTS、CTS 短
接。
相對于MODEM 而言,微型機和終端機一樣被稱為數據終端DTE(Data Terminal
Equipment)而MODEM 被稱為數據通信裝置DCE(Data Communications Equipment),DTE 和
DCE 之間的連接不能像圖3-2 中有“扭接”現象,而應該是按接插件芯號,同名端對應相
接。此處介紹的RS—232C 的信號名稱及信號流向都是對DTE 而言的。
DTR/DSR 數據終端準備好時發DTR 信號,在收到數據通信裝置裝備好DSR 信號后,
方可通信。圖3-2(a)中將這一對信號以“自握手”方式短接。
RI 原意是在MODEM 接收到電話交換機有效的撥號時,使RI 有效,通知數據終端準
備傳送。在無MODEM 時也可和DTR 相接。
圖3-2(b)給出了無MODEM 情況下,DTE 對DTE 異步串行通信線路的完整連接,它不
僅適用于微型機和微型機之間的通信,還適用于微型機和異步串行外部設備(如終端機、繪
圖儀、數字化儀等)的連接。
⑵ 信號電平規定
RS—232C 規定了雙極性的信號邏輯電平:
-3V 到-25V 之間的電平表示邏輯“1”。
+3V 到+25V 之間的電平表示邏輯“0”。
因此這是一套負邏輯定義。
以上標準稱為EIA 電平。PC/XT 系列使用的信號電平是-12V 和+12V,符合EIA 標準,
但在計算機內部流動的信號都是TTL 電平,因此這中間需要用電平轉換電路。常用芯片
MCl488 或SN75150 將TTL 電平轉換為EIA 電平,MCl489 或SN75154 將EIA 電平轉換為
TTL 電平。PC/XT 系列以這種方式進行串行通信時,在波特率不高于9600 的情況下,理
論上通信線的長度限制為15 米。
(二)20mA 電流環
20mA 電流環并沒有形成一套完整的標準,主要是將數字信號的表示方法不使用電子的
高低,而改用20mA 電流的有無:“1”信號在環路中產生20mA 電流;“0”信號無電流產生。
當然也需要有電路來實現TTL 電平和20mA 電流之間的轉換。圖3-3 是PC/XT 微機中使用的一種20mA 電流環接口。當發送方SOUT=1 時,便有20mA 電流灌入接收方的光耦合器,
于是光耦合器導通,使SIN=1。反之當發送方SOUT=0 時環路電流為零,接收方光耦合器截
止,SIN=0。顯然,當要求雙工方式通信時,雙方都應各有收發電路,通信聯線至少要4 根。
由于通信雙方利用光耦合器實現電氣上隔離,而且信號又是雙端回路方式,故有很強的抗干
擾性,可以傳送遠至1 千米的距離。
“0”、“1”信號的表示方法不同外,其他方面(如字符的傳輸格式)常借用RS—232C 標
準。因此PC/XT 微機中的異步串行道信接口往往將這兩種標準做在一起,實際通過跨接線
從二者中擇一使用。
3.ARM 自帶的串行口寄存器
ARM 自帶兩個串行口,各帶有16 字節的FIFO(先入先出寄存器),最大波特率115.2K。
每個UART 有7 種狀態:溢出錯誤,校驗錯誤,幀錯誤,暫停態,接收緩沖區準備好,發
送緩沖區空,發送移位緩沖器空,這些狀態可以由相應的UTRSTATn/UERSTATn 表示,并
且與發送接收緩沖區相對應的有錯誤緩沖區。波特率的大小可以通過控制波特率寄存器
(UBRDIVn)控制,計算公式如下:
由上表可以看出,該寄存器的第6 位決定是否使用紅外模式,位5~3 決定校驗方式,
位2 決定停止位長度,位1 和0決定每幀的數據位數。
⑵ UART 控制寄存器UCONn,該寄存器決定UART 的各種模式。UART FIFO 控制寄
存器UFCONn,UART MODEM 控制寄存器,分別決定UART FIFO 和MODEM 的模式。其
中UFCONn 的第0 位決定是否啟用FIFO,UMCONn 的第0 位是請求發送位,對我們來說
是比較重要的。另外讀寫狀態寄存器UTRSTAT 以及錯誤狀態寄存UERSTAT,可以反映芯
片目前的讀寫狀態以及錯誤類型。FIFO 狀態寄存器UFSTAT 和MODEM 狀態寄存器
UMSTAT,通過前者可以讀出目前FIFO 是否滿以及其中的字節數;通過后者可以讀出目前
MODEM 的CTS 狀態。
⑶ 發送寄存器UTXH 和接收寄存器URXH,這兩個寄存器存放著發送和接收的數據,
當然只有一個字節8位數據。需要注意的是在發生溢出錯誤的時候,接收的數據必須要被讀
出來,否則會引發下次溢出錯誤。
⑷ 最后是波特率引子寄存器UBRDIV。該寄存器為十六位,算法參見上頁的部分。
注意:由于ARM 工作時存在小端和大端兩種工作模式,所以同樣一個寄存器在不同模
式時地址也不一樣,需要加以區別。
六、 實驗步驟
1.不帶操作系統的ARM 實現串行功能
⑴ 學習上述串行通訊原理,了解ARM 上相應寄存器的功能和各位的意義。
⑵ 打開一個新工程,定義與UART 有關的各個寄存器地址和一些特殊的位命令。主
要有以下各寄存器:
/* UART 的全部功能寄存器 */
#define rULCON0 (*(volatile unsigned *)0x1d00000)
#define rULCON1 (*(volatile unsigned *)0x1d04000)
#define rUCON0 (*(volatile unsigned *)0x1d00004)
#define rUCON1 (*(volatile unsigned *)0x1d04004)
#define rUFCON0 (*(volatile unsigned *)0x1d00008)
#define rUFCON1 (*(volatile unsigned *)0x1d04008)
#define rUMCON0 (*(volatile unsigned *)0x1d0000c)
#define rUMCON1 (*(volatile unsigned *)0x1d0400c)
#define rUTRSTAT0 (*(volatile unsigned *)0x1d00010)
#define rUTRSTAT1 (*(volatile unsigned *)0x1d04010)
#define rUERSTAT0 (*(volatile unsigned *)0x1d00014)
#define rUERSTAT1 (*(volatile unsigned *)0x1d04014)
#define rUFSTAT0 (*(volatile unsigned *)0x1d00018)
#define rUFSTAT1 (*(volatile unsigned *)0x1d04018)
#define rUMSTAT0 (*(volatile unsigned *)0x1d0001c)
#define rUMSTAT1 (*(volatile unsigned *)0x1d0401c)
#define rUBRDIV0 (*(volatile unsigned *)0x1d00028)
#define rUBRDIV1 (*(volatile unsigned *)0x1d04028)
#ifdef __BIG_ENDIAN //大端模式
#define rUTXH0 (*(volatile unsigned char *)0x1d00023)
#define rUTXH1 (*(volatile unsigned char *)0x1d04023)
#define rURXH0 (*(volatile unsigned char *)0x1d00027)
#define rURXH1 (*(volatile unsigned char *)0x1d04027)
#define WrUTXH0(ch) (*(volatile unsigned char *)(0x1d00023))=(unsigned char)(ch)
#define WrUTXH1(ch) (*(volatile unsigned char *)(0x1d04023))=(unsigned char)(ch)
#define RdURXH0() (*(volatile unsigned char *)(0x1d00027))
#define RdURXH1() (*(volatile unsigned char *)(0x1d04027))
#define UTXH0 (0x1d00020+3) //byte_access address by BDMA
#define UTXH1 (0x1d04020+3)
#define URXH0 (0x1d00024+3)
#define URXH1 (0x1d04024+3)
#else //小端模式
#define rUTXH0 (*(volatile unsigned char *)0x1d00020)
#define rUTXH1 (*(volatile unsigned char *)0x1d04020)
#define rURXH0 (*(volatile unsigned char *)0x1d00024)
#define rURXH1 (*(volatile unsigned char *)0x1d04024)
#define WrUTXH0(ch) (*(volatile unsigned char *)0x1d00020)=(unsigned char)(ch)
#define WrUTXH1(ch) (*(volatile unsigned char *)0x1d04020)=(unsigned char)(ch)
#define RdURXH0() (*(volatile unsigned char *)0x1d00024)
#define RdURXH1() (*(volatile unsigned char *)0x1d04024)
#define UTXH0 (0x1d00020) //byte_access address by BDMA
#define UTXH1 (0x1d04020)
#define URXH0 (0x1d00024)
#define URXH1 (0x1d04024)
#endif
將上述定義為一個AD 功能的頭文件,并且加入到工程中(44b.h)。
⑶ 在上面的文件中添加下面的執行函數,并且建立一個庫文件(*.C)包含各函
數。主要為如下幾個:
void Uart_Init(int Uartnum, int mclk,int baud)//初始化函數,參數為端口號,時鐘,波特率
{
int i;
if(mclk==0)
mclk=MCLK;
if(Uartnum==0){ //UART0
rUFCON0=0x0; //FIFO disable
rUMCON0=0x0; //UART0
rULCON0=0x3; //Normal,No parity,1 stop,8 bit
rUCON0=0x245; //rx=edge,tx=level,disable timeout int.,enable rx
//error int.,normal,interrupt or polling
rUBRDIV0=( (int)(mclk/16./baud + 0.5) -1 );
}
else{
rUFCON1=0x0;
rUMCON1=0x0; //UART1
rULCON1=0x3;
rUCON1=0x245;
rUBRDIV1=( (int)(mclk/16./baud + 0.5) -1 );
}
for(i=0;i<100;i++);
}
void Uart_SendString(int Uartnum, char *pt)//發送字符串函數,參數為端口號,發送數組
{
while(*pt){
if(*pt=='\n'){
Uart_SendByte(Uartnum, '\r');
Uart_SendByte(Uartnum, *pt++);
}
else
Uart_SendByte(Uartnum, *pt++);
}
}
void Uart_Printf(char *fmt,...) //串行口發送字符串函數,僅向口0發送。
{
va_list ap;
char string[256];
va_start(ap,fmt);
vsprintf(string,fmt,ap);
Uart_SendString(0, string);
va_end(ap);
}
void Uart_TxEmpty(int Uartnum) //檢查發送緩沖區是否滿函數,參數為端口號
{
if(Uartnum==0)
while(!(rUTRSTAT0 & 0x4)); //wait until tx shifter is empty.
else
while(!(rUTRSTAT1 & 0x4)); //wait until tx shifter is empty.
}
char Uart_Getch(char* Revdata, int Uartnum, int timeout) //串行口檢測函數,將串口接收
//到的數據放入變量,參數為變量,端口號,超時時間
{
int i=0;
if(Uartnum==0){
while(!(rUTRSTAT0 & 0x1)){ //Receive data read
OSTimeDly(1);
if(timeout==0)
continue;
if(++i>=timeout)
return FALSE;
}
*Revdata=RdURXH0();
return TRUE;
}
else{
while(!(rUTRSTAT1 & 0x1)){ //Receive data read
OSTimeDly(1);
if(timeout==0)
continue;
if(++i>=timeout)
return FALSE;
}
*Revdata=RdURXH1();
return TRUE;
}
}
上面的是與串行口操作有關的各種函數(44blib.c)。
⑷ 在主函數中首先初始化串行口0,然后通過串行口0 向外發送字符"U"。
/*************************串行口發送主程序**************************/
int Main(int argc, char **argv)
{
char c1;
Uart_Init(0,115200);
while(1)
{
Uart_SendByte(0xa);
Uart_SendByte(0xd);
c1=Uart_Getch();
Uart_SendByte(c1);
}
return 0;
}
2.帶操作系統的ARM 實現串行口功能
當操作系統啟動時,將自動初始化各串行口,所以應用程序調用串行口資源將變得非常
容易。值的注意的是,應用程序往往是多任務系統,為了實時監測串行口信息,在本操作環
境中必須單開一個串行口掃描任務,保證信息不丟失。
⑴ 打開一個已有的工程文件,在其中的主函數MAIN 中添加串行口的寄存器初始化
代碼,并添加串行口和鍵盤掃描任務,串行口掃描任務的代碼如下:
void Uart_Scan_Task1(void *Id)
{
char c1;
POSMSG pmsg1;
for (;;){
if(Uart_Getch(&c1,0,1))
{
pmsg1=OSCreateMessage(NULL,OSM_SERIAL,0,c1);
if(pmsg1)
SendMessage(pmsg1);
}
}
}//Uart_Scan_Task
當系統收到串行口信息時,將會自動向主任務發送一個串行口消息。主任務接收到該消
息,將會調用響應函數,響應該消息。
⑵ 添加消息響應函數的代碼如下:
void onSerial(int portn, char c)
{
LCD_ChangeMode(DspTxtMode);
LCD_printf("%c\n",c);
Uart_SendByte(0,c);
}
⑶ 添加主任務
void Main_Task(void *Id) //Main_Test_Task
{
POSMSG pMsg=0;
ClearScreen();
//消息循環
for(;;){
pMsg=WaitMessage(0); //等待消息switch(pMsg->Message)
{
case OSM_SERIAL:
onSerial(pMsg->WParam,pMsg->LParam);
break;
}
DeleteMessage(pMsg);//刪除消息,釋放資源
}
}
通過上面兩小段代碼,系統就可以調用串行口資源發送信息和接收信息,相對于不
帶操作系統串行口任務調用,上面的程序要簡單的多。從中大家也可以體會到在操作系
統的基礎上開發應用程序的好處。
七、 思考題
1.232 串行通訊的數據格式是什么?
2.串行通訊最少需要幾根線,分別如何連接?
3.ARM 的串行口有幾個,相應的寄存器是什么?
4.帶操作系統和不帶操作系統控制ARM 的串行口有什么區別?
5.為什么需要建立串行口掃描任務?
評論
查看更多