串口設置之輸入輸出字符
S5PV210 UART相關說明
通用異步收發器簡稱UART,即UNIVERSAL ASYNCHRONOUS RECEIVER AND TRANSMITTER,它用來傳輸串行數據。發送數據時,CPU將并行數據寫入UART,UART按照一定的格式在一根電線上串行發出;接收數據時,UART檢測另一根電線的信號,將串行收集在緩沖區中,CPU即可讀取UART獲得這些數據。
在S5PV210中,UART提供了4對獨立的異步串口I/O端口,有4個獨立的通道,每個通道可以工作于DMA模式或者中斷模式。其中,通道0有256byte的的發送FIFO和256byte的接收FIFO,通道1有64byte的的發送FIFO和64byte的接收FIFO,而通道2和3只有16byte的的發送FIFO和16byte的接收FIFO。
S5PV210的uart結構圖如下:
S5PV210的uart結構圖
UART使用標準的TTL/CMCOS邏輯電平來表示數據,為了增強數據抗干擾能力和提高傳輸長度,通常將TTL/CMOS邏輯電平轉換為RS-232邏輯電平,查看原理圖可知Mini210S使用的是MAX3232SOP芯片,使用的是TX0和DX0:
搜索“XuTXD0”,可知:
通過設置UART相關寄存器,我們就可以驅動UART工作,達到發送和接收字符的目的。
程序例子:(完整代碼見鏈接)
/*main.c*/
int main()
{
char c;
uart_init(); // 初始化串口
while (1)
{
c = getc (); // 接收一個字符c
putc(c+1); // 發送字符c+1
}
return 0;
}
在main函數中,先會調用uart_init()初始化UART,然后使用getc接收PC發過來的字符,再調用putc()將該字符+1回復給PC。
/*uart.c*/
void uart_init()
{
// 1 配置引腳用于RX/TX功能
GPA0CON = 0x22222222;
GPA1CON = 0x2222;
// 2 設置數據格式等
UFCON0 = 0x1; // 使能FIFO
UMCON0 = 0x0; // 無流控
ULCON0 = 0x3; // 數據位:8, 無校驗, 停止位: 1
UCON0 = 0x5; // 時鐘:PCLK,禁止中斷,使能UART發送、接收 // 3 設置波特率
UBRDIV0 = UART_UBRDIV_VAL; // 35
UDIVSLOT0 = UART_UDIVSLOT_VAL; // 0x1
}
上述代碼共有3個步驟,下面我們來一一講解每一個步驟:
第一步 配置引腳用于RX/TX功能
參考UART引腳連接圖,我們需要設置GPA0CON和GPA1CON寄存器使GPA0和GPA1引腳用于UART功能。
GPA0CON寄存器圖
GPA1CON寄存器圖
第二步 設置數據格式等
<1> ULCON0用來設置數據格式,見下圖
Word Length = 11,8bit的數據;
Number of Stop Bit = 0,1bit的停止位;
Parity Mode = 000,無校驗;
Infrared Mode =0,使用普通模式;
所以ULCON0=0x3
<2> 9UCON0是UART的配置寄存器,見下圖
Receive Mode = 01 ,使用中斷模式或者輪詢模式;
Transmit Mode = 01,使用中斷模式或者輪詢模式;
Send Break Signal = 0,普通傳輸;
Loop-back Mode = 0,不使用回環方式;
我們采用輪詢的方式接受和發送數據,不使用中斷,所以bit[6-9]均為0;
Clock Selection = 0,使用PCLK作為UART的工作時鐘;
我們不使用DMA,所以bit[16]和bit[20]均為0;
所以UCON0 = 0x5
<3> UFCON0和UMCON0
這兩個寄存器比較簡單,UFCON0用來使能FIFO,UMCON0用來設置無流控。
第三步 設置波特率
波特率即每秒傳輸的數據位數,涉及兩個寄存器:UBRDIV0和UDIVSLOT0
波特率設置相關公式:UBRDIVn + (num of 1's in UDIVSLOTn)/16 = (PCLK / (bps x 16)) ?1
其中,由Maximum Operating Frequency for Each Sub-block圖可知,UART工作于PSYS下,所以PCLK即PCLK_PSYS = 66.5MHz,我們的波特率bps設置為115200,所以
(66.5MHz/(115200 x 16)) – 1 = 35.08 = UBRDIVn + (num of 1's in UDIVSLOTn)/16,所以我們設置UBRDIV0=35,UDIVSLOT0=0x1
getc()和putc()的代碼如下:
// 接收一個字符
char getc(void)
{
while ((UFSTAT0 & 0xff) == 0); // 如果RX FIFO空,等待
return URXH0; // 取數據
}
// 發送一個字符
void putc(char c)
{
while (UFSTAT0 & (1<<24)); // 如果TX FIFO滿,等待
UTXH0 = c; // 寫數據
}
UART數據發送寄存器
UART數據接收寄存器
發送/接收狀態寄存器
通過讀UTRSTAT0 發送/接收狀態寄存器,當Receive buffer data ready= 1時說明接收到數據,讀URXH0寄存器可以得到8bit的數據;當Transmitter empty = 1時說明可以發送數據,寫8bit的數據到UTXH0。
移植printf和scanf功能
第一節 移植的途徑
對于如何移植printf和scanf,我們有許多選擇:
1) 移植linux的printk功能,版本越新越難移植,但是功能也越強大;
2) 移植uboot的printf和scanf功能,實際uboot也是從linux內核中移植而來的;
3) 完全自己編寫,但是功能比較弱;
在保證整個裸機其他代碼部分沒有任何問題,且編譯器也沒有任何問題的情況下,上述三種方法都是可行的。下面我們只是直接利用網友從linux中移植好的printk,為我們的裸機代碼增加上該部分功能。
第二節 移植步驟
第一步 解壓printf.rar到uart_stdio目錄,解壓成功后會多出include 和lib兩個目錄,其中include放的是相關頭文件,lib放的是printf和scanf相關的代碼。
第二步 修改uart_stdio目錄下的makefile,將lib目錄下的代碼編譯鏈接成lib.a,然后將lib.a編譯進bin中,具體修改見源碼。
第三步 編寫main函數進行測試。
第三節 程序相關講解
完整代碼見目錄uart_stdio,與前一章的代碼相比,BL1目錄的代碼沒有任何變化,BL2目錄的代碼多了include和lib目錄以及main.c的內容被修改了。
1. /lib/printf.c
<1> printf的定義如下:
int printf(const char *fmt, ...)
{
int i;
int len;
va_list args; // va_list 即 char *
va_start(args, fmt);
len = vsprintf(g_PCOutBuf,fmt,args);// 內部使用了va_arg()
va_end(args);
for (i = 0; i < strlen(g_PCOutBuf); i++)
{
putc(g_PCOutBuf[i]);
}
return len;
}
<2> printf函數是個變參函數,什么是變參函數:
可變參數函數的原型聲明為type VAFunction(type arg1, type arg2, … ); 參數可以分為兩部分:個數確定的固定參數和個數可變 的可選參數。函數至少需要一個固定參數,固定參數的聲明和普通函數一樣;可選參數由于個數不確定,聲明時用'...'表示。固定參數和可選參數公同構成一個函數的參數列表。
<3> printf函數涉及了3個十分重要的宏:
宏1: #define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
宏2: #define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
宏3: #define va_end(ap) (void) 0
在這些宏中,va 就是 variable argument(可變參數)的意思;
ap:是指向可變參數表的指針;
A:指可變參數表的前一個固定參數;
T:可變參數的類型。
va_list 也是一個宏,其定義為 typedef char * va_list,實質上是一char型指針。
<4> 三個宏的作用:
1) va_start 宏 作用:
根據v取得可變參數表的首指針并賦值給ap,方法:最后一個固定參數A的地址 + 第一個變參對A的偏移地址,然后賦值給 ap,這樣 ap 就是可變參數表的首地址。 舉例:
如果有變參函數的聲明是 void va_test(char a, char b,char c, …),則它的固定參數依次是 a,b,c,最后一個固定參數 為 c,因此就是 va_start(ap, c)。
2) va_arg 宏 作用:
指取出當前 ap 所指的可變參數并將 ap 指針指向下一可變參數。
3) va_end 宏 作用:
結束可變參數的獲取。va_end ( list )實際上被定義為空,沒有任何真實對應的代碼,用于代碼對稱,與 va_start 對應。
<5> 得到可變參數個數的三種辦法:
1) 函數的第一個參數,指定后續的參數個數,如 func(int num,...);
2) 根據隱含參數,判斷參數個數,如 printf 系列的,通過字符串中%的個數判斷;
3) 特殊情況下(如參數都是不大于 0xFFFF 的 int),可以一直向低處訪問堆棧,直到返回地址。
有了上述知識我們就可以看懂printf()函數的內容了,首先va_start(args, fmt);會將可變參數的首地址保存在args中,然后調用vsprintf(g_PCOutBuf,fmt,args)進行處理,在vsprintf()中,會調用va_arg()逐個的取出變參,然后進行解析。如果是普通字符則無須轉換,直接保存在g_PCOutBuf;如果是字符串,則從可變參數表中拿到指向字符串的指針,將字符串的內容拷貝到g_PCOutBuf;如果是數字,則調用number函數進行處理,并把解析的結果存放在g_PCOutBuf。所有,最后只調用putC函數把g_PCOutBuf里的字符一個個的打印出來就可以了。scanf函數的原理和printf類似,這里不再進行解釋.
2. main.c
完整代碼如下:
int main()
{
int a = 0,b = 0;
char *str = 'hello world';
uart_init();
printf('%sn',str);
while (1)
{
printf('please enter two number: rn');
scanf('%d %d', &a, &b);
printf('rn');
printf('the sum is: %drn', a+b);
}
return 0;
}
首先會打印“hello world”,然后從串口接收兩個數字,最后輸出它們的和。
上一篇:S5PV210按鍵控制LED
下一篇:S5PV210 NAND Flash
- 熱門資源推薦
- 熱門放大器推薦
設計資源 培訓 開發板 精華推薦
- AN-784,用于高性能顯示接口評估板的 10 位接口板
- ADR392B 4V 輸出微功率、低噪聲精密電壓基準的典型應用
- 使用 Analog Devices 的 LTC3602EFE 的參考設計
- CAT3224 4 安培超級電容器閃光燈 LED 驅動器的典型應用
- TLV431A 低壓精密可調并聯穩壓器的典型應用
- 5050雙色補光燈
- LTC4162EUFD-FFSM 1-9 節、3.2A 降壓型開關電池充電器的典型應用,具有 PowerPath
- 使用 Clare OptoMOS 組件的FXO/DAA 設計,使用 TS117 進行來電顯示檢測
- [CW32] 無刷電機控制器+205989A
- 可拆電池的高顯色攝影LED燈