航芯ACM32G103开发板评测 03 RT-Thread Nano移植 线程管理测试

航芯ACM32G103开发板评测 07 RT-Thread Nano移植 线程管理测试

1. 软硬件平台

  1. ACM32G103 Board开发板
  2. MDK-ARM Keil
  3. RT-Thread Nano 源码
    在这里插入图片描述

2. 物联网RTOS—RT-Thread

​ RT-Thread诞生于2006年,是一款以开源、中立、社区化发展起来的物联网操作系统。 RT-Thread主要采用 C 语言编写,浅显易懂,且具有方便移植的特性(可快速移植到多种主流 MCU 及模组芯片上)。RT-Thread把面向对象的设计方法应用到实时系统设计中,使得代码风格优雅、架构清晰、系统模块化并且可裁剪性非常好。RT-Thread完整版,通过在线的软件包管理工具,配合系统配置工具实现直观快速的模块化裁剪,并且可以无缝地导入丰富的软件功能包,从而实现复杂功能。

RT-Thread Nano 是一个极简版的硬实时内核,它是由 C 语言开发,采用面向对象的编程思维,具有良好的代码风格,是一款可裁剪的、抢占式实时多任务的 RTOS。其内存资源占用极小,功能包括任务处理、软件定时器、信号量、邮箱和实时调度等相对完整的实时操作系统特性。适用于大量使用的 32 位 ARM 入门级 MCU 的场合。下图是 RT-Thread Nano 的软件框图,包含支持的 CPU 架构与内核源码,还有可拆卸的 FinSH 组件:
在这里插入图片描述

支持架构:ARM:Cortex M0/ M3/ M4/ M7 等、RISC-V 及其他。

功能:线程管理、线程间同步与通信、时钟管理、中断管理、内存管理。

RT-Thread Nano的特点
  1. 下载简单

    RT-Thread Nano 以软件包的方式集成在 Keil MDK 与 CubeMX 中,可以直接在软件中下载 Nano 软件包获取源码,获取方式详见 基于 Keil MDK 移植 RT-Thread Nano 与 基于 CubeMX 移植 RT-Thread Nano 。同时也提供 下载 Nano 源码压缩包 的途径,方便在其他开发环境移植 RT-Thread Nano,如 基于 IAR 移植 RT-Thread Nano。

  2. 代码简单
    与RT-Thread 完整版不同的是,Nano 不含 Scons 构建系统,不需要 Kconfig 以及 Env 配置工具,也去除了完整版特有的 device 框架和组件,仅是一个纯净的内核。

  3. 移植简单
    由于 Nano 的极简特性,使 Nano 的移植过程变得极为简单。添加 Nano 源码到工程,就已完成 90% 的移植工作。

  4. 易裁剪:Nano 的配置文件为 rtconfig.h,该文件中列出了内核中的所有宏定义,有些默认没有打开,如需使用,打开即可。

  5. 易添加 FinSH 组件:FinSH 组件 可以很方便的在 Nano 上进行移植,而不再依赖 device 框架,只需要对接两个必要的函数即可完成 FinSH 移植。

  6. 资源占用小:对 RAM 与 ROM 的开销非常小,在支持 semaphore 和 mailbox 特性,并运行两个线程 (main 线程 + idle 线程) 情况下,ROM 和 RAM 依然保持着极小的尺寸,RAM 占用约 1K 左右,ROM 占用 4K 左右。

移植过程

  1. 先准备好rt-thread nano 源码
    在这里插入图片描述
    在这里插入图片描述

  2. 建立工程,添加相关文件

直接全部添加src文件,添加port文件时选择了arm cortex-m4架构的。ACM32G103是基于ARMv8-M架构,支持Cortex-M33和Cortex-M4F指令集,支持浮点运算和DSP。但rt-thread nano里面没有Cortex-M33文件,可能标准版支持的力度好点,因此就选择了cortex-m4的,里面的具体差别不怎么清楚,我也没有找到相关官方资料,目前按照这个去移植吧,后续出现问题的时候再移植。
在这里插入图片描述
在这里插入图片描述

  1. 注释中断处理函数

    void PendSV_Handler(void);
    void SysTick_Handler(void);
    void HardFault_Handler(void);
    

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

  1. 添加board.c rtconfig.h文件,这两个文件非常重要。重点需要修改这两个文件。

    参考官方教程 基于 Keil MDK 移植 RT-Thread Nano

