前言

RT-Thread官网有很多通过IDE一键移植的方法,本文选择的是手动移植,文末提供移植好的完整工程。

RT-Thread 有3个版本,分别是标准版本、Nano版本、Smart版本,本文选择的是最简单的Nano版本,RT-Thread Nano 是一个极简版的硬实时内核,其架构如下:

stm32f103移植OpenCV stm32f103移植rtthread_RTOS

其启动流程如下,黄色表示 libcpu 移植相关的内容(RT-Thread提供的libcpu文件已完成 ),绿色部分表示板级移植相关的内容(需要我们完成)

stm32f103移植OpenCV stm32f103移植rtthread_stm32_02



基础工程

演示STM32F103移植RT-Thread完整过程,基础工程采用STM32CubeMX生成

需要注意如下几点:

①取消HardFault_Handler、PendSV_Handler、SysTick_Handler中断函数

SysTick_Handlerboard.c中实现

HardFault_HandlerPendSV_Handler在context_rvds.S中实现(RT-Thread已完成)

stm32f103移植OpenCV stm32f103移植rtthread_嵌入式_03

②将基准时钟由SysTick换成其它,例如TIM4

stm32f103移植OpenCV stm32f103移植rtthread_嵌入式_04



③使能UART1,便于调试

stm32f103移植OpenCV stm32f103移植rtthread_stm32f103移植OpenCV_05



1.下载源码

下载RT-Thread v4.1.1解压如下图,下载可能有点慢,文末提供压缩包

stm32f103移植OpenCV stm32f103移植rtthread_RTOS_06



2.拷贝源码

stm32f103移植OpenCV stm32f103移植rtthread_单片机_07


在工程目录下创建RT-Thread目录,并创libcpu目录和bsp目录

rt-thread-v4.1.1中的includesrc文件夹拷贝进RT-Thread

rt-thread-v4.1.1 libcpu\arm中的cortex-m3文件夹拷贝进RT-Thread\libcpu

RT-Thread\bsp下创建board.c

RT-Thread下创建rtconfig.h


3.添加到工程

stm32f103移植OpenCV stm32f103移植rtthread_单片机_08


新建RT-Thread组

添加RT-Thread\src中的全部.c文件

添加RT-Thread\libcpu\cortex-m3中的cpuport.ccontext_rvds.S

添加RT-Thread\lbsp中的board.c

stm32f103移植OpenCV stm32f103移植rtthread_嵌入式_09

头文件路径包含RT-ThreadRT-Thread\include


4.修改配置

将如下代码拷贝进board.c

#include <rthw.h>
#include <rtthread.h>

#include "main.h"

#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
/*
 * Please modify RT_HEAP_SIZE if you enable RT_USING_HEAP
 * the RT_HEAP_SIZE max value = (sram size - ZI size), 1024 means 1024 bytes
 */
#define RT_HEAP_SIZE (15*1024)
static rt_uint8_t rt_heap[RT_HEAP_SIZE];

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

void SysTick_Handler(void)
{
    rt_interrupt_enter();
    
    rt_tick_increase();

    rt_interrupt_leave();
}

/**
 * This function will initial your board.
 */
void rt_hw_board_init(void)
{
    extern void SystemClock_Config(void);
    
   	HAL_Init();
    SystemClock_Config();
    SystemCoreClockUpdate();

    /* 
     * 1: OS Tick Configuration
     * Enable the hardware timer and call the rt_os_tick_callback function
     * periodically with the frequency RT_TICK_PER_SECOND. 
     */
    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/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_USER_MAIN) && defined(RT_USING_HEAP)
    rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#endif
}

#ifdef RT_USING_CONSOLE
static UART_HandleTypeDef UartHandle;
static int uart_init(void)
{
    /* TODO: Please modify the UART port number according to your needs */
    UartHandle.Instance = USART1;
    UartHandle.Init.BaudRate = 115200;
    UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
    UartHandle.Init.StopBits = UART_STOPBITS_1;
    UartHandle.Init.Parity = UART_PARITY_NONE;
    UartHandle.Init.Mode = UART_MODE_TX_RX;
    UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;

    if (HAL_UART_Init(&UartHandle) != HAL_OK)
    {
        while (1);
    }
    return 0;
}
INIT_BOARD_EXPORT(uart_init);

void rt_hw_console_output(const char *str)
{
    rt_size_t i = 0, size = 0;
    char a = '\r';

    __HAL_UNLOCK(&UartHandle);

    size = rt_strlen(str);

    for (i = 0; i < size; i++)
    {
        if (*(str + i) == '\n')
        {
            HAL_UART_Transmit(&UartHandle, (uint8_t *)&a, 1, 1);
        }
        HAL_UART_Transmit(&UartHandle, (uint8_t *)(str + i), 1, 1);
    }
}
#endif

#ifdef RT_USING_FINSH
char rt_hw_console_getchar(void)
{
    /* Note: the initial value of ch must < 0 */
    int ch = -1;

    if (__HAL_UART_GET_FLAG(&UartHandle, UART_FLAG_RXNE) != RESET)
    {
        ch = UartHandle.Instance->DR & 0xff;
    }
    else
    {
        rt_thread_mdelay(10);
    }
    return ch;
}
#endif


将如下代码拷贝进rtconfig.h

/* RT-Thread config file */

#ifndef __RTTHREAD_CFG_H__
#define __RTTHREAD_CFG_H__

