RT-Thread移植到stm32

澆築菜鳥發表於2022-01-20

一、移植RT-Thread準備

  1. RT-Thread原始碼
    原始碼版本和下載方式,可以參考RT-Thread移植入門學習

  2. keil軟體

  3. STM32工程專案模板
    因為每一廠家提供的庫檔案可能有一些區別,在移植時可能會出現各種不同的問題,對於剛瞭解RT-Thread的小夥伴不友好,所以我已經將之前建立好的專案模板放在百度網盤了,當然也可以參考STM32新建模板之庫檔案,百度的下載連線是:https://pan.baidu.com/s/1_H3l4Dy5aZHfZ_FirBjgtA ,提取碼是:vbzt

  4. STM32F103C8T6開發版
    想要購買通關開發版的我也提供了賣家的連線,需要的小夥伴可以參考STM32零基礎入門教程

二、RT-Thread 幫助文件

這裡我移植的是標準版,當然需要移植的元件也是很多的,我們從核心開始移植,然後在進行外設的移植。學習RT-Thread 過程中有什麼不明白的可以參考官方提供的幫助文件。
RT-Thread標準版文件:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/basic/basic?id=rt-thread-核心配置示例

移植RT-Thread的核心有兩種方法,一種是通過keil提供的外掛進行一起,一種是通過下載官方的原始碼進行移植,這裡主要是瞭解通過原始碼的方式進行移植,這樣在後面進行外設的移植時比較方便。

三、使用keil提供的工具進行移植

  1. 開啟模板工程

  2. 通過keil下載RT-Thread核心介面

  3. 新增RT-Thread

  4. 新增完成後專案工程中會增加一個RTOS路徑

  5. 編譯,編譯完成後會發現兩個錯誤

    注意:這裡主要的錯誤是在board.c檔案中,宣告瞭SystemCoreClockUpdate(void)方法和SystemCoreClock變數,但是沒有對進進行定義導致的錯誤。

  6. 函式SystemCoreClockUpdate()

    這個函式主要是用於獲取SystemCoreClock 定時器和處理器的頻率,這一我們只是想測試一下移植的效果我們可以不用對其進行實現,我們會在下次筆記中進行分析。當然有在system_stm32fqox.c檔案中已經實現了,這裡就不會出現這樣的錯誤,現在的解決辦法比較簡單,在檔案最後定義一下這個函式即可。

  7. SystemCoreClock變數
    SystemCoreClock是當前系統時鐘評率,並且是通過函式SystemCoreClockUpdate()中獲取的,這裡我們也不用過多瞭解,因為不同的庫檔案對系統時脈頻率的命名是不一樣的,比如我現在使用庫檔案的命名如下圖所示:

    從圖中可知,SystemFrequency的時脈頻率是通過變數SYSCLK_FREQ_72MHz的賦值,所有我們只需要知道當前系統的頻率賦值給SystemCoreClock變數即可。

  • 解決辦法
    • 在board.c檔案中引入標頭檔案stm32f10x.h
    • 在函式rt_hw_board_init()中定義SystemCoreClock變數並賦值。
  1. 開啟堆記憶體

    開啟的方式比較簡單,只需要在rtconfig.h檔案中取消RT_USING_HEAP巨集的註釋即可

  2. 在次編譯,這次編譯即便會發現沒有錯誤了,其中的警告我們先忽視。

  3. 編寫主程式,線上程中進行led燈閃爍,main.c檔案如下所示


#include "stm32f10x.h"
#include "led.h"
#include <rtthread.h>


int main(void)
{  
    
	LED_GPIO_Config();

	while (1)
	{	 
		GPIO_ResetBits(GPIOB,GPIO_Pin_12);
		rt_thread_delay(1000);	// 延時1000 ms
		GPIO_SetBits(GPIOB, GPIO_Pin_12 );
		rt_thread_delay(1000);	// 延時1000 ms
	}
}


到這裡我們便可以簡單的使用RT-Thread的延時函式進行led的閃爍試驗了。

四、通過官方原始碼移植

  1. 在模板工程中建立RT_Thread、RT_Thread/kernel、board檔案

  2. 將原始碼路徑下的include和src檔案拷貝到建立的RT_Thread/kernel檔案中

  3. 將路徑libcpu\arm中的cortex-m3檔案拷貝到建立的RT_Thread檔案中

    注意:這裡拷貝的是專案架構檔案,因為我這裡使用的是M3的晶片,小夥們需要根據自己的晶片型別拷貝相應的檔案

  4. 在board檔案中建立board.c和rtconfig.h檔案

  5. 新增原始碼到工程組檔案

  6. board.c檔案,相信這個檔案已經不陌生了,沒錯我們將上一流程更改的內容拷貝過來

