UART採非同步的全雙工傳輸方式,傳輸串列資料,非同步在傳送資料時需插入額外資訊,表示資料起始、結束,非同步的好處是設定時間短、硬體成本低、機器時脈不同也能傳資料,缺點是單次傳輸的資料量較少。
Async/Sync
最精簡的連線方式,僅需三條線,TxD、RxD、Gnd,如下圖UART使用標準的TTL/CMOS邏輯電壓(0~5v、0~3.3v、0~2.5v或0~1.8v)來表示資料,高電壓表示1,低電壓表示0。為了增強資料的抗干擾能力、提高傳輸長度、與電腦溝通,電腦一般使用RS232介面,通常將TTL/CMOD邏輯電壓透過轉壓晶片(ex. MAX232)轉換為RS-232邏輯電壓,3~12v表示0,-3~-12v表示1。
UART資料的傳送與接收符合RS-232/RS-485標準,TxD、RxD資料線以位元為最小單位傳輸資料,而幀(frame)由具有完整意義的、不可分割的若干位組成,它包含開始位元、資料位元、校驗位元(需要的話)和停止位元。發送資料之前,UART之間要約定好資料的傳輸速率(即每位所佔據的時間,其倒數稱為串列傳輸速率)、資料的傳輸格式(即有多少個資料位元、是否使用校驗位、是奇數同位檢查還是偶校驗、有多少個停止位)。
S3C2440 Feature:
- RxD0, TxD0, RxD1, TxD1, RxD2, and TxD2 with DMA-based or interrupt-based operation
- UART Ch 0, 1, and 2 with IrDA 1.0 & 64-byte FIFO
- UART Ch 0 and 1 with nRTS0, nCTS0, nRTS1, and nCTS1
- Supports handshake transmit/receive
S3C2440 UART有UART有三個獨立的通道,UART0、UART1、UART2,每個通道都可以工作於中斷模式和DMA模式,既UART可發出中斷請求或者DMA請求,以便在CPU與UART間傳輸數據。
UART FIFO的深度為64,發送數據時,CPU先將數據寫入發送FIFO中,然後UART會自動將FIFO中的資料複製到“發送移位器”(Transmit Shifter)中,發送移位元器將資料一位元一位元地發送到TxDn資料線上(根據設定的格式,插入開始位、較驗位和停止位)。
接收資料時,“接收移位元器”(Receive Shifter)將RxDn資料線上的資料一位元一位元接收進來,然後複製到接收FIFO中,CPU即可從中讀取資料。
UART BLOCK DIAGRAM |
- 將所涉及到的UART通道管腳設為UART功能
- UBRDIVn暫存器:設置串列傳輸速率(baud rate)
- ULCONn暫存器:設置傳輸格式
- UCONn暫存器:選擇UART時鐘源、設置UART中斷方式等
- UFCONn暫存器和UFSTATn暫存器:用來設置是否使用FIFO,設置各個FIFO的觸發閥值
- UMCONn暫存器和UMSTATn暫存器:用於流量控制
- UTRSTATn暫存器:用來表明數據是否已經發送/接收完畢,實驗未使用
- UERSTATn暫存器:用來表示個整錯誤是否發生,實驗未使用
- UTXHn暫存器:CPU將數據寫入這個暫存器,UAR即會將它保存保緩衝區,並自動發送,實作上將預發送資料寫入這暫存器,即可發送資料
- URXHn暫存器:當UART接收到數據時,CPU讀取這個暫存器,即可獲取資料,實作上直接讀取此暫存器,即可獲得資料
Interrupt/DMA Request Generation :
- When the receiver transfers the data of the receive shifter to the receive FIFO register in FIFO mode and the number of received data reaches Rx FIFO Trigger Level, Rx interrupt is generated. If the Receive mode is in control register (UCONn) and is selected as 1 (Interrupt request or polling mode). In the Non-FIFO mode, transferring the data of the receive shifter to receive holding register will cause Rx interrupt under the Interrupt request and polling mode.
- When the transmitter transfers data from its transmit FIFO register to its transmit shifter and the number of data left in transmit FIFO reaches Tx FIFO Trigger Level, Tx interrupt is generated, if Transmit mode in control register is selected as Interrupt request or polling mode. In the Non-FIFO mode, transferring data from the transmit holding register to the transmit shifter will cause Tx interrupt under the Interrupt request and polling mode.
- If the Receive mode and Transmit mode in control register are selected as the DMAn request mode then DMAn request occurs instead of Rx or Tx interrupt in the situation mentioned above.
實驗:透過UART接口,在串口上輸入一個字元,開發版收到後,將它的ASCII值加1後,從串口輸出。
serial.c
#include "s3c24xx.h"
#include "serial.h"
#define TXD0READY (1<<2)
#define RXD0READY (1)
#define PCLK 50000000 // init.c中的clock_init函數設置PCLK為50MHz
#define UART_CLK PCLK // UART0的時鐘源設為PCLK
#define UART_BAUD_RATE 115200 // 串列傳輸速率
#define UART_BRD ((UART_CLK / (UART_BAUD_RATE * 16)) - 1)
/*
* 初始化UART0
*
115200,8N1,無流控
*/
void uart0_init(void)
{
GPHCON |= 0xa0; // GPH2,GPH3用作TXD0,RXD0
GPHUP = 0x0c; // GPH2,GPH3內部上拉
ULCON0 = 0x03;
// 8N1(8個資料位元,無較驗,1個停止位)
UCON0 = 0x05; // 查詢方式,UART時鐘源為PCLK
UFCON0 = 0x00;
// 不使用FIFO
UMCON0 = 0x00; // 不使用流控
UBRDIV0 = UART_BRD; // 串列傳輸速率為115200
}
/*
* 發送一個字元
*/
unsigned char putc(unsigned char c)
{
// wait, until data already deliver out
while(!(UTRSTAT0 & TXD0READY))
// write data to UTXH0, UART send out automatically
UTXH0 = c;
}
/*
* 接收字元
*/
unsigned char getc(unsigned char c)
{
// wait, until receive valid data in the receive buffer register
while(!(UTRSTAT0 & RXD0READY));
// get data from URXH0 register
return URXH0;
}
/*
* 判斷一個字元是否數位
*/
void int isDigit(unsigned char c)
{
if(c >= '0' && c<= '9') return 1;
else
return 0;
}
/*
* 判斷一個字元是否英文字母
*/
int isLetter(unsigned char c)
{
if(c >= 'a' && c <= 'z') return 1;
else if (c >= 'A' && c <= 'Z') return 1;
else
return 0;
}
============================================
/*
* init.c: 进行一些初始化
*/
#include "s3c24xx.h"
void disable_watch_dog(void);
void clock_init(void);
void memsetup(void);
void copy_steppingstone_to_sdram(void);
/*
* 关闭WATCHDOG,否则CPU会不断重启
*/
void disable_watch_dog(void)
{
WTCON = 0; // 关闭WATCHDOG很简单,往这个寄存器写0即可
}
#define S3C2410_MPLL_200MHZ ((0x5c<<12)|(0x04<<4)|(0x00))
#define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01))
/*
* 对于MPLLCON寄存器,[19:12]为MDIV,[9:4]为PDIV,[1:0]为SDIV
* 有如下计算公式:
* S3C2410: MPLL(FCLK) = (m * Fin)/(p * 2^s)
* S3C2440: MPLL(FCLK) = (2 * m * Fin)/(p * 2^s)
* 其中: m = MDIV + 8, p = PDIV + 2, s = SDIV
* 对于本开发板,Fin = 12MHz
* 设置CLKDIVN,令分频比为:FCLK:HCLK:PCLK=1:4:8,
* FCLK=400MHz,HCLK=100MHz,PCLK=50MHz
*/
void clock_init(void)
{
LOCKTIME = 0xffffffff; // default value
CLKDIVN = 0X05 // FCLK:HCLK:FCLK = 1:4:8,CAMDIVN初始值为0,不用再
// 对其设置
/* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
__asm__(
"mrc p15, 0, r1, c1, c0, 0\n" /* 读出控制寄存器 */
"orr r1, r1, #0xc0000000\n" /* 设置为“asynchronous bus mode” */
"mcr p15, 0, r1, c1, c0, 0\n" /* 写入控制寄存器 */
);
/* 判断是S3C2410还是S3C2440 */
if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))
{
/* 现在FCLK=200MHz,HCLK=100MHz,PCLK=50MHz */
MPLLCON = S3C2410_MPLL_200MHZ;
}
else
{
/* 现在FCLK=400MHz,HCLK=100MHz,PCLK=50MHz */
MPLLCON = S3C2440_MPLL_400MHZ;
}
}
/*
* 设置存储控制器以使用SDRAM
*/
void memsetup(void)
{
volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
/* 这个函数之所以这样赋值,而不是像前面的实验(比如mmu实验)那样将配置值
* 写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到
* SDRAM之前就可以在steppingstone中运行
*/
/* 存储控制器13个寄存器的值 */
p[0] = 0x22011110; //BWSCON
p[1] = 0x00000700; //BANKCON0
p[2] = 0x00000700; //BANKCON1
p[3] = 0x00000700; //BANKCON2
p[4] = 0x00000700; //BANKCON3
p[5] = 0x00000700; //BANKCON4
p[6] = 0x00000700; //BANKCON5
p[7] = 0x00018005; //BANKCON6
p[8] = 0x00018005; //BANKCON7
/* REFRESH,
* HCLK=12MHz: 0x008C07A3,
* HCLK=100MHz: 0x008C04F4,
* Refresh Counter = 2^11 + 1 – SDRAM时钟频率(MHz)* SDRAM刷新周期(uS)
* Refresh Count = 211 + 1 - 100x7.8125 = 1268
* Refresh register = 0x8C0000[23-11] + 1268[10-0] = 0x8C04F4
*/
p[9] = 0x008C04F4;
p[10] = 0x000000B1; //BANKSIZE
p[11] = 0x00000030; //MRSRB6
p[12] = 0x00000030; //MRSRB7
}
void copy_steppingstone_to_sdram(void)
{
unsigned int *pdwSrc = (unsigned int *)0;
unsigned int *pdwDest = (unsigned int *)0x30000000;
while (pdwSrc < (unsigned int *)4096)
{
*pdwDest = *pdwSrc;
pdwDest++;
pdwSrc++;
}
}
============================================
main.c
#include "serial.h"
int main()
{
unsigned char c;
uart0_init();
while(1)
{
// 從串口接收資料後,判斷其是否數位或子母,若是則加1後輸出
c = getc();
if(isDigit(c) || isLetter(c)) putc(c+1);
}
}
====================================================
@***********************************************************
@ File:head.S
@ 功能:設置SDRAM,將程式複製到SDRAM,然後跳到SDRAM繼續執行
@***********************************************************
.extern main
.text
.global _start
_start:
Reset:
ldr sp, =4096
bl disable_watch_dog
@ bl是位置無關碼,相當於:PCnew = PC + 偏移
@ PCnew = (4+8) + 0x28 = 0x34
@ ldr pc, =disable_watch_dog
bl clock_init
bl memsetup
bl copy_steppingstone_to_sdram
ldr pc, =on_sdram
on_sdram:
ldr sp, =0x3400000
ldr lr, =halt_loop
ldr pc, =on_sdram
halt_loop:
b halt_loop
====================================================
參考資料:/*
* 發送一個字元
*/
unsigned char putc(unsigned char c)
{
// wait, until data already deliver out
while(!(UTRSTAT0 & TXD0READY))
// write data to UTXH0, UART send out automatically
UTXH0 = c;
}
/*
* 接收字元
*/
unsigned char getc(unsigned char c)
{
// wait, until receive valid data in the receive buffer register
while(!(UTRSTAT0 & RXD0READY));
// get data from URXH0 register
return URXH0;
}
/*
* 判斷一個字元是否數位
*/
void int isDigit(unsigned char c)
{
if(c >= '0' && c<= '9') return 1;
else
return 0;
}
/*
* 判斷一個字元是否英文字母
*/
int isLetter(unsigned char c)
{
if(c >= 'a' && c <= 'z') return 1;
else if (c >= 'A' && c <= 'Z') return 1;
else
return 0;
}
============================================
/*
* init.c: 进行一些初始化
*/
#include "s3c24xx.h"
void disable_watch_dog(void);
void clock_init(void);
void memsetup(void);
void copy_steppingstone_to_sdram(void);
/*
* 关闭WATCHDOG,否则CPU会不断重启
*/
void disable_watch_dog(void)
{
WTCON = 0; // 关闭WATCHDOG很简单,往这个寄存器写0即可
}
#define S3C2410_MPLL_200MHZ ((0x5c<<12)|(0x04<<4)|(0x00))
#define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01))
/*
* 对于MPLLCON寄存器,[19:12]为MDIV,[9:4]为PDIV,[1:0]为SDIV
* 有如下计算公式:
* S3C2410: MPLL(FCLK) = (m * Fin)/(p * 2^s)
* S3C2440: MPLL(FCLK) = (2 * m * Fin)/(p * 2^s)
* 其中: m = MDIV + 8, p = PDIV + 2, s = SDIV
* 对于本开发板,Fin = 12MHz
* 设置CLKDIVN,令分频比为:FCLK:HCLK:PCLK=1:4:8,
* FCLK=400MHz,HCLK=100MHz,PCLK=50MHz
*/
void clock_init(void)
{
LOCKTIME = 0xffffffff; // default value
CLKDIVN = 0X05 // FCLK:HCLK:FCLK = 1:4:8,CAMDIVN初始值为0,不用再
// 对其设置
/* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
__asm__(
"mrc p15, 0, r1, c1, c0, 0\n" /* 读出控制寄存器 */
"orr r1, r1, #0xc0000000\n" /* 设置为“asynchronous bus mode” */
"mcr p15, 0, r1, c1, c0, 0\n" /* 写入控制寄存器 */
);
/* 判断是S3C2410还是S3C2440 */
if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))
{
/* 现在FCLK=200MHz,HCLK=100MHz,PCLK=50MHz */
MPLLCON = S3C2410_MPLL_200MHZ;
}
else
{
/* 现在FCLK=400MHz,HCLK=100MHz,PCLK=50MHz */
MPLLCON = S3C2440_MPLL_400MHZ;
}
}
/*
* 设置存储控制器以使用SDRAM
*/
void memsetup(void)
{
volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
/* 这个函数之所以这样赋值,而不是像前面的实验(比如mmu实验)那样将配置值
* 写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到
* SDRAM之前就可以在steppingstone中运行
*/
/* 存储控制器13个寄存器的值 */
p[0] = 0x22011110; //BWSCON
p[1] = 0x00000700; //BANKCON0
p[2] = 0x00000700; //BANKCON1
p[3] = 0x00000700; //BANKCON2
p[4] = 0x00000700; //BANKCON3
p[5] = 0x00000700; //BANKCON4
p[6] = 0x00000700; //BANKCON5
p[7] = 0x00018005; //BANKCON6
p[8] = 0x00018005; //BANKCON7
/* REFRESH,
* HCLK=12MHz: 0x008C07A3,
* HCLK=100MHz: 0x008C04F4,
* Refresh Counter = 2^11 + 1 – SDRAM时钟频率(MHz)* SDRAM刷新周期(uS)
* Refresh Count = 211 + 1 - 100x7.8125 = 1268
* Refresh register = 0x8C0000[23-11] + 1268[10-0] = 0x8C04F4
*/
p[9] = 0x008C04F4;
p[10] = 0x000000B1; //BANKSIZE
p[11] = 0x00000030; //MRSRB6
p[12] = 0x00000030; //MRSRB7
}
void copy_steppingstone_to_sdram(void)
{
unsigned int *pdwSrc = (unsigned int *)0;
unsigned int *pdwDest = (unsigned int *)0x30000000;
while (pdwSrc < (unsigned int *)4096)
{
*pdwDest = *pdwSrc;
pdwDest++;
pdwSrc++;
}
}
============================================
main.c
#include "serial.h"
int main()
{
unsigned char c;
uart0_init();
while(1)
{
// 從串口接收資料後,判斷其是否數位或子母,若是則加1後輸出
c = getc();
if(isDigit(c) || isLetter(c)) putc(c+1);
}
}
====================================================
@***********************************************************
@ File:head.S
@ 功能:設置SDRAM,將程式複製到SDRAM,然後跳到SDRAM繼續執行
@***********************************************************
.extern main
.text
.global _start
_start:
Reset:
ldr sp, =4096
bl disable_watch_dog
@ bl是位置無關碼,相當於:PCnew = PC + 偏移
@ PCnew = (4+8) + 0x28 = 0x34
@ ldr pc, =disable_watch_dog
bl clock_init
bl memsetup
bl copy_steppingstone_to_sdram
ldr pc, =on_sdram
on_sdram:
ldr sp, =0x3400000
ldr lr, =halt_loop
ldr pc, =on_sdram
halt_loop:
b halt_loop
PIC |
====================================================
- 嵌入式Linux應用開發完全手冊
- S3C2440A 手冊
- http://wiki.csie.ncku.edu.tw/embedded/USART
- http://wpp9977777.blog.163.com/blog/static/4625100720138411281980/
- https://zh.wikipedia.org/wiki/UART
- https://zh.wikipedia.org/wiki/RS-232
- http://blog.sina.com.cn/s/blog_8bbf650701013lpy.html
- http://liu1227787871.blog.163.com/blog/static/2053631972012594354464/
- http://m.blog.csdn.net/blog/chunlovenan/25073259
- http://blog.chinaunix.net/uid-28576788-id-4189598.html
- http://www.embedu.org/column/Column266.htm
- http://blog.csdn.net/ustc_dylan/article/details/6965330
- http://blog.sina.com.cn/s/blog_adc0f78a01016vln.html
- http://www.crifan.com/files/doc/docbook/uboot_starts_analysis/release/htmls/why_adr_not_move.html
補充資料:
No comments:
Post a Comment