RT-Thread編程高階用法-函數(shù)擴(kuò)展之$Sub$$與$Super$$
前面移植了RT-Thread Nano,其實(shí)準(zhǔn)確來說那不叫移植,那叫做部署
,因?yàn)橐浦驳墓ぷ鞴俜揭呀?jīng)幫我們做好了。
文章鏈接:小熊派移植RT-Thread Nano
1、引發(fā)思考-相關(guān)資料檢索
在之前的文章提到過,RT-Thread已經(jīng)提前在main函數(shù)以前就把跟硬件配置、系統(tǒng)初始化、啟動(dòng)調(diào)度器等相關(guān)的都做好了,所以我們后來看到的main函數(shù)非常簡(jiǎn)潔,真是讓人感覺神清氣爽,有繼續(xù)往下寫代碼的欲望,如下:
main.c
int main(void)
{
while(1)
{
rt_kprintf("Hello RTT_NANO\n");
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
rt_thread_mdelay(500);
}
}
那具體RT-Thread又是如何實(shí)現(xiàn)在main函數(shù)執(zhí)行之前就把所有初始化硬件、時(shí)鐘的工作都做了呢?跟隨官方文檔的RT-Thread代碼啟動(dòng)流程:
跟代碼,最后發(fā)現(xiàn)如下代碼:
/* re-define main function */
int $Sub$$main(void)
{
rtthread_startup();
return 0;
}
/* the system main thread */
void main_thread_entry(void *parameter)
{
extern int main(void);
extern int $Super$$main(void);
/* RT-Thread components initialization */
rt_components_init();
/* invoke system main function */
#if defined(__CC_ARM) || defined(__CLANG_ARM)
$Super$$main(); /* for ARMCC. */
#elif defined(__ICCARM__) || defined(__GNUC__)
main();
#endif
}
平時(shí)工作開發(fā)中沒用到這樣的語法,于是只能搜索文檔來看看到底是如何實(shí)現(xiàn)的,果然在Keil幫助手冊(cè)中找到了答案:
從文檔中得知,Keil MDK編譯器用$Sub$$
和$Super$$
這兩個(gè)符號(hào)來擴(kuò)展了 main 函數(shù),這使得使用$Sub$$main
可以在main函數(shù)執(zhí)行之前就預(yù)先執(zhí)行$Sub$$main
函數(shù),所以在$Sub$$main
函數(shù)里就可以完成一些基本的硬件、時(shí)鐘初始化功能,做完這些工作以后,還是得跳轉(zhuǎn)到main函數(shù)去執(zhí)行往后邏輯的呀,這就需要通過調(diào)用$Super$$main
來實(shí)現(xiàn)了。(注:在Keil MDK編譯器中是這樣的情況,但在IAR以及GCC環(huán)境下有差別,這里不做分析,等后面用到再說)。
既然main函數(shù)之前能這么用,是不是換個(gè)函數(shù)也能這么用呢?這引發(fā)我的好奇,于是繼續(xù)查找文檔,在armlink_user_guide手冊(cè)中找到:
接下來開始做實(shí)驗(yàn),然后我用stm32cubeMX生成一個(gè)基本裸機(jī)工程,下載到小熊派上來驗(yàn)證是否正確。
2、小熊派上進(jìn)行實(shí)踐
2.1 基本功能配置
配置外部時(shí)鐘、調(diào)試串口、調(diào)試接口以及LED
最后生成代碼。
2.2 編寫代碼進(jìn)行驗(yàn)證
首先添加一個(gè)串口重定向函數(shù),后面才能使用printf
int fputc(int ch,FILE *file)
{
return HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,1000);
}
接下來結(jié)合文檔模仿RT-Thread寫出以下函數(shù):
void $Sub$$main(void)
{
extern int main(void);
extern int $Super$$main(void);
//初始化HAL
HAL_Init();
//初始化系統(tǒng)時(shí)鐘
SystemClock_Config();
//初始化GPIO
MX_GPIO_Init();
//初始化串口
MX_USART1_UART_Init();
printf("初始化已完成\n");
//點(diǎn)燈
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
//回到真正的main函數(shù)里
$Super$$main();
}
main函數(shù)如下:
int main(void)
{
//延時(shí)2s
HAL_Delay(2000);
printf("回到main函數(shù)中\(zhòng)n");
while(1)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
HAL_Delay(500);
}
}
將程序編譯后下載到小熊派開發(fā)板中,然后打開串口調(diào)試助手可以看到:
由此可見,這是一個(gè)很有逼格的技能,以后可以在支持這種擴(kuò)展符號(hào)的編譯器下將這種技能應(yīng)用起來,從而簡(jiǎn)化代碼,接下來我們?cè)偻厦孢@個(gè)程序里添加功能:添加Function
函數(shù)和在它之前運(yùn)行的$Sub$$Function
,然后在main函數(shù)里調(diào)用Function
函數(shù):
void $Sub$$Function(void)
{
extern void Function(void);
extern void $Super$$Function(void);
printf("在Function函數(shù)之前調(diào)用$Sub$$Function\n");
$Super$$Function();
}
void Function(void)
{
printf("執(zhí)行Function函數(shù)\n");
}
int main(void)
{
//延時(shí)2s
HAL_Delay(2000);
printf("回到main函數(shù)中\(zhòng)n");
//調(diào)用Function函數(shù)
Function();
while(1)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
HAL_Delay(500);
}
}
然后編譯后將程序下載到小熊派開發(fā)板后,通過串口調(diào)試助手看到:
至此,我們已經(jīng)完全弄明白RT-Thread是如何實(shí)現(xiàn)在main函數(shù)執(zhí)行之前就把初始化硬件、系統(tǒng)初始化、啟動(dòng)調(diào)度器等工作都完成了的基本原理。
3、案例下載
公眾號(hào)后臺(tái)回復(fù):main擴(kuò)展 即可獲取本節(jié)案例的下載鏈接。
往期精彩
什么?C/C++面試過不了?因?yàn)槟氵€沒看過這個(gè)!
MCU SPI屏也能跑這么炫酷的特效?來,移植起來秀一秀
推薦三個(gè)我工作中經(jīng)常使用的驅(qū)動(dòng)大全wiki(建議收藏并轉(zhuǎn)發(fā)讓更多人知道!)
會(huì)C/C++就可以開發(fā)Linux/Android應(yīng)用程序?替代傳統(tǒng)串口屏的Yoxios了解一下!
覺得本次分享的文章對(duì)您有幫助,隨手點(diǎn)[在看]
并轉(zhuǎn)發(fā)分享,也是對(duì)我的支持。
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!