上一篇博客简单说了下如何使用Keil创建STM32F103的工程,并且完成了LED点亮,及让LED等闪烁的功能,那是诸多同学学习单片机的起手式。本篇博客继续上一篇博客的内容,依旧是点亮LED,不同的是,这次点亮LED等,是在RT-Thread操作系统中进行的。 创建工程创建一个Keil工程,芯片依旧选择STM32F103C8T6,然后在Manage Run-Time Environment对话框中选择需要用的的软件组件,与上文不同的是,我们需要把RTT一起勾上,这里的RTOS入口,可以通过RTT的官网搜索pack进行安装。如下图: 上图中,红线框中即为RTT操作系统的组件,分别为设备驱动,系统内核以及shell。蓝线框中为Keil的RTX操作系统。我们现在要用的是RTT,所以勾选RTT的组件即可,其中Kernel为必选项,device drivers依赖kernel,shell又依赖device drivers。 shell也提一下,shell强翻成中文就是命令行外壳,如同linux操作系统一样,RTT也提供了一套共用户在命令行操作的操作接口。RTT提供的这套接口叫做finsh,主要用于调试、查看系统信息。finsh支持两种模式:1. C语言解释器模式, 为行文方便称之为c-style;2. 传统命令行模式,此模式又称为msh(module shell)。 创建工程后,相对上一篇博客创建的工程,项目中会多出了RTT,如下图。至于各个文件及其作用,后续使用的时候再逐步理解。我们当前最需要关注的是board.c和rtthread.h两个文件。从图中可以看出,只有这两个文件上没有标注钥匙,有钥匙标注的是不允许更改,也就是我们能更改就是这两个文件。后面再分析这两个文件。且走下一步。 编写点灯程序创建好工程后,开始编写点灯程序了,与上篇博客一样,直接贴上代码: #include 'rtthread.h'#include 'stm32f10x.h'#include 'stm32f10x_gpio.h'int main(){ GPIO_InitTypeDef gpioInit; //打开GPIOB的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //LED上拉连接GPIOB 12引脚,所以设置如下,推挽输出,Pin12,2MHz输出速度 gpioInit.GPIO_Mode=GPIO_Mode_Out_PP; gpioInit.GPIO_Pin=GPIO_Pin_12; gpioInit.GPIO_Speed=GPIO_Speed_2MHz; GPIO_Init(GPIOB,&gpioInit); while(1){ //点亮LED GPIO_ResetBits(GPIOB,GPIO_Pin_12); //延时0.5s rt_thread_delay(RT_TICK_PER_SECOND/2); //关闭LED GPIO_SetBits(GPIOB,GPIO_Pin_12); //延时0.5s rt_thread_delay(RT_TICK_PER_SECOND/2); }}
这样编写程序后,编译通过,烧写后却发现LED根本无法按照预期进行工作,这是因为我们还缺少工作没有做。
再次编译,烧写程序,LED开始闪烁。 RTT第一次分析board.c修改后,程序就正常工作了。可是为什么呢?根据经验来说,C程序不是从main开始的么,board中的程序又是何时执行的呢?在main中我们有死循环,如果是从main开始执行的,那么board.c的函数就绝对不可能执行了。 为什么不是从main开始执行的Ctrl+F搜索 #if defined (__CC_ARM)extern int $Super$$main(void);/* re-define main function */int $Sub$$main(void){ rt_hw_interrupt_disable(); rtthread_startup(); return 0;}#elif defined(__ICCARM__)extern int main(void);/* __low_level_init will auto called by IAR cstartup */extern void __iar_data_init3( void );int __low_level_init(void){ // call IAR table copy function. __iar_data_init3(); rt_hw_interrupt_disable(); rtthread_startup(); return 0;}#elif defined(__GNUC__)extern int main(void);/* Add -eentry to arm-none-eabi-gcc argument */int entry(void){ rt_hw_interrupt_disable(); rtthread_startup(); return 0;}#endif
在上面预处理指令有三段,分别判断三个宏是否被定义—— 和 $Sub模式来修补现有符号的方法。$Super 标识的是原函数, $Sub$$标识的是新函数。上面的代码就是它们用法的最好示例了。
这样,程序的执行就不是从用户写的main方法开始了。而是从这个$Sub$$main(void)开始的了。 main是怎么执行的已经知道了程序不是从main开始执行的是RTT系统作的怪,那么用户写的main方法是何时执行的呢?接着搜索**$Super$$main**,得到其调用如下: /* 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) $Super$$main(); /* for ARMCC. */#elif defined(__ICCARM__) || defined(__GNUC__) main();#endif}
接着搜索
从名字就可以看得出来,这是在造线程啊,查阅下rtthread的官方文档果然如此。 至此,RTT操作系统就已经在STM32C8T6核心板上跑起来了。后续使用RTT操作系统得先看下官方文档,然后在使用中实践,在实践中深入理解,以便更快更好的掌握RTT。 |
|
来自: goodwangLib > 《RT-Thread》