CMSIS-Driver  Version 2.02
Peripheral Interface for Middleware and Application Code
 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:

msc_inline_mscgraph_1

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_transmit_small.png
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);

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)
{
/* Success: Wakeup Thread */
osSignalSet(tid_mySPI_Thread, 0x01);
break;
/* 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;
/* 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_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 */
/* Configure the SPI to Master, 8-bit mode @10000 kBits/sec */
/* Set CS line */
/* Clear CS line */
/* 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 */
}
}
}