移植RT-Thread Nano到STM32F407ZGT6上运行

1. RT-Thread Nano简单介绍

RT-Thread Nano 是国内开源的一个实时内核库,RAM与ROM占用极小,而且该有的内核功能如:线程管理、线程同步与通信、时钟管理、中断管理、内存管理等功能一个不少。

下图是 RT-Thread Nano 的软件框架:
在这里插入图片描述
Finsh组件是 RT-Thread Nano 的控制台命令系统,可实现用户命令交互。

RT-Thread Nano 它与标准版本的区别就是它只留下实时内核相关的代码,去除了标准版的 device 框架和各种组件,还有不使用 Scons 构建系统,也没有了 Kconfing 已经 ENV 配置工具,所以代码会更简单。

2. 移植准备工作

3. 移植哪些文件

移植过程主要分为两个部分:libcpu 移植与板级移植。

实际上对于不同架构要移植的代码,RT-Thread 官方也已经帮我们把常见的CPU架构相关的代码写好了,我们到 libcpu 目录下找到对应自己芯片架构的代码即可。所以真正要我们移植提供的代码就只是板级硬件初始化相关的代码。

  • libcpu 移植

    libcpu 向下提供了一套统一的 CPU 架构移植接口,是针对CPU架构(比如ARM,RISC-V)的移植。这部分接口包含了全局中断开关函数、线程上下文切换函数、时钟节拍的配置和中断函数、Cache 等等内容,RT-Thread 支持的 cpu 架构在源码的 libcpu 文件夹下。

  • 板级移植

    板级移植主要是针对 rt_hw_board_init() 函数内容的实现,该函数在板级配置文件 board.c 中,函数中做了许多系统启动必要的工作,其中包含:

    1. 配置系统时钟。
    2. 实现 OS 节拍。(其中步骤 1 和 2 为 3.1.5 版本中 #error TODO 1 的部分:#error "TODO 1: OS Tick Configuration."
    3. 初始化外设:如 GPIO/UART 等等,若需要请在此处调用。
    4. 初始化系统内存堆,实现动态堆内存管理。
    5. 板级自动初始化,使用 INIT_BOARD_EXPORT() 自动初始化的函数会在此处被初始化。
    6. 其他必要的初始化,如 MMU 配置(需要时请自行在 rt_hw_board_init 函数中调用应用函数实现)。

4. 动手移植

4.1 添加 Nano 源码到keil工程

1、把 RT-Thread Nano 下面目录的源码复制到我们准备好的裸机工程目录 rtthread_nano 下。

  • Nano 源码中的 include、libcpu、src 文件夹。
  • 配置文件:源码代码 rtthread/bsp 文件夹中的两个文件:board.crtconfig.h
    在这里插入图片描述

2、使用 keil 打开我们事先准备好的裸机工程,新建 rtthread_nano 分组,并在该分组下添加以下源码:

  • 添加工程下 rtthread/src/ 文件夹中所有文件到工程;

  • 添加工程下 rtthread/libcpu/ 文件夹中相应内核的 CPU 移植文件及上下文切换文件: cpuport.c 以及 context_rvds.S

    注意:该目录下选择我们对应芯片架构的文件目录,比如我使用的是 STM32F407 ,那么就选择 Cortex-M4 架构里面的文件。

  • 添加 rtthread/ 文件夹下的 board.c

    在这里插入图片描述

4.2 添加头文件路径

因为源码目录只有两个目录(include和bsp目录)下有头文件,只添加这两个头文件路径即可。
在这里插入图片描述

4.3 解决编译报错

添加文件和头文件路径后,编译报如下错误:

STM32F407_FreeRTOS_Template\STM32F407_FreeRTOS_Template.axf: Error: L6200E: Symbol HardFault_Handler multiply defined (by context_rvds.o and stm32f4xx_it.o).

报错说重复定义了。实际上 RT-Thread 接管异常处理函数 HardFault_Handler() 和悬挂处理函数 PendSV_Handler(),这两个函数已由 RT-Thread 实现,所以我们删除 stm32f4xx_it.c 文件定义的这两个中断函数(如果该文件没有定义这两个文件,则不用管)。

4.4 配置系统时钟和外设初始化

在 RT-Thread 启动的入口函数 rtthread_startup(void) 中,会调用一个初始化板级硬件的函数 rt_hw_board_init() ,这个函数就是需要我们在 board.c 中实现的板级初始化相关的代码。比如要完成系统的时钟配置,设备上要到的一些外设初始化等等,都要在这个函数里面实现。

我使用的是 CubeMX 生成的工程,已经生成了系统时钟的配置函数了,函数复制到 rt_hw_board_init() 调用即可,而且还用到了GPIOB和UART外设,所以初始化函数也一起放到这个函数里面调用。如下代码:

/**
 * This function will initial your board.
 */
void rt_hw_board_init()
{
	HAL_Init();						// HAL库初始化
	SystemClock_Config();			// 系统时钟配置
	MX_GPIO_Init();					// GPIO外设初始化
	MX_USART1_UART_Init();			// USART外设初始化
    /* System Clock Update */
    SystemCoreClockUpdate();
    
    /* System Tick Configuration */
    _SysTick_Config(SystemCoreClock / 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
}

另外,board.c 文件中已经实现了系统嘀嗒中断服务函数,为 RT-Thread 提供心跳。

void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    rt_tick_increase();

    /* leave interrupt */
    rt_interrupt_leave();
}

如果编译报错重复定义了这个函数的话,需要删除原来实现的 SysTick_Handler 中断服务程序,保留RT-Thread Nano 提供的这个函数。

4.5 内存堆初始化配置

系统内存堆的初始化在 board.c 中的 rt_hw_board_init() 函数中完成,内存堆功能是否使用取决于宏 RT_USING_HEAP 是否开启,RT-Thread Nano 是默认不开启内存堆功能。

当开启了内存堆功能之后,就可以使用动态内存分配了,也可以动态创建线程等等。RT-Thread Nano 实现了一套动态内存分配接口函数,如 rt_malloc、rt_free 等。

我们要使用内存堆功能,只需要在 rtconfig.h 文件中定义这个 RT_USING_HEAP 宏即可,然后内存堆初始化函数就会在 rt_hw_board_init 中被调用。

在这里插入图片描述

内存堆的默认设置大小,使用的是一个数组给定的一个固定大小了的。如下代码:

#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
#define RT_HEAP_SIZE 1024
static uint32_t rt_heap[RT_HEAP_SIZE];     // heap default size: 4K(1024 * 4)
RT_WEAK void *rt_heap_begin_get(void)
{
    return rt_heap;
}

RT_WEAK void *rt_heap_end_get(void)
{
    return rt_heap + RT_HEAP_SIZE;
}
#endif

上面代码实际上只是定义了1024字节大小的数组作为内存堆,但是我们可以通过散列文件或者链接脚本得知还剩下多少内存空间,可以作为内存堆使用。

获取内存堆最大可用RAM大小的方法

实际上 ZI数据段 之后的所有内存空间,都可以作为内存堆使用,这样就这样设置最大的内存堆大小了。关于ZI数据段的结束地址,我们可以通过链接脚本来获取。对于 keil 来说,代码如下:

#define STM32_SRAM1_START              (0x20000000)						
#define STM32_SRAM1_END                (STM32_SRAM1_START + 20 * 1024)   // 结束地址 = 0x20000000(基址) + 20K(RAM大小)

#if defined(__CC_ARM) || defined(__CLANG_ARM)  // 编译器判断
// RW_IRAM1就是 keil 散列文件定义的RAM空间大小,ZI就是获取该数据段的结束地址。从ZI段之后的所有内存空间都可以作为内存堆使用
extern int Image$$RW_IRAM1$$ZI$$Limit;                   				
#define HEAP_BEGIN      ((void *)&Image$$RW_IRAM1$$ZI$$Limit)			// 获取到 ZI 数据段的结束地址,作为内存堆的起始地址
#endif

#define HEAP_END                       STM32_SRAM1_END					// 内存堆结束地址就是芯片可以RAM空间的最大地址

5. 移植验证

当 RT-Thread Nano 启动起来之后,会创建一个 main 线程的,我们在main函数中添加实验代码。如下:

int main(void)
{
    while (1)
    {
        rt_thread_mdelay(1000);
        HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_4);
        HAL_UART_Transmit(&huart1, "hello world.\r\n", strlen("hello world.\r\n"), 10);
    }
}

