STM32CubeMX工具版本:Version 6.12.1
RT-Thread版本:X-CUBE-RT-Thread_Nano,Release version :4.1.1
MCU型号:STM32H743IIT6
1.设置System Core选项中的SYS中的Timebase Sourece为SysTick,先通过STM32CubeMx工具创建一个基于STM32H743IIT6的裸机的串口工程文件,并测试串口发送数据是否正常。

2.选择Middleware and Software Packs中的X-CUBE-RT-Thread_Nano选项,勾选RTOS RT-Thread选项下的Kernel,shell,libcpu这三项

3.勾选RTOS RT-Thread,使能Console Configuration选项中的Using console for rt_kprintf。

4.选择System Core中的NVIC选项卡中的Code generation选项,取消勾选Hard fault interrupt,Memory management fault(因为RT中已经定义了,需要取消,否则会导致编译错误))。

5.点击GENERATE CODE选项生成代码。
6.由于生成的RT-Thread工程代码中cpu_cache.c文件中包含#include <board.h>头文件,而生成的工程代码中没有board.h文件,所以需要新建一个board.h文件。

/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-11-5 SummerGift first version
*/
#ifndef __BOARD_H__
#define __BOARD_H__
#include <rtthread.h>
#include <stm32h7xx.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__CC_ARM) || defined(__CLANG_ARM)
extern int Image$$RW_IRAM1$$ZI$$Limit;
#define HEAP_BEGIN (&Image$$RW_IRAM1$$ZI$$Limit)
#elif __ICCARM__
#pragma section="CSTACK"
#define HEAP_BEGIN (__segment_end("CSTACK"))
#else
extern int __bss_end;
#define HEAP_BEGIN (&__bss_end)
#endif
void SystemClock_Config(void);
#ifdef __cplusplus
}
#endif
#endif
7.由于生成的工程代码中没有创建RT-Thread任务,所以需要新建创建任务的文件,新建app_rt_thread.h文件,新建app_rt_thread.c文件,以静态创建线程为例。
#ifndef __APP_RT_THREAD_H__
#define __APP_RT_THREAD_H__
void MX_RT_Thread_Init(void);
void Task1_entry(void *parameter);
void Task2_entry(void *parameter);
#endif
#include "app_rt_thread.h"
#include "rtthread.h"
#include "main.h"
#include "stdio.h"
struct rt_thread Task1_thread;
rt_uint8_t rt_Task1_thread_stack[1024];
struct rt_thread Task2_thread;
rt_uint8_t rt_Task2_thread_stack[1024];
//初始化线程函数
void MX_RT_Thread_Init(void)
{
rt_thread_init(&Task1_thread,"Task1",Task1_entry,RT_NULL,&rt_Task1_thread_stack[0],sizeof(rt_Task1_thread_stack),3,20);
//开启线程调度
rt_thread_startup(&Task1_thread);
rt_thread_init(&Task2_thread,"Task2",Task2_entry,RT_NULL,&rt_Task2_thread_stack[0],sizeof(rt_Task2_thread_stack),4,20);
//开启线程调度
rt_thread_startup(&Task2_thread);
}
//任务1
void Task1_entry(void *parameter)
{
static unsigned int dwCnt=0;
while(1)
{
printf("任务1_Count=%d\r\n",dwCnt++);
printf("HAL_RCC_GetSysClockFreq=%d\r\n",HAL_RCC_GetSysClockFreq());
rt_thread_delay(1000);
}
}
//任务2
void Task2_entry(void *parameter)
{
static unsigned int dwCnt=0;
while(1)
{
printf("任务2_Count=%d\r\n",dwCnt++);
rt_thread_delay(1000);
}
}
8.由于工程代码中已经实现了RT-Thread操作系统,所以需要注释掉main.c中while(1)循环函数,并且添加MX_RT_Thread_Init()初始化函数。

9.由于工程代码中添加了RT-Thread操作系统,而RT-Thread操作系统默认使用的是SYSTICK时钟作为时间基准,而HAL库中也是使用的是SYSTICK作为时间基准,在利用STM32CubeMx软件中生成代码时会出现警告,基于此情况,重新配置SYS的时间基准,这里使用TIM6为例,因为TIM6为基本定时器,涉及到的一些复杂的定时功能比较少,比较适合应用基本的定时时间。


