This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRF52 - spi_master_with_spi_slave example always faults out

I am trying to communicate with a SPI peripheral, using the nRF52 as the master, and the peripheral as the slave. What happens is that it still faults out, and the presence of a delay call changes the point at which the fault occurs. Since the loopback example works, and also calls the delay function, I suspect the delay is fine, but omitting it allows the first call to spi_send_recv() to return when it otherwise would fault out.

I have done is two things:

  1. Tried the standard loopback example, spi_master, with my pin definitions in pca10036.h, it works.

  2. Since I want to connect to a slave, I am using spi_master_with_spi_slave, which has no such check_buf_equal() call. The following modifications have been made to this example:

    i) Changed the pin settings as per my peripheral connections. This is in pca10036.h:

    #define SPIM0_SCK_PIN 4 /29/ // SPI clock GPIO pin number. #define SPIM0_MOSI_PIN 29 /25/ // SPI Master Out Slave In GPIO pin number. #define SPIM0_MISO_PIN 28 // SPI Master In Slave Out GPIO pin number. #define SPIM0_SS_PIN 30 /12/ // SPI Slave Select GPIO pin number.

    ii)Set the chip select line to low in the main():

    nrf_gpio_cfg_output(30); //for the CS pin nrf_drv_gpiote_out_clear(30); //This should assert the CS for the SPI peripheral

    iii) Commented out the call to init_buffers() - no idea why this is even here as it annoyingly overwrites the data you're trying to send, so I removed it in the spi_send_recv() call:

    static void spi_send_recv(uint8_t * const p_tx_data, uint8_t * const p_rx_data, const uint16_t len) { // Initalize buffers. // init_buffers(p_tx_data, p_rx_data, len); //comment this out since I want to send certain bytes // Start transfer. uint32_t err_code = nrf_drv_spi_transfer(&m_spi_master, p_tx_data, len, p_rx_data, len); APP_ERROR_CHECK(err_code); //From what I can see it is this call to nrf_delay_ms() that is causing the processor to fault out nrf_delay_ms(DELAY_MS); }

iv)Set the ss_pin setting to NRF_DRV_SPI_PIN_NOT_USED to prevent the driver from setting this line high again (it's an active low CS on the peripheral).

nrf_drv_spi_config_t const config =
{
    #if (SPI0_ENABLED == 1)
        .sck_pin  = SPIM0_SCK_PIN,
        .mosi_pin = SPIM0_MOSI_PIN,
        .miso_pin = SPIM0_MISO_PIN,
        .ss_pin   = SPIM0_SS_PIN,
    #elif (SPI1_ENABLED == 1)
        .sck_pin  = SPIM1_SCK_PIN,
        .mosi_pin = SPIM1_MOSI_PIN,
        .miso_pin = SPIM1_MISO_PIN,
        .ss_pin   = SPIM1_SS_PIN,
    #elif (SPI2_ENABLED == 1)
        .sck_pin  = SPIM2_SCK_PIN,
        .mosi_pin = SPIM2_MOSI_PIN,
        .miso_pin = SPIM2_MISO_PIN,
        .ss_pin   = SPIM2_SS_PIN,
    #endif
    .irq_priority = APP_IRQ_PRIORITY_LOW,
    .orc          = 0xCC,
    .frequency    = NRF_DRV_SPI_FREQ_1M,
    .mode         = NRF_DRV_SPI_MODE_0,
    .bit_order    = NRF_DRV_SPI_BIT_ORDER_LSB_FIRST,
	.ss_pin       = NRF_DRV_SPI_PIN_NOT_USED, //if not specified, the nrf_drv_spi_init() will set this to high, which is wrong, CS is active low for this slave  peripheral
};

TO BE CLEAR: I am not concerned with the lack of response data, what I am concerned with is WHY THIS PERSISTENTLY FAULTS OUT. WHAT AM I DOING WRONG?

What I know for sure:

a) The EVENT HANDLER IS NOT BEING CALLED AT ALL

b) The behavior of the fault is such that when I comment out the delay call in spi_send_recv(), the fault does not occur until after the next call to spi_send_recv(). What I mean is not calling the delay seems to allow it to return from the first call to spi_send_recv(), but not the second one.

Here is all my code in main.c:

/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved.
 *
 * The information contained herein is property of Nordic Semiconductor ASA.
 * Terms and conditions of usage are described in detail in NORDIC
 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 *
 * Licensees are granted free, non-transferable use of the information. NO
 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
 * the file.
 *
 */

/**@file
 * @defgroup spi_master_example_with_slave_main main.c
 * @{
 * @ingroup spi_master_example
 *
 * @brief SPI master example application to be used with the SPI slave example application.
 */

#include <stdio.h>
#include <stdbool.h>
#include "app_error.h"
#include "app_util_platform.h"
#include "nrf_delay.h"
#include "bsp.h"
#include "app_timer.h"
#include "nrf_drv_spi.h"
#include "nordic_common.h"

#include "nrf_drv_gpiote.h" //added to get the GPIO clear workinf- DS

