嵌入式技术论坛
直播中

贾熹

8年用户 1687经验值
私信 关注
[经验]

Linux系统下STM32CUBEMX+手工移植RT-Thread Nano

0.环境

软件:

操作系统:20.04.1-Ubuntu x86_64 x86_64 x86_64 GNU/Linux
编译工具:gcc-arm-none-eabi-10.3-2021.10
代码编辑工具:visual studio code
代码生成:stm32cubemx-linux版本
下载:如在linux下使用串口下载方式,可使用stm32flash工具
串口调试:可使用minicom进行调试

硬件:

芯片:stm32f103c8t6(64KB FLASH+20KB SRAM)
时钟:均使用内部时钟
外设:本实验中以 GPIOA1为LED显示,GPIOA9-10为UART1作为shell端口

1.stm32CubeMX配置

1.打开cubeMX->File->new project,搜索并选择STM32F103C8
2.Project Manager配置
	主要配置IDE为makefile
	设置生成独立的.h.c文件


3.Clock configuration 这里全部使用内部时钟,使内部时钟最大化

4.Pinout&Configuration 配置LED引脚和USART1,USART1开启全局中断

5.保存并生成代码 目录结构如下:tree -L 2

.
├── Core
│   ├── Inc
│   └── Src
├── Drivers
│   ├── CMSIS
│   └── STM32F1xx_HAL_Driver
├── Makefile
├── rt_thread.ioc
├── startup_stm32f103xb.s
└── STM32F103C8Tx_FLASH.ld

2.下载并移动rt-thread nano

本例中使用3.1.5版本,解压至本项目文件夹中 新建bsp目录,移动board.c、rtconfig.h 为使内容看起来更清晰,删除了代码无关的部分文件 因个人喜好并不喜欢目录过深,因此在根目录下新建了bsp,也可以使用rtthread中的bsp目录新建一个板子。

.
├── bsp
│   ├── Inc
│   │   └── rtconfig.h
│   └── Src
│       └── board.c
├── Core
│   ├── Inc
│   │   ├── gpio.h
│   │   ├── main.h
│   │   ├── stm32f1xx_hal_conf.h
│   │   ├── stm32f1xx_it.h
│   │   └── usart.h
│   └── Src
│       ├── gpio.c
│       ├── main.c
│       ├── stm32f1xx_hal_msp.c
│       ├── stm32f1xx_it.c
│       ├── system_stm32f1xx.c
│       └── usart.c
├── Drivers
│   ├── CMSIS
│   │   ├── 省略
│   └── STM32F1xx_HAL_Driver
│       ├── Inc
│       ├── License.md
│       └── Src
├── Makefile
├── rt-thread
│   ├── components
│   │   ├── device
│   │   └── finsh
│   ├── include
│   │   ├── libc
│   │   ├── rtdbg.h
│   │   ├── rtdebug.h
│   │   ├── rtdef.h
│   │   ├── rthw.h
│   │   ├── rtlibc.h
│   │   ├── rtm.h
│   │   ├── rtservice.h
│   │   └── rtthread.h
│   ├── libcpu
│   │   ├── arm
│   │   └── risc-v
│   └── src
│       ├── clock.c
│       ├── components.c
│       ├── cpu.c
│       ├── idle.c
│       ├── ipc.c
│       ├── irq.c
│       ├── kservice.c
│       ├── mem.c
│       ├── memheap.c
│       ├── mempool.c
│       ├── object.c
│       ├── scheduler.c
│       ├── slab.c
│       ├── thread.c
│       └── timer.c
├── rt_thread.ioc
├── startup_stm32f103xb.s
└── STM32F103C8Tx_FLASH.ld

3.修改main.c文件内容

