| CMSIS-Driver Version 2.02 Peripheral Interface for Middleware and Application Code

- CMSIS
- CORE
- Driver
- DSP
- RTOS API
- Pack
- SVD

- Main Page
- Usage and Description
- Reference

All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
Theory of Operation

This section gives an overview of the general operation of CMSIS-Drivers. It explains the Driver Functions that are common in all CMSIS-Drivers along with the Generic Function Call Sequence. The topic Data Transfer Functions describes how data read/write operations to the peripheral are implemented.

# Data Transfer Functions

Each CMSIS-Driver defines an Access Struct for calling the various driver functions and each peripheral (that is accessed via a CMSIS-Driver) has one Driver Instance.

# Driver Functions

Each CMSIS-Driver contains these functions:

- GetVersion: can be called at any time to obtain version information of the driver interface.

- GetCapabilities: can be called at any time to obtain capabilities of the driver interface.

- Initialize: must be called first. Initializes the software resources used by the interface (clear variables, allocate memory, create CMSIS-RTOS objects, ...). Initialize may also register a SignalEvent callback function. Initialize leaves the peripheral in powered-off state.

- SignalEvent: is an optional callback function that is registered with the Initialize function. This callback function is initiated from interrupt service routines and indicates hardware events or the completion of a data block transfer operation.

- PowerControl: Controls the power profile of the peripheral and may be called after Initialize. Typically three power options are available:
  - ARM_POWER_FULL: Peripheral is turned on and fully operational. The driver may initialize at that point the peripheral registers.
  - ARM_POWER_LOW: (optional) Peripheral is in low power mode and partially operational; usually it can detect external events and wake-up.
  - ARM_POWER_OFF: Peripheral is turned off and not operational (it will also stop any pending operations). This is also the state of the peripheral after device reset.

- Uninitialize: Complementary function to Initialize. It will free all software resources used by the interface (de-allocate memory, release CMSIS-RTOS objects, ...).

- Control: Several drivers provide a control function to configure communication parameters or execute miscellaneous control functions.

Additional functions are specific to each driver interface and are described in the individual sections of each driver.

# Generic Function Call Sequence

The driver API functions GetVersion, GetCapabilities, Initialize, PowerControl, Uninitialize are called in the following order:

# Data Transfer Functions

A CMSIS-Driver implements non-blocking functions to transfer data to a peripheral. This means that the driver configures the read or write access to the peripheral and instantly returns to the calling application. The function names for data transfer end with:

- Send to write data to a peripheral.
- Receive to read data from a peripheral.
- Transfer to indicate combined read/write operations to a peripheral.

During a data transfer, the application can query the number of transferred data items using functions named GetxxxCount. On completion of a data transfer, the driver calls a callback function with a specific event code.

During the data exchange with the peripheral, the application can decide to:

- Wait (using an RTOS scheduler) for the callback completion event. The RTOS is controlled by the application code which makes the driver itself RTOS independent.
- Use polling functions that return the number of transferred data items to show progress information or partly read or fill data transfer buffers.
- Prepare another data transfer buffer for the next data transfer.

The following diagram shows the basic communication flow when using the _Send function in an application.

Non-blocking Send Function

# Access Struct

A CMSIS-Driver publishes a Access Struct with the data type name ARM_DRIVER_xxxx that gives to access the driver functions.

Example: Function Access of the SPI driver

typedef struct _ARM_DRIVER_SPI {
ARM_DRIVER_VERSION (*GetVersion) (void);
ARM_SPI_CAPABILITIES (*GetCapabilities) (void);
int32_t (*Initialize) (ARM_SPI_SignalEvent_t cb_event);
int32_t (*Uninitialize) (void);
int32_t (*PowerControl) (ARM_POWER_STATE state);
int32_t (*Send) (const void *data, uint32_t num);
int32_t (*Receive) ( void *data, uint32_t num);
int32_t (*Transfer) (const void *data_out, void *data_in, uint32_t num);
uint32_t (*GetDataCount) (void);
int32_t (*Control) (uint32_t control, uint32_t arg);
ARM_SPI_STATUS (*GetStatus) (void);
} const ARM_DRIVER_SPI;

