三.結合uart實現
在之前stm32的學習中,我在串列埠輸出除錯資訊的時候,經常採用printf()函式作為串列埠輸出函式,這樣不僅方便除錯而且**易讀。
在s3c2440的學習中,對於uart同樣需要對串列埠輸出資訊進行除錯,那麼在這裡可不可以使用printf函式呢?
當然是可以的,不過相比於stm32中簡單的配置,s3c2440中對printf的使用要深入到printf函式的原理,將printf函式進行徹頭徹尾的刨析,然後結合底層的uart輸出,使用printf列印除錯資訊,其實現的前提是:
對printf函式的刨析,由my_printf.c實現,由my_printf.c呼叫uart底層的putchar函式即可實現uart的printf輸出。
在標準庫中定義了printf函式,其宣告如下:
可以看出printf的引數為:(const char *format, …)
其中:format代表固定引數,…代表可變引數,固定引數中含有格式字元
(聽起來有點懵逼,下面解讀一下引數的內容)
顧名思義,固定引數就是一串固定的字串, *format就是字串的首位址,根據首位址我們可以將字串的全部內容輸出。欸,可是固定引數中也有特殊的字元啊,那就是格式字元,格式字元用來說明可變引數的資料型別和個數,根據固定引數中的格式字元,可以將可變引數按照格式列印出來。**所以利用printf列印資訊的主要步驟就是解析固定引數以及固定引數中的格式字元,將可變引數在格式字元位置輸出,直到固定引數解析完畢!**這樣就可以將引數中像表達的內容完整地列印出來。
格式字元:
例如:
printf
("i'm %s, my id is %d ,my score is %.2f !!!\n"
,"bob",25
,98.2
);
執行結果如下:
printf是從固定引數」i』m %s, my id is %d ,my score is %.2f !!!\n」的首位址開始解析的,逐個輸出字元,直到第乙個格式字元%s,對應了後面可變引數的「bob」字串,在格式字元的位置上列印可變引數,然後繼續解析固定引數繼續列印字元,直到第二個格式字元%d,在%d的位置列印可變引數25,然後繼續解析直到第三個格式字元%.2f,根據格式字元列印出98.20,然後繼續解析,直到最後乙個轉義字元「\n」,結束了固定引數的解析,也完成了資料的列印輸出。
沒遇到%就直接輸出,遇到%根據格式符前導碼分情況處理。
我們了解了引數組成,以及printf列印輸出的流程,那麼問題來了,固定引數的首位址是直接傳進來的,那可變引數的位址怎麼得到呢???
實際上,引數都儲存在棧上的,而且固定引數和可變引數的位址是連續的,對!!!因為是連續的,所以我們就可以通過指標的移動和取值,由固定引數得到可變引數的位址,從而列印出可變引數。
下面有一段**來解釋獲取可變引數的過程:
(參考自百問科技**)
#include
struct person
;/*
*int printf(const char *format, ...);
*依據:x86平台,函式呼叫時引數傳遞是使用堆疊來實現的
*目的:將所有傳入的引數全部列印出來
*/int
push_test
(const
char
*format,..
.)intmain
(void
)
這是已知可變引數情況下的輸出,目的就是理解如何通過固定引數得到可變引數,通過棧空間的移動取值就可以!
在stdarg.h中,有解決變參問題的一些巨集定義,改進後作注釋如下:
/* 引數指標 */
typedef
char
* va_list;
/* 考慮到位址對齊,對齊大小為sizeof(int) */
/* 比如,如果sizeof(n)在1-4之間,那麼_intsizeof(n)=4;如果sizeof(n)在5-8之間,那麼 _intsizeof(n)=8。 */
/* & ~(sizeof(int) - 1)相當於將0~3掩蓋掉 sizeof(int) - 1 相當於增加0~3 如此,就只取決於sizeof(n)的大小 */
#define _intsizeof(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
/* 得到第乙個可變引數的起始位址,傳給ap */
#define va_start(ap,v) ( ap = (va_list)&v + _intsizeof(v) )
/* 移動指標到下乙個引數,取值 */
#define va_arg(ap,t) (*(t *)(ap = ap + _intsizeof(t), ap - _intsizeof(t)))
/* 結束,防止ap成為野指標 */
解決了變參問題後,剩下的就是對printf函式的封裝了,封裝引出的介面就是:outc()輸出乙個字元、outs()輸出字串,到時候只要將這倆個介面和uart的putchar()函式、puts()函式對應起來,就可以實現uart使用printf了。
printf輸出列印的核心部分:輸出固定引數及可變引數的函式
/*reference : int vprintf(const char *format, va_list ap); */
/* 輸出固定引數及可變引數的函式 */
static
intmy_vprintf
(const
char
*fmt, va_list ap)
//format : %08d, %8d,%d,%u,%x,%f,%c,%s
fmt++;if
(*fmt ==
'0')
lead=
' ';
maxwidth=0;
while
(*fmt >=
'0'&&
*fmt <=
'9')
switch
(*fmt)
}return0;
}
裡面的out_num()函式是根據格式字元輸出不同形式的數字,其函式定義如下:
static
intout_num
(long n,
int base,
char lead,
int maxwidth)
while
((m /
= base)!=0
);if( maxwidth && count < maxwidth)
if(n <0)
*--s =
'-';
return
outs
(s);
}
所以,printf函式的封裝就如下:
int
printf
(const
char
*fmt,..
.)
上面我們知道,對printf函式的刨析,由my_printf.c實現,由my_printf.c呼叫uart底層的putchar函式即可實現uart的printf輸出,呼叫介面就是outc()、outs()函式,只要如下設定就ok:
static
intoutc
(int c)
static
int outs (
const
char
*s)
對uart的配置已經在之前的部落格中有詳細介紹了。
有問題可以q我,一起學習:2723808286
s3c2410 s3c2440串列埠波特率的計算
要正確計算串列埠波特率,首先要搞清楚晶元的時鐘工作原理,這部分在s3c2410 s3c2440datasheet上寫的比較詳細,但對新手來說結合thisway同志 s3c2410完全開發流程 中的timer和clock兩個實驗,邊做實驗邊看資料,更容易理解。我這裡只根據我的失敗經驗談一下設定波特率暫...
289 S32K144串列埠的查詢接收模式
完整的s32k144的學習彙總如下 繼續s32k144的學習,還是繼續串列埠的學習。因為我覺得前面實現的這種阻塞收發模式雖然穩定,但是還有改進空間。尤其是dma的使用,在這種模式下似乎是不奏效的。其實,傳送功能倒還好一點,接收功能,我覺得還是得實現查詢的方式。檢視介面資訊,發現其實這個似乎也是已經實...
STC12C5A60S2筆記8(串列埠)
1.基本特性 1 概念 1.1 並行通訊 資料的各位同時進行傳輸,傳輸速度快 1.2 序列通訊 通過一根或兩根資料線傳輸資料,資料在訊號線上依次傳輸 序列通訊分為同步和非同步兩種 1.2.1 非同步通訊 接收器和傳送器使用各自的時鐘,非同步 每個字元要用起始位和停止位作為資料傳輸的開始和結束 在非同...