1.UART
UART(Universal Asynchronous Receiver and Transmitter)通用異步收發器(異步串行通信口),是一種通用的數據通信協議,它包括了RS232、RS499、RS423、RS422和RS485等接口標準規範和總線標準規範,即UART是異步串行通信口的總稱。
而RS232、RS499、RS423、RS422和RS485等,是對應各種異步串行通信口的接口標準和總線標準,它規定了通信口的電氣特性、傳輸速率、連接特性和接口的機械特性等內容,這些東東都是物理層的概念。
通信協議,是屬於通信網絡中的數據鏈路層的概念。
1.UART協議的工作特點
1.1數據採樣
UART協議是實現設備之間低速數據通信的標準協議。因發送時不需同時發送時鐘,故此協議爲異步。UART鏈接典型爲38400,9600波特 。
如圖1,UART字符格式爲1個起始位,5~8個數據位,1個地址位或奇偶位(可選),1個停止位。
由於接收器、發送器異步工作,無需聯接接收和發送時鐘。接收器採取對輸入數據流高度採樣方式,通常採樣爲16,並根據採樣值確定位值。按慣例,使用16個採樣值的中間三個值。
1.2 UART幀區分
UART一參數MAX-IDL,用來設置空閒字符的多少。一旦一字符在線上被接收,UART控制器開始計數接收到的空閒字符。若下一數據字符接收前,一MAX-IDL多個空閒字符被接收,則產生空閒時間,緩衝區被關閉。順次對CPU32+核心發出一中斷請求,要求從緩衝區接收數據。因此,MAX-IDL給UART模式提供一區分幀的便利方法。
空閒字符按以下公式計算其位數:1(起始)+數據長度(5,6,7,8)+1(若奇偶校驗被使用)+停止位(1)。例如,1個(起始),8位數據,無校驗,1個停止位,則空閒字符MAX-IDL爲10位。
1.3 UART地址識別
多站系統中,網絡上可能會有兩個以上的站,每個站有一特定的地址。圖2爲此種結構的兩個示例。由許多字符構成的幀可被廣播,其第一字符做爲目的地址。爲實現此功能,UART幀被擴展一位,以區別地址字符和正常數據字符。
UART可被設置爲操作於一多站環境,此環境下,支持以下兩種模式:
自動多站模式 當地址於兩個預置值之一相匹配時,UART控制器自動檢查到來地址字符,接收隨後的數據。
非自動多站模式 UART控制器接收所有數據。一地址字符總被寫入一新緩衝區。
綜上所述,UART協議採取一種通過數據採樣來確定位值的機理,具有簡單準確的定幀模式,而且廣泛用於多站系統中,具有自動多站和非自動多站兩種模式,來區分地址和數據。
1.1 RS232
COM口是PC(個人計算機)上,異步串行通信口的簡寫。由於歷史原因,IBM的PC外部接口配置爲RS232,成爲實際上的PC界默認標準。所以,現在PC機的COM口均爲RS232。
上圖最右邊的是串口接口,統稱爲RS232接口(封裝DB9)
通信過程中實際只有兩個管腳參與通信
2腳:電腦的輸入RXD
3腳:電腦的輸出TXD
5腳:接地
通過2,3腳就實現全雙工(可同時收發)的串行異步通信
1.2 UART通信協議
UART使用的是 異步,串行通信。
串行通信是指利用一條傳輸線將資料一位位地順序傳送。特點是通信線路簡單,利用簡單的線纜就可實現通信,降低成本,適用於遠距離通信,但傳輸速度慢的應用場合。
異步通信以一個字符爲傳輸單位,通信中兩個字符間的時間間隔多少是不固定的,然而在同一個字符中的兩個相鄰位間的時間間隔是固定的。
數據傳送速率用波特率來表示,即每秒鐘傳送的二進制位數。例如數據傳送速率爲120字符/秒,而每一個字符爲10位(1個起始位,7個數據位,1個校驗位,1個結束位),則其傳送的波特率爲10×120=1200字符/秒=1200波特。
數據通信格式如下圖:
其中各位的意義如下:
起始位:先發出一個邏輯”0”信號,表示傳輸字符的開始。
數據位:可以是5~8位邏輯”0”或”1”。如ASCII碼(7位),擴展BCD碼(8位)。小端傳輸
校驗位:數據位加上這一位後,使得“1”的位數應爲偶數(偶校驗)或奇數(奇校驗)
停止位:它是一個字符數據的結束標誌。可以是1位、1.5位、2位的高電平。
空閒位:處於邏輯“1”狀態,表示當前線路上沒有資料傳送。
注:異步通信是按字符傳輸的,接收設備在收到起始信號之後只要在一個字符的傳輸時間內能和發送設備保持同步就能正確接收。下一個字符起始位的到來又使同步重新校準(依靠檢測起始位來實現發送與接收方的時鐘自同步的)
3. Uboot 串口驅動
- board_init_r()
- --》devices_init()
- --》drv_system_init()
- 創建串口設備 serial,放到全局數組devlist[]中去
- struct device_t serial{
- .name
= "serial"
- .putc= serial_putc; //這些函數是芯片相關的
- .puts= serial_puts;
- .getc= serial_getc;
- .gets= serial_gets;
- }
- --》console_init_r()
- --》設置鉤子函數,這些函數是硬件相關的
- gd->jt[XF_getc]= serial_getc;
- gd->jt[XF_tstc]= serial_tstc;
- gd->jt[XF_putc]= serial_putc;
- gd->jt[XF_puts]= serial_puts;
- gd->jt[XF_printf]= serial_printf;
- --》在devlist[]中搜索,初始化標準輸入,標準輸出和錯誤輸出的device_t指針
- inputdev = search_device(DEV_FLAGS_INPUT,"serial");
- outputdev = search_device(DEV_FLAGS_OUTPUT,"serial");
- errdev = search_device(DEV_FLAGS_OUTPUT,"serial");
- --》文件描述符file_no與設備device_t綁定,初始化stdio_devices[file_no]= dev
- console_setfile (stdout, outputdev);
- console_setfile (stderr, errdev);
- console_setfile (stdin, inputdev);
Uboot中,串口打印流程
- printf-->puts-->fputs
- void fputs
(intfile,constchar*s){
- if (file< MAX_FILES)
- stdio_devices[file]->puts(s);
- //調用的函數爲serial_puts,芯片相關不分析
- }
4. Linux串口驅動
- start_kernel()
- --> console_init()
- -->cpm_uart_console_init()
- -->register_console(&cpm_scc_uart_console)
- console->setup()
- //最終調用cpm_uart_cnsole_setup()
- //初始化uart控制器,並將console 結構加入到 console_drivers 列表中去
由於我們在console_init中調用cpm_uart_console_init時,
因爲當時uart port的基址等基本參數都沒有確立, 故其中調用console->setup 以失敗返回,
即調用register_console 沒有成功註冊,
故後面在do_initcalls中調用 cpm_uart_init()進行初始化時,
其初始化中調用uart_configure_port會再次調用register_console
這次由於uart port的基址等參數得到初始化,故register_console成功
- do_initcalls()
- -->cpm_uart_init()
- -->uart_register_dirver(&cpm_reg)
- for( i=0; i< cpm_uart_nr; i++)
- con = cpm_uart_port_map[i]
- -->uart_add_one_port(&cpm_reg,&cpm_uart_ports[con].port)
- //行參(struct uart_driver *drv, struct uart_port *port)
- 注意這裏有 port->cons= drv->cons;
- -->uart_configuare_port(drv, state, port)
- --> port->ops->config_port(port,
flags)
- //這裏port的ops是 cpm_uart_pops
- //即調用cpm_uart_config_port, 初始化buf,bd,controlller,enable rx/tx
- --> cpm_uart_request_port(port)
- cpm_uart_allocbuf(pinfo,0)
- cpm_uart_initbd(pinfo)
- cpm_uart_init_scc(pinfo)
- -->uart_report_port(drv,port) //打印串口相關信息
- -->register_console(port->cons) //形參(struct
console *console)
- //加入到 console_drivers 列表中去
- -->uart_console(port)
printk在src/kernel/printk.c中實現
- int printk(constchar*fmt,...)
- static char printk_buf[1024];
- // 函數內申請了一塊靜態內存printk_buf[]作爲format緩衝區,然後把緩衝區內容放到LOG_BUF中
- // 不管console是否存在,printk都成功返回。
- --> release_console_sem();
- --> call_console_drivers();
- -->_call_console_drivers(start_print, cur_index,
msg_level);
- // 對console驅動中write的調用
- for
(con = console_drivers; con; con= con->next){
- if
((con->flags& CON_ENABLED)&&
con->write)
- con->write(con,&LOG_BUF(start),
end- start);
- }
- con->write() 實際上是 cpm_scc_uart_console.write
- 即 cpm_uart_console_write()