一、CMSIS 6 的主要更新特性及CMSIS-RTOS2简介

CMSIS 6已经发布一段时间了,但是目前绝大多数MDK应用还在使用CMSIS 5(对应ARM Compiler 5)。
CMSIS 6是ARM公司工具链和API的一次重大升级,其官方链接可参考:https://developer.arm.com/community/arm-community-blogs/b/tools-software-ides-blog/posts/cmsis-v6-is-here
安装方法见笔者历史帖子:
https://blog.csdn.net/NJPJI_Yang/article/details/156329889?spm=1011.2415.3001.5331
CMSIS6 系统架构

CMSIS 6 是面向 Cortex‑M/Armv8‑M 的新一代嵌入式标准,相比 CMSIS 5 做了架构重构、工具链升级、新增组件与安全 / AI 增强,核心是更现代、更模块化、更适配 CI/ML/ 安全。以下是最重要特性总结:

1、架构与仓库重构(基础变革)

单仓库统一管理:合并 Core (M) 与 Core (A),废弃分散仓库,适配 GitHub 与 CMSIS‑Pack 工作流ARM。
组件独立化:DSP/NN/RTOS2 等拆分为独立 CMSIS‑Pack,按需加载、独立更新ARM。
移除废弃特性:清理 CMSIS 5 中已弃用 API,精简代码、减少冗余arm-software.github.io。

2、CMSIS‑Core 升级(内核层)

全新头文件设计:对齐 Cortex‑M 官方 TRM,统一内核寄存器与中断定义,兼容性更强arm-software.github.io。
全面支持新内核:原生支持 Cortex‑M52/M55/M85、Armv8.1‑M(含 MVE 向量扩展)、STAR‑MC3 等最新内核。
编译器适配升级:
移除 Arm Compiler 5 支持,仅支持 Arm Compiler 6、GCC 10+、IAR 8+、LLVM/Clang 17+ 现代工具链ARM。
新增 GCC 对 picolibc 启动机制支持,优化跨编译器兼容性arm-software.github.io。
MPU / 安全增强:修复 Armv8‑M MPU 内存属性定义,完善 TrustZone 与安全启动相关接口arm-software.github.io。

3、新增核心组件(能力扩展)

1). CMSIS‑Toolbox(命令行工具链)
提供 pack 管理、项目创建、CMake 构建、CI 集成 全套命令行工具ARM。
支持 VS Code 等现代 IDE,打通 “命令行 + IDE” 混合开发流程。
2). CMSIS‑View(调试与可视化)
把 MDK 专属的 Event Recorder、Component Viewer 开放为标准组件ARM。
支持事件日志、性能分析、组件状态可视化,跨工具链可用ARM。
3). CMSIS‑Compiler(C 库重定向)
跨平台统一 printf/fopen/fwrite 等标准 C 函数重定向,可输出到串口、文件或 CMSIS‑View 事件记录器ARM。
解决不同编译器 C 库行为不一致问题,提升代码可移植性ARM。
4). CMSIS‑Stream(数据流优化)
专为 DSP/ML 应用 设计,优化数据块在处理节点间的流式传输ARM。
减少内存拷贝、提升 AI / 信号处理效率,适配边缘推理场景ARM。

4、CMSIS‑RTOS2 增强(实时系统)

新增 进程隔离(Process Isolation):支持内存 / 执行域隔离,保护关键任务免受其他模块故障影响(RTX 已实现)ARM。
兼容 6 种主流 RTOS 内核(FreeRTOS、RTX、Zephyr 等),API 统一、移植更简单ARM。

5、DSP/NN 与 AI 优化

CMSIS‑DSP:深度优化 MVE 向量指令,适配 Cortex‑M55/M85,算力提升显著。
CMSIS‑NN:强化边缘 AI 推理,与 CMSIS‑Stream 协同,低功耗下高效运行神经网络。

6、工具链与生态现代化

CMake 原生支持:构建系统标准化,适配 CI/CD 与自动化测试。
CMSIS‑Pack 增强:包管理更规范,支持快速部署、版本管理与依赖解析ARM。
跨工具链一致性:统一内核 API、启动代码与调试接口,降低厂商适配成本。

7、安全与可靠性提升

完善 Armv8‑M TrustZone、MPU、安全启动 接口,满足工业 / 车规 / 医疗安全要求。
强化内存保护、错误检测与故障分析能力,提升系统鲁棒性