1.修改前在main.c中添加LED闪烁测试,此时并未移植RTOS
修改makefile中的交叉编译工具环境
#######################################
# binaries at makefile
#######################################
GCC_PATH=/usr/local/gcc-arm-none-eabi-10.3-2021.10/bin
PREFIX = arm-none-eabi-
2.makefile中添加ASMsrc Csrc INC
C_SOURCES =  \
...省略原有的
rt-thread/src/clock.c	\
rt-thread/src/components.c	\
rt-thread/src/cpu.c	\
rt-thread/src/idle.c	\
rt-thread/src/ipc.c	\
rt-thread/src/irq.c	\
rt-thread/src/kservice.c	\
rt-thread/src/mem.c	\
rt-thread/src/memheap.c	\
rt-thread/src/mempool.c	\
rt-thread/src/object.c	\
rt-thread/src/scheduler.c	\
rt-thread/src/slab.c	\
rt-thread/src/thread.c	\
rt-thread/src/timer.c	\
rt-thread/libcpu/arm/cortex-m3/cpuport.c	\
rt-thread/components/device/device.c	\
bsp/Src/board.c

# ASM sources
ASM_SOURCES =  \
startup_stm32f103xb.s	\
rt-thread/libcpu/arm/cortex-m3/context_gcc.s

# C includes
C_INCLUDES =  \
-ICore/Inc \
-IDrivers/STM32F1xx_HAL_Driver/Inc \
-IDrivers/STM32F1xx_HAL_Driver/Inc/Legacy \
-IDrivers/CMSIS/Device/ST/STM32F1xx/Include \
-IDrivers/CMSIS/Include	\
-Irt-thread/components/finsh	\
-Irt-thread/include	\
-Ibsp/Inc

4.RT-Thread 操作系统重定义 HardFault_Handler、PendSV_Handler、SysTick_Handler 中断函数

为了避免重复定义的问题,在cubemx中,需要在中断配置中,代码生成的选项中,取消选择三个中断函数(对应注释选项是 Hard fault interrupt, Pendable request, Time base :System tick timer)重新生成并编译测试。

5.在main中调用entry

调整rt_config.h中的#define RT_THREAD_PRIORITY_MAX 32 否则可能会导致出错

/* USER CODE BEGIN 2 */
	entry();
  /* USER CODE END 2 */

由于entry会启动rtthread,启动过程中会绑定main函数作为main线程。这里需修改绑定的main函数为usr_main,新建bsp/Src/usr_main.c并添加到makefile编写usr_main函数

main_thread_entry中main修改为usr_main,将main.c中的main作为了一个启动函数,但可以避免重新编译时移动board.c,因此main中取消while循环

在我们新建的usr_main函数中添加led测试代码,注意rtthead正常情况下已被启动,故此时调用的函数不是HAL_Delay()而是rt_thread_mdelay

在main.h中包含#include <rtthread.h>

6.在 RT-Thread Nano 上添加控制台与 FinSH

添加 UART 控制台(实现打印)

串口初始化和系统输出函数,即可完成 UART 控制台打印功能。
串口初始化工作我们已使用cubemx完成,rtconfig.h 中使能 RT_USING_CONSOLE,在此前我们可以在usr_main中使用HAL_UART_Transmit();测试串口输出
Core/Src/usart.c中添加对接函数rt_hw_console_output,打印出了启动信息
//对接的 rt_hw_console_output() 函数,实现控制台字符输出
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);
    }
}
\ | /
- RT -     Thread Operating System
 / | \     3.1.5 build Dec 22 2021
 2006 - 2020 Copyright by rt-thread team

添加 FinSH 组件(实现命令输入)

在 rtconfig.h 中使能 #define RT_USING_FINSH 宏定义
makefile src中添加bsp/Src/ringbuffer.c
rt-thread/components/finsh/cmd.c \
rt-thread/components/finsh/finsh_port.c \
rt-thread/components/finsh/msh.c \
rt-thread/components/finsh/shell.c

此时编译会出现

#error "TODO 4: Read a char from the uart and assign it to 'ch'."
   24 | #error "TODO 4: Read a char from the uart and assign it to 'ch'."

对接控制台输入函数,实现字符输入:实现 rt_hw_console_getchar Core/Src/usart.c

具体修改见代码
bsp/Src/ringbuffer.c
Core/Src/usart.c

