前面移植了rt-thread nano,其实准确来说那不叫移植,那叫做部署,因为移植的工作官方已经帮我们做好了。
1、引发思考-相关资料检索
在之前的文章提到过,rt-thread已经提前在main函数以前就把跟硬件配置、系统初始化、启动调度器等相关的都做好了,所以我们后来看到的main函数非常简洁,真是让人感觉神清气爽,有继续往下写代码的欲望,如下:
main.c
int main(void) { while(1) { rt_kprintf(hello rtt_nano ); hal_gpio_togglepin(led_gpio_port, led_pin); rt_thread_mdelay(500); } }
那具体rt-thread又是如何实现在main函数执行之前就把所有初始化硬件、时钟的工作都做了呢?跟随官方文档的rt-thread代码启动流程:
跟代码,最后发现如下代码:
/* 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 }
平时工作开发中没用到这样的语法,于是只能搜索文档来看看到底是如何实现的,果然在keil帮助手册中找到了答案:
从文档中得知,keil mdk编译器用$sub$$和$super$$这两个符号来扩展了 main 函数,这使得使用$sub$$main可以在main函数执行之前就预先执行$sub$$main函数,所以在$sub$$main函数里就可以完成一些基本的硬件、时钟初始化功能,做完这些工作以后,还是得跳转到main函数去执行往后逻辑的呀,这就需要通过调用$super$$main来实现了。(注:在keil mdk编译器中是这样的情况,但在iar以及gcc环境下有差别,这里不做分析,等后面用到再说)。
既然main函数之前能这么用,是不是换个函数也能这么用呢?这引发我的好奇,于是继续查找文档,在armlink_user_guide手册中找到:
接下来开始做实验,然后我用stm32cubemx生成一个基本裸机工程,下载到小熊派上来验证是否正确。
2、小熊派上进行实践
2.1 基本功能配置
配置外部时钟、调试串口、调试接口以及led
最后生成代码。
2.2 编写代码进行验证
首先添加一个串口重定向函数,后面才能使用printf
int fputc(int ch,file *file) { return hal_uart_transmit(&huart1,(uint8_t *)&ch,1,1000); }
接下来结合文档模仿rt-thread写出以下函数:
void $sub$$main(void) { extern int main(void); extern int $super$$main(void); //初始化hal hal_init(); //初始化系统时钟 systemclock_config(); //初始化gpio mx_gpio_init(); //初始化串口 mx_usart1_uart_init(); printf(初始化已完成 ); //点灯 hal_gpio_writepin(gpioc, gpio_pin_13, gpio_pin_set); //回到真正的main函数里 $super$$main(); }
main函数如下:
int main(void) { //延时2s hal_delay(2000); printf(回到main函数中 ); while(1) { hal_gpio_togglepin(gpioc, gpio_pin_13); hal_delay(500); } }
将程序编译后下载到小熊派开发板中,然后打开串口调试助手可以看到:
由此可见,这是一个很有逼格的技能,以后可以在支持这种扩展符号的编译器下将这种技能应用起来,从而简化代码,接下来我们再往上面这个程序里添加功能:添加function函数和在它之前运行的$sub$$function,然后在main函数里调用function函数:
void $sub$$function(void) { extern void function(void); extern void $super$$function(void); printf(在function函数之前调用$sub$$function ); $super$$function(); } void function(void) { printf(执行function函数 ); } int main(void) { //延时2s hal_delay(2000); printf(回到main函数中 ); //调用function函数 function(); while(1) { hal_gpio_togglepin(gpioc, gpio_pin_13); hal_delay(500); } }
然后编译后将程序下载到小熊派开发板后,通过串口调试助手看到:
至此,我们已经完全弄明白rt-thread是如何实现在main函数执行之前就把初始化硬件、系统初始化、启动调度器等工作都完成了的基本原理。
氮化镓目前大规模商用的领域介绍
低非线性失真拓扑的7阶1-bit∑-△调制器的设计和仿真验证研究
为何如此火爆?华为p10:8G运存+麒麟960+2000万摄像和OPPO R11相比到底差哪了?
“绿色领跑者”美的:以绿色产品与绿色制造为可持续发展“破题”
led控制系统行业排名_led控制系统厂家排名
RT-Thread编程高阶用法-函数扩展之$Sub$$与$Super$$
莱尔德一应俱全的EMI产品
开拓海外生态版图!中软国际与中建八局投资发展公司签署战略合作协议
利用数字电位计实现输出电压的调整和三种方法的比较分析
委内瑞拉的第一颗国有通信卫星将停止工作
Oculus将在下一代AR和VR产品中实现眼球追踪功能
无颗粒生产,故障率低
高功率同步降压转换器可提供高达50A的电流
这几大理由表明iPhone 8仍将使用Lightning
欧司朗晶蕾LED台灯 时尚风格 精致耐用
设计中DFX的重要性表现
机器人还被解雇?科技并非一成不变
苹果13对比苹果12promax的区别
长城汽车大禹电池有效解决不同化学体系电芯发生热失控问题
新型电流驱动放大器电路图