STM32編程中printf函數(shù)重定向背后的原理
在C語言中,printf是一個(gè)非常好用的函數(shù),尤其是在程序調(diào)試階段,我們可以通printf打印變量的值來幫助查錯(cuò)。在學(xué)習(xí)C語言的時(shí)候我們的開發(fā)環(huán)境和運(yùn)行環(huán)境都是PC機(jī),printf函數(shù)打印到PC機(jī)的屏幕上是順理成章的事。但當(dāng)我們?cè)谧銮度胧介_發(fā)時(shí),即使目標(biāo)機(jī)器上有LCD屏幕,直接使用printf函數(shù)也是觀察不到結(jié)果的。這時(shí)有經(jīng)驗(yàn)的工程師一般都會(huì)通過重定向printf函數(shù)來將printf的定向到串口,再通過USB轉(zhuǎn)TTL等工具從目標(biāo)主機(jī)的串口上讀取數(shù)據(jù)流到電腦。
本文引用地址:http://m.butianyuan.cn/article/202312/453992.htm下面先介紹在Keil中如何重定向printf的輸出到USART3。實(shí)際操作很簡單,總共分為3步:
1.在工程設(shè)置中開啟Use MicroLIB選項(xiàng):
開啟Use MicroLIB選項(xiàng)
2.在代碼中實(shí)現(xiàn)自定義的fputc函數(shù),其函數(shù)原型為 int fputc(int c, FILE *stream)。printf函數(shù)底層是通過調(diào)用fputc函數(shù)來實(shí)現(xiàn)字符輸出的,所以我們只需對(duì)fputc函數(shù)重定義即可。本例中將printf重定向到STM32的USART3,所以函數(shù)中用到的寄存器是USART3->SR:
添加自定義的fputc函數(shù)
3.最后一步,初始化USART3,使能輸出。初始化函數(shù):
USART3初始化函數(shù)
經(jīng)過以上3個(gè)步驟,STM32的USART3已經(jīng)能夠通過printf打印輸出了,效果如下:
測(cè)試代碼
printf實(shí)際輸出效果
可以看到,要想在STM32開發(fā)中實(shí)現(xiàn)printf重定向在操作上很簡單。但其背后的原理又是什么?我們知道C語言是不支持函數(shù)重定義語法的,如果我們自己在同一作用域下定義2個(gè)同名函數(shù)編譯是必然報(bào)錯(cuò)的。為什么我們可以重定義fputc函數(shù)而不會(huì)和函數(shù)庫中原有的fputc發(fā)生沖突呢?要解釋這個(gè)問題,就要引入新的概念:“強(qiáng)符號(hào)”、“弱符號(hào)”。在gcc編譯器中使用 "__attribute__((weak)) " 修飾的函數(shù)或變量屬于弱符號(hào),否則就是強(qiáng)符號(hào)。關(guān)于"強(qiáng)符號(hào)"和"弱符號(hào)",有以下規(guī)則:
規(guī)則1:不允許強(qiáng)符號(hào)被多次定義(即不同的目標(biāo)文件中不能有同名的強(qiáng)符號(hào));如果有多個(gè)強(qiáng)符號(hào)定義,則鏈接器報(bào)符號(hào)重復(fù)定義錯(cuò)誤。
規(guī)則2:如果一個(gè)符號(hào)在某個(gè)目標(biāo)文件中是強(qiáng)符號(hào),在其他文件中都是弱符號(hào),那么選擇強(qiáng)符號(hào)。
規(guī)則3:如果一個(gè)符號(hào)在所有目標(biāo)文件中都是弱符號(hào),那么選擇其中占用空間最大的一個(gè)。比如目標(biāo)文件A定義全局變量global為int型,占4個(gè)字節(jié);目標(biāo)文件B定義global為doulbe型,占8個(gè)字節(jié),那么目標(biāo)文件A和B鏈接后,符號(hào)global占8個(gè)字節(jié)(盡量不要使用多個(gè)不同類型的弱符號(hào),否則容易導(dǎo)致很難發(fā)現(xiàn)的程序錯(cuò)誤)。
在函數(shù)庫中默認(rèn)的fputc函數(shù)就是通過"__attribute__((weak)) " 修飾的弱符號(hào)函數(shù)。所以在我們重定義了fputc函數(shù)后,編譯器就選擇了我們定義的fputc函數(shù)進(jìn)行鏈接。我們定義的fputc函數(shù)將字符輸出到USART3,printf函數(shù)在底層調(diào)用了fputc函數(shù),因此printf的輸出便重定向到了USART3。如果你愿意,你也可以將printf函數(shù)重定向到SPI等外設(shè)輸出,但由于串口使用方便,我們一般選擇重定向到串口。
最后要注意一點(diǎn):強(qiáng)弱符號(hào)的鏈接特性是由鏈接器決定的,并不是C語言語法本身的特性。所以如果使用的是不同的編譯工具鏈,這個(gè)特性不一定存在。看到這里,想必大家已經(jīng)清楚STM32編程中printf重定向背后的原理了。
評(píng)論