由于对接完成后完成后依旧报错"TODO 4:",在makefile SRC中去掉了rt-thread/components/finsh/finsh_port.c

链接脚本

在lds中的text段添加以下内容,否则系统无法进行自动初始化函数,导致finsh线程不会被开启

/***************RTOS add**********************/
	/* section information for finsh shell */
	. = ALIGN(4);
	__fsymtab_start = .;
	KEEP(*(FSymTab))
	__fsymtab_end = .;

	. = ALIGN(4);
	__vsymtab_start = .;
	KEEP(*(VSymTab))
	__vsymtab_end = .;

	/* section information for utest */
	. = ALIGN(4);
	__rt_utest_tc_tab_start = .;
	KEEP(*(UtestTcTab))
	__rt_utest_tc_tab_end = .;

	/* section information for at server */
	. = ALIGN(4);
	__rtatcmdtab_start = .;
	KEEP(*(RtAtCmdTab))
	__rtatcmdtab_end = .;
	. = ALIGN(4);

	/* section information for initial. */
	. = ALIGN(4);
	__rt_init_start = .;
	KEEP(*(SORT(.rti_fn*)))
	__rt_init_end = .;

	. = ALIGN(4);

	PROVIDE(__ctors_start__ = .);
	KEEP (*(SORT(.init_array.*)))
	KEEP (*(.init_array))
	PROVIDE(__ctors_end__ = .);

	. = ALIGN(4);
/********************************************/

7.完成

编译后FINSH组建已经可用,并且修改cubemx后无需再移动main中的文件 目录与修改的文件如下所示

.
├── bsp								-我们自己的代码使用的文件夹
│   ├── Inc
│   │   ├── ringbuffer.h			-对接Finsh的缓冲头文件
│   │   └── rtconfig.h				-由Rtthread复制出来做rtos功能配置
│   └── Src
│       ├── board.c					-与官方移植不同的是我们没有动这个文件
│       ├── ringbuffer.c			-对接Finsh的缓冲实现
│       └── usr_main.c				-usr_main函数,也是main线程调用的函数
├── Core							-使用cubemx生成的代码
│   ├── Inc
│   │   └── ...
│   └── Src							-只修改了main.c和usart.c,且写在合适位置处,cubemx重新生成代码时不会被冲掉
│       └── ...
├── Drivers							-cubemx生成的驱动文件和HAL库
│   ├── CMSIS
│   │   └── ...
│   └── STM32F1xx_HAL_Driver
│       └── ...
├── Makefile						-makefile文件,用来指导编译,最初由cubemx生成,修改了编译环境,添加了一些源码结构
├── README.md						-说明,即本文
├── rt-thread
│   ├── components					-组件,用到了其中的finsh组建,没有修改源码
│   │   ├── device
│   │   └── finsh
│   ├── include						-rtthread使用的头文件,未修改
│   │   └── ...
│   ├── libcpu						-rtthread对于不同cpu支持的汇编代码,这里我们选用了arm/m3下的gcc.s
│   │   ├── arm
│   │   └── risc-v
│   └── src							-rtthread相关的源码,未作修改
│       └── ...
├── rt_thread.ioc					-cubemx项目文件
├── startup_stm32f103xb.s			-启动汇编代码cubemx生成,与官方不同的是,未做修改
└── STM32F103C8Tx_FLASH.ld			-链接脚本,cubemx生成,添加了rtthread所需的代码段

可以看到,我们并未修改rt-thread下的文件,因此无需关心这些源码的修改 完成后三层目录结构如下