10.经过实际测试发现,当SYS选择TIM6定时的时候,使用rt_thread_delay(1000)函数延时发现时间只有500ms,而当SYS选择SysTick的时候延时时间是1000ms,是正确的时间,通过查看源代码发现无论SYS选择哪种时钟源,生成的RT-Thread操作系统始终使用的是SysTick作为时间基准源。继续查看代码发现RT_thread中配置SYSTICK定时的函数在函数rt_hw_board_init(void)中,而当SYS配置为SysTick的时候,在main.c文件中HAL_Init(void)函数中会重新配置SYSTICK时钟,实际调用的配置函数是HAL_InitTick(TICK_INT_PRIORITY),即当SYS配置为SysTick的时候,代码中首先在rt_hw_board_init(void)函数中配置SysTick,之后又在main.c文件中的HAL_Init(void)中HAL_InitTick(TICK_INT_PRIORITY)配置了SysTick,所以最终是以HAL_InitTick(TICK_INT_PRIORITY)配置的有效。而当SYS配置为TIM6的时候,HAL_InitTick(TICK_INT_PRIORITY)配置的实际上是TIM6时钟,即当SYS配置为TIM6的时候,SYSTICK仅仅在rt_hw_board_init(void)函数中配置过,仔细查看rt_hw_board_init(void)中的函数代码,发现SYSTICK配置错误,原函数调用了HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/RT_TICK_PER_SECOND)函数配置SYSTICK,通过调试发现HAL_RCC_GetHCLKFreq()返回的值为240000000HZ,而SYSTICK的时钟应该和系统时钟一致,应该值为480000000HZ,所以此处代码错误,应该改写为HAL_SYSTICK_Config(HAL_RCC_GetSysClockFreq()/RT_TICK_PER_SECOND);,修改之后,测试发现当SYS配置为TIM6的时候,rt_thread_delay(1000)函数延时的时间为1000ms,问题解决。


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);
HAL_SYSTICK_Config(HAL_RCC_GetSysClockFreq()/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
}
11.以上是创建的RT-Thread静态线程的方法,若需要创建动态线程,则应使能Memory Configuration中的Using dynamic Heap Management,Using small memory algorihm as heap,Using Small Memory Algorithm,三个必须要全部使能,若不全部使能则会导致程序进入hard fault中。

12.修改app_rt_thread.h和app_rt_thread.c文件,创建动态线程函数。
#ifndef __APP_RT_THREAD_H__
#define __APP_RT_THREAD_H__
void MX_RT_Thread_Create(void);
void MX_RT_Thread_Process(void);
void Task1_entry(void *parameter);
void Task2_entry(void *parameter);
#endif
#include "app_rt_thread.h"
#include "rtthread.h"
#include "main.h"
#include "stdio.h"
static rt_thread_t Task1_thread = RT_NULL;
static rt_thread_t Task2_thread = RT_NULL;
//创建线程函数
void MX_RT_Thread_Create(void)
{
Task1_thread = rt_thread_create("Task1",Task1_entry, RT_NULL,1024, 3, 20);
//开启线程调度
if (Task1_thread != RT_NULL)
{
rt_thread_startup(Task1_thread);
}
Task2_thread = rt_thread_create("Task2",Task2_entry, RT_NULL,1024, 4, 20);
if (Task2_thread != RT_NULL)
{
rt_thread_startup(Task2_thread);
}
}
//主任务
void MX_RT_Thread_Process(void)
{
printf("Hello RT_Thread!!!");
rt_thread_delay(2000);
}
//任务1
void Task1_entry(void *parameter)
{
static unsigned int dwCnt=0;
while(1)
{
printf("任务1_Count=%d\r\n",dwCnt++);
rt_thread_delay(1000);
}
}
//任务2
void Task2_entry(void *parameter)
{
static unsigned int dwCnt=0;
while(1)
{
printf("任务2_Count=%d\r\n",dwCnt++);
rt_thread_delay(1000);
}
}
13.若需要添加静态内存管理或者动态内存管理,则需要开启宏定义Using mempool Management。
注意在动态堆中,无论是静态内存管理还是动态内存管理使用的源内存都来自于动态堆中申请的内存,若申请的静态内存不足或者动态内存不足的时候,只需要扩大动态堆的空间。
其中静态内存创建的函数rt_mp_create(“test_mp”,BLOCK_COUNT,BLOCK_SIZE),BLOCK_SIZE是以字节为单位,例如当
#define BLOCK_COUNT 20 //内存块数量
#define BLOCK_SIZE 10 //内存块大小
即表示创建了20个内存块,每块的字节数占用10个字节,实际测试发现当赋值的数据长度超过限定的内存块2个字节时,程序也可以正常运行,不会进入HardFault中,但是赋值的长度不要超过限定的内存块的大小。


