gitee代码:
https://gitee.com/xuwenqiang_1989/rt-thread-nano
本移植参考了【野火rt-thread内核应用开发】
1.简介
RT-Thread Nano 是一个极简版的硬实时内核,它是由 C 语言开发,采用面向对象的编程思维,具有良好的代码风格,是一款可裁剪的、抢占式实时多任务的 RTOS。其内存资源占用极小,功能包括任务处理、软件定时器、信号量、邮箱和实时调度等相对完整的实时操作系统特性。适用于家电、消费电子、医疗设备、工控等领域大量使用的 32 位 ARM 入门级 MCU 的场合。
2.软件准备
STM32Cubemx 下载地址:
https://www.st.com/zh/development-tools/stm32cubemx.html
KEIL下载地址:
https://www.keil.com/demo/eval/arm.htm
开发板芯片为STM32H750XBH6,提前安装好安装Keil.STM32H7xx_DFP.3.1.1.pack系列器件支持包
支持包下载地址:
https://www.keil.arm.com/packs/stm32h7xx_dfp-keil/boards/
3.硬件准备
EVB_AIoT_STM32开发板,STM32H750XBH6主控芯片
4.rt-thread nano移植
4.1.新建裸机工程
参考【开放原子训练营(第四季)TobudOS-基于STM32H750XBH+STM32CubeMX+HAL库+KEIL 移植 TobudOS】中3.1部分。
本工程中引脚对应:
UART5_RX = PB5
UART5_TX = PB13
LED1_OUT = PB1
LED2_OUT = PC13
KEY1_IN = PJ3
KEY2_IN = PJ4
注意取消生成中断源有变化,多取消了Memory management fault
4.2.设置KEIL
参考【开放原子训练营(第四季)TobudOS-基于STM32H750XBH+STM32CubeMX+HAL库+KEIL 移植 TobudOS】中3.2部分。
4.3.准备rt-thread nano内核源码
rt-thread nano v3.1.5 下载地址:
https://github.com/RT-Thread/rtthread-nano/archive/refs/heads/master.zip
4.4. rt-thread nano 内核移植
4.4.1.新建文件夹
在工程下新建user和rt-thread文件夹。
user用于存放用户驱动,rt-thread用于存放内核。
将rt-thread内核代码复制到新建的rt-thread文件夹中
将rt-thread\bsp\board.c和rt-thread\bsp\rtconfig.h复制到user文件夹中。
board.c用于修改与本工程相关的函数。
rtconfig.h用于修改对RTOS的设置。
在user文件夹下新建一个board.h用于board.c的声明
4.4.2.给工程添加groups
添加user,rtt/source,rtt/ports的groups
user用于存放用户工程相关文件,目前只有user\board.c
rtt/source用于存放rt-thread\src\内所有rt-thread内核源文件
rtt/ports用于存放rt-thread\libcpu\arm\cortex-m7\内所有与硬件相关的源文件
4.4.3.给工程指定头文件目录
4.4.4.修改rtconfig.h
注释掉 #include "RTE_Components.h"
/* RT-Thread config file */
#ifndef __RTTHREAD_CFG_H__
#define __RTTHREAD_CFG_H__
#include <rtthread.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 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
// #define RT_USING_MEMPOOL
// <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
4.4.5修改board.c和board.h
我们使用STM32Cubemx生成的时钟初始化,所以注释掉rt-thread自己的时钟初始化和rt_hw_board_init函数中的时钟初始化
将main.c开头由STM32Cubemx生成的头文件复制到board.h中
#ifndef __BOARD_H__
#define __BOARD_H__
/*
*************************************************************************
* 包含的头文件
*************************************************************************
*/
/* STM32 固件库头文件 */
#include "main.h"
#include "usart.h"
#include "gpio.h"
/*
*************************************************************************
* 函数声明
*************************************************************************
*/
void rt_hw_board_init(void);
void SysTick_Handler(void);
#endif /* __BOARD_H__ */
将main.c中时钟初始化函数SystemClock_Config在board.c中引用
用SystemClock_Config和
HAL_SYSTICK_Config(HAL_RCC_GetSysClockFreq() / RT_TICK_PER_SECOND)
初始化时钟,并将main.c开始部分的硬件初始化复制过来,将main函数中的初始化全部注释掉。
这样以后由STM32Cubemx生成的代码,都可以复制到这里初始化。
最好用#if 0...#endif 注释,这样以后生成新的代码,不用重复注释。
自定义rt_hw_console_output函数,实现串口的重映射到rt_kprintf()
/**
* @brief 重映射串口DEBUG_USARTx到rt_kprintf()函数
* Note:DEBUG_USARTx是在bsp_usart.h中定义的宏,默认使用串口1
* @param str:要输出到串口的字符串
* @retval 无
*
* @attention
*
*/
void rt_hw_console_output(const char *str)
{
uint8_t char_r = '\r';
/* 进入临界段 */
rt_enter_critical();
/* 直到字符串结束 */
while (*str != '\0')
{
/* 换行 */
if (*str == '\n')
{
HAL_UART_Transmit(&huart5, &char_r, 1, 1000);
}
HAL_UART_Transmit(&huart5, (uint8_t *)(str++), 1, 1000);
}
/* 退出临界段 */
rt_exit_critical();
}
附上修改完毕的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 "board.h"
#include <stdint.h>
#include <rthw.h>
#include <rtthread.h>
#if 0
#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;
}
#endif
#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.
*/
extern void SystemClock_Config(void);
void rt_hw_board_init()
{
#if 0
/* System Clock Update */
SystemCoreClockUpdate();
/* System Tick Configuration */
_SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
#endif
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
SystemClock_Config();
HAL_SYSTICK_Config(HAL_RCC_GetSysClockFreq() / RT_TICK_PER_SECOND);
MX_GPIO_Init();
MX_UART5_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)
{
/* enter interrupt */
rt_interrupt_enter();
rt_tick_increase();
/* leave interrupt */
rt_interrupt_leave();
}
/**
* @brief 重映射串口DEBUG_USARTx到rt_kprintf()函数
* Note:DEBUG_USARTx是在bsp_usart.h中定义的宏,默认使用串口1
* @param str:要输出到串口的字符串
* @retval 无
*
* @attention
*
*/
void rt_hw_console_output(const char *str)
{
uint8_t char_r = '\r';
/* 进入临界段 */
rt_enter_critical();
/* 直到字符串结束 */
while (*str != '\0')
{
/* 换行 */
if (*str == '\n')
{
HAL_UART_Transmit(&huart5, &char_r, 1, 1000);
}
HAL_UART_Transmit(&huart5, (uint8_t *)(str++), 1, 1000);
}
/* 退出临界段 */
rt_exit_critical();
}
4.4.7.编译下载
编译下载,无错误,到此rt-thread内核移植完成。
5.测试
5.1.动态创建线程
创建一个LED线程,让LED间隔1000ms亮灭,并打印任务信息
static rt_thread_t led_thread = RT_NULL; //led线程句柄
static void led_thread_entry(void *parameter) //led线程入口函数
{
while (1)
{
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); //开启led
rt_kprintf("led_thread running,LED ON\n"); //打印信息
rt_thread_delay(1000); //阻塞延时
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); //关闭led
rt_kprintf("led_thread running,LED OFF\n"); //打印信息
rt_thread_delay(1000); //阻塞延时
}
}
创建一个KEY线程,让按键单击时,打印单击信息
static rt_thread_t key1_thread = RT_NULL; //key1线程句柄
static void key1_thread_entry(void *parameter) //key1线程入口函数
{
uint8_t press = 0; //按键记录
while (1)
{
if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET) // KEY按下
{
if (press == 0)
{
press = 1; // 记录KEY状态
rt_kprintf("key1_thread runing,KEY被单击\r\n"); // 打印信息
}
}
else
press = 0;
rt_thread_delay(20);
}
}
线程创建并启动
// 创建led线程
led_thread = rt_thread_create("led_thread", // 线程名称
led_thread_entry, // 入口函数
RT_NULL, // 传入参数
512, // 内存大小
3, // 优先级
20); // 时间片
if (led_thread != RT_NULL)
rt_thread_startup(led_thread); // 启动线程
else
return -1;
key1_thread = rt_thread_create("key1_thread",
key1_thread_entry,
RT_NULL,
512,
2,
20);
if (key1_thread != RT_NULL)
rt_thread_startup(key1_thread);
else
return -1;
附上main.c完整代码
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "board.h"
#include "rtthread.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
static rt_thread_t led_thread = RT_NULL; // led线程句柄
static rt_thread_t key1_thread = RT_NULL; // key1线程句柄
static void led_thread_entry(void *parameter) // led线程入口函数
{
while (1)
{
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); // 开启led
rt_kprintf("led_thread running,LED ON\n"); // 打印信息
rt_thread_delay(1000); // 阻塞延时
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); // 关闭led
rt_kprintf("led_thread running,LED OFF\n"); // 打印信息
rt_thread_delay(1000); // 阻塞延时
}
}
static void key1_thread_entry(void *parameter) // key1线程入口函数
{
uint8_t press = 0; // 按键记录
while (1)
{
if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET) // KEY按下
{
if (press == 0)
{
press = 1; // 记录KEY状态
rt_kprintf("key1_thread runing,KEY被单击\r\n"); // 打印信息
}
}
else
press = 0;
rt_thread_delay(20);
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
#if 0
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_UART5_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
#endif
// 创建led线程
led_thread = rt_thread_create("led_thread", // 线程名称
led_thread_entry, // 入口函数
RT_NULL, // 传入参数
512, // 内存大小
3, // 优先级
20); // 时间片
if (led_thread != RT_NULL)
rt_thread_startup(led_thread); // 启动线程
else
return -1;
key1_thread = rt_thread_create("key1_thread",
key1_thread_entry,
RT_NULL,
512,
2,
20);
if (key1_thread != RT_NULL)
rt_thread_startup(key1_thread);
else
return -1;
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Supply configuration update enable
*/
HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);
/** Configure the main internal regulator output voltage
*/
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);
while (!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY))
{
}
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 2;
RCC_OscInitStruct.PLL.PLLN = 32;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3;
RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_D3PCLK1 | RCC_CLOCKTYPE_D1PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
5.2.编译下载
编译下载,查看串口信息。