(1)初始化HAL库

(2)配置系统时钟

(3)对系统时钟进行更新

(4)RT-Thread nano OS tick频率配置

在这里插入图片描述
在这里插入图片描述

在SysTick_Config中,修改了一些文件,原始库没有。

extern uint32_t g_SystemCoreClock; /*!< System Clock Frequency (Core Clock) */
uint32_t System_Get_SystemClock(void)
{
    return g_SystemCoreClock;
}
  1. 由于 SysTick_Handler() 中断服务例程由用户在 board.c 中重新实现,做了系统 OS Tick,所以还需要删除工程里中原本已经实现的 SysTick_Handler() ,避免在编译时产生重复定义。如果此时对工程进行编译,没有出现函数重复定义的错误,则不用做修改。

    在这里插入图片描述

  2. 系统内存堆的初始化
    系统内存堆的初始化在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() 将被调用.
    初始化内存堆需要堆的起始地址与结束地址这两个参数,系统中默认使用数组作为 heap,并获取了 heap 的起始地址与结束地址,该数组大小可手动更改.
    在这里插入图片描述
    综述所述,内核基本上就移植完成了,接下来进行线程管理测试。

线程管理测试

  1. 线程管理函数

    在这里插入图片描述

    线程的创建(动态创建、静态创建)

    动态创建 rt_thread_create

    rt_thread_t rt_thread_create(const char* name,
                                void (*entry)(void* parameter),
                                void* parameter,
                                rt_uint32_t stack_size,
                                rt_uint8_t priority,
                                rt_uint32_t tick);
    
    参数描述
    name线程的名称;线程名称的最大长度由 rtconfig.h 中的宏 RT_NAME_MAX 指定,多余部分会被自动截掉
    entry线程入口函数
    parameter线程入口函数参数
    stack_size线程栈大小,单位是字节
    priority线程的优先级。优先级范围根据系统配置情况(rtconfig.h 中的 RT_THREAD_PRIORITY_MAX 宏定义),如果支持的是 256 级优先级,那么范围是从 0~255,数值越小优先级越高,0 代表最高优先级
    tick线程的时间片大小。时间片(tick)的单位是操作系统的时钟节拍。当系统中存在相同优先级线程时,这个参数指定线程一次调度能够运行的最大时间长度。这个时间片运行结束时,调度器自动选择下一个就绪态的同优先级线程进行运行
    返回值
    thread线程创建成功,返回线程句柄
    RT_NULL线程创建失败

    一般情况下,入口参数没有的情况比较多。

    静态创建 rt_thread_init

    rt_err_t rt_thread_init(struct rt_thread *thread,
                            const char       *name,
                            void (*entry)(void *parameter),
                            void             *parameter,
                            void             *stack_start,
                            rt_uint32_t       stack_size,
                            rt_uint8_t        priority,
                            rt_uint32_t       tick);
    
    参数描述
    thread线程句柄。线程句柄由用户提供出来,并指向对应的线程控制块内存地址
    name线程的名称;线程名称的最大长度由 rtconfig.h 中定义的 RT_NAME_MAX 宏指定,多余部分会被自动截掉
    entry线程入口函数
    parameter线程入口函数参数
    stack_start线程栈起始地址
    stack_size**线程栈大小,单位是字节。**在大多数系统中需要做栈空间地址对齐(例如 ARM 体系结构中需要向 4 字节地址对齐)
    priority线程的优先级。优先级范围根据系统配置情况(rtconfig.h 中的 RT_THREAD_PRIORITY_MAX 宏定义),如果支持的是 256 级优先级,那么范围是从 0 ~ 255,数值越小优先级越高,0 代表最高优先级
    tick**线程的时间片大小。**时间片(tick)的单位是操作系统的时钟节拍。当系统中存在相同优先级线程时,这个参数指定线程一次调度能够运行的最大时间长度。这个时间片运行结束时,调度器自动选择下一个就绪态的同优先级线程进行运行
    返回
    RT_EOK线程创建成功
    RT_ERROR线程创建失败

    在静态创建过程中,需要预先定义一个数组,提前申请空间,动态创建过程则不需要,系统会从动态堆内存中分配一个线程句柄以及按照参数中指定的栈大小从动态堆内存中分配相应的空间。

    线程启动

    创建(初始化)的线程状态处于初始状态,并未进入就绪线程的调度队列,我们可以在线程初始化 / 创建成功后调用下面的函数接口让该线程进入就绪态:

    rt_err_t rt_thread_startup(rt_thread_t thread);
    

    当调用这个函数时,将把线程的状态更改为就绪状态,并放到相应优先级队列中等待调度。如果新启动的线程优先级比当前线程优先级高,将立刻切换到这个线程。线程启动接口 rt_thread_startup() 的参数和返回值见下表:

    参数描述
    thread线程句柄
    返回
    RT_EOK线程启动成功
    RT_ERROR线程启动失败
  2. 主函数

