Skip to content

Commit

Permalink
feat(Examples,PeriphDrivers)!: Update SPI v2 Implementation and add T…
Browse files Browse the repository at this point in the history
…arget mode functionality (#662)

Co-authored-by: sihyung-maxim <[email protected]>
Co-authored-by: Jake Carter <[email protected]>
Co-authored-by: Jake Carter <[email protected]>
  • Loading branch information
4 people authored and EricB-ADI committed Oct 26, 2023
1 parent 3dad371 commit f9d77f5
Show file tree
Hide file tree
Showing 41 changed files with 3,519 additions and 1,894 deletions.
86 changes: 6 additions & 80 deletions Examples/MAX78002/SPI/README.md
Original file line number Diff line number Diff line change
@@ -1,106 +1,32 @@
## Description

This example configures the SPI to send data between the MISO (P0.22) and
MOSI (P0.21) pins. Connect these two pins together.
This example configures the SPI to send data between the MISO (P0.22) and MOSI (P0.21) pins. Connect these two pins together.

Multiple word sizes (2 through 16 bits) are demonstrated.

By default, the example performs blocking SPI transactions. To switch to non-blocking (asynchronous) transactions, reset the BLOCKING macro to 0 and set the NON_BLOCKING macro to 1. To use DMA transactions, set the DMA macro to 1 instead.
By default, the example performs blocking SPI transactions. To switch to non-blocking (asynchronous) transactions, reset the `CONTROLLER_SYNC` macro to 0 and set the `CONTROLLER_ASYNC` macro to 1. To use DMA transactions, set the `CONTROLLER_DMA` macro to 1 instead.

This example also demonstrates the feature to use custom Target Selects that the SPI v2 Driver will automatically assert/deassert during transactions. Set the CUSTOM_TARGET macro to 1 to use the custom target. To use the default TS pins, set the CUSTOM_TARGET macro to 0 instead.
This example uses the Hardware Target Select control scheme (application does not assert the TS pins).

## Software

This example uses the SPI v2 Library. To use the previous SPI library, set `MXC_SPI_BUILD_LEGACY=1` in the Project's project.mk file.

### Porting Guide

The SPI v2 Library is backwards compatible with the previous SPI API - meaning the previously existing function prototypes have not changed. There are functional differences with SPI DMA interrupt handling that must be updated when porting a project from using the previous SPI API to SPI v2.

#### SPI v2 API Differences

##### SPI Init Function

The `MXC_SPI_Init(...)` function is still supported with SPI v2, but there is some added overhead due to the limited settings that this function can set.

Use the `MXC_SPI_Init_v2(...)` function for 1) to decrease overhead and 2) to give the caller more control in the SPI setup.

**`mxc_spi_init_t init` Fields**
- `mxc_spi_regs_t *spi` //<== SPI Instance
- `mxc_gpio_cfg_t *spi_pins` //<== (Optional) Caller supplied SPI pins.
- `mxc_spi_type_t type` //<== Controller (L. Master) or Target (L. Slave) Modes
- `uint32_t freq` //<== SPI Frequency
- `mxc_spi_clkmode_t clk_mode` //<== Clock Mode (CPOL:CPHA)
- `mxc_spi_interface_t mode` //<== Select Interface (Standard 4-wire, 3-wire, dual, quad)
- `mxc_spi_tscontrol_t ts_control` //<== HW Auto, SW Driver, or SW Application Target Control
- `mxc_spi_target_t target` //<== Target settings (custom TS pins, init mask, active polarity)
- `mxc_gpio_vssel_t vssel` //<== Select Pin Voltage Level (VDDIO/VDDIOH)
- `bool use_dma` //<== TRUE/FALSE DMA setting
- `mxc_dma_regs_t *dma` //<== DMA Instance
- `mxc_spi_callback_t callback` //<== Set Callback function for end of transaction
- `void* callback_data` //<== Data to pass through callback function

##### SPI DMA Interrupt Handling

