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

SPI through PPI repeated read not working

Hello,

Currently, we trying to use PPI with the SPI module (we use the SDK 15.3.0)
We expected that the SPI makes one read transaction every 5 us ; to do this, we use 2 PPI channels with 2 timers.
One channel with one timer to toggle the slave select and the second channel with the second timer to start the SPI transfer.
We have followed :
infocenter.nordicsemi.com/index.jsp
devzone.nordicsemi.com/.../ppi-to-spim-task-how-to

#include <stdbool.h>
#include <stdint.h>
#include "nrf.h"
#include "nrf_gpiote.h"
#include "nrf_gpio.h"
#include "boards.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_timer.h"
#include "nrf_drv_gpiote.h"
#include "nrf_drv_spi.h"
#include "app_error.h"

#include <nrf_log.h>
#include <nrf_log_ctrl.h>
#include <nrf_log_default_backends.h>

// Intialize pin part.
#define ADS7866_CS      NRF_GPIO_PIN_MAP(0,11)
#define SPI_MISO_PIN    NRF_GPIO_PIN_MAP(0, 7)   // MISO signal  P0.07
#define SPI_MOSI_PIN    NRF_GPIO_PIN_MAP(0, 5)   // MOSI signal  P0.05
#define SPI_SCK_PIN     NRF_GPIO_PIN_MAP(0, 8)   // CLK signal   P0.08

// Create SPI instance and configurations.
static nrf_drv_spi_t spi_driver = NRF_DRV_SPI_INSTANCE(2);
static nrf_drv_timer_t timer_cs = NRF_DRV_TIMER_INSTANCE(0);
static nrf_drv_timer_t timer_spi = NRF_DRV_TIMER_INSTANCE(1);
static nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
static nrf_drv_spi_xfer_desc_t xfer;
static uint16_t p_rx_data = 0x0000;
static uint8_t p_tx_data = 0xff;

static void spi_handler_test(nrf_drv_spi_evt_t const * p_event, void *p_context)
{
    NRF_LOG_INFO(
        "SPI_HANDLER !\n"
        "read: 0x%x, 0x%x",
        p_rx_data >> 8, p_rx_data & 0x00ff
        );
    NRF_LOG_FLUSH();
}

static void spi_setup(void)
{
    nrf_ppi_channel_t ppi_channel;
    uint32_t compare_evt_addr;
    uint32_t spi_task_addr;
    ret_code_t err_code;

    // Configure SPI driver.
    spi_config.ss_pin   = NRF_DRV_SPI_PIN_NOT_USED;
    spi_config.miso_pin = SPI_MISO_PIN;
    spi_config.mosi_pin = SPI_MOSI_PIN;
    spi_config.sck_pin  = SPI_SCK_PIN;

    // Initialize SPI driver.
    err_code = nrf_drv_spi_init(&spi_driver, &spi_config, spi_handler_test, NULL);
    APP_ERROR_CHECK(err_code);

    // Initialize timer.
    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    uint32_t ticks = nrf_drv_timer_us_to_ticks(&timer_spi, 5);
    err_code = nrf_drv_timer_init(&timer_spi, &timer_cfg, NULL);
    APP_ERROR_CHECK(err_code);
    nrf_drv_timer_extended_compare(&timer_spi, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);

    // Configure SPI.
    xfer.p_tx_buffer = (uint8_t const *)&p_tx_data;
    xfer.p_rx_buffer = (uint8_t *)&p_rx_data;
    xfer.tx_length = 1;
    xfer.rx_length = 2;
    NRF_LOG_INFO("NRF DRIVER SPI XFER START !");
    NRF_LOG_FLUSH();
    err_code = nrf_drv_spi_xfer(&spi_driver, &xfer, NRF_DRV_SPI_FLAG_REPEATED_XFER | NRF_DRV_SPI_FLAG_HOLD_XFER);
    NRF_LOG_INFO("NRF DRIVER SPI XFER END !");
    NRF_LOG_FLUSH();
    NRF_LOG_INFO("err_code = %d", err_code);
    NRF_LOG_FLUSH();
    APP_ERROR_CHECK(err_code);

    // Get event / task addresses.
    compare_evt_addr = nrf_drv_timer_event_address_get(&timer_spi, NRF_TIMER_EVENT_COMPARE0);
    spi_task_addr    = nrf_drv_spi_start_task_get(&spi_driver);

    // Intialize PPI channel.
    err_code = nrf_drv_ppi_channel_alloc(&ppi_channel);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_assign(ppi_channel, compare_evt_addr, spi_task_addr);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_enable(ppi_channel);
    APP_ERROR_CHECK(err_code);
}

static void cs_setup(void)
{
    nrf_ppi_channel_t ppi_channel;
    uint32_t compare_evt_addr;
    uint32_t gpiote_task_addr;
    ret_code_t err_code;

    // Initialize pin.
    nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false);
    err_code = nrf_drv_gpiote_out_init(ADS7866_CS, &config);
    APP_ERROR_CHECK(err_code);

    // Initialize timer.
    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    uint32_t ticks = nrf_drv_timer_us_to_ticks(&timer_cs, 2);
    err_code = nrf_drv_timer_init(&timer_cs, &timer_cfg, NULL);
    APP_ERROR_CHECK(err_code);
    nrf_drv_timer_extended_compare(&timer_cs, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);

    // Get event / task addresses.
    compare_evt_addr = nrf_drv_timer_event_address_get(&timer_cs, NRF_TIMER_EVENT_COMPARE0);
    gpiote_task_addr = nrf_drv_gpiote_out_task_addr_get(ADS7866_CS);

    // Intialize PPI channel.
    err_code = nrf_drv_ppi_channel_alloc(&ppi_channel);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_assign(ppi_channel, compare_evt_addr, gpiote_task_addr);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_enable(ppi_channel);
    APP_ERROR_CHECK(err_code);

    // Enable CS pin.
    nrf_drv_gpiote_out_task_enable(ADS7866_CS);
}

/**
 * @brief Function for application main entry.
 */
int main(void)
{
    ret_code_t err_code;

    err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);
    NRF_LOG_DEFAULT_BACKENDS_INIT();

    err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_gpiote_init();
    APP_ERROR_CHECK(err_code);

    cs_setup();
    spi_setup();

    // Enable timers
    nrf_drv_timer_enable(&timer_cs);
    nrf_drv_timer_enable(&timer_spi);

    while (true)
    {
        // Do Nothing - GPIO can be toggled without software intervention.
    }
}






As you can see, the `spi_cs` is toggled correctly but the SPI does not repeat the read transaction, and we never have the event handler callback triggered.
What do we have to change to have a repeated read SPI transaction ?
Can we use only one timer with 2 differents compare channels instead of the current 2 timers ?

  • Hi,

    Not sure what the problem here is, looks reasonable.

    You can use the same timer and compare event to trigger two (or more ) tasks, as long as you use two (or more)  ppi channels to connect the event to the individual task(s). You can also use different compare events from the same timer to trigger tasks. For instance compare0 toggle cs, compare1 trigger spi, compare2 toggle cs, compare3 clear timer. It's up to you. 

    In this case I suggest to strip away cs for test, and check if you can make the spi work standalone for now.

    Best regards,
    Kenneth

Related