SPI Communication on Apollo3 Blue Plus as Slave

I need the Apollo3 Blue Plus board to receive data sent from an ESP32 board using the SPI protocol. Specifically, the ESP32 (as the SPI Master) is sending the message “DJ” every second. However, the Apollo3 Blue Plus (configured as the SPI Slave) is not receiving this message. Below I am mentioning the code that I am using for Apollo3 as a slave.

#include "string.h"
#include "am_mcu_apollo.h"
#include "am_bsp.h"
#include "am_util.h"

#define IOMN (2) // SPI instance
#define SPI_MODE (0) // Match ESP32 SPI mode (CPOL = 0, CPHA = 0)
#define SPI_FREQ (AM_HAL_IOM_2MHZ) // Match ESP32 clock speed
#define CS_PIN (16) // GPIO pin for Chip Select (CS)

void* iom_handle = NULL;
am_hal_iom_config_t iom_cfg;
am_hal_iom_transfer_t xfer = {0};

#define report(s) am_util_stdio_printf("status: 0x%08X (function: %s, file: %s, line: %d)\n", s, __PRETTY_FUNCTION__, __FILE__, __LINE__)

void init_iom(void) {
    uint32_t status = AM_HAL_STATUS_SUCCESS;

    iom_cfg.eInterfaceMode = AM_HAL_IOM_SPI_MODE;
    iom_cfg.ui32ClockFreq = SPI_FREQ;
    iom_cfg.eSpiMode = AM_HAL_IOM_SPI_MODE_0; // SPI Mode 0 (CPOL = 0, CPHA = 0)
    iom_cfg.pNBTxnBuf = NULL;
    iom_cfg.ui32NBTxnBufLength = 0;

    status = am_hal_iom_initialize(IOMN, &iom_handle);
    if (status != AM_HAL_STATUS_SUCCESS) { report(status); }

    status = am_hal_iom_power_ctrl(iom_handle, AM_HAL_SYSCTRL_WAKE, false);
    if (status != AM_HAL_STATUS_SUCCESS) { report(status); }

    status = am_hal_iom_configure(iom_handle, &iom_cfg);
    if (status != AM_HAL_STATUS_SUCCESS) { report(status); }

    status = am_hal_iom_enable(iom_handle);
    if (status != AM_HAL_STATUS_SUCCESS) { report(status); }

    // Configure SPI pins
    status = am_hal_gpio_pinconfig(AM_BSP_GPIO_IOM2_MISO, g_AM_BSP_GPIO_IOM2_MISO);
    if (status != AM_HAL_STATUS_SUCCESS) { report(status); }

    status = am_hal_gpio_pinconfig(AM_BSP_GPIO_IOM2_MOSI, g_AM_BSP_GPIO_IOM2_MOSI);
    if (status != AM_HAL_STATUS_SUCCESS) { report(status); }

    status = am_hal_gpio_pinconfig(AM_BSP_GPIO_IOM2_SCK, g_AM_BSP_GPIO_IOM2_SCK);
    if (status != AM_HAL_STATUS_SUCCESS) { report(status); }

    // Configure CS pin as output
    status = am_hal_gpio_pinconfig(AM_BSP_GPIO_IOM2_CS, g_AM_BSP_GPIO_IOM2_CS);
    if (status != AM_HAL_STATUS_SUCCESS) { report(status); }
}