rt-thread-nano$ tree -L 3
.
├── bsp
│   ├── Inc
│   │   ├── ringbuffer.h
│   │   └── rtconfig.h
│   └── Src
│       ├── board.c
│       ├── ringbuffer.c
│       └── usr_main.c
├── Core
│   ├── Inc
│   │   ├── gpio.h
│   │   ├── main.h
│   │   ├── stm32f1xx_hal_conf.h
│   │   ├── stm32f1xx_it.h
│   │   └── usart.h
│   └── Src
│       ├── gpio.c
│       ├── main.c
│       ├── stm32f1xx_hal_msp.c
│       ├── stm32f1xx_it.c
│       ├── system_stm32f1xx.c
│       └── usart.c
├── Drivers
│   ├── CMSIS
│   │   ├── Core
│   │   ├── Core_A
│   │   ├── Device
│   │   ├── docs
│   │   ├── DSP
│   │   ├── Include
│   │   ├── Lib
│   │   ├── LICENSE.txt
│   │   ├── NN
│   │   ├── RTOS
│   │   └── RTOS2
│   └── STM32F1xx_HAL_Driver
│       ├── Inc
│       ├── License.md
│       └── Src
├── Makefile
├── README.md
├── rt-thread
│   ├── components
│   │   ├── device
│   │   └── finsh
│   ├── include
│   │   ├── libc
│   │   ├── rtdbg.h
│   │   ├── rtdebug.h
│   │   ├── rtdef.h
│   │   ├── rthw.h
│   │   ├── rtlibc.h
│   │   ├── rtm.h
│   │   ├── rtservice.h
│   │   └── rtthread.h
│   ├── libcpu
│   │   ├── arm
│   │   └── risc-v
│   └── src
│       ├── clock.c
│       ├── components.c
│       ├── cpu.c
│       ├── idle.c
│       ├── ipc.c
│       ├── irq.c
│       ├── kservice.c
│       ├── mem.c
│       ├── memheap.c
│       ├── mempool.c
│       ├── object.c
│       ├── scheduler.c
│       ├── slab.c
│       ├── thread.c
│       └── timer.c
├── rt_thread.ioc
├── startup_stm32f103xb.s
└── STM32F103C8Tx_FLASH.ld

附录:思路

rt-thread的执行过程

本部分分析文件启动过程

startup_stm32f103xb.s:
	Reset_Handler
		64:将数据段初始化器从闪存复制到SRAM
		81:清BSS
		96:系统时钟初始化 ->Core/Src/system_stm32f1xx.c-SystemInit()空的		
		98:静态构造函数 ->__libc_init_array	 c 库的初始化
		101:跳转到入口函数(原来是main)	-> RTOS/src/components.c-entry(void) -> rtthread_startup();
	中断异常向量表:
	中断入口函数定义:
RTOS/src/components.c
	rtthread_startup()
		关中断//RTOS/libcpu/context_gcc.s-rt_hw_interrupt_disable
		板初始化//RTOS/board.c-rt_hw_board_init
		打印RTOS版本信息//RTOS/src/kservice.c-rt_show_version();
		初始化系统系统定时器 //RTOS/src/timer.c-rt_system_timer_init();
		调度器初始化//RTOS/src/scheduler.c-rt_system_scheduler_init
		初始化应用程序//RTOS/src/components.c-rt_application_init
			初始化main线程并启动绑定main_thread_entry函数
				main_thread_entry:rt线程组件初始化
					函数指针遍历执行了许多函数...
					执行了main();
		定时器线程初始化//RTOS/src/timer.c-rt_system_timer_thread_init
			此线程未开启需开启RT_USING_TIMER_SOFT宏//定时器线程绑定timer_thread函数
		空闲线程初始化//RTOS/src/idle.c-rt_thread_idle_init
			绑定rt_thread_idle_entry函数
			while(1){
				未开启RT_USING_IDLE_HOOK,若开启会执行idle钩子函数
				rt_thread_idle_excute该功能将在系统空闲时执行系统后台任务。内部未开启RT_USING_HEAP
				未开启RT_USING_PM,否则会进入电源管理器rt_system_power_manager
			}
		开启调度器//RTOS/src/scheduler.c-rt_system_scheduler_start
			进行调度执行RTOS/libcpu/context_gcc.s-rt_hw_context_switch_to

cubemx生成的默认执行过程

startup_stm32f103xb.s 之前同上,101:跳转到入口函数main中直接执行。

原作者:姬绪孔

更多回帖

发帖
×
20
完善资料,
赚取积分