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

using SPI with PPI in zephyr

HI,

we are using the nrf52840 in our new poroduct and we are running the zephyr/ncs RTOS on it.

for real time reasons I need to read from a SPI bus upon a GPIO trigger and in the context of the SPI done interrupt save the read data to a buffer.

I was able to do this on another product that we have without zephyr using the nrf SDK on a baremetal application.

the problem with zephyr is that the SPI HW abstraction controls the event handler from SPIM and I don't have access to it and also even if I want to hack this by directly accessing it with the nrfx spim driver I don;t know how this will work with the zephyr power manager!

if someone has an Idea how to best do this in zephyr/ncs I would appreciate the help.

Thanks,

Samer.

  • I believe the attached example may be of help. It show how to setup gpiote event to trigger a callback, which again trigger a spi transfer, which again trigger a spi callback when finished. If you are using an nRF52-DK, then pushing button1 will execute the transfer, shown by a very quick blink on led1.

    3554.nrfx_with_spi.zip

    /*
     * Copyright (c) 2019 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr.h>
    
    #include <drivers/gpio.h>
    #include <nrfx_gpiote.h>
    #include <nrfx_ppi.h>
    #include <nrfx_spim.h>
    
    #include <logging/log.h>
    LOG_MODULE_REGISTER(nrfx_sample, LOG_LEVEL_INF);
    
    #define INPUT_PIN DT_GPIO_PIN(DT_ALIAS(sw0), gpios)
    #define CSN DT_GPIO_PIN(DT_ALIAS(led1), gpios)
    
    #define BUF_SIZE 10
    uint8_t buffer_tx[BUF_SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    uint8_t buffer_rx[BUF_SIZE] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    
    nrfx_spim_xfer_desc_t transfer = {
    	.p_tx_buffer = buffer_tx, ///< Pointer to TX buffer.
    	.tx_length = BUF_SIZE, ///< TX buffer length.
    	.p_rx_buffer = buffer_rx, ///< Pointer to RX buffer.
    	.rx_length = BUF_SIZE ///< RX buffer length.
    };
    
    nrfx_spim_t spi_instance = NRFX_SPIM_INSTANCE(1);
    nrfx_spim_config_t spi_config =
    	NRFX_SPIM_DEFAULT_CONFIG(28, 29, 30, NRFX_SPIM_PIN_NOT_USED);
    
    static void button_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
    {
    	nrfx_err_t err;
    
    	LOG_INF("GPIO input event callback, start SPI transfer");
    
    	nrf_gpio_pin_clear(CSN);
    	err = nrfx_spim_xfer(&spi_instance, &transfer, 0);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("SPI transfer error: %08x", err);
    		return;
    	}
    }
    
    static void spim_handler(nrfx_spim_evt_t const *p_event, void *p_context)
    {
    	if (p_event->type == NRFX_SPIM_EVENT_DONE) {
    		LOG_INF("SPI transfer finished");
    		nrf_gpio_pin_set(CSN);
    	}
    }
    
    void main(void)
    {
    	nrfx_err_t err;
    
    	LOG_INF("gpiote + spim sample on %s", CONFIG_BOARD);
    
    	IRQ_CONNECT(GPIOTE_IRQn, 7, nrfx_isr, nrfx_gpiote_irq_handler, 0);
    
    	err = nrfx_gpiote_init(0);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_gpiote_init error: %08x", err);
    		return;
    	}
    
    	nrfx_gpiote_in_config_t const in_config = {
    		.sense = NRF_GPIOTE_POLARITY_HITOLO,
    		.pull = NRF_GPIO_PIN_PULLUP,
    		.is_watcher = false,
    		.hi_accuracy = true,
    		.skip_gpio_setup = false,
    	};
    
    	err = nrfx_gpiote_in_init(INPUT_PIN, &in_config, button_handler);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_gpiote_in_init error: %08x", err);
    		return;
    	}
    
    	nrfx_gpiote_in_event_enable(INPUT_PIN, true);
    
    	LOG_INF("nrfx_gpiote is initialized");
    
    	IRQ_CONNECT(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQn, 6, nrfx_isr,
    		    nrfx_spim_1_irq_handler, 0);
    
    	err = nrfx_spim_init(&spi_instance, &spi_config, spim_handler, NULL);
    	if (err != NRFX_SUCCESS) {
    		LOG_ERR("nrfx_spim_init error: %08x", err);
    		return;
    	}
    
    	nrf_gpio_pin_set(CSN);
    	nrf_gpio_cfg_output(CSN);
    
    	LOG_INF("nrfx_spim is initialized, push button1 to start spi transfer");
    }
    

    Hope it helps.
    Kenneth

  • To reduce current you may want to set .hi_accuracy = false.

    Kenneth

  • but how does this affect zephyr's device power manager? I mean the power manager does the init and sets the event handler to it's handler!

    how do you:

    1. make sure that the device manager doesn't try to disable SPI device

    2. return control of the SPI device to the device power manager?

  • You may find this thread useful:
    https://devzone.nordicsemi.com/f/nordic-q-a/65959/how-to-enter-system-on-sleep-in-zephyr

    I guess the question is do you need to use the power manager? The spi doesn't need to be unit (it doesn't use any current by being initalized).

    If you choose to use the power manager, then you can find that it will unit/init the spim as shown in the source:
    https://github.com/nrfconnect/sdk-zephyr/blob/master/drivers/spi/spi_nrfx_spim.c#L322-L367

    Also check out the documentation for the power management if you want to use it:
    https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.3.0/zephyr/reference/power_management/index.html 

    Note that if you are using nrfx directly (as in the example shown here), then zephyr is unaware of the power management in the nrfx driver (since zephyr is only aware of it if you are using the zephyr drivers that include the DEVICE_DEFINE, which include the spim_nrfx_pm_control()).

Related