/*
 * This example uses only one instance of the SPI master.
 * Please make sure that only one instance of the SPI master is enabled in config file.
 */

#define APP_TIMER_PRESCALER      0                      /**< Value of the RTC1 PRESCALER register. */
#define APP_TIMER_MAX_TIMERS     BSP_APP_TIMERS_NUMBER  /**< Maximum number of simultaneously created timers. */
#define APP_TIMER_OP_QUEUE_SIZE  2                      /**< Size of timer operation queues. */

#define DELAY_MS                 1000                /**< Timer Delay in milli-seconds. */

#define TX_RX_BUF_LENGTH         16u                 /**< SPI transaction buffer length. */
#define TX_RX_BUF_LENGTH_CUSTOM  2                   //Added for AS3911 read register command
#define AS3911_READ_MODE   (1 << 6)

#if (SPI0_ENABLED == 1)
    static const nrf_drv_spi_t m_spi_master = NRF_DRV_SPI_INSTANCE(0);
#elif (SPI1_ENABLED == 1)
    static const nrf_drv_spi_t m_spi_master = NRF_DRV_SPI_INSTANCE(1);
#elif (SPI2_ENABLED == 1)
    static const nrf_drv_spi_t m_spi_master = NRF_DRV_SPI_INSTANCE(2);
#else
    #error "No SPI enabled."
#endif

// Data buffers.
//static uint8_t m_tx_data[TX_RX_BUF_LENGTH] = {0}; /**< A buffer with data to transfer. */
//static uint8_t m_rx_data[TX_RX_BUF_LENGTH] = {0}; /**< A buffer for incoming data. */

static uint8_t m_tx_data[TX_RX_BUF_LENGTH_CUSTOM] = {0}; /**< A buffer with data to transfer. */
static uint8_t m_rx_data[TX_RX_BUF_LENGTH_CUSTOM] = {0}; /**< A buffer for incoming data. */

static volatile bool m_transfer_completed = true; /**< A flag to inform about completed transfer. */


/**@brief Function for error handling, which is called when an error has occurred.
 *
 * @param[in] error_code  Error code supplied to the handler.
 * @param[in] line_num    Line number where the handler is called.
 * @param[in] p_file_name Pointer to the file name.
 */
void app_error_handler(uint32_t error_code, uint32_t line_num, const uint8_t * p_file_name)
{
    UNUSED_VARIABLE(bsp_indication_set(BSP_INDICATE_FATAL_ERROR));

    for (;;)
    {
        // No implementation needed.
    }
}


/**@brief Function for checking if data coming from a SPI slave are valid.
 *
 * @param[in] p_buf     A pointer to a data buffer.
 * @param[in] len       A length of the data buffer.
 * 
 * @note Expected ASCII characters from: 'a' to: ('a' + len - 1).
 *
 * @retval true     Data are valid.
 * @retval false    Data are invalid.
 */
static __INLINE bool buf_check(uint8_t * p_buf, uint16_t len)
{
    uint16_t i;

    for (i = 0; i < len; i++)
    {
        if (p_buf[i] != (uint8_t)('a' + i))
        {
            return false;
        }
    }

    return true;
}


/**@brief Function for SPI master event callback.
 *
 * Upon receiving an SPI transaction complete event, checks if received data are valid.
 *
 * @param[in] spi_master_evt    SPI master driver event.
 */
static void spi_master_event_handler(nrf_drv_spi_event_t event)
{
    uint32_t err_code = NRF_SUCCESS;
    bool result = false;

    switch (event)
    {
        case NRF_DRV_SPI_EVENT_DONE:
            // Check if data are valid.
            result = buf_check(m_rx_data, TX_RX_BUF_LENGTH);
            APP_ERROR_CHECK_BOOL(result);

            err_code = bsp_indication_set(BSP_INDICATE_RCV_OK);
            APP_ERROR_CHECK(err_code);

            // Inform application that transfer is completed.
            m_transfer_completed = true;
            break;

        default:
            // No implementation needed.
            break;
    }
}


/**@brief The function initializes TX buffer to values to be sent and clears RX buffer.
 *
 * @note Function initializes TX buffer to values from 'A' to ('A' + len - 1)
 *       and clears RX buffer (fill by 0).
 *
 * @param[in] p_tx_data     A pointer to a buffer TX.
 * @param[in] p_rx_data     A pointer to a buffer RX.
 * @param[in] len           A length of the data buffers.
 */
static void init_buffers(uint8_t * const p_tx_data,
                         uint8_t * const p_rx_data,
                         const uint16_t  len)
{
    uint16_t i;

    for (i = 0; i < len; i++)
    {
        p_tx_data[i] = (uint8_t)('A' + i);
        p_rx_data[i] = 0;
    }
}


/**@brief Functions prepares buffers and starts data transfer.
 *
 * @param[in] p_tx_data     A pointer to a buffer TX.
 * @param[in] p_rx_data     A pointer to a buffer RX.
 * @param[in] len           A length of the data buffers.
 */
