CMSIS – or how arm standardized core access

Ever since arm started selling their processor IP back in 2001, they placed the steppingstone in processing hardware and software standardization. The ARMv7 and ARMv8 based Cortex processors are used globally in almost every smart device starting from smartphones, tablets, laptops to smart appliances, in-car infotainment systems, wearables and others.

For low-cost, real-time processing, arm offers the Cortex-M family of processor cores which has been adopted by top chip manufacturers like STMicroelectronics, Microchip, Texas Instruments and NXP. Besides the vast documentation available, arm also provides code to initialize and use the Cortex-M core features and the guidelines to write the micro-controller header file. All of these are grouped together into what arm calls the Cortex Micro-controller Software Interface Standard or CMSIS for short.

More info on CMSIS can be found on arm’s website:

From all the CMSIS components, we will take a look at the one called the CMSIS-Driver which defines generic peripheral interfaces for middleware and application code. Usually, on any type of microcontroller, the peripherals (e.g. GPIO, timers, communication interfaces) are mapped in the micrcontroller random access memory at a specific address known as the base address. Starting from this base address, each peripheral register is mapped successively. As an example, let’s take the header file of the STM32L462RE micro-controller from STMicroelectronics which follows the CMSIS guidelines:

At line 1044, starts the peripheral memory map, which defines the base addresses for all the peripherals. To better understand how this works, let’s take the first USART peripheral as an example. The base address for this peripheralis found at line 1091:

#define USART1_BASE           (APB2PERIPH_BASE + 0x3800UL)

For each peripheral, we can find defined a structure with the peripheral registers as members. In our case, the structure type definition for the USART peripherals can be found starting at line 895:

  * @brief Universal Synchronous Asynchronous Receiver Transmitter

typedef struct
  __IO uint32_t CR1;         /*!< USART Control register 1,                 Address offset: 0x00 */
  __IO uint32_t CR2;         /*!< USART Control register 2,                 Address offset: 0x04 */
  __IO uint32_t CR3;         /*!< USART Control register 3,                 Address offset: 0x08 */
  __IO uint32_t BRR;         /*!< USART Baud rate register,                 Address offset: 0x0C */
  __IO uint16_t GTPR;        /*!< USART Guard time and prescaler register,  Address offset: 0x10 */
  uint16_t  RESERVED2;       /*!< Reserved, 0x12                                                 */
  __IO uint32_t RTOR;        /*!< USART Receiver Time Out register,         Address offset: 0x14 */
  __IO uint16_t RQR;         /*!< USART Request register,                   Address offset: 0x18 */
  uint16_t  RESERVED3;       /*!< Reserved, 0x1A                                                 */
  __IO uint32_t ISR;         /*!< USART Interrupt and status register,      Address offset: 0x1C */
  __IO uint32_t ICR;         /*!< USART Interrupt flag Clear register,      Address offset: 0x20 */
  __IO uint16_t RDR;         /*!< USART Receive Data register,              Address offset: 0x24 */
  uint16_t  RESERVED4;       /*!< Reserved, 0x26                                                 */
  __IO uint16_t TDR;         /*!< USART Transmit Data register,             Address offset: 0x28 */
  uint16_t  RESERVED5;       /*!< Reserved, 0x2A                                                 */
} USART_TypeDef;

Next, to access the peripheral registers using this structure, we can find defined at line 1205 the peripheral as a pointer to a structure at the peripheral address base:

#define USART1              ((USART_TypeDef *) USART1_BASE)

Accessing different peripheral registers is possible by accessing the structure members through the pointer. We can access, for example, the control register 1 like this:


The header file also contains the bit mapping of the register contents as we can now see at line 14627. So, to enable the peripheral, we must write 1 at position 0. We can to this using the definitions from the header file:


The same approach can be used for all the peripherals and all the peripheral registers. Having a standardized, straight-forward way to access the peripherals and their registers, helps make the code more readable and eases the transition from one arm Cortex-based micro controller to another.

CMSIS is available on Github:

You may also like...

Popular Posts

Leave a Reply