一、CMSIS 是什么?

CMSIS 的全称是 Cortex Microcontroller Software Interface Standard,即 Cortex 微控制器软件接口标准

简单来说,它是由 ARM 公司制定并提供的一套标准化软件框架。它的目的是为基于 ARM Cortex-M 处理器的芯片厂商和软件开发者提供一致性的软件接口。

您可以把它理解为芯片厂商和开发者之间的“普通话”。在没有“普通话”之前,每家芯片公司(如 ST、NXP、Microchip 等)都有自己的外设寄存器定义和函数库,导致为一家公司芯片写的代码很难移植到另一家。CMSIS 的出现就是为了解决这个碎片化问题。

二、CMSIS 的组成模块(核心内容)

其中最常用和核心的组件包括:

1. CMSIS-Core (Cortex-M)

这是最基础、最核心的部分,提供了对 Cortex-M 处理器本身的标准化访问。包含:

  • 寄存器定义:对内核寄存器(如 NVIC, SCB, SysTick 等)的地址和位定义。

  • 设备头文件:由芯片厂商提供,包含该型号芯片所有的外设寄存器定义(如 GPIOA->BSRR)、中断向量表等。这个文件(如 stm32f10x.h)是您项目的基础。

  • 系统初始化函数:如 SystemInit(),用于配置时钟和初始化关键系统。

  • 内核访问函数:用于开关中断、休眠模式、内存屏障指令等。例如 __enable_irq()__WFI()

2. CMSIS-DSP

一个功能强大且高度优化的数字信号处理库。包含:

  • 数学函数:基本数学(乘、加)、快速数学(三角函数)、滤波器、矩阵运算、变换(FFT)、电机控制函数等。

  • 优势:ARM 为 Cortex-M 内核进行了手写汇编优化,性能远超开发者自己编写的 C 代码。

3. CMSIS-RTOS API

一个为实时操作系统定义的通用 API 接口。

  • 作用:应用程序通过调用 CMSIS-RTOS2 的接口(如 osThreadNewosMutexAcquire),而不是操作系统的原生接口。这使得代码可以在不同的 RTOS(如 FreeRTOS, Azure RTOS, RT-Thread)之间无缝移植,因为只需要更换底层的 RTOS 实现适配层即可。

4. CMSIS-Driver

定义了通用外设驱动(如 UART, I2C, SPI, ETH, USB)的 API 标准。

  • 目的:让中间件和应用程序可以不依赖具体的芯片型号,通过统一的驱动接口访问外设。芯片厂商或社区会提供符合此标准的驱动实现。

5. CMSIS-Pack

一个软件包分发机制。开发者可以通过 Keil MDK 的 Pack Installer 或类似的工具,一键安装芯片支持、设备驱动、中间件和示例代码,极大地简化了项目管理。

6. CMSIS-NN

专门为 Cortex-M 处理器优化的神经网络内核库。用于在微控制器上高效地运行机器学习推理模型,是 TinyML 领域的关键基础。

三、CMSIS 与MCU库的联系

拿stm32的hal库来举例,其关系如下:

核心关系概括

所以,CMSIS首先是ARM用来“约束”和“帮助”MCU设计商的工具。

2. MCU设计商(ST)的角色 & HAL库的目的

所以,HAL库是MCU设计商用来“服务”和“吸引”最终开发者的工具。


一个具体的例子:配置一个定时器中断
  • CMSIS 是 ARM 制定的基础标准。它定义了如何访问内核(如NVIC、SysTick)和芯片基本外设的通用方法。它更底层,更接近硬件。

  • HAL库 是 ST(意法半导体) 根据CMSIS标准,为自己家的STM32芯片实现的高级应用库。它基于CMSIS,但在其上构建了更易用、更抽象的一层。

  • 为了更深入理解,我们再细化一下这个分工:
    1. ARM 的角色 & CMSIS 的目的
  • ARM 只设计 Cortex-M CPU 核心(相当于设计了一个强大的“发动机和底盘平台”)。

  • 但一个完整的MCU,除了CPU核心,还有内存、中断控制器、调试接口等,这些都是CPU核心必须的“贴身部件”。

  • CMSIS 的核心作用就在这里:ARM 通过 CMSIS-Core 为这些 “CPU核心及其贴身部件” 定义了统一的软件访问标准。这确保了无论哪个厂商,只要用了Cortex-M核心,操作内核寄存器、配置中断的方式都是一样的。

  • ST 这样的公司,拿到ARM的Cortex-M核心后,围绕它添加了GPIO、UART、I2C、ADC等各种各样的外设,最终做成STM32这颗具体的MCU(相当于在“发动机底盘”上造出了完整的“汽车”)。

  • 对于开发者来说,他们面对的是成千上万个控制这些外设的寄存器,操作非常底层和复杂。

  • CMSIS层(ARM为MCU设计商提供的标准):

    • MCU设计商提供的设备头文件(如 stm32f4xx.h)中,会使用CMSIS标准定义 TIM2 外设的寄存器结构体。

    • 开发者可以使用 NVIC_EnableIRQ(TIM2_IRQn); 这个CMSIS函数来开启定时器2的中断,因为这个中断控制器是CPU核心的一部分,遵循ARM标准。

  • HAL库层(MCU设计商为开发者提供的服务):

    • 开发者调用 HAL_TIM_Base_Start_IT(&htim2); 这个HAL函数。

    • 这个HAL函数在内部帮我们做了所有事:

      • 使用CMSIS标准设置了内核的SysTick计时器。

      • 直接操作STM32独有的定时器外设寄存器(通过CMSIS定义的结构体)来配置预分频、重载值。

      • 它最终还会调用 NVIC_EnableIRQ(TIM2_IRQn); 来开启中断。