/******************************************************************************
*@file  : main.c
*@brief : main program
******************************************************************************/

#include "main.h" 
#include "app.h"   



/*
 * 程序清单:创建、初始化/脱离线程
 *
 * 这个例子会创建两个线程,一个动态线程,一个静态线程。
 * 静态线程在运行完毕后自动被系统脱离,动态线程一直打印计数。
 */
#include <rtthread.h>

#define THREAD_PRIORITY         19
#define THREAD_STACK_SIZE       512
#define THREAD_TIMESLICE        5

static rt_thread_t tid1 = RT_NULL;

/* 线程1的入口函数 */
static void thread1_entry(void *parameter)
{
    rt_uint32_t count = 0;

    for (count =0 ;count< 20;count++)
    {
        /* 线程1采用低优先级运行,打印计数值20 */
        rt_kprintf("thread1 count: %d\n", count ++);
    }
    rt_kprintf("thread1 exit\n");
}

ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;

/* 线程2入口 */
static void thread2_entry(void *param)
{
    rt_uint32_t count = 0;

    /* 线程2拥有较高的优先级,以抢占线程1而获得执行 */
    for (count = 0; count < 10 ; count++)
    {
        /* 线程2打印计数值 */
        rt_kprintf("thread2 count: %d\n", count);
    }
    rt_kprintf("thread2 exit\n");

    /* 线程2运行结束后也将自动被系统脱离 */
}

/* 线程示例 */
int thread_sample(void)
{
    /* 创建线程1,名称是thread1,入口是thread1_entry*/
    tid1 = rt_thread_create("create_hread1",
                            thread1_entry, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY, THREAD_TIMESLICE);

    /* 如果获得线程控制块,启动这个线程 */
    if (tid1 != RT_NULL)
        rt_thread_startup(tid1);

    /* 初始化线程2,名称是thread2,入口是thread2_entry */
    rt_thread_init(&thread2,
                   "init_thread2",
                   thread2_entry,
                   RT_NULL,
                   &thread2_stack[0],
                   sizeof(thread2_stack),
                   THREAD_PRIORITY - 1, THREAD_TIMESLICE);
    rt_thread_startup(&thread2);

    return 0;
}
/******************************************************************************
*@brief : main program
*@param : none
*@return: none
******************************************************************************/
int main(void)
{
   
    printfS("board_hardware_init [ok] \r\n");
    printfS("acm32g103-board testing by 2023.12.31 [ok] \r\n");
    printfS("rt-thread nano thread_sample [ok] \r\n");
	thread_sample();

}
  1. 测试效果
    在这里插入图片描述

​ 整个程序先执行thread2,因为THREAD_PRIORITY优先级比thread1高,thread2打印完成10次计数值之后,就执行thread1,打印完成20次计数值结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/285598.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

