如需下载相关开源资料请点击阅读原文

这是一个航顺 HK32F030 的 RT-Thread Nano 移植示例,记录了在 Keil 裸机工程的基础上进行 RT-Thread Nano 移植的全过程。在按文档中心的指导进行移植的过程中基本没有遇到问题,只是由于 HK32F030 的RAM 较小,无法启用 FinSH。移植工程已经分享在Gitee RT-Thread-Nano-HK32F030。

开源地址:https://gitee.com/CraztTnspt/rt-thread-nano-hk32-f030

(请复制至外部浏览器打开)

硬件信息:

MCU: 航顺 HK32F030MF4P6 , RAM: 2KB, ROM:16KB

开发板:hk32f030 - 立创EDA  (https://lceda.cn/whj4674672/hk32f030)由 @whj467467222 设计

参考文档

RT-Thread Nano 简介与下载(https://docs.rt-thread.org/#/rt-thread-version/rt-thread-nano/an0038-nano-introduction)

0.准备移植

在移植 RT-Thread Nano 之前,需要准备一个能正常运行的裸机工程。航顺的库文件包 HK32F030Mxx_Library_V1.1.4.7z 中提供了 HK32F030 的标准库、启动文件等,还有一个裸机工程模板,整理后得到这里移植使用的裸机工程。工程目录如下:

编译后烧录,看到LED闪烁,裸机程序正常运行。实测可以使用Jlink 或 CMSIS-DAP 烧录调试,而使用 ST-Link 无法识别到 HK32F030。

之后就可以开始 RT-Thread Nano 的移植了。

1.Nano Pack 安装

Nano Pack 可以在 Keil MDK IDE 内进行安装,也可以手动安装。这里选择手动安装Pack,从官网下载安装文件:RT-Thread Nano 离线安装包,下载结束后双击文件进行安装。

然后将 RT-Thread Nano 添加到工程中。如下图,点击 Manage Run-Time Environment

在 Manage Rum-Time Environment 内打开 RTOS 栏,勾选 kernal,点击 OK 后就将 RT-Thread 内核加入到工程中了。

现在能在 Keil 的 Project 栏看到 RTOS,展开后可以看到 RT-Thread Nano 的文件已经加入了工程。

2.适配 RT-Thread Nano

中断与异常处理

RT-Thread 会接管异常处理函数 HardFault_Handler() 和悬挂处理函数 PendSV_Handler(),这两个函数已由 RT-Thread 实现,所以需要删除工程里中断服务例程文件 Drivers/hk32f030m_it.c 中的这两个函数,避免在编译时产生重复定义。如果此时对工程进行编译,没有出现函数重复定义的错误,则不用做修改。

系统时钟配置

现在需要在 RTOS/board.c 中实现 系统时钟配置(为 MCU、外设提供工作时钟)与 os tick 的配置 (为操作系统提供心跳 / 节拍)。

 1void rt_hw_board_init()2{3    /* System Clock Update */4    SystemCoreClockUpdate();56    /* System Tick Configuration */7    _SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);89    /* Call components board initial (use INIT_BOARD_EXPORT()) */
10#ifdef RT_USING_COMPONENTS_INIT
11    rt_components_board_init();
12#endif
13
14#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
15    rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
16#endif
17}

上面的代码中,SystemCoreClockUpdate() 对系统时钟进行更新,_SysTick_Config() 配置了 OS Tick。此处 OS Tick 使用滴答定时器 systick 实现,需要在 board.c 中实现 SysTick_Handler() 中断服务例程,调用 RT-Thread 提供的 rt_tick_increase() ,如下:

 1void SysTick_Handler(void)2{3    /* enter interrupt */4    rt_interrupt_enter();56    rt_tick_increase();78    /* leave interrupt */9    rt_interrupt_leave();
10}

由于 SysTick_Handler() 中断服务例程由用户在 board.c 中重新实现,作为系统 OS Tick,所以还需要删除工程里中原本已经实现的 SysTick_Handler() ,避免在编译时产生重复定义。如果此时对工程进行编译,没有出现函数重复定义的错误,则不用做修改。

内存堆初始化

系统内存堆的初始化在 board.c 中的 rt_hw_board_init() 函数中完成,内存堆功能是否使用取决于宏 RT_USING_HEAP 是否开启,RT-Thread Nano 默认不开启内存堆功能,这样可以保持一个较小的体积,不用为内存堆开辟空间。开启系统 heap 将可以使用动态内存功能,如使用 rt_malloc、rt_free 以及各种系统动态创建对象的 API。若需要使用系统内存堆功能,则打开 RT_USING_HEAP 宏定义即可,此时内存堆初始化函数 rt_system_heap_init() 将被调用。

3.编写第一个应用

移植好 RT-Thread Nano 之后,则可以开始编写第一个应用代码验证移植结果。此时 main() 函数就转变成 RT-Thread 操作系统的一个线程,现在可以在 main() 函数中实现第一个应用:板载 LED 指示灯闪烁。

  1. 首先在文件首部增加 RT-Thread 的相关头文件 <rtthread.h> 。

  2. 在 main() 函数中(也就是在 main 线程中)实现 LED 闪烁代码:初始化 LED 引脚、在循环中点亮 / 熄灭 LED。

  3. 将延时函数替换为 RT-Thread 提供的延时函数 rt_thread_mdelay()。该函数会发起系统调度,切换到其他线程运行,体现了线程的实时性。

此时可以看到 LED 闪烁,虽然现象与裸机程序一致,但 RT-Thread 已经在 HK32F030 上成功运行。

使用 RTOS 造成固件变大后,通过CMSIS-DAP 烧录程序可能出现失败现象:

将 CMSIS-DAP 的 SW 调试速度调低为 500kHz 后烧录成功:

4.移植控制台 FinSH

由于 RAM 的大小有限,这里 FinSH 的移植未能完成

在 Nano 上添加 UART 控制台

在 RT-Thread Nano 上添加 UART 控制台打印功能后,就可以在代码中使用 RT-Thread 提供的打印函数 rt_kprintf() 进行信息打印,从而获取自定义的打印信息,方便定位代码 bug 或者获取系统当前运行状态等。实现控制台打印(需要确认 rtconfig.h 中已使能 RT_USING_CONSOLE 宏定义),需要完成基本的硬件初始化,以及对接一个系统输出字符的函数,本小节将详细说明。

实现串口初始化

使用串口对接控制台的打印,首先需要初始化串口,如引脚、波特率等。 uart_init() 需要在 board.c 中的 rt_hw_board_init() 函数中调用。

1static int uart_init(void);

示例代码:如下是基于 HK32 库的 HK32F030 串口初始化程序,参考航顺例程编写。实现了串口的发送并配置了串口中断接收。

 1#define  USART1_TX_PORT         GPIOA2#define  USART1_TX_PIN          GPIO_Pin_33#define  USART1_TX_IO_CLK_EN()  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE)45#define  USART1_RX_PORT         GPIOD6#define  USART1_RX_PIN          GPIO_Pin_67#define  USART1_RX_IO_CLK_EN()  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE)89static void USART_GPIO_Configurature(void);
10static void USART_NVIC_Configurature(void);
11static int uart_init(void);
12
13static int uart_init(void)
14{
15       USART_InitTypeDef m_usart;
16
17    USART_GPIO_Configurature();
18
19    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
20    m_usart.USART_BaudRate = 115200;
21    m_usart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
22    m_usart.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
23    m_usart.USART_Parity = USART_Parity_No;
24    m_usart.USART_StopBits = USART_StopBits_1;
25    m_usart.USART_WordLength = USART_WordLength_8b;
26    USART_Init(USART1, &m_usart);
27    USART_Cmd(USART1, ENABLE);
28
29    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
30
31    USART_NVIC_Configurature();
32
33    return 0;
34}
35//INIT_BOARD_EXPORT(uart_init);
36
37static void USART_GPIO_Configurature(void)
38{
39    GPIO_InitTypeDef m_gpio;
40
41    USART1_TX_IO_CLK_EN();
42    USART1_RX_IO_CLK_EN();
43
44    m_gpio.GPIO_Mode = GPIO_Mode_AF;
45    m_gpio.GPIO_OType = GPIO_OType_PP;
46    m_gpio.GPIO_Pin = USART1_TX_PIN;
47    m_gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
48    m_gpio.GPIO_Speed = GPIO_Speed_10MHz;
49    GPIO_Init(USART1_TX_PORT, &m_gpio);
50    GPIO_PinAFConfig(USART1_TX_PORT,GPIO_PinSource3,GPIO_AF_1);
51
52    m_gpio.GPIO_Pin = USART1_RX_PIN;
53    GPIO_Init(USART1_RX_PORT, &m_gpio);
54    GPIO_PinAFConfig(USART1_RX_PORT,GPIO_PinSource6,GPIO_AF_1);
55}
56static void USART_NVIC_Configurature(void)
57{
58    NVIC_SetPriority(USART1_IRQn,0);
59    NVIC_EnableIRQ(USART1_IRQn);
60}

实现 rt_hw_console_output

实现 finsh 组件输出一个字符,即实现 uart 输出一个字符:

注:注意:RT-Thread 系统中已有的打印均以 \n结尾,而并非 \r\n,所以在字符输出时,需要在输出 \n之前输出 \r,完成回车与换行,否则系统打印出来的信息将只有换行。

示例代码:如下是基于HK32 库实现的串口驱动对接 rt_hw_console_output()

 1void USART1_SendByte(uint8_t ch)2{3    while((USART1->ISR & USART_ISR_TXE) == 0);4    USART1->TDR = ch;5}6void rt_hw_console_output(const char *str)7{8    rt_size_t i = 0, size = 0;9    char a = '\r';
10
11    size = rt_strlen(str);
12    for (i = 0; i < size; i++)
13    {
14        if (*(str + i) == '\n')
15        {
16            USART1_SendByte((uint8_t)a);
17        }
18        USART1_SendByte(*(str + i));
19    }
20}

将对应代码加入 board.c ,编译并烧录后,可以看到终端(或串口助手中输出的rtt logo):

至此就可以使用 rt_kprintf() 打印调试信息了。

在 Nano 上添加 FinSH 组件

RT-Thread FinSH 是 RT-Thread 的命令行组件(shell),提供一套供用户在命令行调用的操作接口,主要用于调试或查看系统信息。它可以使用串口 / 以太网 / USB 等与 PC 机进行通信。这里使用串口方式,在 Nano 上实现 FinSH 功能。

Keil 添加 FinSH 源码

打开 Manage Run-Environment:

勾选 shell 然后点击OK,将 FinSH 组件的源码到工程。

这时看到 RTOS Group 中加入了以下 FinSH 文件:

实现 rt_hw_console_getchar()

要实现 FinSH 组件功能(既可以打印也能输入命令进行调试),需要在 board.c 中对接控制台输入函数,实现字符输入:

  • rt_hw_console_getchar():控制台获取一个字符,即在该函数中实现 uart 获取字符,可以使用查询方式获取(注意不要死等,在未获取到字符时,需要让出 CPU),也可以使用中断方式获取。

示例代码:(未实现)

1char rt_hw_console_getchar(void)
2{
3    int ch = -1;
4    // 接收一个字符
5    ...
6    return ch;
7}

加入 FinSH 后 RAM 空间不足

这时编译会出现报错:

看编译输出应该是存储空间不足,超出RAM大小154 Bytes,尝试将编译器优化等级调高至 Level2 ,但仍会报错。

然后尝试在 rtconfig.h 中调小 RT_CONSOLEBUF_SIZE 与 FINSH_THREAD_STACK_SIZE ,编译成功。可以看此时的内存占用:Program Size: Code=9458 RO-data=922 RW-data=144 ZI-data=1832 ,ROM占用为 Code+RO+RW=10524 Byte,RAM 占用为 RW+ZI=1976 Byte,RAM即将耗尽。同时因为调小了线程运行栈,程序运行时会产生 hard fault,因此不再考虑将 finsh 移植至nano上。

为了正常使用,应当关闭 FinSH 组件,RTE_Components.h中注释 RTE_USING_FINSH,此时程序大小为:Program Size: Code=5756 RO-data=572 RW-data=120 ZI-data=1264 ,ROM占用为 Code+RO+RW=6448 Byte,RAM 占用为 RW+ZI=1384 Byte,剩余空间较为充裕。也可以通过在 Manage Run-Environment 中关闭 shell,移除 FinSH 组件。

至此,在 HK32F030MF4P6 上的 RT-Thread Nano 移植工作就完成了。

你可以添加微信17775982065为好友,注明:公司+姓名,拉进 RT-Thread 官方微信交流群!

???? 点击阅读原文进入原文出处

【国产MCU系列】在 HK32F030 上移植 RT-Thread Nano相关推荐

  1. 基于GD32F103C8T6添加RT Thread nano设备框架并添加串口设备(以控制台console( uart0 )为例)

    最近没事琢磨了一下使用设备框架的问题.因为将串口注册到设备框架可以应用十分丰富的软件包. 于是就整理了一下手上的工程,重新将工程梳理了一遍. 像这样是十分清爽了,其中RTOS是操作系统源代码 并且学习 ...

  2. RT Thread根据开发板制作BSP方法

    之前一直不懂怎么使用RT Thread的软件包,感谢网上的大神,看了你们的博客后大概了解一些,在此做下记录.用RT Thread软件包需要RT Thread的系统,但是RT Thread和RT Thr ...

  3. 【国产MCU移植】移植RT-Thread到国产芯片HC32L196

    大家好我是惊觉.是的,失踪人口回来了.最近参加了rt-thread的国产MCU移植活动,移植rt-thread到华大的HC32L196.rtt论坛中已有许多介绍移植到各种平台的文章,详细讲述移植步骤, ...

  4. 【国产MCU移植】移植RT-Thread到国产芯片FM33LC026

    本文由RT-Thread论坛用户@jiao96 原创发布:https://club.rt-thread.org/ask/article/3020.html 摘要 因为项目需要,使用了复旦微FM33LC ...

  5. stm32移植到国产MCU雅特力AT32

    第一章 STM32移植AT32 概述 前言 雅特力科技于2016年在重庆成立的国产MCU品牌,全系列产品采用55nm先进工艺主打M4内核的高性能32位单片机,目前正式发布的有AT32F403.AT32 ...

  6. 文末赠书《GD32 MCU原理及固件库开发指南》5本 | 国产MCU中GD32系列有望成为未来32位MCU的主流

    学习优秀博文([guo产MCU移植]手把手教你使用RT-Thread制作GD32系列BSP)有感 一篇优秀的博文是什么样的?它有什么规律可循吗?优秀的guo产32位单片机处理器是否真的能成功替换掉st ...

  7. STM32的国产替代,盘点下我知道的国产MCU

    电子元件涨价和缺货是多少嵌入式工程师的痛,一年内上游厂家晶圆产能告急能有数十次之多.而MCU更是重灾区,且不说国内有超75%的市场都是被国外产品占据,就是本国内的代理和供应商也是漫天要价,而交期更是长 ...

  8. 华大 MCU 之一 HC32F460 替换 STM32F411 移植记录

    更新 2020年 10 月 21 日,将驱动库更新到了最新版 1.1.1 2020年 10 月 20 日,MCU 由原来的 HC32F460KCTA 更换为 HC32F460KETA 简介   目前, ...

  9. 客户回访|国产MCU测试解决方案 助力中国“芯”智造

    半导体技术持续更新迭代,MCU也在与时俱进,为了更好地迎接市场未来趋势,国产MCU厂商积极布局各系列MCU产品线,开始逐渐在特定细分领域实现突破.随着应用场景的进化升级,MCU 中包含越来越多的功能模 ...

最新文章

  1. Mozilla在Firefox Nightly 92 版本测试兼容性影响
  2. CodeForces 645B Mischievous Mess Makers
  3. 博客作业2---线性表
  4. linux 变量获取问题,有关read命令从管道输出读取变量参数的问题
  5. 密码学基础知识(九)密钥管理
  6. C++高手总结的编程规律
  7. HDOJ1874 ( 畅通工程续 ) 【单源最短路径】
  8. Python 标准库 —— fractions
  9. 《linux核心应用命令速查》连载三:sa:报告、清理并维护进程统计文件
  10. RHCE 学习笔记(32) - DNS
  11. php和js封装函数,介绍javascript-ecma中的几种封装函数
  12. 《大话数据结构》学习笔记
  13. C语言程序设计实践-C语言应用实践
  14. sql创建和添加时间字段
  15. 智象运维干货 | HP iLo4 Smash CLP命令行参考
  16. Go语言utf8汉字字符串截取处理小记
  17. PMP项目管理知识点:双因素理论
  18. 【转】苏宁500万日订单下的高可用拼购系统,到底暗藏了什么“独门秘籍”?
  19. 【iOS-Cocos2d游戏开发之二】Cocos2D 游戏开发资源贴(教程以及源码)
  20. 基于百度AI使用H5实现调用摄像头进行人脸注册、人脸搜索功能(Java)

热门文章

  1. PDCP 功能与基本概念
  2. 【机器学习】Radius Neighbors Classifier(rNN,radius nearest neighbors)
  3. spring boot 获取配置项值:@Value的使用
  4. matlab多元回归分析怎么计算,第11讲_matlab多元回归分析
  5. FIR滤波器窗口设计法和频率采样设计法
  6. 使用unity制作AR的方法 EasyAR+ARCore
  7. win10家庭版系统 修改docker为国内镜像
  8. 【python 笔记/小白快速入门python】python浅谈(一)犹抱琵琶半遮面
  9. 抖音新号怎么快速涨粉(8种抖音运营吸粉技巧)
  10. 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 7