CMSIS-Driver Implementations  Version 2.6.1
MCU independent device driver implementations and template files of the CMSIS-Driver API specification
 All Pages
SPI

The SPI MultiSlave wrapper (SPI_MultiSlave.c) resides on top of an arbitrary SPI CMSIS-Driver and exports a maximum of four SPI CMSIS-Drivers with SPI Master functionality only. Slave functionalities are disabled and calling the slave functions will return with an error. An SPI slave device connects to one of the exported drivers and uses it as any other SPI CMSIS-Driver (in master mode only). The wrapper provides multi thread protection.

Each slave can use a different bus configuration. The MultiSlave wrapper will detect which slave device is addressed by a particular function call and reconfigures the SPI bus accordingly. When using the SPI MultiSlave wrapper, the slave select mode must always be configured as ARM_SPI_SS_MASTER_UNUSED, since the underlying bus controlling the SPI driver can only control one slave select line at the time. The slave select line for each particular slave device is instead controlled by the MultiSlave wrapper using the function SPI_Control_SlaveSelect that must be implemented in the user application. A function prototype can be found in the SPI_Multislave.h header file and must be included in the project.

When called from different threads, the MultiSlave wrapper can be busy (if any data transfer is in progress). In such a case, transfer operations will be queued and executed immediately after the current transfer completes. The transfer queue operates as a FIFO, so transfers will be executed in the same call order as expected by the application.

The wrapper is configured using the SPI_MultiSlave_Config.h file, which contains the following options:

  • #define SPI_DRIVER specifies the underlying SPI CMSIS-Driver, which actually controls the SPI peripheral and the accesses the bus. The wrapper will connect to that driver.
  • #define SPI_ENABLE_SLAVE_x enables each SPI bus connected slave. This basically means that the driver control block Driver_SPIn will be exported by the wrapper for each particular slave.
  • #define SPI_DRIVER_SLAVE_x sets the exported control block number n, for example Driver_SPIn. The application connects to this driver.

Code example

This is a demo application which demonstrates the usage of the SPI MultiSlave driver wrapper. It consists of two threads that periodically access two SPI slave devices.

#include <string.h>
#include "cmsis_os2.h"
#include "RTE_Components.h"
#include CMSIS_device_header
#include "stm32f2xx_hal.h" // Keil::Device:STM32Cube HAL:Common
#include "SPI_MultiSlave.h" // Keil::CMSIS Driver:SPI:Multi-Slave
/* Thread prototypes */
static void Thread_A (void *argument);
static void Thread_B (void *argument);
static void app_main (void *argument);
/* A and B Thread IDs */
static osThreadId_t ThreadId_A;
static osThreadId_t ThreadId_B;
/* SPI A Driver, controls Slave Device 0, uses underlying Driver_SPI1 (see SPI_MultiSlaveConfig.h) */
extern ARM_DRIVER_SPI Driver_SPI10;
#define SPI_A (&Driver_SPI10)
/* SPI B Driver, controls Slave Device 1, uses underlying Driver_SPI1 (see SPI_MultiSlaveConfig.h) */
extern ARM_DRIVER_SPI Driver_SPI11;
#define SPI_B (&Driver_SPI11)
void SPI_Control_SlaveSelect (uint32_t device, uint32_t ss_state) {
GPIO_TypeDef* GPIOx;
uint16_t pin;
if (device == 0) {
/* Select Device 0 SS pin (SPI_A) */
GPIOx = GPIOE;
pin = GPIO_PIN_0;
}
else {
/* Select Device 1 SS pin (SPI_B) */
GPIOx = GPIOE;
pin = GPIO_PIN_15;
}
if (ss_state == ARM_SPI_SS_INACTIVE) {
/* Set GPIO pin high */
HAL_GPIO_WritePin(GPIOx, pin, GPIO_PIN_SET);
} else {
/* Set GPIO pin low */
HAL_GPIO_WritePin(GPIOx, pin, GPIO_PIN_RESET);
}
}
/*----------------------------------------------------------------------------
* SPI Thread A
*---------------------------------------------------------------------------*/
__NO_RETURN static void Thread_A (void *argument) {
char *p = "Sending data to Slave Device 0";
(void)argument;
SPI_A->Initialize(NULL);
SPI_A->PowerControl(ARM_POWER_FULL);
SPI_A->Control(ARM_SPI_MODE_MASTER | ARM_SPI_CPOL0_CPHA0 \
| ARM_SPI_DATA_BITS(8) \
| ARM_SPI_MSB_LSB \
| ARM_SPI_SS_MASTER_UNUSED,
10000000);
SPI_A->Control(ARM_SPI_SET_DEFAULT_TX_VALUE, 0xFF);
while(1) {
/* Send to Slave Device 0 */
SPI_A->Send(p, strlen(p));
osDelay(10);
}
}
/*----------------------------------------------------------------------------
* SPI Thread B
*---------------------------------------------------------------------------*/
__NO_RETURN static void Thread_B (void *argument) {
char *p = "Sending data to Slave Device 1";
(void)argument;
/* Initialize and configure SPI */
SPI_B->Initialize(NULL);
SPI_B->PowerControl(ARM_POWER_FULL);
SPI_B->Control(ARM_SPI_MODE_MASTER | ARM_SPI_CPOL1_CPHA1 \
| ARM_SPI_DATA_BITS(8) \
| ARM_SPI_MSB_LSB \
| ARM_SPI_SS_MASTER_UNUSED,
15000000);
SPI_B->Control(ARM_SPI_SET_DEFAULT_TX_VALUE, 0xFF);
while(1) {
/* Send to Slave Device 1 */
SPI_B->Send(p, strlen(p));
osDelay(10);
}
}
/*----------------------------------------------------------------------------
* Application main thread
*---------------------------------------------------------------------------*/
__NO_RETURN static void app_main (void *argument) {
(void)argument;
/* Create SPI threads */
ThreadId_A = osThreadNew(Thread_A, NULL, NULL);
ThreadId_B = osThreadNew(Thread_B, NULL, NULL);
osDelay(osWaitForever);
for (;;) {}
}
int main (void) {
// System Initialization
SystemCoreClockUpdate();
osKernelInitialize(); // Initialize CMSIS-RTOS
osThreadNew(app_main, NULL, NULL); // Create application main thread
osKernelStart(); // Start thread execution
for (;;) {}
}