之前的的推文简易讲了下怎么使用Keil创建STM32F103的工程,而且完成了LED照亮,及让LED等闪动的作用,那就是众多同学们学习单片机的起手势。这篇推文是再次上一篇推文的內容,依然是照亮LED,不一样的是,此次照亮LED等,是在RT-Thread操作系统中开展的。
创建工程
创建一个Keil工程,集成ic依然挑选STM32F103C8T6,随后在Manage Run-Time Environment提示框中挑选必须用的的手机软件部件,与上文不一样的是,大家必须把RTT一起勾上。如下图:
图中中,红杠框中即是RTT操作系统的部件,各自为机器设备驱动器,系统软件核心及其shell。蓝线条中为Keil的RTX操作系统。大家如今要用的是RTT,因此 启用RTT的部件就可以,在其中Kernel为必选择项,device drivers依靠kernel,shell又依靠device drivers。
shell也提一下,shell强译成汉语便是cmd机壳,好似linux操作系统一样,RTT也出示了一套共客户在cmd实际操作的实际操作插口。RTT出示的这套插口称为finsh,关键用以调节、查询系统信息。finsh适用二种方式:
1. C语言编译器方式, 为写作便捷称作c-style;
2. 传统式cmd方式,此方式又称之为msh(module shell)。
在绝大多数嵌入式操作系统中,一般开发设计调节都应用硬件配置程序调试和printf日志打印出,在有一些状况下,这二种方法并并不是那麼功能强大。例如针对RT-Thread这一线程同步系统软件,大家想要知道某一時刻系统软件中的进程运作情况、手动式自动控制系统情况。假如有一个shell,就可以键入指令,立即相对的涵数实行得到必须的信息内容,或是管理程序的个人行为。这毫无疑问会十分便捷。finsh便是根据此而设计方案,它运作于单片机开发板,能够应用串口通信/以太网接口/USB等与PC机开展通讯。
创建工程后,相对性上一篇推文创建的工程,新项目中会空出了RTT,如下图。对于每个文档以及功效,事后应用的情况下再逐渐了解。大家当今最必须关心的是board.c和rtthread.h2个文档。从图上能够看得出,仅有这两个文档上沒有标明锁匙,有锁匙标明的是不允许变更,也就是大家能变更便是这两个文档。后边再剖析这两个文档。且走下一步。
撰写上灯程序流程
创建好工程后,逐渐撰写上灯程序流程了,与续篇推文一样,立即贴上编码:
#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没办法依照预估开展工作中,这是由于大家还缺乏工作中沒有做。
开启board.c,能够见到它上边有一两句注解,依据注解,改动以下:
#include
#include
#include "stm32f10x_rcc.h"
// rtthread tick configuration
// 1. include header files
// 2. configure rtos tick and interrupt
// 3. add tick interrupt handler
// rtthread tick configuration
// 1. include some header file as need
//#include
#ifdef __CC_ARM
extern int Image$$RW_IRAM1$$ZI$$Limit;
#define HEAP_BEGIN (&Image$$RW_IRAM1$$ZI$$Limit)
#elif __ICCARM__
#pragma section="HEAP"
#define HEAP_BEGIN (__segment_end("HEAP"))
#else
extern int __bss_end;
#define HEAP_BEGIN (&__bss_end)
#endif
#define SRAM_SIZE 8
#define SRAM_END (0 SRAM_SIZE * 1)
/**
* This function will initial STM32 board.
*/
void rt_hw_board_init()
{
// rtthread tick configuration
// 2. Configure rtos tick and interrupt
//SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
/* Call components board initial (use INIT_BOARD_EXPORT()) */
#ifdef RT_USING_COMPONENTS_INIT
rt_components_board_init();
#endif
#if defined(RT_USING_CONSOLE) && defined(RT_USING_DEVICE)
rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
rt_system_heap_init((void*)HEAP_BEGIN, (void*)SRAM_END);
#endif
}
// rtthread tick configuration
// 3. add tick interrupt handler
// tickvoid SysTick_Handler(void)
// {
// /* enter interrupt */
// rt_interrupt_enter();
//
// rt_tick_increase();
//
// /* leave interrupt */
// rt_interrupt_leave();
// }
void SysTick_Handler(void)
{
// /* enter interrupt */
rt_interrupt_enter();
//
rt_tick_increase();
//
// /* leave interrupt */
rt_interrupt_leave();
}
再度编译程序,烧写程序,LED逐渐闪动。
RTT第一次剖析
board.c改动后,程序流程就一切正常工作中了。但是为什么呢?依据工作经验而言,C程序流程并不是从main逐渐的么,board中的程序流程也是什么时候实行的呢?在main中大家有无限循环,假如是以main逐渐实行的,那麼board.c的涵数就肯定不太可能实行了。
为何并不是从main逐渐实行的
Ctrl F检索rt_hw_board_init
涵数。发觉他在components.c中的rtthread_startup
启用,再检索rtthread_startup
构造发觉其启用以下:
#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
在上面预备处理命令有三段,各自分辨三个宏是不是被界定——__CC_ARM
、__ICCARM__
、__GNUC__
,这三个是什么呢?假如全局搜索,会发觉在core_cm3.h中他们发生很数次了。
ARM 系列产品现阶段适用三大流行的专用工具链,即ARM RealView (armcc), IAR EWARM (iccarm), and GNU Compler Collection (gcc),这三个便是用于标示当今应用的是哪一个专用工具链。由于大家应用的便是RealView(Keil)了。
能够见到$Super$$main和$Sub$$main,这又是什么呢?
在一些状况下,没法改动目前标记,比如,因为标记坐落于外界库或 ROM 编码中。为了更好地处理这个问题,Keil出示了应用 $Super$$ 和 $Sub$$ 方式来修复目前标记的方式。 $Super$$标志的是原函数,$Sub$$标志的是新涵数。上边的编码便是他们使用方法的最好是实例了。
extern int $Super$$main(void);
/* re-define main function */
int $Sub$$main(void)
{
rt_hw_interrupt_disable();
rtthread_startup();
return
0;
}
那样,程序流程的实行就并不是从客户写的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
}
接着搜索main_thread_entry得带代码如下:
void rt_application_init(void)
{
rt_thread_t tid;
#ifdef RT_USING_HEAP
tid = rt_thread_create("main", main_thread_entry, RT_NULL,
RT_MAIN_THREAD_STACK_SIZE, R
T_THREAD_PRIORITY_MAX / 3, 20);
RT_ASSERT(tid != RT_NULL);
#else
rt_err_t result;
tid = &main_thread;
result = rt_thread_init(tid, "main", main_thread_entry, RT_NULL,
main_stack, sizeof(main_st
ack), RT_THREAD_PRIORITY_MAX / 3, 20);
RT_ASSERT(result == RT_EOK);
#endif
rt_thread_startup(tid);
}
从名字就可以看得出来,这是在造线程啊,查阅下rtthread的官方文档果然如此。rt_application_init
被rtthread_startup
调用,然后它创建了一个线程,并在线程中调用了用户定义的main函数。至此就真相大白了。RTT利用工具链提供的方式,替换掉了用户的main,来启动操作系统,并创建了一条线程,在线程中调用了用户的main方法。
至此,RTT操作系统就已经在STM32C8T6核心板上跑起来了。后续使用RTT操作系统得先看下官方文档,然后在使用中实践,在实践中深入理解,以便更快更好的掌握RTT。
物联网开发 - 直播课程 - 创客学院嵌入式驱动开发 - Linux开发必备:1小时玩转儿文件I/O编程 - 创客学院直播室