8、CMSIS-RTOS2简介

CMSIS RTOS2的官网:
https://arm-software.github.io/CMSIS_6/latest/RTOS2/index.html
CMSIS-RTOS2 规定了在基于 Arm® Cortex® 处理器的设备上运行的实时操作系统内核上的通用 RTOS 接口。应用程序和中间件组件可以使用 CMSIS_RTOS2 API,以实现更好的代码复用,并在各种软件生态系统中更轻松地集成。
CMSIS-RTOS2 还为 RTOS 内核指定了一个标准的 操作系统节拍接口。它提供了多种操作系统节拍实现,以便将简单内核移植到不同的 Cortex-M 和 Cortex-A 处理器上。

二、安装Keil MDK最新版

安装盘见笔者的文章:https://blog.csdn.net/NJPJI_Yang/article/details/156329889?spm=1011.2415.3001.5331

三、创建及配置工程

**1.工程初始化 **
安装好最新的Keil MDK后,新建一个工程(新建工程的目录最好为全英文,且不超过128字符),按照如下配置输入Device 配置
在这里插入图片描述

2. 设置运行环境

在随后弹出的Manage Run-Time Environment窗口中选择如下几个选项:
在CMSIS下,选择CORE,以及RTOS2 (API) > Keil RTX5,注意格式必须为Source,不能是Library,否则无法调整代码。您还需要选择CMSIS > OS Tick (API) > SysTick。在Device下,选择Startup(C Startup)。
事件记录器和半主机(semihosting)也要打开,这样就可以无需屏幕,无需串口,。直接在PC上看到你要打印的信息。
必须严格按下图配置,否则程序无法正常运行。
在这里插入图片描述
这里面要注意,RTOS2本身并不是一个完整的操作系统,只是一个抽象,其底层是内核是Keil RTX5,也是ARM官方的唯一内核。
如果第一次没有选择正确,也可以通过选择如下菜单项重新调整
在这里插入图片描述
3. 设置编译选项
为完整使用CMSIS 6和ARM Compiler6 的特性,要打开如下开关:
在这里插入图片描述
在这里插入图片描述

4. 改名(可选)

然后给Target1换一个名字,Source Group1改为Source(可选)
在这里插入图片描述

5. 选择调试器
在这里插入图片描述

6. 定义内存映射
导航至Linker选项卡,然后取消选中Use Memory Layout from Target Dialog(因为您将创建自己的内存布局)。
在这里插入图片描述
创建内存散点分布文件RTOS.sct,定义内存的散点分布文件,这个文件来源于
在这里插入图片描述
在这里插入图片描述

LOAD 0x0 0x400000 {
	ROOT 0x0 0x400000 {
		*.o (RESET, +First)
		*(InRoot$$Sections)
		.ANY (+RO)   }

	RAM 0x20000000 0x40000 {
		.ANY (+RW +ZI)     }

	ARM_LIB_HEAP  0x20040000 EMPTY 0x10000 {}

	ARM_LIB_STACK 0x20050000 EMPTY 0x10000 {}
}

该文件解释如下:

  • ROOT(运行地址 = 加载地址)
    LOAD:定义一个加载区域(程序下载到 Flash/ROM 的位置)
    加载基地址:0x0
    最大大小:0x400000 = 4MB
    ROOT:运行地址 = 下载地址,一般放中断向量、启动代码、只读代码
    地址:0x0,最大 4MB
    RESET +First:复位向量放在最开头(必须在 0x0)
    InRoot$$Sections:编译器自动放的根段
    +RO:所有只读数据 / 代码(code、const)都放这里 → 对应 Flash

  • RAM(读写变量区)
    运行地址:0x20000000(典型 Cortex‑M RAM 起始)
    大小:0x40000 = 256KB
    +RW:已初始化全局 / 静态变量
    +ZI:未初始化变量(BSS 段,上电清 0)
    → 这一段是真正的内存变量区

  • ARM_LIB_HEAP :定义堆
    起始:0x20040000
    大小:0x10000 = 64KB
    EMPTY:只占地址空间,不烧录内容
    → 给 malloc/free 使用

  • ARM_LIB_STACK :定义堆栈
    起始:0x20050000
    大小:0x10000 = 64KB
    → 函数调用、局部变量、中断现场保存

四、输入源码

- 1.创建main.c

如果要运行RTOS,必须提供main()的入口
在这里插入图片描述