/*
 * Copyright (c) 2006-2019, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2017-07-24     Tanek        the first version
 * 2018-11-12     Ernest Chen  modify copyright
 */
 
#include <stdint.h>
#include <rthw.h>
#include <rtthread.h>
#include "stm32f10x.h"

#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))

// 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

/**
 * This function will initial your board.
 */
void rt_hw_board_init()
{
	uint32_t SystemCoreClock = 72000000;
	
    /* System Clock Update */
    SystemCoreClockUpdate();
    
    /* System Tick Configuration */
    _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_USER_MAIN) && defined(RT_USING_HEAP)
    rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#endif
}

void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    rt_tick_increase();

    /* leave interrupt */
    rt_interrupt_leave();
}

void SystemCoreClockUpdate(void)
{

}

  1. 拷貝rtconfig.h檔案,同樣是將上一流程中更改的內容拷貝過來
/* RT-Thread config file */

#ifndef __RTTHREAD_CFG_H__
#define __RTTHREAD_CFG_H__

#if defined(__CC_ARM) || defined(__CLANG_ARM)
#include "RTE_Components.h"

#if defined(RTE_USING_FINSH)
#define RT_USING_FINSH
#endif //RTE_USING_FINSH

#endif //(__CC_ARM) || (__CLANG_ARM)

// <<< 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  8
// <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     256

// </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>Dynamic Heap Management
//  <i>Dynamic Heap Management
#define RT_USING_HEAP
// </c>
// <c1>using small memory
//  <i>using small memory
#define RT_USING_SMALL_MEM
// </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          128
// </h>

#if defined(RT_USING_FINSH)
    #define FINSH_USING_MSH
    #define FINSH_USING_MSH_ONLY
    // <h>Finsh Configuration
    // <o>the priority of finsh thread <1-7>
    //  <i>the priority of finsh thread
    //  <i>Default: 6
    #define __FINSH_THREAD_PRIORITY     5
    #define FINSH_THREAD_PRIORITY       (RT_THREAD_PRIORITY_MAX / 8 * __FINSH_THREAD_PRIORITY + 1)
    // <o>the stack of finsh thread <1-4096>
    //  <i>the stack of finsh thread
    //  <i>Default: 4096  (4096Byte)
    #define FINSH_THREAD_STACK_SIZE     512
    // <o>the history lines of finsh thread <1-32>
    //  <i>the history lines of finsh thread
    //  <i>Default: 5
    #define FINSH_HISTORY_LINES         1

    #define FINSH_USING_SYMTAB
    // </h>
#endif

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

#endif

  1. 編譯,這是會發現找不到RTE_Components.h檔案

    因為這個是之前keil軟體中的標頭檔案,編譯的時候會自動生成,這裡我們不需要,直接刪除這個引用即可。

  2. main.c檔案


#include "stm32f10x.h"
#include "led.h"
#include <rtthread.h>


int main(void)
{  
    
	LED_GPIO_Config();

	while (1)
	{	 
		GPIO_ResetBits(GPIOB,GPIO_Pin_12);
		rt_thread_delay(1000);	// 延時1000 ms
		GPIO_SetBits(GPIOB, GPIO_Pin_12 );
		rt_thread_delay(1000);	// 延時1000 ms
	}
}

五、檔案保護

防止在開發中不小心更改到核心檔案,導致新的錯誤產生,所以我們需要進行檔案保護。

處理辦法也比較簡單,在專案檔案中把需要保護的檔案改為只讀即可


設定為只讀模式後,在專案檔案上就可以看到一把鑰匙的存在,這樣就可以避免在更改程式時,意外改動導致新的錯誤產生,如下圖所示:

到此移植RT-Thread的核心也算基本完成了,當然還存在一些問題,接下來只需要哦邊學習邊修改即可,感興趣的小夥伴可以看我之後的文章,哪裡寫得不好望小夥本們指出。

六、常見問題

  1. HardFault_Handler、PendSV_Handler、SysTick_Handler三個函式重複定義
  • 解決辦法,這個問題是因為在工程中匯入了stm32f10x_it.c檔案,而這個檔案主要是提供了一些模板,這裡我們不需要,所以解決方法有兩種,
    • 方式一: 直接將stm32f10x_it.c檔案重專案中刪除即可。
    • 方式二: 在stm32f10x_it.c檔案中將重複定義的函式遮蔽即可。

參考文獻

stm32 移植 rt-thread:https://blog.csdn.net/qq_36958104/article/details/111604665

相關文章