,
(國電南京自動化股份有限公司,南京210032)
圖1 多串口電路設計
串口通信具有穩(wěn)定可靠、簡單易實現(xiàn)的特點,因此在自動化領域有著廣泛的應用。以一個智能變電站的通信管理服務器為例,需要設計一路調(diào)試口和多路規(guī)約通信口,同時還要支持多個串口外設,由于CPU本身提供的串口資源有限,往往不能滿足上述的設計需求,而通過在FPGA上設計出支持串口協(xié)議的IP核則可以很容易地對串口進行擴展,解決CPU串口資源不足的問題。
本方案用AM3352作為主控CPU,AM3352是一款基于ARM Cortext-A8內(nèi)核的微處理器,主頻最高支持720 MHz,運算能力高達1 600 DMIPS,具有豐富的外圍接口,帶6路異步串口,能搭配DDR3,支持大容量的eMMC和NAND FLASH。圖1 給出了基于AM3352的多串口電路設計示意圖,AM3352提供了6路本機串口UART0~UART5,另2路串口通過FPGA進行擴展。為FPGA設計了支持串口協(xié)議的IP核,對IP核進行實例化,實現(xiàn)了擴展串口UART7~UART8。AM3352 利用GPMC接口連接FPGA,實現(xiàn)對擴展串口的控制,同時將中斷請求資源GPIO3_16、GPIO3_17分別分配給UART7和UART8,由FPGA通過中斷的方式通知CPU讀取接收緩存區(qū)存放的數(shù)據(jù)。
在串口IP核里按圖2設計了寄存器組和存儲器,存儲器Tx_Mem和Rx_Mem用于緩存發(fā)送數(shù)據(jù)和接收數(shù)據(jù),寄存器Tx_Len存儲有效發(fā)送數(shù)據(jù)長度用于CPU通知FPGA要發(fā)送的字節(jié)數(shù),寄存器Rx_Len存儲有效接收數(shù)據(jù)長度,用于FPGA告訴CPU放在接收緩存區(qū)Rx_Mem可被讀取的字節(jié)數(shù),寄存器BUGR用于擴展串口通信速率的設置,寄存器IER用于中斷使能的配置,寄存器IFR標識中斷源,中斷響應后應由中斷服務程序?qū)ζ湎鄳恢?予以清除中斷。
圖2 擴展串口地址空間分配
QNX是類UNIX操作系統(tǒng),由加拿大QSSL公司(QNX Software System Ltd.)開發(fā)的分布式實時操作系統(tǒng)。它采用獨特的微內(nèi)核結構,由內(nèi)核實現(xiàn)進程通信、進程調(diào)度、中斷處理和底層網(wǎng)絡通信,因此內(nèi)核非常小,運行速度極快。將驅(qū)動程序、應用程序、網(wǎng)絡協(xié)議和文件系統(tǒng)的運行地址空間同內(nèi)核的運行地址空間進行分離,使得應用程序無法直接訪問內(nèi)核空間,這種封閉的微內(nèi)核結構保證了任何外部模塊的故障都不會影響內(nèi)核的運行,從而使系統(tǒng)的穩(wěn)定性大大提高。同時,QNX支持裁剪和擴展,能針對用戶需求定制不同的功能模塊,實現(xiàn)靈活的嵌入式開發(fā)。
由于要通過GPMC模塊控制FPGA來實現(xiàn)對擴展串口的訪問控制,因此需要在驅(qū)動工作前完成對GPMC接口的配置,配置代碼放在startup里,由startup設置GPMC接口的位寬、控制時鐘、訪問時序等,將GPMC_AD0~GPMC_AD15設置成地址數(shù)據(jù)復用的工作模式,并把起始地址0x100 0000的16 MB地址空間分配給擴展串口設備。具體配置代碼如下:
out32(GPMC_CONFIG1_0, 0x2a001200);
/*NOR設備,GPMC_CLK配置成50 MHz*/
out32(GPMC_CONFIG2_0, 0x000a0a00);/*CS時序控制*/
out32(GPMC_CONFIG3_0, 0x00020201);/*ADV時序控制*/
out32(GPMC_CONFIG4_0, 0x04020602);/*OE WE時序控制*/
out32(GPMC_CONFIG5_0, 0x00040a0a);/*讀寫訪問時序*/
out32(GPMC_CONFIG6_0, 0x030201c0);
out32(GPMC_CONFIG7_0, 0x00000F41);
/*16M地址空間,CS有效電平,基地址0x1000000*/
由于要用GPIO3_16、GPIO3_17產(chǎn)生中斷請求信號,接下來需要對中斷資源進行配置。AM3352提供了128個中斷信號,而GPIO3位于第62個中斷源,所以要對中斷信號62進行級聯(lián),將級聯(lián)后的起始中斷號設置成130,從而可知GPIO3_16對應的中斷號是146,GPIO3_17對應的中斷號是147。此外,還需要向系統(tǒng)提供中斷相關的callout函數(shù),在此定義了id callout函數(shù)interrupt_id_am335x_gpio用于系統(tǒng)獲取中斷號、屏蔽和并清除中斷,定義了eio callout函數(shù)interrupt_eoi_am335x_gpio用于中斷服務程序結束后重新開放中斷,定義了mask callout函數(shù)interrupt_mask_am335x_gpio用于屏蔽中斷,定義了unmask callout函數(shù)interrupt_unmask_am335x_gpio用于開放中斷。具體的中斷信息結構體按下面代碼定義:
const static struct startup_intrinfo intrs[] =
{
{
_NTO_INTR_CLASS_EXTERNAL, /* vector base*/
128,
/* number of vectors*/
_NTO_INTR_SPARE, /* cascade vector*/
0, /* CPU vector base*/
0, /* CPU vector stride*/
0, /* flags*/
{INTR_GENFLAG_LOAD_SYSPAGE, 0, &interrupt_id_am335x},
{INTR_GENFLAG_LOAD_SYSPAGE | INTR_GENFLAG_LOAD_INTRMASK, 0, &interrupt_eoi_am335x},
&interrupt_mask_am335x, /*maskcallout*/
&interrupt_unmask_am335x, /*unmask callout*/
0, /*config callout*/
&am335x_intc_base
},
/* GPIO3 interrupt*/
{
130, /*vector base*/
32, /* number of vectors*/
62, /* cascade vector*/
0, /*CPU vector base*/
0, /* CPU vector stride*/
0, /* flags*/
{ 0, 0, &interrupt_id_am335x_gpio},
{ INTR_GENFLAG_LOAD_INTRMASK, 0, &interrupt_eoi_am335x_gpio},
&interrupt_mask_am335x_gpio, /* mask callout*/
&interrupt_unmask_am335x_gpio, /* unmask callout*/
0,
/* config callout*/
&am335x_gpio3_base
},
};
中斷請求到來后,內(nèi)核首先調(diào)用第一級id callout函數(shù), 返回第一級ID,因為有級聯(lián),內(nèi)核會自動調(diào)用級聯(lián)id callout函數(shù)返回級聯(lián)ID,緊接著調(diào)用通過InterruptAttach綁定在級聯(lián)ID上的中斷服務函數(shù)interrupt handler處理用戶任務,最后調(diào)用eoi callout重新開放中斷。如果是通過調(diào)用InterruptAttachEvent進行中斷綁定,內(nèi)核會先調(diào)用mask callout屏蔽中斷, 此時必須由驅(qū)動程序在中斷響應函數(shù)里調(diào)用InterruptUnmask即unmask callout,重新使能中斷。
串口工作過程見圖3。串口屬于字符設備,由io-char進行統(tǒng)一管理。用戶進程通過write將數(shù)據(jù)寫到數(shù)據(jù)發(fā)送隊列obuf,驅(qū)動調(diào)用函數(shù)tto將obuf隊列里的數(shù)據(jù)發(fā)送給串口,從而實現(xiàn)了數(shù)據(jù)的發(fā)送。接收過程則由中斷發(fā)起,CPU響應中斷,最終進入串口驅(qū)動提供的中斷服務程序讀取串口數(shù)據(jù),并調(diào)用函數(shù)tti將讀取的數(shù)據(jù)寫到數(shù)據(jù)接收隊列ibuf,用戶進程便可通過read讀取ibuf隊列中的數(shù)據(jù)。
圖3 串口驅(qū)動工作原理
初始化程序?qū)⒋谠O置成RAW工作模式,然后解析命令行參數(shù),從命令行參數(shù)獲取波特率、時鐘等參數(shù),接著創(chuàng)建字符串設備并注冊設備名,使設備在命名空間中產(chǎn)生相應的名稱,隨后對串口進行配置、設置通信速率, 調(diào)用InterruptAttach綁定中斷服務函數(shù)并開啟中斷,之后系統(tǒng)便進入消息循環(huán)處理,等待并響應中斷請求。為了區(qū)分串口,在設備描述結構體里定義了dev_id成員用于記錄設備ID,由設備創(chuàng)建函數(shù)create_device對ID賦值。驅(qū)動初始化流程見圖4。
圖4 初始化流程圖
接收過程由中斷服務程序?qū)崿F(xiàn)。對于擴展串口,F(xiàn)PGA將接收到的數(shù)據(jù)緩存到Rx_Mem,將有效數(shù)據(jù)長度寫到Rx_Len,同時通過GPIO3_16或GPIO3_17向CPU發(fā)出中斷請求信號,CPU響應中斷請求并進入中斷服務程序,在中斷服務程序里調(diào)用系統(tǒng)函數(shù)tti將接收緩存區(qū)Rx_Mem中的有效數(shù)據(jù)放到數(shù)據(jù)接收隊列ibuf,隨后應用程序便可通過read讀取ibuf中的數(shù)據(jù)。
對于本機串口,數(shù)據(jù)準備好后由串口控制器產(chǎn)生中斷請求,CPU響應中斷請求并進入中斷服務程序,由中斷服務程序調(diào)用tti函數(shù),讀取接收保持寄存器RHR里的數(shù)據(jù)并送到接收隊列ibuf供應用程序讀取。
發(fā)送數(shù)據(jù)由系統(tǒng)調(diào)用tto函數(shù)實現(xiàn)。tto函數(shù)通過調(diào)用系統(tǒng)函數(shù)tto_getchar從obuf隊列獲取待發(fā)送的數(shù)據(jù),對于本機串口需將待發(fā)送數(shù)據(jù)寫到發(fā)送保持寄存器THR啟動數(shù)據(jù)發(fā)送,對于擴展串口需要將待發(fā)送數(shù)據(jù)寫到發(fā)送緩存區(qū)Tx_Mem,然后將有效數(shù)據(jù)長度寫到Tx_Len寄存器啟動FPGA發(fā)送數(shù)據(jù)。
tto函數(shù)除了實現(xiàn)發(fā)送數(shù)據(jù)外,還實現(xiàn)了串口參數(shù)設置命令STTY、流控制命令CTRL和設備狀態(tài)檢測命令LINESTATUS。很多設備都帶有打印機,為了避免打印機出現(xiàn)打印亂碼,需要在驅(qū)動里實現(xiàn)LINESTATUS命令對打印機端口CTS進行監(jiān)視,這樣打印服務程序就可以通過調(diào)用devctl(fd,DCMD_CHR_LINESTATUS,&dat,4,NULL)檢測打印機是否準備就緒,待打印機準備好后方能向打印口寫數(shù)據(jù)。
驅(qū)動程序編寫好后還需要配置腳本文件,通過腳本啟動串口設備。腳本配置如下:
devc-seromap-F-S-u1-b115200-c48000000 0x44E09000^2,72 0x48022000^2,73 0x48024000^2,74 0x481A6000^2,44 0x481A8000^2,45 0x481AA000^2,46 0x1000000^2,146 0x1000300^2,147
運行腳本后便在/dev下生成了對應于串口UART0~UART7的設備文件ser1~ser8。為了方便系統(tǒng)調(diào)試,往往將UART0配置成console,因此需在上面腳本后添加“reopen/dev/ser1”。
許學芳、羅銘(工程師),主要研究方向為電力系統(tǒng)主設備繼電保護。