参考2. 移植RT-Thread到STM32 — [野火]RT-Thread内核实现与应用开发实战——基于STM32 文档 (embedfire.com)
https://doc.embedfire.com/rtos/rtthread/zh/latest/application/porting_to_stm32.html#
使用STM32F103VET6,野火STM32指南者,基于HAL库
1.下载nano(3.1.5),RT-Thread
RT-dhread 文件夹内容组成:
文件夹 | 文件夹 | 描述 |
---|---|---|
rtthread/3.0.3 | bsp | 板级支持包 |
components/finsh | RT-Thread组件 | |
include | 头文件 | |
include/libc | 头文件 | |
libcpu/arm/cortex-m0 | 与处理器相关的接口文件 | |
libcpu/arm/cortex-m3 | 与处理器相关的接口文件 | |
libcpu/arm/cortex-m4 | 与处理器相关的接口文件 | |
libcpu/arm/cortex-m7 | 与处理器相关的接口文件 | |
src | RT-Thread内核源码 |
1.1 bsp文件夹:
只需要修改board.c和rtconfig.h这两个文件的内容即可,
1.2 components文件夹:
保留finsh文件夹,finsh是RT- Thread组件里面最具特色的,它通过串口打印的方式来输出各种信息,方便我们调试程序。
1.3 include/src文件夹:
include目录下存放的是RT-Thread内核的头文件,是内核不可分割的一部分。 src目录下面存放的是RT-Thread内核的源文件,是内核的核心。
1.4 libcpu文件夹:
根据单片机选择对应文件,此处选择arm /cortex-m3下的contex_rvds.s文件以及cpuport.c文件,
2. 添加RT-Thread源码到工程组文件夹
使用STM32CubeMx生成工程文件
添加对应文件到工程目录(暂时没有使用components/finsh文件)
添加对应头文件路径
2.1 rtconfig.h文件
修改其为默认大小, RT_MAIN_THREAD_STACK_SIZE表示main线程栈大小,取值范围为1~~~4086,单位为字节,默认为512。
2.2 board.c文件
RT-Thread启动的时候会调用一个名为rt_hw_board_init()的函数,从函数名称 我们可以知道它是用来初始化开发板硬件的,比如时钟,比如串口等,具体初始化什么由用户选择。当这些硬件 初始化好之后,RT-Thread才继续往下启动。
添加头文件main.h
修改两处,可以注释掉,也可以删除
把main.c里 SystemClock_Config(void)复制到board.c,改成静态函数并声明其原型
SystemClock_Config(void)需要 去掉Error_Handler();修改如下
static void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
HAL_StatusTypeDef ret = HAL_OK;
/** 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.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
if(ret != HAL_OK)
{
while(1);
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
if(ret != HAL_OK)
{
while(1) { ; }
}
}
rt_hw_board_init()代码如下:
void rt_hw_board_init()
{
SystemClock_Config();
/* 初始化SysTick */
HAL_SYSTICK_Config( HAL_RCC_GetSysClockFreq() / RT_TICK_PER_SECOND );
#if 0
/* System Clock Update */
SystemCoreClockUpdate();
/* System Tick Configuration */
_SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
#endif
/* 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
}
在stm32f1xx_i.cstm32f1xx_i.h文件里删点或注释下面三个函数
SysTick_Handler() 、PendSV_Handler()、HardFault_Handler(void)
程序编译无误
到此已经创建好了基于野火STM32开发板的RT-Thread工程模板
3. 创建线程
3.1. 硬件初始化
将main.c里初始化函数放在 rt_hw_board_init()里,其他硬件BSP初始化统统放在这里,比如LED,串口,LCD等。
void rt_hw_board_init()
{
SystemClock_Config();
/* 初始化SysTick */
HAL_SYSTICK_Config( HAL_RCC_GetSysClockFreq() / RT_TICK_PER_SECOND );
HAL_Init();
MX_GPIO_Init();
#if 0
/* System Clock Update */
SystemCoreClockUpdate();
/* System Tick Configuration */
_SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
#endif
/* 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
}
此处用HAL库点灯,所以用到GPIO初始化,需要我们在main.h里添加gpio.h
首先测试测试硬件是否正常工作
void rt_hw_board_init()
{
SystemClock_Config();
/* 初始化SysTick */
HAL_SYSTICK_Config( HAL_RCC_GetSysClockFreq() / RT_TICK_PER_SECOND );
HAL_Init();
MX_GPIO_Init();
HAL_GPIO_WritePin(RGB_B_GPIO_Port, RGB_B_Pin, GPIO_PIN_RESET);
while (1);
#if 0
/* System Clock Update */
SystemCoreClockUpdate();
/* System Tick Configuration */
_SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
#endif
/* 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
}
编译无误,下载到开发板上,结果如下,
3.2. 创建单线程—SRAM静态内存
首先将rt_hw_board_init()里测试亮灯去掉。
void rt_hw_board_init()
{
SystemClock_Config();
/* 初始化SysTick */
HAL_SYSTICK_Config( HAL_RCC_GetSysClockFreq() / RT_TICK_PER_SECOND );
HAL_Init();
MX_GPIO_Init();
//HAL_GPIO_WritePin(RGB_B_GPIO_Port, RGB_B_Pin, GPIO_PIN_RESET);
//while (1);
#if 0
/* System Clock Update */
SystemCoreClockUpdate();
/* System Tick Configuration */
_SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
#endif
/* 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
}
3.2.1. 定义线程函数
让开发板上面的RGB蓝色灯以1s的频率闪烁,
static void RGB_link_thread_entry(void* parameter)
{
while(1)
{
HAL_GPIO_WritePin(RGB_B_GPIO_Port, RGB_B_Pin, GPIO_PIN_RESET);
rt_thread_delay(1000);
HAL_GPIO_WritePin(RGB_B_GPIO_Port, RGB_B_Pin, GPIO_PIN_SET);
rt_thread_delay(1000);
}
}
3.2.2. 定义线程栈
ALIGN(RT_ALIGN_SIZE)
static rt_uint8_t rt_rgb_link_thread_stack[512];
3.2.3. 定义线程控制块
static struct rt_thread rgb_thread;
3.2.4. 初始化线程/启动线程
int main(void)
{
rt_thread_init(&rgb_thread, "RGB_Link", RGB_link_thread_entry,\
RT_NULL, &rt_rgb_link_thread_stack[0], \
sizeof(rt_rgb_link_thread_stack), 3, 20);
rt_thread_startup(&rgb_thread);
}
编译无误,下载到开发板上,测试无误。
3.3 创建单线程—SRAM动态内存
RT_USING_USER_MAIN 和RT_USING_HEAP这两个宏,在rtconfig.h定义, RT_USING_USER_MAIN默认开启,RT_USING_HEAP在使用动态内存时需要开启。
#include "main.h"
#include "rtthread.h"
#if 0
ALIGN(RT_ALIGN_SIZE)
static rt_uint8_t rt_rgb_link_thread_stack[512];
static struct rt_thread rgb_thread;
static void RGB_link_thread_entry(void* parameter);
int main(void)
{
rt_thread_init(&rgb_thread, "RGB_Link", RGB_link_thread_entry,\
RT_NULL, &rt_rgb_link_thread_stack[0], \
sizeof(rt_rgb_link_thread_stack), 3, 20);
rt_thread_startup(&rgb_thread);
}
static void RGB_link_thread_entry(void* parameter)
{
while(1)
{
HAL_GPIO_WritePin(RGB_B_GPIO_Port, RGB_B_Pin, GPIO_PIN_RESET);
rt_thread_delay(1000);
HAL_GPIO_WritePin(RGB_B_GPIO_Port, RGB_B_Pin, GPIO_PIN_SET);
rt_thread_delay(1000);
}
}
#else
static rt_thread_t rgb_thread = RT_NULL;
static void RGB_link_thread_entry(void* parameter);
int main(void)
{
rgb_thread = rt_thread_create("RGB_Link", RGB_link_thread_entry,\
RT_NULL, 512, 3, 20);
if (rgb_thread != RT_NULL)
rt_thread_startup(rgb_thread);
else
return -1;
}
static void RGB_link_thread_entry(void* parameter)
{
while(1)
{
HAL_GPIO_WritePin(RGB_R_GPIO_Port, RGB_R_Pin, GPIO_PIN_RESET);
rt_thread_delay(1000);
HAL_GPIO_WritePin(RGB_R_GPIO_Port, RGB_R_Pin, GPIO_PIN_SET);
rt_thread_delay(1000);
}
}
#endif
编译无误,下载到开发板上,测试无误。此处换成RGB红灯闪烁。读者根据实际自行修改。
4. 重映射串口到rt_kprintf函数
将串口控制台重映射到rt_kprintf函数,rt_hw_console_output函数 在board.c实现,
void rt_hw_console_output(const char *str)
{
/* 进入临界段 */
rt_enter_critical();
/* 直到字符串结束 */
while (*str!='\0')
{
/* 换行 */
if (*str=='\n')
{
HAL_UART_Transmit(&huart1 ,(uint8_t *)'\r', 1, 1000);
}
HAL_UART_Transmit(&huart1, (uint8_t *)(str++), 1, 1000);
}
/* 退出临界段 */
rt_exit_critical();
}
在board.c的rt_hw_board_init()函数中 对串口初始化
void rt_hw_board_init()
{
SystemClock_Config();
/* 初始化SysTick */
HAL_SYSTICK_Config( HAL_RCC_GetSysClockFreq() / RT_TICK_PER_SECOND );
HAL_Init();
MX_GPIO_Init();
MX_USART1_UART_Init();
#if 0
/* System Clock Update */
SystemCoreClockUpdate();
/* System Tick Configuration */
_SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
#endif
/* 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
}
当rt_kprintf函数对应的输出控制台初始化好之后(在rt_hw_board_init()完成), 系统接下来会调用函数rt_show_version()来打印RT-Thread的版本号,该函数在 kservice.c中实现,
#include "main.h"
#include "rtthread.h"
#if 0
ALIGN(RT_ALIGN_SIZE)
static rt_uint8_t rt_rgb_link_thread_stack[512];
static struct rt_thread rgb_thread;
static void RGB_link_thread_entry(void* parameter);
int main(void)
{
rt_thread_init(&rgb_thread, "RGB_Link", RGB_link_thread_entry,\
RT_NULL, &rt_rgb_link_thread_stack[0], \
sizeof(rt_rgb_link_thread_stack), 3, 20);
rt_thread_startup(&rgb_thread);
}
static void RGB_link_thread_entry(void* parameter)
{
while(1)
{
HAL_GPIO_WritePin(RGB_B_GPIO_Port, RGB_B_Pin, GPIO_PIN_RESET);
rt_thread_delay(1000);
HAL_GPIO_WritePin(RGB_B_GPIO_Port, RGB_B_Pin, GPIO_PIN_SET);
rt_thread_delay(1000);
}
}
#else
static rt_thread_t rgb1_thread= RT_NULL;
static rt_thread_t rgb2_thread = RT_NULL;
static void RGB1_link_thread_entry(void* parameter);
static void RGB2_link_thread_entry(void* parameter);
int main(void)
{
rgb1_thread = rt_thread_create("RGB_Link", RGB1_link_thread_entry,\
RT_NULL, 512, 3, 1000);
if (rgb1_thread != RT_NULL)
rt_thread_startup(rgb1_thread);
else
return -1;
rt_kprintf("led1_thread running,LED1_ON\r\n");
rgb2_thread = rt_thread_create("RGB_Link", RGB2_link_thread_entry,\
RT_NULL, 512, 3, 1000);
if (rgb2_thread != RT_NULL)
rt_thread_startup(rgb2_thread);
else
return -1;
}
static void RGB1_link_thread_entry(void* parameter)
{
while(1)
{
HAL_GPIO_WritePin(RGB_R_GPIO_Port, RGB_B_Pin, GPIO_PIN_RESET);
rt_thread_delay(500);
HAL_GPIO_WritePin(RGB_R_GPIO_Port, RGB_B_Pin, GPIO_PIN_SET);
rt_thread_delay(500);
}
}
static void RGB2_link_thread_entry(void* parameter)
{
while(1)
{
HAL_GPIO_WritePin(RGB_B_GPIO_Port, RGB_R_Pin, GPIO_PIN_RESET);
rt_thread_delay(1000);
HAL_GPIO_WritePin(RGB_B_GPIO_Port, RGB_R_Pin, GPIO_PIN_SET);
rt_thread_delay(1000);
}
}
#endif