```c
void DMA_TX_IRQHandler(void)
{
MXC_SPI_DMA_TX_Handler(SPI);
}

void DMA_RX_IRQHandler(void)
{
MXC_SPI_DMA_RX_Handler(SPI);
}

```
The previous SPI API uses the MXC_DMA_Handler, but SPI v2 supplies its own Handler processing functions to call for TX and RX DMA.
##### SPI DMA Setup
```c
...
TX_DMA_CH = MXC_SPI_DMA_GetTXChannel(SPI);
RX_DMA_CH = MXC_SPI_DMA_GetRXChannel(SPI);
NVIC_EnableIRQ(MXC_DMA_CH_GET_IRQ(TX_DMA_CH));
NVIC_EnableIRQ(MXC_DMA_CH_GET_IRQ(RX_DMA_CH));
MXC_NVIC_SetVector(MXC_DMA_CH_GET_IRQ(TX_DMA_CH), DMA_TX_IRQHandler);
MXC_NVIC_SetVector(MXC_DMA_CH_GET_IRQ(RX_DMA_CH), DMA_RX_IRQHandler);
MXC_SPI_MasterTransactionDMA(&req);
...
```
Following the DMA channel interrupt changes from the previous section, it is recommended to set up a generic name DMA TX/RX vector because the the TX and RX DMA channels won't always acquire DMA_CH0 and DMA_CH1, respectively.