//说明:静态内存管理
#define BLOCK_COUNT 20 //内存块数量
#define BLOCK_SIZE 10 //内存块大小
//任务3
void Task3_entry(void *parameter)
{
static unsigned int dwCnt=0;
static rt_mp_t test_mp = RT_NULL;
static rt_uint8_t *bp_test = RT_NULL;
uint8_t bCnt=0;
rt_err_t uwRet = RT_EOK;
/* 创建一个静态内存池 */
test_mp = rt_mp_create("test_mp",BLOCK_COUNT,BLOCK_SIZE);
if(test_mp != RT_NULL)
{
rt_kprintf("静态内存池创建成功!\n\n");
}
else
{
rt_kprintf("静态内存池创建失败!\n\n");
}
for(bCnt=0;bCnt<BLOCK_COUNT+2;bCnt++)
{
bp_test = rt_mp_alloc(test_mp,0);
if(RT_NULL == bp_test)
{
rt_kprintf("静态内存申请失败!\n");
}
else
{
rt_kprintf("静态内存申请成功,数目为%d,地址为0x%x!\n\n",bCnt,bp_test);
memcpy(bp_test,"ABCDEFG",strlen("ABCDEFG"));
rt_kprintf("赋值的内容=%s\n",bp_test);
rt_mp_free(bp_test);
// if(bCnt>10)
// {
// uwRet = rt_mp_delete(test_mp);
// if(RT_EOK == uwRet)
// {
// rt_kprintf("删除内存成功!\n");
// }
// else
// {
// rt_kprintf("删除内存失败!\n");
// }
// }
}
}
while(1)
{
rt_kprintf("任务3_Count=%d\r\n",dwCnt++);
rt_thread_delay(1000);
}
}
//说明:动态内存管理
//任务4
void Task4_entry(void *parameter)
{
static unsigned int dwCnt=0;
static rt_uint8_t *bp_test = RT_NULL;
while(1)
{
bp_test = rt_malloc(1024);
if(RT_NULL == bp_test)
{
rt_kprintf("动态内存申请失败!\n");
}
else
{
rt_kprintf("动态内存申请成功,地址为0x%x!\n\n",bp_test);
}
rt_free(bp_test);
rt_kprintf("任务4_Count=%d\r\n",dwCnt++);
rt_thread_delay(1000);
}
}
14.若需要测试CPU利用率,则添加测试CPU利用率的代码,同时需要启用空闲钩子函数宏定义,使能Using hook,因为rt_thread_idle_sethook()函数是一个钩子函数,该函数是系统定义的函数, 只有启用空闲钩子函数的时候编译器才会将空闲钩子函数相关的代码编译进来,需要在rtconfig.h中将RT_USING_HOOK,RT_USING_IDLE_HOOK 宏定义打开,还需要在程序初始化的时候添加cpu_usage_init()初始化代码。


//说明:测试CPU使用率
//任务5
void Task5_entry(void *parameter)
{
rt_uint8_t major,minor;
while (1)
{
/* 获取CPU利用率数据 */
cpu_usage_get(&major,&minor);
/* 打印CPU利用率 */
rt_kprintf("CPU利用率 = %d.%d%\r\n",major,minor);
rt_thread_delay(1000); /* 延时1000个tick */
}
}
添加cpuusage.h,cpuusage.c文件
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2011-03-03 lgnq
*/
#ifndef __CPUUSAGE_H__
#define __CPUUSAGE_H__
#include <rtthread.h>
#include <rthw.h>
void cpu_usage_init(void);
void cpu_usage_get(rt_uint8_t *major, rt_uint8_t *minor);
#endif /*__ADC_H__ */
CPU_USAGE_CALC_TICK宏定义为统计多少个Tick下的利用率,由于1个TICK为1ms,则10则代表测试10ms内CPU的使用率
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
*/
#include <rtthread.h>
#include <rthw.h>
#include "cpuusage.h"
#define CPU_USAGE_CALC_TICK 10
#define CPU_USAGE_LOOP 100
static rt_uint8_t cpu_usage_major = 0, cpu_usage_minor= 0;
static rt_uint32_t total_count = 0;
static void cpu_usage_idle_hook()
{
rt_tick_t tick;
rt_uint32_t count;
volatile rt_uint32_t loop;
if (total_count == 0)
{
loop = 0;
/* get total count */
rt_enter_critical();
tick = rt_tick_get();
while(rt_tick_get() - tick < CPU_USAGE_CALC_TICK)
{
total_count ++;
while (loop < CPU_USAGE_LOOP) loop ++;
}
rt_exit_critical();
}
count = 0;
loop = 0;
/* get CPU usage */
tick = rt_tick_get();
while (rt_tick_get() - tick < CPU_USAGE_CALC_TICK)
{
count ++;
while (loop < CPU_USAGE_LOOP) loop ++;
}
/* calculate major and minor */
if (count < total_count)
{
count = total_count - count;
cpu_usage_major = (count * 100) / total_count;
cpu_usage_minor = ((count * 100) % total_count) * 100 / total_count;
}
else
{
total_count = count;
/* no CPU usage */
cpu_usage_major = 0;
cpu_usage_minor = 0;
}
}
void cpu_usage_get(rt_uint8_t *major, rt_uint8_t *minor)
{
RT_ASSERT(major != RT_NULL);
RT_ASSERT(minor != RT_NULL);
*major = cpu_usage_major;
*minor = cpu_usage_minor;
}
void cpu_usage_init()
{
/* set idle thread hook */
rt_thread_idle_sethook(cpu_usage_idle_hook);
}
15.若需要查看任务栈的使用情况,则开启宏定义RT_USING_FINSH,调用list_thread()函数。

//任务5
void Task5_entry(void *parameter)
{
rt_uint8_t major,minor;
while (1)
{
/* 获取CPU利用率数据 */
cpu_usage_get(&major,&minor);
list_thread();
/* 打印CPU利用率 */
rt_kprintf("CPU利用率 = %d.%d%\r\n",major,minor);
rt_thread_delay(1000);
}
}

2612