static void spi_send_recv(uint8_t * const p_tx_data,
                          uint8_t * const p_rx_data,
                          const uint16_t  len)
{
    // Initalize buffers.
   // init_buffers(p_tx_data, p_rx_data, len); //comment this out since I want to read a register

    // Start transfer.
    uint32_t err_code = nrf_drv_spi_transfer(&m_spi_master,
        p_tx_data, len, p_rx_data, len);
    APP_ERROR_CHECK(err_code);
  nrf_delay_ms(DELAY_MS); //not having this delay allows the function to return at least once
}


/**@brief Function for initializing bsp module.
 */
void bsp_configuration()
{
    uint32_t err_code = NRF_SUCCESS;

    NRF_CLOCK->LFCLKSRC            = (CLOCK_LFCLKSRC_SRC_Xtal << CLOCK_LFCLKSRC_SRC_Pos);
    NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
    NRF_CLOCK->TASKS_LFCLKSTART    = 1;

    while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0)
    {
        // Do nothing.
    }

    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, NULL);
        
    err_code = bsp_init(BSP_INIT_LED, APP_TIMER_TICKS(100, APP_TIMER_PRESCALER), NULL);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for application main entry. Does not return. */
int main(void)
{
		unsigned char reg = 0x3F; //IC Identity Register
		nrf_gpio_cfg_output(30); //for the CS pin
    nrf_drv_gpiote_out_clear(30);   //This should assert the CS for the SPI peripheral
	
	  m_tx_data[0] = reg | AS3911_READ_MODE;
    m_tx_data[1] = 0x00;

    m_rx_data[0] = 0x00;
    m_rx_data[1] = 0x00;
	
    // Setup bsp module.
    bsp_configuration();

    nrf_drv_spi_config_t const config =
    {
        #if (SPI0_ENABLED == 1)
            .sck_pin  = SPIM0_SCK_PIN,
            .mosi_pin = SPIM0_MOSI_PIN,
            .miso_pin = SPIM0_MISO_PIN,
            .ss_pin   = SPIM0_SS_PIN,
        #elif (SPI1_ENABLED == 1)
            .sck_pin  = SPIM1_SCK_PIN,
            .mosi_pin = SPIM1_MOSI_PIN,
            .miso_pin = SPIM1_MISO_PIN,
            .ss_pin   = SPIM1_SS_PIN,
        #elif (SPI2_ENABLED == 1)
            .sck_pin  = SPIM2_SCK_PIN,
            .mosi_pin = SPIM2_MOSI_PIN,
            .miso_pin = SPIM2_MISO_PIN,
            .ss_pin   = SPIM2_SS_PIN,
        #endif
        .irq_priority = APP_IRQ_PRIORITY_LOW,
        .orc          = 0xCC,
        .frequency    = NRF_DRV_SPI_FREQ_1M,
        .mode         = NRF_DRV_SPI_MODE_0,
			.bit_order    = NRF_DRV_SPI_BIT_ORDER_LSB_FIRST,
				.ss_pin       = NRF_DRV_SPI_PIN_NOT_USED, //added by DS - if not specified, the nrf_drv_spi_init() will set this to high, which is wrong, CS for AS3911 is active low
    };
    ret_code_t err_code = nrf_drv_spi_init(&m_spi_master, &config, spi_master_event_handler);
    APP_ERROR_CHECK(err_code);

    for (;;)
    {
        if (m_transfer_completed)
        {
            m_transfer_completed = false;

            // Set buffers and start data transfer.
            spi_send_recv(m_tx_data, m_rx_data, TX_RX_BUF_LENGTH_CUSTOM);
					 spi_send_recv(m_tx_data, m_rx_data, TX_RX_BUF_LENGTH_CUSTOM);
        }
    }
}


/** @} */
Parents
  • Hi

    1. Good!

    2. The spi_master_with_spi_slave example actually does have a buffer check function. Only it is called buf_check() and not check_buf_equal() as in the spi_master_example example. You can actually find it in the code you have posted above in the spi_master_event_handler() function. I agree it is confusing. Sorry about that.

    i) This is fine.

    ii) Good practice maybe, but shouldn't be necessary as the SPI driver handles the SS pin. You can see how it is cleared in nrf_drv_spi_transfer() and set in finish_transfer() in nrf_drv_spi.c. I know you don't need SS, but just for your information.

    iii) The init_buffers() function is there to fill the SPI buffers with example data as a demonstration. Of course you are free to remove it and use the example as you wish.

    iv) Perfectly fine.

    a) I copy pasted the code you posted and tested it myself, so things should behave similarly on your system as on mine. What I see is that the event handler actually does get called. Unless you are debugging the way described here it might be difficult to spot though, and it often looks like your are stopping in the delay functions etc. Try to follow the steps in my link and then add these lines in your app_error_handler() function:

    volatile uint32_t code = error_code;
    volatile uint32_t line = line_num;
    

    Then you can track what kind of error is returned and at what line it occurs.

    Using the method described above I was able to track the error to line 127 in your code which is indeed where the RX and TX buffers are compared. So try to remove these lines and see if that works.

Reply Children
No Data
Related