##### Blocking SPI Transaction (MXC_SPI_MasterTransaction(...))
```c
void SPI_IRQHandler(void)
{
MXC_SPI_Handler(SPI); // Or MXC_SPI_AsyncHandler(SPI); Same function, different names.
}

...
NVIC_EnableIRQ(SPI);
MXC_SPI_MasterTransaction(&req);
...
```
The blocking SPI transaction function is now interrupt driven for SPI v2 - meaning the SPI instances' IRQ must be enabled and the MXC_SPI_Handler(...) or MXC_SPI_AsyncHandler(...) function must be called in the interrupt routine.
This project uses the SPI v2 library. More information on the SPI v2 library can be found in the **[MSDK User Guide Developer Notes](https://analog-devices-msdk.github.io/msdk/USERGUIDE/#spi-v2-library)**.

### Project Usage

Universal instructions on building, flashing, and debugging this project can be found in the **[MSDK User Guide](https://analog-devices-msdk.github.io/msdk/USERGUIDE/)**.

### Project-Specific Build Notes

(None - this project builds as a standard example)
Set `MXC_SPI_VERSION=v2` to build the SPI v2 libraries.

## Required Connections

- Connect a USB cable between the PC and the CN2 (USB/PWR) connector.
- Connect the 5V power cable at (5V IN).
- Close jumper (RX - P0.0) and (TX - P0.1) at Headers JP23 (UART 0 EN).
- Open an terminal application on the PC and connect to the EV kit's console UART at 115200, 8-N-1.
- Connect P0.21 (MOSI) to P0.22 (MISO).

## Expected Output

Expand Down
140 changes: 65 additions & 75 deletions Examples/MAX78002/SPI/main.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
/**
* @file main.c
* @brief SPI Controller Demo
* @details Shows Controller loopback demo for QSPI0
* Read the printf() for instructions
*/

/******************************************************************************
* Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved.
*
Expand Down Expand Up @@ -38,6 +31,12 @@
*
******************************************************************************/

/**
* @file main.c
* @brief SPI Controller Demo
* @details This example demonstrates the SPI controller loopback transactions.
*/

/***** Includes *****/
#include <stdio.h>
#include <stdbool.h>
Expand All @@ -50,24 +49,25 @@
#include "uart.h"
#include "spi.h"
#include "dma.h"
#include "led.h"

/***** Preprocessors *****/
#define BLOCKING 1
#define NON_BLOCKING 0
#define DMA 0
#define CONTROLLER_SYNC 0
#define CONTROLLER_ASYNC 0
#define CONTROLLER_DMA 1

#define CUSTOM_TARGET 0

#if (!(BLOCKING || NON_BLOCKING || DMA))
#error "You must set either BLOCKING or NON_BLOCKING or DMA to 1."
// Preprocessor Error Checking
#if (!(CONTROLLER_SYNC || CONTROLLER_ASYNC || CONTROLLER_DMA))
#error "You must set either CONTROLLER_SYNC or CONTROLLER_ASYNC or CONTROLLER_DMA to 1."
#endif
#if ((BLOCKING && NON_BLOCKING) || (NON_BLOCKING && DMA) || (DMA && BLOCKING))
#error "You must select either BLOCKING or NON_BLOCKING or DMA, not all 3."
#if ((CONTROLLER_SYNC && CONTROLLER_ASYNC) || (CONTROLLER_ASYNC && CONTROLLER_DMA) || \
(CONTROLLER_DMA && CONTROLLER_SYNC))
#error "You must select either CONTROLLER_SYNC or CONTROLLER_ASYNC or CONTROLLER_DMA, not all 3."
#endif

/***** Definitions *****/
#define DATA_LEN 100 // Words
#define DATA_VALUE 0xA5B7 // This is for master mode only...
#define DATA_VALUE 0xA5B7 // This is for Controller mode only...
#define VALUE 0xFFFF
#define SPI_SPEED 100000 // Bit Rate

Expand Down Expand Up @@ -115,21 +115,20 @@ int main(void)
{
int i, j, retVal;
uint16_t temp;
mxc_spi_init_t init;
mxc_spi_target_t target;
mxc_spi_cfg_t cfg;

printf("\n**************************** SPI CONTROLLER TEST *************************\n");
printf("This example configures the SPI to send data between the MISO (P0.22) and\n");
printf("MOSI (P0.21) pins. Connect these two pins together.\n\n");
printf("Multiple word sizes (2 through 16 bits) are demonstrated.\n\n");

#if BLOCKING
#if CONTROLLER_SYNC
printf("Performing blocking (synchronous) transactions...\n");
#endif
#if NON_BLOCKING
#if CONTROLLER_ASYNC
printf("Performing non-blocking (asynchronous) transactions...\n");
#endif
#if DMA
#if CONTROLLER_DMA
printf("Performing transactions with DMA...\n");
#endif

Expand All @@ -139,79 +138,68 @@ int main(void)
tx_data[j] = DATA_VALUE;
}

// Initialization Settings.
init.spi = SPI;
init.freq = SPI_SPEED;
init.spi_pins = NULL; // Use default, predefined pins
init.mode = MXC_SPI_INTERFACE_STANDARD; // 4-wire
init.type = MXC_SPI_TYPE_CONTROLLER;
init.clk_mode = MXC_SPI_CLKMODE_0; // CPOL: 0, CPHA: 0
init.frame_size = i;
init.callback = SPI_Callback;

// Target Select Settings
#if CUSTOM_TARGET
// Example to select a custom target.
mxc_gpio_cfg_t target_pins;
target_pins.port = MXC_GPIO0;
target_pins.mask = MXC_GPIO_PIN_9;
target_pins.func = MXC_GPIO_FUNC_OUT;
target_pins.pad = MXC_GPIO_PAD_PULL_UP;
target_pins.vssel = MXC_GPIO_VSSEL_VDDIOH; // Set custom target pin to VDDIOH (3.3V).

init.ts_control =
MXC_SPI_TSCONTROL_SW_DRV; // SPI Driver will handle deassertion for TS pins.
init.target.pins = target_pins;
init.target.active_polarity = 0;
init.vssel = MXC_GPIO_VSSEL_VDDIOH; // Set SPI pins to VDDIOH (3.3V).

// Select target for transaction.
target.pins = target_pins; // Custom pins
#else
init.ts_control = MXC_SPI_TSCONTROL_HW_AUTO; // HW will deassert/assert TS pins.
init.target.active_polarity = 0;
init.target.init_mask = 0x01; // Initialize Target Select 0 pin.
init.vssel = MXC_GPIO_VSSEL_VDDIO;
mxc_spi_pins_t spi_pins;
// This example enables the TS0 HW pin.
spi_pins.ss0 = true; // TS0
spi_pins.ss1 = false; // TS1
spi_pins.ss2 = false; // TS2
spi_pins.vddioh = true;
spi_pins.drvstr = MXC_GPIO_DRVSTR_0;

// Select target for transaction.
target.index = 0; // TS0
#endif
retVal = MXC_SPI_Init(SPI, MXC_SPI_TYPE_CONTROLLER, MXC_SPI_INTERFACE_STANDARD, 0, 0b000,
SPI_SPEED, spi_pins);
if (retVal != E_NO_ERROR) {
printf("\nSPI INITIALIZATION ERROR\n");
return retVal;
}

// SPI Settings.
cfg.spi = SPI;
cfg.clk_mode = MXC_SPI_CLKMODE_0; // CPOL: 0, CPHA: 0
cfg.frame_size = i;

// DMA Settings.
#if DMA
init.use_dma = true;
init.dma = MXC_DMA;
#if CONTROLLER_DMA
cfg.use_dma_tx = true;
cfg.use_dma_rx = true;
cfg.dma = MXC_DMA;
#else
init.use_dma = false;
cfg.use_dma_rx = false;
cfg.use_dma_tx = false;
cfg.dma = MXC_DMA;
#endif

retVal = MXC_SPI_Init_v2(&init);
retVal = MXC_SPI_Config(&cfg);
if (retVal != E_NO_ERROR) {
printf("\nSPI INITIALIZATION ERROR\n");
printf("\nSPI CONFIGURATION ERROR\n");
return retVal;
}

memset(rx_data, 0x0, DATA_LEN * sizeof(uint16_t));

// SPI Request (Callback)
mxc_spi_req_t req;
req.spi = SPI;
req.txData = (uint8_t *)tx_data;
req.txLen = DATA_LEN;
req.rxData = (uint8_t *)rx_data;
req.rxLen = DATA_LEN;
req.ssDeassert = 1;
req.completeCB = SPI_Callback;
SPI_FLAG = 1;

#if BLOCKING
// Blocking SPI v2 Implementation is Interrupt driven.
NVIC_EnableIRQ(SPI_IRQ);
MXC_SPI_ControllerTransactionB(SPI, (uint8_t *)tx_data, DATA_LEN, (uint8_t *)rx_data,
DATA_LEN, 1, &target);
#if CONTROLLER_SYNC
MXC_SPI_ControllerTransaction(&req);
#endif

#if NON_BLOCKING
#if CONTROLLER_ASYNC
NVIC_EnableIRQ(SPI_IRQ);
MXC_SPI_ControllerTransaction(SPI, (uint8_t *)tx_data, DATA_LEN, (uint8_t *)rx_data,
DATA_LEN, 1, &target);
MXC_SPI_ControllerTransactionAsync(&req);

while (SPI_FLAG == 1) {}
#endif

#if DMA
#if CONTROLLER_DMA
TX_DMA_CH = MXC_SPI_DMA_GetTXChannel(SPI);
RX_DMA_CH = MXC_SPI_DMA_GetRXChannel(SPI);

Expand All @@ -221,14 +209,14 @@ int main(void)
MXC_NVIC_SetVector(MXC_DMA_CH_GET_IRQ(TX_DMA_CH), DMA_TX_IRQHandler);
MXC_NVIC_SetVector(MXC_DMA_CH_GET_IRQ(RX_DMA_CH), DMA_RX_IRQHandler);

MXC_SPI_ControllerTransactionDMA(SPI, (uint8_t *)tx_data, DATA_LEN, (uint8_t *)rx_data,
DATA_LEN, 1, &target);
MXC_SPI_ControllerTransactionDMA(&req);

while (SPI_FLAG == 1) {}
#endif

uint8_t bits = MXC_SPI_GetFrameSize(SPI);

// Reformat tx_data to match rx_data frame size.
for (j = 0; j < DATA_LEN; j++) {
if (bits <= 8) {
if (j < (DATA_LEN / 2)) {
Expand All @@ -254,6 +242,7 @@ int main(void)
// Printf needs the Uart turned on since they share the same pins
if (memcmp(rx_data, tx_data, sizeof(tx_data)) != 0) {
printf("\n-->%2d Bits Transaction Failed\n", i);
LED_On(1);
return E_COMM_ERR;
} else {
printf("-->%2d Bits Transaction Successful\n", i);
Expand All @@ -268,5 +257,6 @@ int main(void)
}

printf("\nExample Complete.\n");
LED_On(0);
return E_NO_ERROR;
}
2 changes: 2 additions & 0 deletions Examples/MAX78002/SPI/project.mk
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@

# Add your config here!

# Build SPI v2 library for example.
MXC_SPI_VERSION = v2
Loading

0 comments on commit f9d77f5

Please sign in to comment.