#define RT_USING_LIBC

// <<< Use Configuration Wizard in Context Menu >>>

// <h>Basic Configuration
// <o>Maximal level of thread priority <8-256>
//  <i>Default: 32
#define RT_THREAD_PRIORITY_MAX  32
// <o>OS tick per second
//  <i>Default: 1000   (1ms)
#define RT_TICK_PER_SECOND  1000
// <o>Alignment size for CPU architecture data access
//  <i>Default: 4
#define RT_ALIGN_SIZE   4
// <o>the max length of object name<2-16>
//  <i>Default: 8
#define RT_NAME_MAX    8
// <c1>Using RT-Thread components initialization
//  <i>Using RT-Thread components initialization
#define RT_USING_COMPONENTS_INIT
// </c>

#define RT_USING_USER_MAIN

// <o>the stack size of main thread<1-4086>
//  <i>Default: 512
#define RT_MAIN_THREAD_STACK_SIZE     512

// </h>

// <h>Debug Configuration
// <c1>enable kernel debug configuration
//  <i>Default: enable kernel debug configuration
//#define RT_DEBUG
// </c>
// <o>enable components initialization debug configuration<0-1>
//  <i>Default: 0
#define RT_DEBUG_INIT 0
// <c1>thread stack over flow detect
//  <i> Diable Thread stack over flow detect
//#define RT_USING_OVERFLOW_CHECK
// </c>
// </h>

// <h>Hook Configuration
// <c1>using hook
//  <i>using hook
//#define RT_USING_HOOK
// </c>
// <c1>using idle hook
//  <i>using idle hook
//#define RT_USING_IDLE_HOOK
// </c>
// </h>

// <e>Software timers Configuration
// <i> Enables user timers
#define RT_USING_TIMER_SOFT         0
#if RT_USING_TIMER_SOFT == 0
    #undef RT_USING_TIMER_SOFT
#endif
// <o>The priority level of timer thread <0-31>
//  <i>Default: 4
#define RT_TIMER_THREAD_PRIO        4
// <o>The stack size of timer thread <0-8192>
//  <i>Default: 512
#define RT_TIMER_THREAD_STACK_SIZE  512
// </e>

// <h>IPC(Inter-process communication) Configuration
// <c1>Using Semaphore
//  <i>Using Semaphore
#define RT_USING_SEMAPHORE
// </c>
// <c1>Using Mutex
//  <i>Using Mutex
#define RT_USING_MUTEX
// </c>
// <c1>Using Event
//  <i>Using Event
#define RT_USING_EVENT
// </c>
// <c1>Using MailBox
//  <i>Using MailBox
#define RT_USING_MAILBOX
// </c>
// <c1>Using Message Queue
//  <i>Using Message Queue
#define RT_USING_MESSAGEQUEUE
// </c>
// </h>

// <h>Memory Management Configuration
// <c1>Memory Pool Management
//  <i>Memory Pool Management
//#define RT_USING_MEMPOOL
// </c>
// <c1>Dynamic Heap Management(Algorithm: small memory )
//  <i>Dynamic Heap Management
#define RT_USING_HEAP
#define RT_USING_SMALL_MEM

//#define RT_USING_MEMHEAP
//#define RT_MEMHEAP_FAST_MODE
#define RT_USING_SMALL_MEM_AS_HEAP
// </c>
// <c1>using tiny size of memory
//  <i>using tiny size of memory
//#define RT_USING_TINY_SIZE
// </c>
// </h>

// <h>Console Configuration
// <c1>Using console
//  <i>Using console
#define RT_USING_CONSOLE
// </c>
// <o>the buffer size of console <1-1024>
//  <i>the buffer size of console
//  <i>Default: 128  (128Byte)
#define RT_CONSOLEBUF_SIZE          256
// </h>

// <h>FinSH Configuration
// <c1>include finsh config
//  <i>Select this choice if you using FinSH 
//#include "finsh_config.h"
// </c>
// </h>

// <h>Device Configuration
// <c1>using device framework
//  <i>using device framework
//#define RT_USING_DEVICE
// </c>
// </h>

// <<< end of configuration section >>>

#endif


此时的main()已经变成了一个线程,所以需要屏蔽掉main()中硬件相关的操作

相关操作移到了board.crt_hw_board_init()的执行,后期有硬件相关初始化也要添加到rt_hw_board_init()

rt_hw_board_init()会在线程启动前被调用

可能细心的同学会发现,我们没有修改过启动文件,应该最先执行main(),但是实际最先执行的是rtthread_startup()
main()反而变成了一个线程,为什么呢?请参考我的另一篇博客
在MDK(ARMCC)中使用 $ Sub $ $ 和 $ Super $ $

int main(void)
{
	// HAL_Init();
	// SystemClock_Config();
	// MX_GPIO_Init();
	// MX_USART1_UART_Init();

	while (1)
	{
	}
}


5.验证demo

#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "rtthread.h"

int main(void)
{
	// HAL_Init();
	// SystemClock_Config();
	// MX_GPIO_Init();
	// MX_USART1_UART_Init();

	while (1)
	{
		rt_kprintf("hello world!\r\n");
		rt_thread_delay(1000);
	}
}


6.完整工程和RT-Thread源码

0错误 0警告,点击下载

stm32f103移植OpenCV stm32f103移植rtthread_嵌入式_10