linux下STM32CUBEMX+手工移植rt-thread nano

linux下STM32CUBEMX+手工移植rt-thread nano

gitee仓库地址:https://gitee.com/jixukong/rt-thread-nano

因官方推荐的cubemx移植方法导致每次修改stm32CubeMX重新生成后需屏蔽部分代码,cubemx中添加组件方式造成目录复杂,内容杂乱,且网络中对于使用gcc交叉编译工具下的stm32相关教程较少,因此在此探究一种尽量减少移植难度的方法。望大家指正。

by:jixukong

email:1242408274@qq.com

移植过程

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文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aXEcVF9i-1664299905816)(./.figures/cube-project-manager-1.png)]
3.Clock configuration
这里全部使用内部时钟,使内部时钟最大化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CmfIJ9LZ-1664299905817)(./.figures/cube-clock-1.png)]
4.Pinout&Configuration
配置LED引脚和USART1,USART1开启全局中断
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7lSh57UF-1664299905817)(./.figures/cube-pinout-1.png)]
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中直接执行

介绍STM32CubeMX 5.0.0 STM32CubeMX是32位ARM®Cortex®STM32微控制器的图形工具。它是STMCube“计划的一部分,既可以作为独立应用程序使用,也可以作为集成开发环境(IDE)集成的Eclipse插件使用 .STM32CubeMX具有以下主要功能: 轻松的微控制器选择涵盖整个STM32产品组合 从STMicroelectronics板列表中选择电路板。 简单的微控制器配置(引脚,时钟树,外设,中间件)和相应的初始化C代码的生成。 通过将先前保存的配置导入新的MCU项目,可轻松切换到属于同一系列的另一个微控制器。 生成配置报告。 为选择的集成开发环境工具链生成IDE就绪项目。STM32CubeMX项目包括生成的初始化C代码,STM32 HAL驱动程序,用户配置所需的中间件堆栈以及在所选IDE中打开和构建项目所需的所有相关文件。 用户定义的应用程序序列的功耗计算。 自我更新允许用户使STM32CubeMX保持最新状态。 下载和更新用户应用程序开发所需的STM32Cube“嵌入式软件”(有关STM32Cube嵌入式软件产品的详细信息,请参阅附录E:STM32Cube嵌入式软件包)。 安装并运行STM32CubeMX 5.0.0 支持的操作系统和体系结构 Windows®7:32位(x86),64位(x64) Windows®8:32位(x86),64位(x64) Windows®10:32位(x86),64位(x64) Linux®:32位(x86)和64位(x64)(在RedHat,Ubuntu和Fedora上测试) 由于STM32CubeMX是32位应用程序,因此某些版本的Linux 64位 发行版需要安装32位兼容软件包,如作为ia32-libs。 MacOS:64位(x64)(在OS X Yosemite上测试) 软件要求 在Windows和Linux上:Java运行时环境(最低版本1.7.0_45) 在MacOS上:Java开发工具包(最低版本1.7.0_45) 安装STM32CubeMX 要安装STM32CubeMX,请执行以下步骤: 将最新的STM32CubeMX安装包的全部内容从http://www.st.com/stm32cubemx解压缩(解压缩)到同一目录中 在Windows上 确保您拥有管理员权限 双击SetupSTM32CubeMX-5.0.0.exe文件 在Linux上 确保您具有目标安装目录的访问权限,您可以以root(或sudo)身份运行安装,以在共享目录上安装STM32CubeMX 在SetupSTM32CubeMX-5.0.0.linux文件上双击(或从控制台窗口启动) 在MacOS上 确保您具有目标安装目录的访问权限,您可以以root(或sudo)身份运行安装,以在共享目录上安装STM32CubeMX 在SetupSTM32CubeMX-5.0.0应用程序上双击(或从控制台窗口启动) 安装后,您可以安全地从磁盘中删除zip的内容。 有关系统要求或其他可能安装的更多详细信息,请参阅STM32CubeMX用户手册。 运行STM32CubeMX 在Windows上 从Program Files> ST Microelectronics> STM32CubeMX中选择STM32CubeMX。 或者双击桌面上的STM32CubeMX图标。 在Linux上 从STM32CubeMX安装目录启动STM32CubeMX可执行文件 在MacOS上 从启动板启动STM32CubeMX应用程序 卸载STM32CubeMX 要卸载STM32CubeMX,请执行以下步骤: 在Windows上 打开Windows控制面板。 选择“程序和功能”以显示计算机上安装的程序列表。 右键单击STM32CubeMX并选择卸载功能。 在Linux上 使用文件资源管理器,进入STM32CubeMX安装的卸载程序目录,然后双击startuninstall桌面快捷方式 或使用java -jar /Uninstaller/uninstaller.jar手动启动卸载 在MacOS上 使用文件资源管理器,进入STM32CubeMX安装的Uninstaller目录,然后双击uninstaller.jar 或直接将STM32CubeMX应用程序移动到回收站中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hehelot

感谢您的打赏!!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值