这一篇在STM32U5A5移植RT-Thread Nano;毕竟RT-Thread是国产操作系统,现在已经发展的比较完善了。
一、RT-Thread Nano是什么?
RT-Thread Nano 是一个极简版的硬实时内核,它是由 C 语言开发,采用面向对象的编程思维,具有良好的代码风格,是一款可裁剪的、抢占式实时多任务的 RTOS。其内存资源占用极小,功能包括任务处理、软件定时器、信号量、邮箱和实时调度等相对完整的实时操作系统特性。适用于家电、消费电子、医疗设备、工控等领域大量使用的 32 位 ARM 入门级 MCU 的场合。
它的软件框图如下:
二、准备工作
1、电脑安装了stm32CubeMX和STM32CubeIDE;
2、STM32U5A5ZJQ开发板;
三、移植过程
1、建立工程
1)初始化时钟
2)配置三个LED灯
3)打开cache和PWR,和之前操作一样;
4)添加RT-Thread Nano组件
5)安装RT-thread
6)启用和使能RT-Thread
- 选中组件
- 启用RT-Thread
- 配置RT-Thread
7)RT-Thread参数配置
8)配置Systick时钟源为TIM1并产生工程
2、修改配置文件
1)修改board.c文件
- 修改rt_hw_board_init,增加外设初始化函数,并添加相应的头文件
#include "icache.h"
#include "memorymap.h"
#include "usart.h"
#include "gpio.h"
/*......*/
/*省略code*/
void rt_hw_board_init(void)
{
extern void SystemClock_Config(void);
extern void SystemPower_Config(void);
HAL_Init();
SystemClock_Config();
/* add code start*/
SystemPower_Config();
MX_GPIO_Init();
MX_ICACHE_Init();
MX_USART1_UART_Init();
/* add code end */
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
}
- 注释uart_init里面的代码,并替换Uart_Handle 为huart1
//UART_HandleTypeDef huart1;
static int uart_init(void)
{
/* TODO: Please modify the UART port number according to your needs */
// huart1.Instance = USART2;
// huart1.Init.BaudRate = 115200;
// huart1.Init.WordLength = UART_WORDLENGTH_8B;
// huart1.Init.StopBits = UART_STOPBITS_1;
// huart1.Init.Parity = UART_PARITY_NONE;
// huart1.Init.Mode = UART_MODE_TX_RX;
// huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
// huart1.Init.OverSampling = UART_OVERSAMPLING_16;
//
// if (HAL_UART_Init(&huart1) != 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(&huart1);
size = rt_strlen(str);
for (i = 0; i < size; i++)
{
if (*(str + i) == '\n')
{
HAL_UART_Transmit(&huart1, (uint8_t *)&a, 1, 1);
}
HAL_UART_Transmit(&huart1, (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(&huart1, UART_FLAG_RXNE) != RESET)
{
ch = huart1.Instance->RDR & 0xff; //这里也需要修改,默认生成的huart1.Instance->DR
}
else
{
rt_thread_mdelay(10);
}
return ch;
}
#endif
2)配置rtconfig.h
取消finsh_config的注释,启用控制台
// <h>Enable FinSH Configuration
// <c1>include shell config
// <i> Select this choice if you using FinSH
#include "finsh_config.h"
// </c>
// </h>
3)修改启动文件startup_stm32u5a5zjtxq.s
LoopFillZerobss:
ldr r3, = _ebss
cmp r2, r3
bcc FillZerobss
/* Call static constructors */
bl __libc_init_array
/* Call the application's entry point.*/
// bl main /*注释这一句*/
bl entry /*增加这一句;因为RT-Thread板级初始化从entry入口进行,最后初始完后就会跳转到main.c*/
4)修改链接脚本
这里主要是对比了KEIL生成的链接脚本,发现没有包含finsh_shell部分的代码段;包含后,控制台才会有效;
/* The program code and other data into "FLASH" Rom type memory */
.text :
{
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
/*RT-thread GCC Add start*/
/* section information for finsh shell */
. = ALIGN(4);
__fsymtab_start = .;
KEEP(*(FSymTab))
__fsymtab_end = .;
. = ALIGN(4);
__vsymtab_start = .;
KEEP(*(VSymTab))
__vsymtab_end = .;
. = ALIGN(4);
/* section information for initial. */
. = ALIGN(4);
__rt_init_start = .;
KEEP(*(SORT(.rti_fn*)))
__rt_init_end = .;
. = ALIGN(4);
/* RT-thread GCC Add end */
_etext = .; /* define a global symbols at end of code */
} >FLASH
5)修改main.c
- 注释掉HAL初始化相关函数
int main(void)
{
/* USER CODE BEGIN 1 */
/* 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();
/* Configure the System Power */
// SystemPower_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
// MX_GPIO_Init();
// MX_ICACHE_Init();
// MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
thread_sample();
/* USER CODE END 3 */
}
- 创建两个led任务
static void led1_thread_entry(void *parameter) {
while(1)
{
HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin);
rt_thread_mdelay(1000);
rt_kprintf("LD1 test\n");
}
}
static void led2_thread_entry(void *parameter) {
while(1) {
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
rt_thread_mdelay(1000);
rt_kprintf("LD2 test\n");
}
}
/* 线程示例 */
int thread_sample(void)
{
/* 创建线程1,名称是thread1,入口是thread1_entry*/
led1_thread = rt_thread_create("led1-thread",
led1_thread_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
/* 如果获得线程控制块,启动这个线程 */
if (led1_thread != RT_NULL)
rt_thread_startup(led1_thread);
/* 初始化线程2,名称是led2_thread,入口是led2_thread_entry */
led2_thread = rt_thread_create("led2-thread",
led2_thread_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
/* 如果获得线程控制块,启动这个线程 */
if (led2_thread != RT_NULL)
rt_thread_startup(led2_thread);
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(thread_sample, thread sample);
6)编译下载
7)验证
串口打印,如图
四、遇到的错误
1、开始生成工程之后,编译报错Error: thumb conditional instruction,如图
解决方案:修改浮点ABI
2、串口报错
重新初始化串口即可
工程:
参考移植链接:
https://gitee.com/YaoDecheng/rt-thread-gcc-finsh-demo
STM32cubeIDE F4移植RTT系统 context_gcc.s 报错_thumb conditional instruction should be in it bloc-CSDN博客