CMSIS-Driver  
Peripheral Interface for Middleware and Application Code
 
Loading...
Searching...
No Matches
Theory of Operation

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

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.

Common 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 before powering the peripheral using PowerControl. This function performs the following:
    • allocate I/O resources.
    • register an optional SignalEvent callback function.
  • 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 needs to be called after Initialize. Typically, three power options are available (see ARM_POWER_STATE):
    • ARM_POWER_FULL: Peripheral is turned on and fully operational. The driver initializes the peripheral registers, interrupts, and (optionally) DMA.
    • 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 (pending operations are terminated). This is the state after device reset.
  • Uninitialize: Complementary function to Initialize. Releases the I/O pin resources used by the interface.
  • Control: Several drivers provide a control function to configure communication parameters or execute miscellaneous control functions.

The section Function Call Sequence contains more information on the operation of each function. Additional functions are specific to each driver interface and are described in the individual sections of each driver.

Cortex-M Processor Mode

The CMSIS-Driver functions access peripherals and interrupts and are designed to execute in Privileged mode. When calling CMSIS-Driver functions from RTOS threads, it should be ensure that these threads execute in Privileged mode.

Function Call Sequence

For normal operation of the driver, the API functions GetVersion, GetCapabilities, Initialize, PowerControl, Uninitialize are called in the following order:

msc_inline_mscgraph_1

The functions GetVersion and GetCapabilities can be called any time to obtain the required information from the driver. These functions return always the same information.

Start Sequence

To start working with a peripheral the functions Initialize and PowerControl need to be called in this order:

drv->Initialize (...); // Allocate I/O pins
drv->PowerControl (ARM_POWER_FULL); // Power up peripheral, setup IRQ/DMA
@ ARM_POWER_FULL
Power on: full operation at maximum performance.
Definition: Driver_Common.h:66
  • Initialize typically allocates the I/O resources (pins) for the peripheral. The function can be called multiple times; if the I/O resources are already initialized it performs no operation and just returns with ARM_DRIVER_OK.
  • PowerControl (ARM_POWER_FULL) sets the peripheral registers including interrupt (NVIC) and optionally DMA. The function can be called multiple times; if the registers are already set it performs no operation and just returns with ARM_DRIVER_OK.

Stop Sequence

To stop working with a peripheral the functions PowerControl and Uninitialize need to be called in this order:

drv->PowerControl (ARM_POWER_OFF); // Terminate any pending transfers, reset IRQ/DMA, power off peripheral
drv->Uninitialize (...); // Release I/O pins
@ ARM_POWER_OFF
Power off: no operation possible.
Definition: Driver_Common.h:64

The functions PowerControl and Uninitialize always execute and can be used to put the peripheral into a Safe State, for example after any data transmission errors. To restart the peripheral in a error condition, you should first execute the Stop Sequence and then the Start Sequence.

  • PowerControl (ARM_POWER_OFF) terminates any pending data transfers with the peripheral, disables the peripheral and leaves it in a defined mode (typically the reset state).
    • when DMA is used it is disabled (including the interrupts)
    • peripheral interrupts are disabled on NVIC level
    • the peripheral is reset using a dedicated reset mechanism (if available) or by clearing the peripheral registers
    • pending peripheral interrupts are cleared on NVIC level
    • driver variables are cleared
  • Uninitialize always releases I/O pin resources.

Shared I/O Pins

All CMSIS-Driver provide a Start Sequence and Stop Sequence. Therefore two different drivers can share the same I/O pins, for example UART1 and SPI1 can have overlapping I/O pins. In this case the communication channels can be used as shown below:

SPI1drv->Initialize (...); // Start SPI1
SPI1drv->PowerControl (ARM_POWER_FULL);
... // Do operations with SPI1
SPI1drv->PowerControl (ARM_POWER_OFF); // Stop SPI1
SPI1drv->Uninitialize ();
...
USART1drv->Initialize (...); // Start USART1
USART1drv->PowerControl (ARM_POWER_FULL);
... // Do operations with USART1
USART1drv->PowerControl (ARM_POWER_OFF); // Stop USART1
USART1drv->Uninitialize ();

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 an Access Struct with the data type name ARM_DRIVER_xxxx that gives to access the driver functions.

Code 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);
ARM_POWER_STATE
General power states.
Definition: Driver_Common.h:63
Driver Version.
Definition: Driver_Common.h:46
void(* ARM_SPI_SignalEvent_t)(uint32_t event)
Pointer to ARM_SPI_SignalEvent : Signal SPI Event.
Definition: Driver_SPI.h:215
Access structure of the SPI Driver.
Definition: Driver_SPI.h:233
SPI Driver Capabilities.
Definition: Driver_SPI.h:221
SPI Status.
Definition: Driver_SPI.h:137

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.

Code Example: Access Struct for three 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

The access functions can be passed to middleware to specify the driver instance that the middleware should use for communication.

Naming Convention

The access structs need to follow this naming convention: the keyword Driver followed by an underscore _, the interface name IFNAME (usually in upper case letters), and the instance number n. Here's the full list of access struct names for all drivers (n to be replaced with the actual instance number):

Driver_CANn
Driver_ETH_MACn
Driver_ETH_PHYn
Driver_Flashn
Driver_GPIOn
Driver_I2Cn
Driver_MCIn
Driver_NANDn
Driver_SAIn
Driver_SPIn
Driver_Storagen
Driver_USARTn
Driver_USBDn
Driver_USBHn
Driver_WiFin

Example:

void init_middleware (ARM_DRIVER_SPI *Drv_spi) ...
\\ inside the middleware the SPI driver functions are called with:
\\ Drv_spi->function (...);
\\ setup middleware
init_middleware (&Driver_SPI1); // connect middleware to SPI1 interface
:
init_middleware (&Driver_SPI2); // connect middleware to SPI2 interface

CMSIS-Driver Files

The API of each CMSIS-Driver peripheral is published in a corresponding header file in the directory .\CMSIS\Driver\Include\ It is recommended to include such header file in the implementation file of the CMSIS-Driver.

Template files are available to simplify the development of a CMSIS-Driver. These are code skeletons that provide the structure of a CMSIS-Driver. They are available in the directory.\CMSIS\Driver\DriverTemplates\. You can also refer to working CMSIS-Driver Implementations to see how CMSIS-Drivers get implemented on real devices.

The table below summarizes the API header and template files for CMSIS-Driver interfaces, with links to GitHub and API references.

Header File Template File API Reference
Driver_Common.h Not applicable Common Driver Definitions
Driver_CAN.h Driver_CAN.c CAN Interface
Driver_ETH.h - Ethernet Interface
Driver_ETH_MAC.h Driver_ETH_MAC.c Ethernet MAC Interface
Driver_ETH_PHY.h Driver_ETH_PHY.c Ethernet PHY Interface
Driver_Flash.h Driver_Flash.c Flash Interface
Driver_GPIO.h Driver_GPIO.c GPIO Interface
Driver_I2C.h Driver_I2C.c I2C Interface
Driver_MCI.h Driver_MCI.c MCI Interface
Driver_NAND.h Driver_NAND.c NAND Interface
Driver_SAI.h Driver_SAI.c SAI Interface
Driver_SPI.h Driver_SPI.c SPI Interface
Driver_Storage.h Driver_Storage.c Storage Interface
Driver_USART.h Driver_USART.c USART Interface
Driver_USB.h - USB Interface
Driver_USBD.h Driver_USBD.c USB Device Interface
Driver_USBH.h Driver_USBH.c USB Host Interface
Driver_WiFi.h Driver_WiFi.c WiFi Interface

Driver Configuration

For a device family, the drivers may be configurable, but the configuration of the drivers itself is not part of the CMSIS-Driver specification.

Code Example

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. Occurs also in master mode
when driver cannot transfer data fast enough. */
__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 */
/* SS line = INACTIVE = HIGH */
/* thread loop */
while (1)
{
/* SS line = ACTIVE = LOW */
/* Transmit some data */
SPIdrv->Send(testdata_out, sizeof(testdata_out));
/* Wait for completion */
evt = osSignalWait(0x01, 100);
if (evt.status == osEventTimeout) {
__breakpoint(0); /* Timeout error: Call debugger */
}
/* SS line = INACTIVE = HIGH */
/* SS line = ACTIVE = LOW */
/* Receive 8 bytes of reply */
SPIdrv->Receive(testdata_in, 8);
evt = osSignalWait(0x01, 100);
if (evt.status == osEventTimeout) {
__breakpoint(0); /* Timeout error: Call debugger */
}
/* SS line = INACTIVE = HIGH */
}
}
#define ARM_SPI_EVENT_MODE_FAULT
Master Mode Fault (SS deactivated when Master)
#define ARM_SPI_EVENT_DATA_LOST
Data lost: Receive overflow / Transmit underflow.
#define ARM_SPI_EVENT_TRANSFER_COMPLETE
Data Transfer completed.
uint16_t api
API version.
Definition: Driver_Common.h:47
#define ARM_SPI_MSB_LSB
SPI Bit order from MSB to LSB (default)
#define ARM_SPI_DATA_BITS(n)
Number of Data bits.
#define ARM_SPI_CPOL1_CPHA1
Clock Polarity 1, Clock Phase 1.
int32_t(* PowerControl)(ARM_POWER_STATE state)
Pointer to ARM_SPI_PowerControl : Control SPI Interface Power.
Definition: Driver_SPI.h:238
uint32_t event_mode_fault
Signal Mode Fault event: ARM_SPI_EVENT_MODE_FAULT.
Definition: Driver_SPI.h:225
ARM_DRIVER_VERSION(* GetVersion)(void)
Pointer to ARM_SPI_GetVersion : Get driver version.
Definition: Driver_SPI.h:234
int32_t(* Initialize)(ARM_SPI_SignalEvent_t cb_event)
Pointer to ARM_SPI_Initialize : Initialize SPI Interface.
Definition: Driver_SPI.h:236
int32_t(* Control)(uint32_t control, uint32_t arg)
Pointer to ARM_SPI_Control : Control SPI Interface.
Definition: Driver_SPI.h:245
int32_t(* Send)(const void *data, uint32_t num)
Pointer to ARM_SPI_Send : Start sending data to SPI Interface.
Definition: Driver_SPI.h:239
int32_t(* Receive)(void *data, uint32_t num)
Pointer to ARM_SPI_Receive : Start receiving data from SPI Interface.
Definition: Driver_SPI.h:240
ARM_SPI_CAPABILITIES(* GetCapabilities)(void)
Pointer to ARM_SPI_GetCapabilities : Get driver capabilities.
Definition: Driver_SPI.h:235
#define ARM_SPI_CONTROL_SS
Control Slave Select; arg: 0=inactive, 1=active.
#define ARM_SPI_MODE_MASTER
SPI Master (Output on MOSI, Input on MISO); arg = Bus Speed in bps.
#define ARM_SPI_SS_MASTER_SW
SPI Slave Select when Master: Software controlled.
#define ARM_SPI_SS_INACTIVE
SPI Slave Select Signal Inactive.
#define ARM_SPI_SS_ACTIVE
SPI Slave Select Signal Active.