int main(void) {
    uint32_t status = AM_HAL_STATUS_SUCCESS;

    // Perform the standard initialization for clocks, cache settings, and board-level low-power operation
    am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0);
    am_hal_cachectrl_config(&am_hal_cachectrl_defaults);
    am_hal_cachectrl_enable();
    am_bsp_low_power_init();

    // Initialize the printf interface for UART output
    am_bsp_uart_printf_enable();
    am_util_stdio_terminal_clear();
    am_util_stdio_printf("SPI Receive Example with CS Pin Management\n\n");

    // Initialize SPI
    init_iom();

    char rx_buf[32]; // Buffer for receiving data
    memset(rx_buf, 0, sizeof(rx_buf)); // Clear the buffer
    
		while (1) {


        // Pull CS low to select the slave
        am_hal_gpio_output_clear(CS_PIN);

        // Setup transfer
        xfer.uPeerInfo.ui32SpiChipSelect = 0; // CS pin is manually controlled
        xfer.ui32InstrLen = 0;
        xfer.ui32Instr = 0;
        xfer.ui32NumBytes = sizeof(rx_buf); // Receive full buffer size
        xfer.eDirection = AM_HAL_IOM_RX; // SPI Read
        xfer.pui32TxBuffer = NULL;
        xfer.pui32RxBuffer = (uint32_t*)rx_buf; // Receive data into the buffer
        xfer.bContinue = false;

        // Perform the SPI transaction
        status = am_hal_iom_blocking_transfer(iom_handle, &xfer);

        // Pull CS high to deselect the slave
        am_hal_gpio_output_set(CS_PIN);

        // Check transaction status
        if (status != AM_HAL_STATUS_SUCCESS) {
            report(status);
        } else {
            // Print received data
            am_util_stdio_printf("Received data: %s ",rx_buf);
//            for (int i = 0; i < sizeof(rx_buf); i++) {
//                am_util_stdio_printf("%02X ", rx_buf[i]);
//            }
					
            am_util_stdio_printf("\n");
        }

        // Wait for 1 second before the next transaction
        am_util_delay_ms(1000);
    }
}


here you can see that i did not received message. Yes, I have connected the ESP32 SPI pins and Apollo3 SPI using jumper wires. Below i am mentioning hardware connections.

PINS Name Apollo3 ESP32
MOSI -----> 28 12
MISO -----> 25 13
SCK -----> 27 15
CS -----> 16 14

Here are some details about my setup:
Protocol: SPI
Data Sending Mechanism: Polling, with the ESP32 sending the message every second.
Issue: The Apollo3 Blue Plus is not receiving the transmitted data from the ESP32.
Development Environment: I am using Keil software for development.
Goal: Receive and process the “DJ” message on the Apollo3 Blue Plus.

Could you please provide guidance or a sample code snippet for correctly setting up the Apollo3 Blue Plus as an SPI Slave in this scenario? Additionally, any advice on debugging or configuration in Keil would be greatly appreciated.

The Apollo3 library does not support to act as a SPI slave (see ReadME on GitHub - sparkfun/Arduino_Apollo3: Arduino core to support the Apollo3 microcontroller from Ambiq Micro)

I gave it try sometime ago apollo3/Ios at master · paulvha/apollo3 · GitHub. But it all depends on whether your board-pins are available. Read the .odt that is part of that library for the experiences and limitations.

I have a question: can I use the Apollo 3 IOM 2 port as a slave, or must I use the IOS port for SPI slave?
Because, as you can see, the IOM port also has the transfer direction “am_hal_iom_dir_e eDirection,” where I am set as the AM_HAL_IOM_RX. Is it working or not? I am also mentioning the code below.

#include "am_mcu_apollo.h"
#include "am_bsp.h"
#include "am_util.h"

#define IOMN (2) // SPI Module
#define SPI_MODE (3) // SPI Mode 3
#define SPI_FREQ (AM_HAL_IOM_1MHZ) // SPI Clock Frequency
void* iom_handle = NULL;
am_hal_iom_config_t iom_cfg = {0};
am_hal_iom_transfer_t xfer = {0};

#define CS_PIN (16) // Chip Select Pin

#define report(s) am_util_stdio_printf("status: 0x%08X (function: %s, file: %s, line: %d)\n", s, __PRETTY_FUNCTION__, __FILE__, __LINE__)

volatile bool xfer_complete = false;
volatile uint32_t txn_stat = 0;

void xfer_complete_callback(void *pCallbackCtxt, uint32_t transactionStatus) {
    (void)pCallbackCtxt;
    xfer_complete = true;
    txn_stat = transactionStatus;
}