我的2023年度总结:从大学生到程序员的转变

在过去的一年里&#xff0c;我从一名大学生转变为一名计算机专业人士&#xff0c;经历了许多实战经历&#xff0c;其中最让我印象深刻的是我参与的一个校园App项目。在这个项目中&#xff0c;我负责后端开发和数据库设计&#xff0c;成功地将App上线并得到了师生的好评。 在技术…

小企业是否应该采用SD-WAN组网?

在当今数字化时代&#xff0c;企业成功的关键之一是建立稳定高效的网络连接。无论企业规模大小如何&#xff0c;网络都是实现高效运营和达成业务目标的重要组成部分。对于小企业而言&#xff0c;考虑成本效益和资源管理显得尤为重要。那么&#xff0c;对于小企业来说&#xff0…

电子书推荐|VMware 替代与升级攻略:技术路线、产品对比与用户实践

在进行 VMware 国产化替代时&#xff0c;您是否会遇到以下问题&#xff1a; 如何实现 VMware 整体架构/部分组件替换&#xff1f;是否可以不仅“为替换而替换”&#xff0c;而是同时实现架构的升级&#xff0c;带来更多业务价值&#xff1f;哪些国产方案具备 VMware 同等能力&…

自动化测试报告生成【Allure】

之前尝试使用过testNG自带的测试报告、优化过reportNG的测试报告&#xff0c;对这两个报告都不能满意。后经查找资料&#xff0c;发现有个神器&#xff1a; Allure&#xff08;已经有allure2了&#xff0c;笔者使用的就是allure2&#xff09;&#xff0c;生成的测试报告与上述…

便捷开发技巧——在Vscode中使用Git-翻译自Vscode官网

介绍 Want to easily manage your source code and collaborate with others? Git and GitHub are the tools you need! And with Visual Studio Code, you can set up and use them in a snap. Even if you’re a beginner, VS Code’s user-friendly interface guides you …

一些想法:关于行人检测与重识别

本文主要是介绍我们录用于 ECCV18 的一个工作&#xff1a;Person Search via A Mask-guided Two-stream CNN Model. 这篇文章着眼于 Person Search 这个任务&#xff0c;即同时考虑行人检测&#xff08;Pedestrian Detection&#xff09;与行人重识别&#xff08;Person Re-ide…

【ArcGIS微课1000例】0083:地震灾害图件制作之土壤类型分布图

本文基于1:400万矢量土壤图,制作甘肃积石山6.2级地震100km范围内土壤类型分布图。 文章目录 一、土壤分布图预览二、数据集来源及简介三、土壤分布图制作一、土壤分布图预览 二、数据集来源及简介 1. 数据来源 数据集为1:400万中国土壤图,1:400万中国土壤图(2000)由中国科…

骨传导蓝牙耳机什么牌子好用?为你揭晓不踩雷的骨传导耳机排行

喜欢运动的朋友们&#xff0c;你们一定不能错过骨传导耳机&#xff01;它真的是我们运动时的好帮手。为什么这么说呢&#xff1f;因为它不会像普通耳机那样塞住我们的耳朵&#xff0c;让我们在运动时感觉不舒服&#xff0c;甚至伤害耳朵。而且&#xff0c;它还可以帮助我们听到…

详解动态顺序表

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

ES6之Promise的链式调用

✨ 专栏介绍 在现代Web开发中&#xff0c;JavaScript已经成为了不可或缺的一部分。它不仅可以为网页增加交互性和动态性&#xff0c;还可以在后端开发中使用Node.js构建高效的服务器端应用程序。作为一种灵活且易学的脚本语言&#xff0c;JavaScript具有广泛的应用场景&#x…

老品牌新玩法?经济内循环下逆势开出100多家门店,他被央视青睐!

2023年12月26日&#xff0c;CCTV-2整点财经栏目以“抢抓复苏机遇&#xff0c;连锁品牌主打新活力”为主题&#xff0c;播报我国老品牌发展现状&#xff0c;新消费时代以来&#xff0c;消费者的选择多样化、分众化、小众化、个性化&#xff0c;给“老品牌”发展带来前所未有的挑…

