S3C2416裸機(jī)開發(fā)系列十_串口打印調(diào)試
掃描二維碼
隨時(shí)隨地手機(jī)看文章
在嵌入式軟件開發(fā)過程中,往往都會(huì)用到串口進(jìn)行打印信息以跟蹤調(diào)試代碼的運(yùn)行。只要在代碼的關(guān)鍵位置加入打印函數(shù),即可分析代碼在這一位置的關(guān)鍵參數(shù)是否正確,運(yùn)行狀態(tài)是否無誤以及相關(guān)的出錯(cuò)信息。通過串口線連接PC端COM口與開發(fā)板的UART即可實(shí)現(xiàn)開發(fā)板與PC機(jī)的通信。在代碼調(diào)試階段,開始板的狀態(tài)信息通過串口打印顯示在PC端屏幕,可以一目了然,是一種非常重要的調(diào)試手段。筆者此處就s3c2416的串口打印使用作一個(gè)簡(jiǎn)單的介紹。
1. UART模塊實(shí)現(xiàn)1.1. UART初始化在使用任何外設(shè)前,一般都是需要對(duì)外設(shè)初始化。UART也不例外,使用前需設(shè)置波特率、通信控制、通信的處理方式(中斷還是查詢)等。設(shè)置波特率為115200,8位數(shù)據(jù),1位停止位,沒有奇偶檢驗(yàn)(uboot默認(rèn)設(shè)置),對(duì)于慢速外設(shè),高速cpu一般不應(yīng)通過查詢的方式去確定外設(shè)發(fā)送完數(shù)據(jù)或接收到數(shù)據(jù),這會(huì)讓cpu處于空等的狀態(tài),cpu效率很低,對(duì)于外設(shè)需發(fā)送或接收大量數(shù)據(jù)的情況更是如此。因此筆者此處主要講解s3c2416串口中斷方式去發(fā)送和接收數(shù)據(jù)的情況。由于s3c2416的UART發(fā)送和接收均有64字節(jié)的FIFO,通過中斷的方式可以連續(xù)裝載發(fā)送的數(shù)據(jù)到FIFO中或從FIFO中連續(xù)讀出接收的數(shù)據(jù),應(yīng)用只需通過Uart0中斷請(qǐng)求告知需發(fā)送或接收的數(shù)據(jù)長(zhǎng)度及數(shù)據(jù)保存位置,即可進(jìn)行等待掛起(如ucos中等待信號(hào)量標(biāo)志等函數(shù)OSSemPend),cpu可轉(zhuǎn)而處理其它的事情,當(dāng)Uart0中斷發(fā)送或接收完應(yīng)用請(qǐng)求的所有數(shù)據(jù),即可發(fā)送相關(guān)的信號(hào)量或標(biāo)志喚醒之前等待掛起的應(yīng)用(如ucos中發(fā)送信號(hào)量標(biāo)志等函數(shù)OSSemPost),cpu再轉(zhuǎn)而繼續(xù)處理之前的應(yīng)用。Uart0_Init()函數(shù)如下:
voidUart0_Init()
{
// 設(shè)置GPH0,GPH1為TX和RX
rGPHCON &= ~((3<<0)|(3<<2));
rGPHCON |= (2<<0)|(2<<2);
// 8位數(shù)據(jù),一個(gè)停止位,沒有奇偶檢驗(yàn)
rULCON0 = 0x3;
// UART0 FIFO使能,Tx發(fā)送空時(shí)中斷,Rx 16bytes中斷
rUFCON0 = (0<<6) | (2<<4) | 0x7;
// 流控制禁止
rUMCON0 = 0;
// 發(fā)送接收中斷使能,使用PCLK時(shí)鐘66M
rUCON0 = 0x5 | (1<<7) | (2<<10);
// 設(shè)置波特率
rUBRDIV0 = 66000000/(16*Baudrate)-1;
rUDIVSLOT0 = 0x0888;
// 注冊(cè)UART0 IRQ中斷
IRQ_Register(INT_UART0, Uart0_IRQ);
// UART0 IRQ
rINTMOD1 &= ~(1 << INT_UART0);
// 開啟RX子中斷
// 在寫入FIFO之前,TX必須關(guān)閉中斷,不然FIFO空引發(fā)中斷
rINTSUBMSK |= 0x7;
rINTSUBMSK &= ~(1 <<0);
// UART0開啟中斷
rINTMSK1 &= ~(1 < } C語言的一些庫函數(shù)功能很強(qiáng)大,沒有必要再自己去實(shí)現(xiàn)。軟件調(diào)試時(shí),可能用的最多的函數(shù)就是printf。由于printf格式化輸出是面向控制臺(tái)的,在arm目標(biāo)中是通過一種半主機(jī)的方式,把printf函數(shù)輸出請(qǐng)求傳送至運(yùn)行調(diào)試器的主機(jī)。如果不對(duì)printf進(jìn)行重定向是不能在目標(biāo)板中使用printf等函數(shù)的(fputc和 fgetc重定向到串口或目標(biāo)板屏幕)。筆者此處為了通用,不重定向改寫printf,而是使用vsnprintf函數(shù)進(jìn)行格式化輸出到字符串中,再把字符串發(fā)送到串口,實(shí)現(xiàn)與printf類似的串口打印輸出。串口格式化打印函數(shù)Uart0_Printf()如下: // 串口打印函數(shù),替換庫函數(shù)printf函數(shù)功能 // printf是向控制臺(tái)輸出信息,通過vsnprintf格式化數(shù)據(jù)輸出 // 到字符串,并通過串口發(fā)送字符串函數(shù)進(jìn)行串口打印 voidUart0_Printf(char *fmt, ...) { va_list ap; char String[1024]; va_start(ap, fmt); vsnprintf(String, sizeof(String), fmt, ap); va_end(ap); Uart0_SendString(String); } Uart0_Printf()通過vsnprintf()格式化輸出到字符串后,即可用Uart0_SendString()進(jìn)行字符串發(fā)送,Uart0_SendString()會(huì)確定出字符串的長(zhǎng)度,即確定串口發(fā)送的數(shù)據(jù)長(zhǎng)度,再調(diào)用Uart0_SendData()進(jìn)行發(fā)送一定長(zhǎng)度的字節(jié)數(shù)據(jù)到串口。 // 通過串口發(fā)送字符串 voidUart0_SendString(char *String) { unsigned int Len; char *Temp = String; if (String == 0) { return; } Len = 0; // 獲得字符串的長(zhǎng)度 while (*Temp++) { Len++; } Uart0_SendData((unsigned char *)String,Len); } Uart0_SendData()會(huì)最終向uart0請(qǐng)求發(fā)送一定長(zhǎng)度的數(shù)據(jù),以及數(shù)據(jù)所在的位置,之后會(huì)進(jìn)入等待,直到uart0中斷發(fā)送完所有請(qǐng)求的數(shù)據(jù)。由于Uart對(duì)外發(fā)送數(shù)據(jù)是很慢的,如果有操作系統(tǒng)或狀態(tài)機(jī)實(shí)現(xiàn)中,在while等待中,可改成類似信號(hào)量等待,把Uart0_SendData()函數(shù)掛起(如OSSemPend(ucos)),直到uart0中斷完成請(qǐng)求后,發(fā)送信號(hào)量或標(biāo)志再喚醒執(zhí)行(如OSSemPost(ucos))。Uart0_SendData()函數(shù)實(shí)現(xiàn)如下: // 通過串口發(fā)送任意長(zhǎng)度的數(shù)據(jù) voidUart0_SendData(unsigned char *pBuffer, unsigned int Len) { if (pBuffer == 0 || Len == 0) { return; } TxLen = Len; // 向中斷請(qǐng)求發(fā)送的數(shù)據(jù)字節(jié)長(zhǎng)度 pTxData = pBuffer; // 發(fā)送數(shù)據(jù)的位置 TxLen--; // 發(fā)送了一字節(jié),發(fā)送數(shù)據(jù)長(zhǎng)度減1 // 發(fā)送第一個(gè)字節(jié)完后會(huì)產(chǎn)生中斷,之后數(shù)據(jù)在中斷函數(shù)中連續(xù)發(fā)送 rUTXH0 = *pTxData++; rINTSUBMSK &= ~(1 <<1); // 數(shù)據(jù)寫入FIFO后開啟TX發(fā)送完中斷 while(TxLen != -1) { // 等待UART0數(shù)據(jù)發(fā)送完 // 可改成操作系統(tǒng)信號(hào)量等待函數(shù),提高cpu效率,如OSSemPend(ucos) } } Uart0_SendByte()用來向串口發(fā)送一字節(jié)的數(shù)據(jù)(字符),其向uart0請(qǐng)求一字節(jié)的數(shù)據(jù)發(fā)送。 // 通過串口發(fā)送一字節(jié)數(shù)據(jù) voidUart0_SendByte(unsigned char Byte) { TxLen = 0; // 發(fā)送1字節(jié)后,發(fā)送數(shù)據(jù)長(zhǎng)度為0 rUTXH0 = Byte; // 1字節(jié)數(shù)據(jù)裝載進(jìn)FIFO中 rINTSUBMSK &= ~(1 <<1); // 數(shù)據(jù)寫入FIFO后開啟TX中斷 while (TxLen != -1) { // 等待中斷中發(fā)送完標(biāo)志 // 等待UART0數(shù)據(jù)發(fā)送完 // 可改成操作系統(tǒng)信號(hào)量等待函數(shù),提高cpu效率,如OSSemPend(ucos) } } Uart0_ReceiveString()用來請(qǐng)求通過串口接收字符串,其會(huì)調(diào)用Uart0_ReceiveByte()來向uart0請(qǐng)求接收一個(gè)字節(jié)的數(shù)據(jù),根據(jù)接收到的字符數(shù)據(jù)判斷是否字符串結(jié)束或者回車結(jié)束。 // 通過串口接收字符串,指定緩存長(zhǎng)度為L(zhǎng)en voidUart0_ReceiveString(char *pBuffer, unsigned int Len) { char *Temp; unsigned int i; if (pBuffer == 0 || Len == 0) { return; } Temp = pBuffer; for (i=0; i *Temp = Uart0_ReceiveByte(); if (*Temp == 0 || *Temp == 'r') { break; // 字符串結(jié)束或回車則結(jié)束輸入 } Temp++; } if (i < Len) { pBuffer[i] = 0; // 字符串末尾加入結(jié)束字符0 } else { pBuffer[Len-1] = 0; } } Uart0_ReceiveByte()會(huì)通過uart0請(qǐng)求接收一個(gè)字節(jié)的數(shù)據(jù),阻塞直到接收到數(shù)據(jù)返回。 // 通過串口接收一字節(jié)數(shù)據(jù) charUart0_ReceiveByte(void) { unsigned char Value; pRxData = &Value; // 中斷中接收的1字節(jié)數(shù)據(jù)放在Value中 RxLen = 1; // 接收長(zhǎng)度為1字節(jié) while(RxLen != 0) { // 等待UART0數(shù)據(jù)接收 // 可改成操作系統(tǒng)信號(hào)量等待函數(shù),提高cpu效率,如OSSemPend(ucos) } return ((char)Value); } Uart0_IRQ()中斷處理函數(shù)用來處理應(yīng)用的發(fā)送請(qǐng)求以及接收請(qǐng)求。如果請(qǐng)求的發(fā)送數(shù)據(jù)長(zhǎng)度或請(qǐng)求的接收數(shù)據(jù)長(zhǎng)度過大,將會(huì)分多個(gè)中斷請(qǐng)求來分包發(fā)送數(shù)據(jù)或接收數(shù)據(jù),直到所有的數(shù)據(jù)請(qǐng)求均完成后,通過發(fā)送信號(hào)量(如OSSemPost(ucos))或完成標(biāo)志告知請(qǐng)求的應(yīng)用。 // 請(qǐng)求通過串口發(fā)送的字節(jié)數(shù) static volatile int TxLen = 0; // 請(qǐng)求通過串口接收的