深圳市鹏海达电子有限公司!为您提供电子元件等相关信息,敬请关注!
服务热线:0755-23997336 网站地图 XML

STM32F103+RT-Thread从零开始(二)——RTT系统中点亮LED

来源 : www.penghaida.com   发布时间 : 2021/3/19 14:26:00

之前的的推文简易讲了下怎么使用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_initrtthread_startup调用,然后它创建了一个线程,并在线程中调用了用户定义的main函数。至此就真相大白了。RTT利用工具链提供的方式,替换掉了用户的main,来启动操作系统,并创建了一条线程,在线程中调用了用户的main方法。


至此,RTT操作系统就已经在STM32C8T6核心板上跑起来了。后续使用RTT操作系统得先看下官方文档,然后在使用中实践,在实践中深入理解,以便更快更好的掌握RTT。

物联网开发 - 直播课程 - 创客学院嵌入式驱动开发 - Linux开发必备:1小时玩转儿文件I/O编程 - 创客学院直播室