# Driver Instances

A device may offer several peripherals of the same type. For such devices, the CMSIS-Driver publishes multiple instances of the Access Struct. The name of each driver instance reflects the names of the peripheral available in the device.

Example: Access Struct for 3 SPIs in a microcontroller device.

ARM_DRIVER_SPI Driver_SPI1; // access functions for SPI1 interface
ARM_DRIVER_SPI Driver_SPI2; // access functions for SPI2 interface
ARM_DRIVER_SPI Driver_SPI3; // access functions for SPI3 interface

# Driver Configuration

For a device family, the drivers may be configurable. The reference implementations store configuration options in a central file with the name RTE_Device.h. However, the configuration of the drivers itself is not part of the CMSIS-Driver specification.

# Example Code

The following example code shows the usage of the SPI interface.

#include "Driver_SPI.h"
#include "cmsis_os.h" // ARM::CMSIS:RTOS:Keil RTX
void mySPI_Thread(void const *argument);
osThreadId tid_mySPI_Thread;
/* SPI Driver */
extern ARM_DRIVER_SPI Driver_SPI0;
void mySPI_callback(uint32_t event)
{
switch (event)
{
case ARM_SPI_EVENT_TRANSFER_COMPLETE:
/* Success: Wakeup Thread */
osSignalSet(tid_mySPI_Thread, 0x01);
break;
case ARM_SPI_EVENT_DATA_LOST:
/* Occurs in slave mode when data is requested/sent by master
but send/receive/transfer operation has not been started
and indicates that data is lost. */
__breakpoint(0); /* Error: Call debugger or replace with custom error handling */
break;
case ARM_SPI_EVENT_MODE_FAULT:
/* Occurs in master mode when Slave Select is deactivated and
indicates Master Mode Fault. */
__breakpoint(0); /* Error: Call debugger or replace with custom error handling */
break;
}
}
/* Test data buffers */
const uint8_t testdata_out[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
uint8_t testdata_in [8];
void mySPI_Thread(void const* arg)
{
ARM_DRIVER_SPI* SPIdrv = &Driver_SPI0;
osEvent evt;
#ifdef DEBUG
ARM_DRIVER_VERSION version;
ARM_SPI_CAPABILITIES drv_capabilities;
version = SPIdrv->GetVersion();
if (version.api < 0x200) /* requires at minimum API version 2.00 or higher */
{ /* error handling */
return;
}
drv_capabilities = SPIdrv->GetCapabilities();
if (drv_capabilities.event_mode_fault == 0)
{ /* error handling */
return;
}
#endif
/* Initialize the SPI driver */
SPIdrv->Initialize(mySPI_callback);
/* Power up the SPI peripheral */
SPIdrv->PowerControl(ARM_POWER_FULL);
/* Configure the SPI to Master, 8-bit mode @10000 kBits/sec */
SPIdrv->Control(ARM_SPI_MODE_MASTER | ARM_SPI_CPOL1_CPHA1 | ARM_SPI_MSB_LSB | ARM_SPI_SS_MASTER_SW | ARM_SPI_DATA_BITS(8), 10000000);
/* Set CS line */
SPIdrv->Control(ARM_SPI_CONTROL_SS, ARM_SPI_SS_INACTIVE);
/* Clear CS line */
SPIdrv->Control(ARM_SPI_CONTROL_SS, ARM_SPI_SS_ACTIVE);
/* thread loop */
while (1)
{
/* Transmit some data */
SPIdrv->Send(testdata_out, sizeof(testdata_in));
/* Wait for completion */
evt = osSignalWait(0x01, 100);
if (evt.status == osEventTimeout) {
__breakpoint(0); /* Timeout error: Call debugger */
}
/* Receive 8 bytes of reply */
SPIdrv->Receive(testdata_in, 8);
osSignalWait(0x01, 100);
if (evt.status == osEventTimeout) {
__breakpoint(0); /* Timeout error: Call debugger */
}
}
}