上面 main 函数里面没没有调用系统时钟配置,外设初始化等函数。这是因为在调用 main 函数之前,就先调用了 rt_hw_board_init() 函数,在这个函数里面配置了系统时钟、板级外设的初始化等工作了。而 main 函数现在其实是 RT-Thread Nano 创建的一个main线程函数了,它参与线程的调度过程。

上面代码就只是让LED闪烁,和向串口输出一行字符串功能。打开串口终端,可以看到打印出的字符串如下:

在这里插入图片描述

说明 RT-Thread Nano 正常运行起来了。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,STMF407ZGT6STM32F407VET6是基于ARM Cortex-M4内核的微控制器。在进行源码移植时,需要注意两者之间的硬件差异。 ADC采样出现问题可能是由于以下几个原因导致的: 1. 外设引脚配置不正确:确认ADC引脚配置正确连接,并且使能对应的GPIO外设。 2. ADC时钟配置不正确:配置ADC时钟时需要考虑不同芯片的时钟树结构和时钟配置寄存器。确保时钟配置正确,并且提供足够的时钟给ADC模块。 3. ADC初始化设置不正确:根据目标芯片的参考手册,确保ADC模块的初始化设置正确。注意参考电压、转换模式、采样时间等参数的配置。 4. ADC触发源设置不正确:确认ADC启动的触发源以及触发模式设置正确,例如定时器触发、软件触发或外部触发。 5. DMA配置不正确:如果使用DMA进行ADC数据转移,需要正确配置DMA通道和DMA传输模式。 如果在确认以上几个方面没有问题的情况下,仍然存在ADC采样问题,可以考虑以下步骤进行调试: 1. 使用调试器:通过串口调试器或仿真器,在代码执行过程中进行断点设置和变量查看,以确定问题出现的具体位置。 2. 输出调试信息:在ADC采样时,可以通过串口输出相关的调试信息,例如采样值、寄存器状态等,以便进一步分析问题。 3. 参考官方例程:查阅STMicroelectronics官方提供的开发板和芯片参考手册,并尝试运行官方的ADC采样示例代码,以验证硬件和软件配置的正确性。 4. 联系技术支持:如果以上步骤仍无法解决问题,可以通过STMicroelectronics的技术支持渠道,向他们报告问题并获取专业指导。 总之,在移植STM32F407ZGT6源码到STM32F407VET6时,需要仔细检查硬件和软件配置的差异,并针对性地进行调试和调整,以解决ADC采样问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值