|
| 1 | +.. _lib_bm_spi_mngr: |
| 2 | + |
| 3 | +SPI transaction manager |
| 4 | +####################### |
| 5 | + |
| 6 | +.. contents:: |
| 7 | + :local: |
| 8 | + :depth: 2 |
| 9 | + |
| 10 | +The SPI transaction manager library serializes SPI master (SPIM) work on one hardware instance. |
| 11 | +Applications describe work as *transactions* (one or more TX/RX *transfers* in order), and the library keeps pending transactions in a FIFO queue and runs them back-to-back on the bus. |
| 12 | + |
| 13 | +Overview |
| 14 | +******** |
| 15 | + |
| 16 | +The library sits on top of the nrfx SPIM driver and uses a Zephyr ring buffer as a FIFO queue of transaction pointers. |
| 17 | +The queue is accessed both from the application (when scheduling) and from the SPIM interrupt handler (when one transaction finishes and the next one is started), so internal queue operations run with interrupts locked. |
| 18 | + |
| 19 | +The library exposes a non-blocking API and a blocking API on top of the same scheduling engine: |
| 20 | + |
| 21 | +* :c:func:`bm_spi_mngr_schedule` enqueues a :c:struct:`bm_spi_mngr_transaction_t` and returns immediately. |
| 22 | + Optional begin and end callbacks on the descriptor run from the SPIM event handler in interrupt context, around the transaction. |
| 23 | + |
| 24 | +* :c:func:`bm_spi_mngr_perform` builds an internal transaction for a one-shot multi-transfer job, schedules it through the same path, and blocks until completion. |
| 25 | + While waiting, it may call an optional idle function (for example ``k_cpu_idle``). |
| 26 | + This idle function must not be called from ISR context, because the SPIM interrupt that ends the transaction must still run to release the wait loop. |
| 27 | + Calling it from an ISR would deadlock silently, and the library asserts on this case as a precaution. |
| 28 | + |
| 29 | +All user-supplied callbacks run in interrupt context, so they should do as little work as possible, typically just setting a flag or releasing a lock that the application checks from its main loop. |
| 30 | + |
| 31 | +Each transaction may supply its own :c:member:`bm_spi_mngr_transaction_t.p_required_spim_cfg` pointer. |
| 32 | +If that member is NULL, the library uses the SPIM configuration passed to :c:func:`bm_spi_mngr_init`. |
| 33 | +If it points at a different :c:struct:`nrfx_spim_config_t`, the driver is reinitialized when the configuration does not match the one already active. |
| 34 | +This allows changing pins, clock, or mode between devices on the same bus, for example to drive a different software chip select line per peripheral. |
| 35 | + |
| 36 | +A typical use is sharing one SPIM instance between multiple peripherals, where each peripheral has its own chip select pin. |
| 37 | +Each peripheral defines its own :c:struct:`nrfx_spim_config_t` with the matching chip select, and transactions targeted at that peripheral point :c:member:`bm_spi_mngr_transaction_t.p_required_spim_cfg` at it. |
| 38 | +The manager then reconfigures the SPIM instance on the fly as transactions for different peripherals are pulled from the queue. |
| 39 | + |
| 40 | +Before calling :c:func:`bm_spi_mngr_init`, connect and enable the SPIM interrupt for the chosen instance (for example using :c:macro:`BM_IRQ_DIRECT_CONNECT`), as required by nrfx SPIM. |
| 41 | + |
| 42 | +Configuration |
| 43 | +************* |
| 44 | + |
| 45 | +Enable the library with the :kconfig:option:`CONFIG_BM_SPI_MNGR` Kconfig option. |
| 46 | + |
| 47 | +The option depends on :kconfig:option:`CONFIG_NRFX_SPIM` and selects :kconfig:option:`CONFIG_RING_BUFFER` for the internal FIFO. |
| 48 | + |
| 49 | +Defining an instance |
| 50 | +==================== |
| 51 | + |
| 52 | +Use the :c:macro:`BM_SPI_MNGR_DEF` macro once per logical bus manager. |
| 53 | +It allocates the queue backing store, control block, :c:struct:`nrfx_spim_t` instance, and a const :c:struct:`bm_spi_mngr_t` handle. |
| 54 | + |
| 55 | +The macro argument ``_queue_size`` is the number of *pending* transaction slots, not counting the transaction currently executing. |
| 56 | +With queue depth ``N``, you can have at most ``N`` waiting transactions while one runs, so up to ``N + 1`` transactions may be in flight in total. |
| 57 | + |
| 58 | +Initialization |
| 59 | +============== |
| 60 | + |
| 61 | +Call :c:func:`bm_spi_mngr_init` with the manager handle and the SPIM configuration the instance should use when a scheduled transaction leaves :c:member:`bm_spi_mngr_transaction_t.p_required_spim_cfg` as NULL. |
| 62 | +The library copies that configuration into its control block and uses it as the default for any transaction that does not provide its own. |
| 63 | + |
| 64 | +Call :c:func:`bm_spi_mngr_uninit` to shut down SPIM and reset the queue. |
| 65 | + |
| 66 | +Scheduling transactions |
| 67 | +======================= |
| 68 | + |
| 69 | +Fill in a :c:struct:`bm_spi_mngr_transaction_t` with: |
| 70 | + |
| 71 | +* :c:member:`bm_spi_mngr_transaction_t.p_transfers` -- array of :c:struct:`bm_spi_mngr_transfer_t` segments. |
| 72 | +* :c:member:`bm_spi_mngr_transaction_t.number_of_transfers` -- length of that array. |
| 73 | +* Optional :c:member:`bm_spi_mngr_transaction_t.begin_callback`, called from the SPIM event handler context just before the first transfer of the transaction is started on the bus. |
| 74 | +* Optional :c:member:`bm_spi_mngr_transaction_t.end_callback`, called from the SPIM event handler context when the transaction completes or aborts after an error. The result is passed as the first argument. |
| 75 | +* Optional :c:member:`bm_spi_mngr_transaction_t.p_user_data`, an opaque pointer passed through to both callbacks. |
| 76 | +* Optional :c:member:`bm_spi_mngr_transaction_t.p_required_spim_cfg` -- per-transaction SPIM settings, or NULL to use the configuration from :c:func:`bm_spi_mngr_init`. |
| 77 | + |
| 78 | +Use the :c:macro:`BM_SPI_MNGR_TRANSFER` macro to initialize simple transfer descriptors. |
| 79 | + |
| 80 | +The transaction descriptor (and any non-NULL configuration pointer it carries) must stay valid until the transaction completes, because the library only stores a pointer to it in the queue. |
| 81 | + |
| 82 | +Pass the descriptor to :c:func:`bm_spi_mngr_schedule`. |
| 83 | +The function returns :c:macro:`NRF_ERROR_NO_MEM` if the queue is full. |
| 84 | +On success, the transaction is started immediately if the bus is idle, otherwise it runs back-to-back after the transactions ahead of it. |
| 85 | + |
| 86 | +Because both callbacks run from interrupt context, keep them short and use them only to signal completion to the application, for example by setting a flag or releasing a lock that protects the transaction descriptor. |
| 87 | + |
| 88 | +Blocking helper |
| 89 | +=============== |
| 90 | + |
| 91 | +:c:func:`bm_spi_mngr_perform` is a convenience wrapper around :c:func:`bm_spi_mngr_schedule` for a single synthetic transaction. |
| 92 | +Its ``p_config`` argument maps to :c:member:`bm_spi_mngr_transaction_t.p_required_spim_cfg`, where NULL means use the configuration from :c:func:`bm_spi_mngr_init`. |
| 93 | +The optional ``idle_fn`` argument is called repeatedly while the function waits for the transaction to finish, and must not be called from ISR context. |
| 94 | + |
| 95 | +Idle detection |
| 96 | +============== |
| 97 | + |
| 98 | +:c:func:`bm_spi_mngr_is_idle` returns true when the manager has no transaction in progress and the pending queue is empty. |
| 99 | +The library is careful not to report a false idle state between back-to-back transactions, so the return value can be used as a reliable signal that all scheduled work has finished. |
| 100 | + |
| 101 | +Dependencies |
| 102 | +************ |
| 103 | + |
| 104 | +* nrfx SPIM (:kconfig:option:`CONFIG_NRFX_SPIM`) |
| 105 | +* Zephyr ring buffer (:kconfig:option:`CONFIG_RING_BUFFER`), pulled in automatically when the library is enabled |
| 106 | +* Zephyr kernel symbols for the ISR assertion in :c:func:`bm_spi_mngr_perform` (``k_is_in_isr``) |
| 107 | + |
| 108 | +API documentation |
| 109 | +***************** |
| 110 | + |
| 111 | +| Header file: :file:`include/bm/bm_spi_mngr.h` |
| 112 | +| Source files: :file:`lib/bm_spi_mngr/` |
| 113 | +
|
| 114 | +:ref:`SPI transaction manager API reference <api_bm_spi_mngr>` |
0 commit comments