文章目錄
一.S3C2440中的UART介紹
UART(universal asynchronous receive transmitter)通用異步收發器,用來收發串行數據,以全雙工的形式進行通信,UART使用的電平標準是TTL/CMOS,一幀數據通常包含開始位、數據位、校驗位、停止位,UART傳輸的雙方要統一波特率。
S3C2440中有三個UART獨立通道,功能類似,下面分塊介紹一下UART比較重要的部分。
UART的用途一般有倆種:
1.作爲與上位機的通信接口,打印調試信息
2.作爲外設模塊的驅動接口,連接驅動外設,比如:藍牙、GPS等
1.1 電平匹配
ARM串口電平爲TTL,要根據PC的匹配電平來選擇電平轉換芯片。
UART直接輸出的電平是TTL電平,以前上位機PC端都有RS-232電平(九針接口),所以以前ARM與上位機連接需要TTL轉RS-232電平的芯片。RS-232適合長距離傳輸。
現在,PC上基本沒有RS-232接口了,取而代之的是USB接口,所以現在ARM與PC的UART通信都使用TTL轉USB芯片了。
1.2 UART數據幀與波特率
一幀數據通常包含開始位、數據位、校驗位、停止位。
開始位:UART空閒時,TxD數據線是高電平的(因此需要給相應引腳上拉引腳),將要發送數據時,TxD數據線會以拉低電平作爲起始信號,所以“0”電平就相當於開始位。
數據位:數據位包含了要發送的信息,數據位的大小可以通過配置寄存器來確定,通常是8bit。
校驗位:爲了保證數據傳輸的準確性,有時會在數據位後面加上一個校驗位,分爲奇、偶校驗,校驗規則:數據位+校驗位中爲1的個數是奇數或者偶數。一般不用。
停止位:會給出一段持續的高電平作爲停止信號,持續時間可以通過配置寄存器來設置,一般設置停止位的持續時間爲1位長度。
最常用的數據幀格式爲:8n1(意爲:8位數據位,不使用校驗位,停止位長度爲一位)
波特率:每秒發送的bit(位)
1.3UART框圖
框圖如下:
-
UART發送數據的流程:CPU從內存中將數據取到FIFO,FIFO中的數據發送到移位器,由移位器逐位發送數據。
-
UART接收數據的流程:移位器逐位接收數據,將接收到的數據放在FIFO中,CPU將數據從FIFO中取到內存。
-
UART中FIFO深度爲64Byte,不使用FIFO時,將數據放在接收/保持寄存器中(1Byte)。
-
數據發送、接收完成,可以利用中斷進行處理,也可以不斷查詢寄存器標誌位。
-
波特率由波特率發生器產生,涉及到時鐘源和分頻因子的選擇
二.UART的配置
這裏介紹一下最基本的UART配置,即:使用UART0、滿足收發數據功能、不使用FIFO(使用1Byte的寄存器)、收發狀態可以通過中斷或查詢標誌位獲知。
2.1 UART引腳的配置
S3C2440中UART0的引腳對應關係爲:TxD:GPH2 RxD:GPH3
首先要將倆個引腳的模式設置爲UART模式,寄存器配置如下:
/* 設置引腳 */
//TxD0:GPH2 RxD0:GPH3
GPHCON &= ~((3<<4)|(3<<6));
GPHCON |= ((2<<4)|(2<<6));
由於數據線平時是高電平,所以要設置引腳的內部上拉。
我們要將內部上拉電阻與引腳連接,通過配置寄存器可以控制,上拉電阻與引腳的連接狀態由GPHUP寄存器控制:
我們要使能GPH2、GPH3的內部上拉,將2、3位復位
/* 使能引腳的內部上拉 */
GPHUP &= ~( (1<<2)|(1<<3) );
2.2 波特率的配置
波特率計算如下:
通過設置分頻係數DIV、選擇時鐘源來配置波特率
UCON0寄存器可以選擇時鐘源:
一般默認PCLK爲時鐘源,所以不必專門配置時鐘源。
UBRDIV寄存器負責分頻因子設置:
直接將分頻因子寫入寄存器即可:
/* 設置波特率 */
/* 波特率設置格公式:UBRDIVn = (int)( UART clock / ( buad rate x 16) ) –1 */
/* 實現波特率:115200 b/s 時鐘源採用PCLK:50MHz 分頻因子:26 (本來26.127,忽略誤差可以) */
/* 默認PCLK:UCON0 &= ~(3<<10)*/
UBRDIV0 = 26;
2.3 數據幀的配置
數據幀的配置主要包括對:校驗位、停止位、數據位的配置
ULCON0寄存器配置:
/* 設置數據格式 */
/* 數據幀格式:8n1 */
ULCON0 = 0x00000003;
2.4 收發模式配置
UCON0寄存器也用來配置RxD、TxD的模式:可以通過中斷和查詢寄存器標誌位來獲取收發的狀態
默認選擇時鐘(PLCK=50MHz)、Rx、Tx模式(中斷和查詢)
/* 收發模式:中斷+查詢 */
UCON0 = 0x00000005;
收發狀態的查詢通過UTRSTAT0寄存器來確定:
可以通過如下程序配合寄存器查詢獲取收發的狀態:
/* 輸出數據 */
int putchar( int c )
{
/* 等待傳輸數據寄存器空 */
while( !(UTRSTAT0 & (1<<2)) );
/* 不使用FIFO,對數據傳輸寄存器UTXH0直接寫 */
UTXH0 = (unsigned char)c;
}
/* 接收數據 */
int getchar( void )
{
while( !(UTRSTAT0 & (1<<0)));
/* 接收數據寄存器URXH0 */
return URXH0;
}
2.5 收發數據寄存器
直接對寄存器進行讀寫就可以。
發送數據寄存器:
接收數據寄存器:
三.代碼
話不多說,直接上代碼,程序的框架是:main.c+uart.h+start.S
main.c:
#include "s3c2440_soc.h"
#include "uart.h"
int main(void)
{
unsigned char c;
uart0_init();
puts("Hello, world!\n\r");
while(1)
{
c = getchar();
if (c == '\r')
{
putchar('\n');
}
if (c == '\n')
{
putchar('\r');
}
putchar(c);
}
return 0;
}
uart.c:
#include "s3c2440_soc.h"
#include "uart.h"
void uart0_init( void )
{
/* 設置引腳 */
//TxD0:GPH2 RxD0:GPH3
GPHCON &= ~((3<<4)|(3<<6));
GPHCON |= ((2<<4)|(2<<6));
/* 使能引腳的內部上拉 */
GPHUP &= ~( (1<<2)|(1<<3) );
/* 設置波特率 */
/* 波特率設置格公式:UBRDIVn = (int)( UART clock / ( buad rate x 16) ) –1 */
/* 實現波特率:115200 b/s 時鐘源採用PCLK:50MHz 分頻因子:26 (本來26.127,忽略誤差可以) */
/* 默認PCLK:UCON0 &= ~(3<<10)*/
UBRDIV0 = 26;
/* 設置數據格式 */
/* 數據幀格式:8n1 */
ULCON0 = 0x00000003;
/* 收發模式:中斷+查詢 */
UCON0 = 0x00000005;
}
/* 輸出數據 */
int putchar( int c )
{
/* 等待傳輸數據寄存器空 */
while( !(UTRSTAT0 & (1<<2)) );
/* 不使用FIFO,對數據傳輸寄存器UTXH0直接寫 */
UTXH0 = (unsigned char)c;
}
/* 接收數據 */
int getchar( void )
{
while( !(UTRSTAT0 & (1<<0)));
/* 接收數據寄存器URXH0 */
return URXH0;
}
/* 連續發送字符串 */
int puts( const char *s )
{
while( *s )
{
putchar( *s );
s++;
}
}
start.S:
.text
.global _start
_start:
/* 關閉看門狗 */
ldr r0, =0x53000000
ldr r1, =0
str r1, [r0]
/* 設置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
ldr r0, =0x4C000000
ldr r1, =0xFFFFFFFF
str r1, [r0]
/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */
ldr r0, =0x4C000014
ldr r1, =0x5
str r1, [r0]
/* 設置CPU工作於異步模式 */
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
/* 設置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0)
* m = MDIV+8 = 92+8=100
* p = PDIV+2 = 1+2 = 3
* s = SDIV = 1
* FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
*/
ldr r0, =0x4C000004
ldr r1, =(92<<12)|(1<<4)|(1<<0)
str r1, [r0]
/* 一旦設置PLL, 就會鎖定lock time直到PLL輸出穩定
* 然後CPU工作於新的頻率FCLK
*/
/* 設置內存: sp 棧 */
/* 分辨是nor/nand啓動
* 寫0到0地址, 再讀出來
* 如果得到0, 表示0地址上的內容被修改了, 它對應ram, 這就是nand啓動
* 否則就是nor啓動
*/
mov r1, #0
ldr r0, [r1] /* 讀出原來的值備份 */
str r1, [r1] /* 0->[0] */
ldr r2, [r1] /* r2=[0] */
cmp r1, r2 /* r1==r2? 如果相等表示是NAND啓動 */
ldr sp, =0x40000000+4096 /* 先假設是nor啓動 */
moveq sp, #4096 /* nand啓動 */
streq r0, [r1] /* 恢復原來的值 */
bl main
halt:
b halt
代碼參考韋東山嵌入式教程。