void init_iom(void) {
    uint32_t status = AM_HAL_STATUS_SUCCESS;

    iom_cfg.eInterfaceMode = AM_HAL_IOM_SPI_MODE;
    iom_cfg.ui32ClockFreq = SPI_FREQ;
    iom_cfg.eSpiMode = SPI_MODE;

    status = am_hal_iom_initialize(IOMN, &iom_handle);
    if (status != AM_HAL_STATUS_SUCCESS) { report(status); }

    status = am_hal_iom_power_ctrl(iom_handle, AM_HAL_SYSCTRL_WAKE, false);
    if (status != AM_HAL_STATUS_SUCCESS) { report(status); }

    status = am_hal_iom_configure(iom_handle, &iom_cfg);
    if (status != AM_HAL_STATUS_SUCCESS) { report(status); }

    status = am_hal_iom_enable(iom_handle);
    if (status != AM_HAL_STATUS_SUCCESS) { report(status); }

    // Configure SPI Pins
    status = am_hal_gpio_pinconfig(AM_BSP_GPIO_IOM2_MISO, g_AM_BSP_GPIO_IOM2_MISO);
    if (status != AM_HAL_STATUS_SUCCESS) { report(status); }

    status = am_hal_gpio_pinconfig(AM_BSP_GPIO_IOM2_MOSI, g_AM_BSP_GPIO_IOM2_MOSI);
    if (status != AM_HAL_STATUS_SUCCESS) { report(status); }

    status = am_hal_gpio_pinconfig(AM_BSP_GPIO_IOM2_SCK, g_AM_BSP_GPIO_IOM2_SCK);
    if (status != AM_HAL_STATUS_SUCCESS) { report(status); }

    // Configure Chip Select (CS) Pin
    am_hal_gpio_pinconfig(CS_PIN, g_AM_BSP_GPIO_IOM2_CS);
    am_hal_gpio_output_set(CS_PIN); // Set CS High (inactive)
}

int main(void) {
    uint32_t status = AM_HAL_STATUS_SUCCESS;

    am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0);
    am_hal_cachectrl_config(&am_hal_cachectrl_defaults);
    am_hal_cachectrl_enable();
    am_bsp_low_power_init();
    am_bsp_uart_printf_enable();

    am_util_stdio_terminal_clear();
    am_util_stdio_printf("SPI Loopback Test on Apollo3\n");

    init_iom();

    // Message to send
    //char cmd[] = "Hello Apollo3"; // Message
    char rx_buf[128] = {0}; // Buffer to receive data

    while (1) {
        // Pull CS Low
        am_hal_gpio_output_clear(CS_PIN);

        // Prepare transfer structure for full-duplex SPI transfer
        xfer.uPeerInfo.ui32SpiChipSelect = 0;
        xfer.ui32NumBytes = sizeof(cmd);
        xfer.eDirection = AM_HAL_IOM_RX;
		//xfer.pui32TxBuffer = (uint32_t*)cmd; // Buffer to Transmit data
        xfer.pui32RxBuffer = (uint32_t*)rx_buf; // Buffer to receive data

        // Execute the SPI transfer
        status = am_hal_iom_blocking_transfer(iom_handle, &xfer);
        if (status != AM_HAL_STATUS_SUCCESS) { report(status); }

        // Pull CS High (end transfer)
        am_hal_gpio_output_set(CS_PIN);

        // Print Transmitted and Received Data
        //am_util_stdio_printf("Transmitted: %s\n", cmd);
		am_util_stdio_printf("Received: %s\n", rx_buf);
        am_util_stdio_printf("Received: %s\n", xfer.pui32RxBuffer);

        am_util_delay_ms(1000); // Delay for readability
    }
}

No…
The Apollo3 IOM stands for Input Output Master…it provides the clock.
The direction is to indicate whether the master is sending or expect to receive data from the slave. A slave is sending and receiving data based on the incoming clock from the master. That is what the single available IOS module does.

Hello paul,
Can you please give me any demo example of an Apollo 3 Blue Plus board working as an SPI slave? Like just it will print the data whatever received on SPI lines.

Thank you

In the library I have mentioned on Jan 2 there are examples for SPI slave (peripheral). I have tested those on an ATP as the board has the necessary pins : Four-wire SPI mode of the IO Slave uses pad 0 as SCK, pad 1 as MOSI, pad 2 as MISO and pad 3 as nCE.

2 Likes