(一) 什么是RT-Thread?
RT-Thread是一款免费开源的国产嵌入式实时操作系统。我们可以发现,在RTOS领域比较知名的都是来自国外。近些年随着国产RTOS的崛起,RT-Thread崭露头角,所以学习国产的RTOS也是一个不错的选择。
(二) RT-Thread移植
1.准备一个F7的简单例程
自己搭建一个F7的工程模板,然后写个简单的LED灯驱动,串口驱动,也可以直接用正点原子的提供的例程。
2.下载 RT-Thread Nano 源码
打开RT-Thread官网下载链接,选择RT-Thread Nano源码下载,下载完成之后解压,便可获得最新的内核源码。
3.向工程中添加 RT-Thread 源码
(1)先在工程目录文件夹下新建一个RT-Thread文件,用于存放RT-Thread源码。
源码文件夹内的文件如上图所示。将源码文件夹中的bsp、components、include、libcpu、src文件夹全部拷贝到自己的创建的RT-Thread文件夹下。打开bsp文件夹,如下图所示:
可以看出来除了board.c、board.h、rtconfig.h文件之外,其余文件夹都是与对应开发板相关的,对于我们移植来说没什么用,直接删除。打开components、include、src文件夹,都是一些源码,直接保留就好了。打开libcpu文件夹,存在arm和risc-v两个文件夹,分别打开这两个文件夹,看一下里面内容,发现arm文件夹下的内容有我们需要的,如下图所示:
我们选择的是正点原子F7的开发板,那只需要保留这个文件夹下的cortex-m7中文件,其余的也都可以删除了。经过修改之后的libcpu文件夹下只有五个文件,如下图所示:
(2)打开工程文件,在工程中添加源码文件。
如上图所示,在工程中新建分组RTT_Source用于
添加src目录下的源文件、RTT_Ports分组用于添加汇编文件和CPU相关的文件、RTT_Bsp分组用于添加board.c文件。添加完文件后,再添加各个文件的头文件路径。然后编译,出现如下图所示错误:
RTE_Components.h是在MDK中添加RT-Thread Package时由MDK自动生成的,由于我们没有使用MDK自带的RT-Thread的Package,所以添加这个头文件,故直接屏蔽掉。再次编译,出现下图错误信息:
前三个警告是由于函数传参类型定义不同引起的,实际山参数类型是一样的,这里改不改没啥影响。主要看四条重复定义的错误,这边直接屏蔽掉这四个函数就OK了。再次编译,无错误。接下来修改一下RT-Thread操作系统的配置文件rtconfig.h。这里我们就先修改一下系统允许的最大优先级和主线程的最大堆栈空间就好了,设置成为默认值。再次编译,无错误。
(三)验证RT-Thread移植
1.修改main.c中程序
这里我直接把main.c中的代码贴出来。
#include "main.h"
#include "rtthread.h"
/*****************************************************************************************************************************************/
static rt_thread_t LED_Thread = RT_NULL; //定义线程控制块
static void LED_Thread_Entry(void *parameter); //定义任务函数
/*****************************************************************************************************************************************/
/*****************************************************************************************************************************************
* Function Name: main
* Input: None
* Output: None
* Returns: None
* Description: 主函数
* Note: None
*****************************************************************************************************************************************/
int main(void)
{
LED_Thread = rt_thread_create("LED", /* 线程名字 */
LED_Thread_Entry, /* 线程入口函数 */
RT_NULL, /* 线程入口函数参数 */
512, /* 线程栈大小 */
3, /* 线程的优先级 */
20); /* 线程时间片 */
if(LED_Thread != RT_NULL)
{
rt_thread_startup(LED_Thread);
}
else
{
return -1;
}
}
/*****************************************************************************************************************************************
* Function Name: LED_Thread_Entry
* Input: None
* Output: None
* Returns: None
* Description: LED 线程处理函数
* Note: None
*****************************************************************************************************************************************/
static void LED_Thread_Entry(void *parameter)
{
while(1)
{
HW_Led0_Off();
HW_Led1_On();
rt_thread_delay(500);
HW_Led0_On();
HW_Led1_Off();
rt_thread_delay(500);
}
}
主函数的主要功能就是创建一个LED的线程,然后调度LED任务。LED任务中主要内容就是两个LED灯轮流闪烁,时间间隔500毫秒。可以发现主函数并没有相关驱动初始化,那么LED灯必然不可能正常工作的,那驱动初始化一般都在哪呢?打开board.c文件中,发现里面有个rt_hw_board_init()函数,这个函数存在系统时间的配置,继续寻找一下,看看哪里调用了这个函数。一层层的寻找发现,在components.c中存在如下代码:
#if defined(__CC_ARM) || defined(__CLANG_ARM)
extern int $Super$$main(void);
/* re-define main function */
int $Sub$$main(void)
{
rtthread_startup();
return 0;
}
看了一下注释,重定义主函数,主要运行rtthread_startup()函数,而这个函数里面初始化了操作系统资源,具体代码如下:
int rtthread_startup(void)
{
rt_hw_interrupt_disable();
/* board level initialization
* NOTE: please initialize heap inside board initialization.
*/
rt_hw_board_init();
/* show RT-Thread version */
rt_show_version();
/* timer system initialization */
rt_system_timer_init();
/* scheduler system initialization */
rt_system_scheduler_init();
#ifdef RT_USING_SIGNALS
/* signal system initialization */
rt_system_signal_init();
#endif
/* create init_thread */
rt_application_init();
/* timer thread initialization */
rt_system_timer_thread_init();
/* idle thread initialization */
rt_thread_idle_init();
/* start scheduler */
rt_system_scheduler_start();
/* never reach here */
return 0;
}
看一下注释,rt_hw_board_init()函数的功能就是板级初始化。联想到裸机程序的时候,我们也都是先初始化系统时钟,然后初始化各个硬件驱动,那么rt_hw_board_init()函数在这里就可以承担硬件初始化的角色了,所以我们给board.c建一个board.h用来存放各个硬件驱动的头文件吧。具体代码如下:
/*
* @file : board.h
* @author : wzhang
* @brief : None
* @version : V1.0.0.1
* @date : 2020.10.21
*/
#ifndef _BOARD_H
#define _BOARD_H
#include "stm32f7xx.h"
#include "stm32f7xx_hal.h"
#include "core_cm7.h"
#include "HW_Led.h"
#include "HW_Uart.h"
#include "HW_System.h"
#include "HW_SysTick.h"
#endif
编译工程之后,报错,错误信息如下:
没有定义rt_thread_create()函数。不会吧,明明在rtthread.h中声明这个函数定义啊,那是什么原因呢?查看具体的函数定义,发现函数定义上有一个条件编译定义,代码如下:
#ifdef RT_USING_HEAP
/**
* This function will create a thread object and allocate thread object memory
* and stack.
*
* @param name the name of thread, which shall be unique
* @param entry the entry function of thread
* @param parameter the parameter of thread enter function
* @param stack_size the size of thread stack
* @param priority the priority of thread
* @param tick the time slice if there are same priority thread
*
* @return the created thread object
*/
rt_thread_t rt_thread_create(const char *name,
void (*entry)(void *parameter),
void *parameter,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
{
struct rt_thread *thread;
void *stack_start;
thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread,
name);
if (thread == RT_NULL)
return RT_NULL;
stack_start = (void *)RT_KERNEL_MALLOC(stack_size);
if (stack_start == RT_NULL)
{
/* allocate stack failure */
rt_object_delete((rt_object_t)thread);
return RT_NULL;
}
_rt_thread_init(thread,
name,
entry,
parameter,
stack_start,
stack_size,
priority,
tick);
return thread;
}
RTM_EXPORT(rt_thread_create);
在rttconfig.h中发现,该定义被屏蔽掉了,那我们就打开这条定义吧。编译后无报错信息。
2.修改board.c文件
很明显,上面编译成功之后,下载进开发板之后,并没有LED闪烁,说明还是存在问题,那我们就详细看一下board.c文件吧。
#define _SCB_BASE (0xE000E010UL)
#define _SYSTICK_CTRL (*(rt_uint32_t *)(_SCB_BASE + 0x0))
#define _SYSTICK_LOAD (*(rt_uint32_t *)(_SCB_BASE + 0x4))
#define _SYSTICK_VAL (*(rt_uint32_t *)(_SCB_BASE + 0x8))
#define _SYSTICK_CALIB (*(rt_uint32_t *)(_SCB_BASE + 0xC))
#define _SYSTICK_PRI (*(rt_uint8_t *)(0xE000ED23UL))
上面这段代码看上去是和SYSTICK寄存器相关的地址定义,那就查看了一下HAL库core_cm7.h中关于Systick的寄存器地址定义,发现两者移植,可以确定这四行代码就是M7的滴答定时器的寄存器地址。
// Updates the variable SystemCoreClock and must be called
// whenever the core clock is changed during program execution.
extern void SystemCoreClockUpdate(void);
// Holds the system core clock, which is the system clock
// frequency supplied to the SysTick timer and the processor
// core clock.
extern uint32_t SystemCoreClock;
static uint32_t _SysTick_Config(rt_uint32_t ticks)
{
if ((ticks - 1) > 0xFFFFFF)
{
return 1;
}
_SYSTICK_LOAD = ticks - 1;
_SYSTICK_PRI = 0xFF;
_SYSTICK_VAL = 0;
_SYSTICK_CTRL = 0x07;
return 0;
}
上面函数是滴答定时器的配置。
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
#define RT_HEAP_SIZE 1024
static uint32_t rt_heap[RT_HEAP_SIZE]; // heap default size: 4K(1024 * 4)
RT_WEAK void *rt_heap_begin_get(void)
{
return rt_heap;
}
RT_WEAK void *rt_heap_end_get(void)
{
return rt_heap + RT_HEAP_SIZE;
}
#endif
这边定义了RT-Thread的堆配置,4K的大小,弱定义了堆起始地址函数和结束地址函数。剩余两个函数一个就是板级初始化函数,一个是Systick中断服务函数。由于我在裸机程序中写了时钟初始化函数和滴答定时器初始化函数,所以这边直接屏蔽掉时钟、滴答定时器初始化相关代码,换上自己的初始化函数,并且顺便初始化LED灯的驱动程序。具体代码如下:
#include
#include
#include
#include "board.h"
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
#define RT_HEAP_SIZE 1024
static uint32_t rt_heap[RT_HEAP_SIZE]; // heap default size: 4K(1024 * 4)
RT_WEAK void *rt_heap_begin_get(void)
{
return rt_heap;
}
RT_WEAK void *rt_heap_end_get(void)
{
return rt_heap + RT_HEAP_SIZE;
}
#endif
/**
* This function will initial your board.
*/
void rt_hw_board_init()
{
//系统初始化
HW_System_Init();
//滴答定时器初始化
HW_SysTick_Init();
//LED初始化
HW_Led_Init();
/* Call components board initial (use INIT_BOARD_EXPORT()) */
#ifdef RT_USING_COMPONENTS_INIT
rt_components_board_init();
#endif
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#endif
}
void SysTick_Handler(void)
{
HAL_IncTick();
/* enter interrupt */
rt_interrupt_enter();
rt_tick_increase();
/* leave interrupt */
rt_interrupt_leave();
}
编译后,无报错;下载进开发板后,LED灯正常闪烁,说明系统运行正常。
(一) 什么是RT-Thread?
RT-Thread是一款免费开源的国产嵌入式实时操作系统。我们可以发现,在RTOS领域比较知名的都是来自国外。近些年随着国产RTOS的崛起,RT-Thread崭露头角,所以学习国产的RTOS也是一个不错的选择。
(二) RT-Thread移植
1.准备一个F7的简单例程
自己搭建一个F7的工程模板,然后写个简单的LED灯驱动,串口驱动,也可以直接用正点原子的提供的例程。
2.下载 RT-Thread Nano 源码
打开RT-Thread官网下载链接,选择RT-Thread Nano源码下载,下载完成之后解压,便可获得最新的内核源码。
3.向工程中添加 RT-Thread 源码
(1)先在工程目录文件夹下新建一个RT-Thread文件,用于存放RT-Thread源码。
源码文件夹内的文件如上图所示。将源码文件夹中的bsp、components、include、libcpu、src文件夹全部拷贝到自己的创建的RT-Thread文件夹下。打开bsp文件夹,如下图所示:
可以看出来除了board.c、board.h、rtconfig.h文件之外,其余文件夹都是与对应开发板相关的,对于我们移植来说没什么用,直接删除。打开components、include、src文件夹,都是一些源码,直接保留就好了。打开libcpu文件夹,存在arm和risc-v两个文件夹,分别打开这两个文件夹,看一下里面内容,发现arm文件夹下的内容有我们需要的,如下图所示:
我们选择的是正点原子F7的开发板,那只需要保留这个文件夹下的cortex-m7中文件,其余的也都可以删除了。经过修改之后的libcpu文件夹下只有五个文件,如下图所示:
(2)打开工程文件,在工程中添加源码文件。
如上图所示,在工程中新建分组RTT_Source用于
添加src目录下的源文件、RTT_Ports分组用于添加汇编文件和CPU相关的文件、RTT_Bsp分组用于添加board.c文件。添加完文件后,再添加各个文件的头文件路径。然后编译,出现如下图所示错误:
RTE_Components.h是在MDK中添加RT-Thread Package时由MDK自动生成的,由于我们没有使用MDK自带的RT-Thread的Package,所以添加这个头文件,故直接屏蔽掉。再次编译,出现下图错误信息:
前三个警告是由于函数传参类型定义不同引起的,实际山参数类型是一样的,这里改不改没啥影响。主要看四条重复定义的错误,这边直接屏蔽掉这四个函数就OK了。再次编译,无错误。接下来修改一下RT-Thread操作系统的配置文件rtconfig.h。这里我们就先修改一下系统允许的最大优先级和主线程的最大堆栈空间就好了,设置成为默认值。再次编译,无错误。
(三)验证RT-Thread移植
1.修改main.c中程序
这里我直接把main.c中的代码贴出来。
#include "main.h"
#include "rtthread.h"
/*****************************************************************************************************************************************/
static rt_thread_t LED_Thread = RT_NULL; //定义线程控制块
static void LED_Thread_Entry(void *parameter); //定义任务函数
/*****************************************************************************************************************************************/
/*****************************************************************************************************************************************
* Function Name: main
* Input: None
* Output: None
* Returns: None
* Description: 主函数
* Note: None
*****************************************************************************************************************************************/
int main(void)
{
LED_Thread = rt_thread_create("LED", /* 线程名字 */
LED_Thread_Entry, /* 线程入口函数 */
RT_NULL, /* 线程入口函数参数 */
512, /* 线程栈大小 */
3, /* 线程的优先级 */
20); /* 线程时间片 */
if(LED_Thread != RT_NULL)
{
rt_thread_startup(LED_Thread);
}
else
{
return -1;
}
}
/*****************************************************************************************************************************************
* Function Name: LED_Thread_Entry
* Input: None
* Output: None
* Returns: None
* Description: LED 线程处理函数
* Note: None
*****************************************************************************************************************************************/
static void LED_Thread_Entry(void *parameter)
{
while(1)
{
HW_Led0_Off();
HW_Led1_On();
rt_thread_delay(500);
HW_Led0_On();
HW_Led1_Off();
rt_thread_delay(500);
}
}
主函数的主要功能就是创建一个LED的线程,然后调度LED任务。LED任务中主要内容就是两个LED灯轮流闪烁,时间间隔500毫秒。可以发现主函数并没有相关驱动初始化,那么LED灯必然不可能正常工作的,那驱动初始化一般都在哪呢?打开board.c文件中,发现里面有个rt_hw_board_init()函数,这个函数存在系统时间的配置,继续寻找一下,看看哪里调用了这个函数。一层层的寻找发现,在components.c中存在如下代码:
#if defined(__CC_ARM) || defined(__CLANG_ARM)
extern int $Super$$main(void);
/* re-define main function */
int $Sub$$main(void)
{
rtthread_startup();
return 0;
}
看了一下注释,重定义主函数,主要运行rtthread_startup()函数,而这个函数里面初始化了操作系统资源,具体代码如下:
int rtthread_startup(void)
{
rt_hw_interrupt_disable();
/* board level initialization
* NOTE: please initialize heap inside board initialization.
*/
rt_hw_board_init();
/* show RT-Thread version */
rt_show_version();
/* timer system initialization */
rt_system_timer_init();
/* scheduler system initialization */
rt_system_scheduler_init();
#ifdef RT_USING_SIGNALS
/* signal system initialization */
rt_system_signal_init();
#endif
/* create init_thread */
rt_application_init();
/* timer thread initialization */
rt_system_timer_thread_init();
/* idle thread initialization */
rt_thread_idle_init();
/* start scheduler */
rt_system_scheduler_start();
/* never reach here */
return 0;
}
看一下注释,rt_hw_board_init()函数的功能就是板级初始化。联想到裸机程序的时候,我们也都是先初始化系统时钟,然后初始化各个硬件驱动,那么rt_hw_board_init()函数在这里就可以承担硬件初始化的角色了,所以我们给board.c建一个board.h用来存放各个硬件驱动的头文件吧。具体代码如下:
/*
* @file : board.h
* @author : wzhang
* @brief : None
* @version : V1.0.0.1
* @date : 2020.10.21
*/
#ifndef _BOARD_H
#define _BOARD_H
#include "stm32f7xx.h"
#include "stm32f7xx_hal.h"
#include "core_cm7.h"
#include "HW_Led.h"
#include "HW_Uart.h"
#include "HW_System.h"
#include "HW_SysTick.h"
#endif
编译工程之后,报错,错误信息如下:
没有定义rt_thread_create()函数。不会吧,明明在rtthread.h中声明这个函数定义啊,那是什么原因呢?查看具体的函数定义,发现函数定义上有一个条件编译定义,代码如下:
#ifdef RT_USING_HEAP
/**
* This function will create a thread object and allocate thread object memory
* and stack.
*
* @param name the name of thread, which shall be unique
* @param entry the entry function of thread
* @param parameter the parameter of thread enter function
* @param stack_size the size of thread stack
* @param priority the priority of thread
* @param tick the time slice if there are same priority thread
*
* @return the created thread object
*/
rt_thread_t rt_thread_create(const char *name,
void (*entry)(void *parameter),
void *parameter,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
{
struct rt_thread *thread;
void *stack_start;
thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread,
name);
if (thread == RT_NULL)
return RT_NULL;
stack_start = (void *)RT_KERNEL_MALLOC(stack_size);
if (stack_start == RT_NULL)
{
/* allocate stack failure */
rt_object_delete((rt_object_t)thread);
return RT_NULL;
}
_rt_thread_init(thread,
name,
entry,
parameter,
stack_start,
stack_size,
priority,
tick);
return thread;
}
RTM_EXPORT(rt_thread_create);
在rttconfig.h中发现,该定义被屏蔽掉了,那我们就打开这条定义吧。编译后无报错信息。
2.修改board.c文件
很明显,上面编译成功之后,下载进开发板之后,并没有LED闪烁,说明还是存在问题,那我们就详细看一下board.c文件吧。
#define _SCB_BASE (0xE000E010UL)
#define _SYSTICK_CTRL (*(rt_uint32_t *)(_SCB_BASE + 0x0))
#define _SYSTICK_LOAD (*(rt_uint32_t *)(_SCB_BASE + 0x4))
#define _SYSTICK_VAL (*(rt_uint32_t *)(_SCB_BASE + 0x8))
#define _SYSTICK_CALIB (*(rt_uint32_t *)(_SCB_BASE + 0xC))
#define _SYSTICK_PRI (*(rt_uint8_t *)(0xE000ED23UL))
上面这段代码看上去是和SYSTICK寄存器相关的地址定义,那就查看了一下HAL库core_cm7.h中关于Systick的寄存器地址定义,发现两者移植,可以确定这四行代码就是M7的滴答定时器的寄存器地址。
// Updates the variable SystemCoreClock and must be called
// whenever the core clock is changed during program execution.
extern void SystemCoreClockUpdate(void);
// Holds the system core clock, which is the system clock
// frequency supplied to the SysTick timer and the processor
// core clock.
extern uint32_t SystemCoreClock;
static uint32_t _SysTick_Config(rt_uint32_t ticks)
{
if ((ticks - 1) > 0xFFFFFF)
{
return 1;
}
_SYSTICK_LOAD = ticks - 1;
_SYSTICK_PRI = 0xFF;
_SYSTICK_VAL = 0;
_SYSTICK_CTRL = 0x07;
return 0;
}
上面函数是滴答定时器的配置。
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
#define RT_HEAP_SIZE 1024
static uint32_t rt_heap[RT_HEAP_SIZE]; // heap default size: 4K(1024 * 4)
RT_WEAK void *rt_heap_begin_get(void)
{
return rt_heap;
}
RT_WEAK void *rt_heap_end_get(void)
{
return rt_heap + RT_HEAP_SIZE;
}
#endif
这边定义了RT-Thread的堆配置,4K的大小,弱定义了堆起始地址函数和结束地址函数。剩余两个函数一个就是板级初始化函数,一个是Systick中断服务函数。由于我在裸机程序中写了时钟初始化函数和滴答定时器初始化函数,所以这边直接屏蔽掉时钟、滴答定时器初始化相关代码,换上自己的初始化函数,并且顺便初始化LED灯的驱动程序。具体代码如下:
#include
#include
#include
#include "board.h"
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
#define RT_HEAP_SIZE 1024
static uint32_t rt_heap[RT_HEAP_SIZE]; // heap default size: 4K(1024 * 4)
RT_WEAK void *rt_heap_begin_get(void)
{
return rt_heap;
}
RT_WEAK void *rt_heap_end_get(void)
{
return rt_heap + RT_HEAP_SIZE;
}
#endif
/**
* This function will initial your board.
*/
void rt_hw_board_init()
{
//系统初始化
HW_System_Init();
//滴答定时器初始化
HW_SysTick_Init();
//LED初始化
HW_Led_Init();
/* Call components board initial (use INIT_BOARD_EXPORT()) */
#ifdef RT_USING_COMPONENTS_INIT
rt_components_board_init();
#endif
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#endif
}
void SysTick_Handler(void)
{
HAL_IncTick();
/* enter interrupt */
rt_interrupt_enter();
rt_tick_increase();
/* leave interrupt */
rt_interrupt_leave();
}
编译后,无报错;下载进开发板后,LED灯正常闪烁,说明系统运行正常。
举报