四、CMSIS的接口设计

1. CMSIS:提供“插槽”和“命名规则”

ARM在制定CMSIS标准时,并不知道ST、NXP、TI这些公司会具体设计多少个定时器、多少个UART。因此,CMSIS所做的是:

  • 定义一个标准化的“中断编号枚举”:它知道Cortex-M内核有一个叫做NVIC的中断控制器,所以它定义了 IRQn_Type 这个枚举类型,就像预留了一排标好数字的“插槽”(例如,位置0, 1, 2...)。

  • 声明一个“设备头文件”必须提供这个枚举的具体内容:它要求芯片厂商(如ST)必须提供一个 device_name.h 的头文件,在这个文件里,必须具体化这个 IRQn_Type 枚举,给每个插槽赋予具体的意义。

2. MCU设计商:填充“插槽”

MCU设计商(如ST)根据自己芯片的实际设计,来填充CMSIS提供的“插槽”。

  • CMSIS 的“弱定义”框架(在通用头文件中):

    c

    // 这是ARM CMSIS框架定义的,一个“不完整”的枚举
    // 它相当于说:“每个芯片厂商必须在这里定义你们自己的中断号”
    typedef enum IRQn {
      // ... 一些Cortex-M内核标准中断(如SysTick, PendSV)是固定的
      MemoryManagement_IRQn = -12,
      // ... 其他内核中断
      SVCall_IRQn          = -5,
      DebugMonitor_IRQn    = -4,
      PendSV_IRQn          = -2,
      SysTick_IRQn         = -1,
      // 非内核中断从这里开始,具体是什么,由芯片决定
    } IRQn_Type;
  • ST(MCU设计商)的具体实现(在 stm32f4xx.h 中):

    c

    // ST在这里具体填充了STM32F4系列芯片的中断号
    typedef enum IRQn {
      // ... 内核中断部分与CMSIS框架完全一致
      // 非内核中断:ST根据自己芯片的设计来定义
      WWDG_IRQn                = 0,
      // ... 其他中断
      EXTI0_IRQn               = 6,
      EXTI1_IRQn               = 7,
      // ...
      TIM2_IRQn                = 28, // 您的例子:定时器2中断被分配到了28号插槽
      TIM3_IRQn                = 29,
      // ...
      USART1_IRQn              = 37, // 您的例子:串口1中断
      USART2_IRQn              = 38,
      // ...
    } IRQn_Type;
3. 地址映射也是同样的道理
  • CMSIS:定义了“外设寄存器应该用一个结构体来组织”这个方法

  • ST:根据自己设计的总线布局和外设寄存器地址,具体实现这个结构体。

例如,CMSIS说GPIO应该有个叫 BSRR 的寄存器。ST就在自己的头文件里,根据STM32芯片上GPIO外设的实际内存地址,定义:

c

#define GPIOA_BASE    (AHB1PERIPH_BASE + 0x0000)
#define GPIOA         ((GPIO_TypeDef *) GPIOA_BASE) // 将地址强制转换为标准结构体类型

// 这个GPIO_TypeDef的结构体模板是由CMSIS标准或ST遵循该标准定义的
typedef struct {
  __IO uint32_t MODER;
  __IO uint32_t OTYPER;
  // ... 其他寄存器
  __IO uint32_t BSRR;   // 位设置/清除寄存器,位于某个特定偏移地址
  // ...
} GPIO_TypeDef;

Logo

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

更多推荐