Android Context在四大组件及Application中的表现

文章目录 Android Context在四大组件及Application中的表现Context是什么Context源码Activity流程分析Service流程分析BroadcastReceiver流程分析ContentProvider流程分析Application流程分析 Android Context在四大组件及Application中的表现 Context是什么 Context可以理解…

【代码随想录】刷题笔记Day43

前言 刚过完非常愉快的元旦假期&#xff0c;唔想反工啊啊啊&#xff0c;先刷刷题找回学习的状态吧 416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; dp[target] target为目标&#xff0c;weight和value相同的01背包问题&#xff0c;用一维遍历dp[j]为容量为j的背…

为什么德国如此重视可持续性有机葡萄酒种植?

可持续性在德国葡萄栽培中越来越重要&#xff0c;它包括对葡萄酒行业的生态、经济和社会问题给予同等的考虑。在过去的几年里&#xff0c;世界范围内出现了许多不同的可持续葡萄酒生产项目。 以可持续发展为导向的酒庄是如何运营的&#xff1f;作为可持续发展整体方法的一部分&…

酒店预订订房小程序源码系统+多商户入驻 完全开源可二开 附带完整的搭建教程

在传统的酒店预订流程中&#xff0c;用户往往需要通过电话、第三方平台或者酒店官网进行预订&#xff0c;这些方式或多或少存在操作繁琐、信息不同步、服务不及时等问题。而小程序的出现&#xff0c;为酒店预订提供了一个全新的解决方案。它无需下载安装&#xff0c;即用即走&a…

租房数据分析可视化大屏+58同城 Django框架 大数据毕业设计(附源码)✅

毕业设计&#xff1a;2023-2024年计算机专业毕业设计选题汇总&#xff08;建议收藏&#xff09; 毕业设计&#xff1a;2023-2024年最新最全计算机专业毕设选题推荐汇总 &#x1f345;感兴趣的可以先收藏起来&#xff0c;点赞、关注不迷路&#xff0c;大家在毕设选题&#xff…

STM32CubeMX教程11 RTC 实时时钟 - 入侵检测和时间戳

目录 1、准备材料 2、实验目标 3、实验流程 3.0、前提知识 3.1、CubeMX相关配置 3.1.1、时钟树配置 3.1.2、外设参数配置 3.1.3、外设中断配置 3.2、生成代码 3.2.1、外设初始化调用流程 3.2.2、外设中断调用流程 3.2.3、添加其他必要代码 4、常用函数 5、烧录验…

php ext-sodium 拓展安装 linux+windows

php编译安装(linux)&#xff0c;可以参考&#xff1a;php编译安装 一、windows soduim源码包自带&#xff0c;直接修改php.ini&#xff0c;取消extensionsodium注释即可 二、linux 1.安装依赖 apt-get install libsodium-dev2.进入源码目录 这里写自己的源码目录 cd /us…

【CASS精品教程】CASS11坐标换带方法(单点计算、批量计算)

参考阅读:【Pix4d精品教程】Pix4d中央子午线细化设置(测区跨两个分带) 文章目录 一、坐标换带概述二、CASS坐标换带1. 单点转换2. 批量转换三、应用场景一、坐标换带概述 坐标换带是将一个投影带的平面直角坐标系换算成另外一个投影带的平面直角坐标系的过程。这一过程主要…

【进阶】【Python网络爬虫】【19.爬虫框架】scrapy分布式采集,增量式,Redis数据库安装(附大量案例代码)(建议收藏)

Python网络爬虫 Scrapy分布式1. 分布式概述什么是分布式&#xff1f;scrapy分布式scrapy和scrapy-redis的区别 2. Redis数据库及可视化工具安装Redis是什么安装Redis数据库windowsLinuxMac电脑请参考如下教程 安装可视化工具Redis基本数据库语法 3. 编码流程&#xff08;重点&a…