// 包含RTE(Run-Time Environment)组件相关头文件
// RTE是MDK-ARM提供的运行时环境配置文件,自动生成,不能修改
#include "RTE_Components.h"

// 包含CMSIS标准设备头文件(由CMSIS自动根据芯片型号选择对应MCU头文件)
#include  CMSIS_device_header

// 包含CMSIS-RTOS2实时操作系统核心API头文件
#include "cmsis_os2.h"

// 包含事件记录器(Event Recorder)头文件,用于调试、日志输出
#include "EventRecorder.h"

// 声明应用程序入口线程函数 app_main
// 该函数将作为RTOS的主线程被创建和执行
void app_main(void *);

// 主函数,__attribute__((noreturn)) 告诉编译器此函数不会返回
int __attribute__((noreturn)) main (void) {
    // 更新系统核心时钟配置
    // 完成系统时钟、总线时钟等初始化,确保系统时钟配置正确
    SystemCoreClockUpdate();

    // 初始化事件记录器(EventRecorder)
    // EventRecordAll:开启所有级别的事件记录
    // 1:表示立即启动事件记录器
    EventRecorderInitialize(EventRecordAll, 1);

    // 初始化CMSIS-RTOS2操作系统内核
    // 完成RTOS内核数据结构、内存、调度器等基础初始化
    osKernelInitialize();

    // 创建应用程序主线程 app_main
    // 参数1:线程入口函数 app_main
    // 参数2:传递给线程的参数(这里为NULL,无参数)
    // 参数3:线程属性(NULL表示使用RTOS默认属性)
    osThreadNew(app_main, NULL, NULL);

    // 判断内核状态是否为已就绪(osKernelReady)
    // 只有内核初始化成功、线程创建成功后才会进入就绪态
    if (osKernelGetState() == osKernelReady) {
        // 启动RTOS内核调度器
        // 开始线程调度,系统从此由RTOS接管,不再返回main函数
        osKernelStart();
    }

    // 无限循环
    // 正常情况下内核启动后不会执行到这里
    // 若执行到此处,说明RTOS启动失败、系统出现异常
    while(1);
}

- 2. 创建app_main.c
这是真正的进程入口,程序如下:

创建3个线程

#include "cmsis_os2.h"

void thread1(void *);
void thread2(void *);
void thread3(void *);

void app_main (void *argument) {
	(void)argument;  // 告诉编译器:我知道没用,别警告
	osThreadNew(thread1, NULL, NULL);	// Create thread1
	osThreadNew(thread2, NULL, NULL);	// Create thread2
	osThreadNew(thread3, NULL, NULL);	// Create thread3
}

- 3.创建threads.c

这是每个线程的入口函数

#include "cmsis_os2.h"
#include <stdio.h>

void __attribute__((noreturn)) thread1(void *argument){
	(void)argument; // 告诉编译器:我知道没用,别警告
	for(;;){
		printf("hello from thread 1\n");
		osDelay(1000);
	}
}

void __attribute__((noreturn)) thread2(void *argument){
	(void)argument;
	for(;;){
		printf("hello from thread 2\n");
		osDelay(1000);
	}
}

void __attribute__((noreturn)) thread3(void *argument){
	(void)argument;
	for(;;){
		printf("hello from thread 3\n");
		osDelay(1000);
	}
}

创建完程序后,工程结构如下:
在这里插入图片描述

- 4.设置EventRecorder的来源
这个非常有趣,保证绝大多数用过Keil的人都不知道的隐藏功能,可以直接用下拉框选择宏定义参数。
在这里插入图片描述

5.编译
这时候就可以用Keil MDK的最新ARM编译器进行编译了,应该没有warning和error,可能编译的时候会出现找不到.h文件的问题,只要增加编译头文件路径即可
在这里插入图片描述
编译输出如下:
在这里插入图片描述

五、调试和运行程序

- 1.用EventRecorder查看系统事件

按ctrl+F5进入调试模式
选择EventRecorder窗口
在这里插入图片描述

不断按F5或者按下运行按钮在这里插入图片描述
就会在Event Recorder窗口不断地看到有各种事件发生
在这里插入图片描述

- 2.用semihosting直接看printf的输出
打开debug-printf的输出窗口
在这里插入图片描述
不断按F5或者按下运行按钮在这里插入图片描述
就会看到各个线程的printf输出信息
在这里插入图片描述

六、总结

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