1 /*
2  * Copyright (c) 2015 - 2020, Nordic Semiconductor ASA
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this
9  *    list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its
16  *    contributors may be used to endorse or promote products derived from this
17  *    software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #ifndef NRFX_SPIM_H__
33 #define NRFX_SPIM_H__
34 
35 #include <nrfx.h>
36 #include <hal/nrf_spim.h>
37 #include <hal/nrf_gpio.h>
38 
39 #ifdef __cplusplus
40 extern "C" {
41 #endif
42 
43 /**
44  * @defgroup nrfx_spim SPIM driver
45  * @{
46  * @ingroup nrf_spim
47  * @brief   Serial Peripheral Interface Master with EasyDMA (SPIM) driver.
48  */
49 
50 /** @brief Data structure of the Serial Peripheral Interface Master with EasyDMA (SPIM) driver instance. */
51 typedef struct
52 {
53     NRF_SPIM_Type * p_reg;        ///< Pointer to a structure with SPIM registers.
54     uint8_t         drv_inst_idx; ///< Index of the driver instance. For internal use only.
55 } nrfx_spim_t;
56 
57 #ifndef __NRFX_DOXYGEN__
58 enum {
59 #if NRFX_CHECK(NRFX_SPIM0_ENABLED)
60     NRFX_SPIM0_INST_IDX,
61 #endif
62 #if NRFX_CHECK(NRFX_SPIM1_ENABLED)
63     NRFX_SPIM1_INST_IDX,
64 #endif
65 #if NRFX_CHECK(NRFX_SPIM2_ENABLED)
66     NRFX_SPIM2_INST_IDX,
67 #endif
68 #if NRFX_CHECK(NRFX_SPIM3_ENABLED)
69     NRFX_SPIM3_INST_IDX,
70 #endif
71 #if NRFX_CHECK(NRFX_SPIM4_ENABLED)
72     NRFX_SPIM4_INST_IDX,
73 #endif
74     NRFX_SPIM_ENABLED_COUNT
75 };
76 #endif
77 
78 /** @brief Macro for creating an instance of the SPIM driver. */
79 #define NRFX_SPIM_INSTANCE(id)                               \
80 {                                                            \
81     .p_reg        = NRFX_CONCAT_2(NRF_SPIM, id),             \
82     .drv_inst_idx = NRFX_CONCAT_3(NRFX_SPIM, id, _INST_IDX), \
83 }
84 
85 /**
86  * @brief This value can be provided instead of a pin number for signals MOSI,
87  *        MISO, and Slave Select to specify that the given signal is not used and
88  *        therefore does not need to be connected to a pin.
89  */
90 #define NRFX_SPIM_PIN_NOT_USED  0xFF
91 
92 /** @brief Configuration structure of the SPIM driver instance. */
93 typedef struct
94 {
95     uint8_t               sck_pin;        ///< SCK pin number.
96     uint8_t               mosi_pin;       ///< MOSI pin number (optional).
97                                           /**< Set to @ref NRFX_SPIM_PIN_NOT_USED
98                                            *   if this signal is not needed. */
99     uint8_t               miso_pin;       ///< MISO pin number (optional).
100                                           /**< Set to @ref NRFX_SPIM_PIN_NOT_USED
101                                            *   if this signal is not needed. */
102     uint8_t               ss_pin;         ///< Slave Select pin number (optional).
103                                           /**< Set to @ref NRFX_SPIM_PIN_NOT_USED
104                                            *   if this signal is not needed. */
105     bool                  ss_active_high; ///< Polarity of the Slave Select pin during transmission.
106     uint8_t               irq_priority;   ///< Interrupt priority.
107     uint8_t               orc;            ///< Overrun character.
108                                           /**< This character is used when all bytes from the TX buffer are sent,
109                                                but the transfer continues due to RX. */
110     nrf_spim_frequency_t frequency;       ///< SPIM frequency.
111     nrf_spim_mode_t      mode;            ///< SPIM mode.
112     nrf_spim_bit_order_t bit_order;       ///< SPIM bit order.
113     nrf_gpio_pin_pull_t  miso_pull;       ///< MISO pull up configuration.
114 #if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED) || defined(__NRFX_DOXYGEN__)
115     uint8_t              dcx_pin;         ///< D/CX pin number (optional).
116     uint8_t              rx_delay;        ///< Sample delay for input serial data on MISO.
117                                           /**< The value specifies the delay, in number of 64 MHz clock cycles
118                                            *   (15.625 ns), from the the sampling edge of SCK (leading edge for
119                                            *   CONFIG.CPHA = 0, trailing edge for CONFIG.CPHA = 1) until
120                                            *   the input serial data is sampled. */
121     bool                 use_hw_ss;       ///< Indication to use software or hardware controlled Slave Select pin.
122     uint8_t              ss_duration;     ///< Slave Select duration before and after transmission.
123                                           /**< Minimum duration between the edge of CSN and the edge of SCK.
124                                            *   Also, minimum duration of CSN inactivity between transactions.
125                                            *   The value is specified in number of 64 MHz clock cycles (15.625 ns).
126                                            *   Supported only for hardware-controlled Slave Select. */
127 #endif
128 } nrfx_spim_config_t;
129 
130 #if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED) || defined(__NRFX_DOXYGEN__)
131 /**
132  * @brief SPIM driver extended default configuration.
133  *
134  * This configuration sets up SPIM additional options with the following values:
135  * - DCX pin disabled
136  * - RX sampling delay: 2 clock cycles
137  * - hardware SS disabled
138  * - hardware SS duration before and after transmission: 2 clock cycles
139  */
140 #define NRFX_SPIM_DEFAULT_EXTENDED_CONFIG   \
141     .dcx_pin      = NRFX_SPIM_PIN_NOT_USED, \
142     .rx_delay     = 0x02,                   \
143     .use_hw_ss    = false,                  \
144     .ss_duration  = 0x02,
145 #else
146     #define NRFX_SPIM_DEFAULT_EXTENDED_CONFIG
147 #endif
148 
149 /**
150  * @brief SPIM driver default configuration.
151  *
152  * This configuration sets up SPIM with the following options:
153  * - SS pin active low
154  * - over-run character set to 0xFF
155  * - clock frequency: 4 MHz
156  * - mode: 0 (SCK active high, sample on leading edge of the clock signa;)
157  * - MSB shifted out first
158  * - MISO pull-up disabled
159  *
160  * @param[in] _pin_sck  SCK pin.
161  * @param[in] _pin_mosi MOSI pin.
162  * @param[in] _pin_miso MISO pin.
163  * @param[in] _pin_ss   SS pin.
164  */
165 #define NRFX_SPIM_DEFAULT_CONFIG(_pin_sck, _pin_mosi, _pin_miso, _pin_ss)   \
166 {                                                                           \
167     .sck_pin        = _pin_sck,                                             \
168     .mosi_pin       = _pin_mosi,                                            \
169     .miso_pin       = _pin_miso,                                            \
170     .ss_pin         = _pin_ss,                                              \
171     .ss_active_high = false,                                                \
172     .irq_priority   = NRFX_SPIM_DEFAULT_CONFIG_IRQ_PRIORITY,                \
173     .orc            = 0xFF,                                                 \
174     .frequency      = NRF_SPIM_FREQ_4M,                                     \
175     .mode           = NRF_SPIM_MODE_0,                                      \
176     .bit_order      = NRF_SPIM_BIT_ORDER_MSB_FIRST,                         \
177     .miso_pull      = NRF_GPIO_PIN_NOPULL,                                  \
178     NRFX_SPIM_DEFAULT_EXTENDED_CONFIG                                       \
179 }
180 
181 /** @brief Flag indicating that TX buffer address will be incremented after transfer. */
182 #define NRFX_SPIM_FLAG_TX_POSTINC          (1UL << 0)
183 /** @brief Flag indicating that RX buffer address will be incremented after transfer. */
184 #define NRFX_SPIM_FLAG_RX_POSTINC          (1UL << 1)
185 /** @brief Flag indicating that the interrupt after each transfer will be suppressed, and the event handler will not be called. */
186 #define NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER (1UL << 2)
187 /** @brief Flag indicating that the transfer will be set up, but not started. */
188 #define NRFX_SPIM_FLAG_HOLD_XFER           (1UL << 3)
189 /** @brief Flag indicating that the transfer will be executed multiple times. */
190 #define NRFX_SPIM_FLAG_REPEATED_XFER       (1UL << 4)
191 
192 /** @brief Single transfer descriptor structure. */
193 typedef struct
194 {
195     uint8_t const * p_tx_buffer; ///< Pointer to TX buffer.
196     size_t          tx_length;   ///< TX buffer length.
197     uint8_t       * p_rx_buffer; ///< Pointer to RX buffer.
198     size_t          rx_length;   ///< RX buffer length.
199 } nrfx_spim_xfer_desc_t;
200 
201 /**
202  * @brief Macro for setting up single transfer descriptor.
203  *
204  * This macro is for internal use only.
205  */
206 #define NRFX_SPIM_SINGLE_XFER(p_tx, tx_len, p_rx, rx_len) \
207     {                                                     \
208     .p_tx_buffer = (uint8_t const *)(p_tx),               \
209     .tx_length = (tx_len),                                \
210     .p_rx_buffer = (p_rx),                                \
211     .rx_length = (rx_len),                                \
212     }
213 
214 /** @brief Macro for setting the duplex TX RX transfer. */
215 #define NRFX_SPIM_XFER_TRX(p_tx_buf, tx_length, p_rx_buf, rx_length) \
216         NRFX_SPIM_SINGLE_XFER(p_tx_buf, tx_length, p_rx_buf, rx_length)
217 
218 /** @brief Macro for setting the TX transfer. */
219 #define NRFX_SPIM_XFER_TX(p_buf, length) \
220         NRFX_SPIM_SINGLE_XFER(p_buf, length, NULL, 0)
221 
222 /** @brief Macro for setting the RX transfer. */
223 #define NRFX_SPIM_XFER_RX(p_buf, length) \
224         NRFX_SPIM_SINGLE_XFER(NULL, 0, p_buf, length)
225 
226 /**
227  * @brief SPIM master driver event types, passed to the handler routine provided
228  *        during initialization.
229  */
230 typedef enum
231 {
232     NRFX_SPIM_EVENT_DONE, ///< Transfer done.
233 } nrfx_spim_evt_type_t;
234 
235 /** @brief SPIM event description with transmission details. */
236 typedef struct
237 {
238     nrfx_spim_evt_type_t  type;      ///< Event type.
239     nrfx_spim_xfer_desc_t xfer_desc; ///< Transfer details.
240 } nrfx_spim_evt_t;
241 
242 /** @brief SPIM driver event handler type. */
243 typedef void (* nrfx_spim_evt_handler_t)(nrfx_spim_evt_t const * p_event,
244                                          void *                  p_context);
245 
246 /**
247  * @brief Function for initializing the SPIM driver instance.
248  *
249  * This function configures and enables the specified peripheral.
250  *
251  * @param[in] p_instance Pointer to the driver instance structure.
252  * @param[in] p_config   Pointer to the structure with the initial configuration.
253  * @param[in] handler    Event handler provided by the user. If NULL, transfers
254  *                       will be performed in blocking mode.
255  * @param[in] p_context  Context passed to event handler.
256  *
257  * @retval NRFX_SUCCESS             Initialization was successful.
258  * @retval NRFX_ERROR_INVALID_STATE The driver was already initialized.
259  * @retval NRFX_ERROR_BUSY          Some other peripheral with the same
260  *                                  instance ID is already in use. This is
261  *                                  possible only if @ref nrfx_prs module
262  *                                  is enabled.
263  * @retval NRFX_ERROR_NOT_SUPPORTED Requested configuration is not supported
264  *                                  by the SPIM instance.
265  */
266 nrfx_err_t nrfx_spim_init(nrfx_spim_t const *        p_instance,
267                           nrfx_spim_config_t const * p_config,
268                           nrfx_spim_evt_handler_t    handler,
269                           void *                     p_context);
270 
271 /**
272  * @brief Function for uninitializing the SPIM driver instance.
273  *
274  * @param[in] p_instance Pointer to the driver instance structure.
275  */
276 void nrfx_spim_uninit(nrfx_spim_t const * p_instance);
277 
278 /**
279  * @brief Function for starting the SPIM data transfer.
280  *
281  * Additional options are provided using the @c flags parameter:
282  *
283  * - @ref NRFX_SPIM_FLAG_TX_POSTINC and @ref NRFX_SPIM_FLAG_RX_POSTINC -
284  *   Post-incrementation of buffer addresses.
285  * - @ref NRFX_SPIM_FLAG_HOLD_XFER - Driver is not starting the transfer. Use this
286  *   flag if the transfer is triggered externally by PPI. Use
287  *   @ref nrfx_spim_start_task_get to get the address of the start task.
288  *   Chip select must be configured to @ref NRFX_SPIM_PIN_NOT_USED and managed outside the driver.
289  * - @ref NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER - No user event handler after transfer
290  *   completion. This also means no interrupt at the end of the transfer.
291  *   If @ref NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER is used, the driver does not set the instance into
292  *   busy state, so you must ensure that the next transfers are set up when SPIM is not active.
293  *   @ref nrfx_spim_end_event_get function can be used to detect end of transfer. Option can be used
294  *   together with @ref NRFX_SPIM_FLAG_REPEATED_XFER to prepare a sequence of SPI transfers
295  *   without interruptions.
296  * - @ref NRFX_SPIM_FLAG_REPEATED_XFER - Prepare for repeated transfers. You can set
297  *   up a number of transfers that will be triggered externally (for example by PPI). An example is
298  *   a TXRX transfer with the options @ref NRFX_SPIM_FLAG_RX_POSTINC,
299  *   @ref NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER, and @ref NRFX_SPIM_FLAG_REPEATED_XFER. After the
300  *   transfer is set up, a set of transfers can be triggered by PPI that will read, for example,
301  *   the same register of an external component and put it into a RAM buffer without any interrupts.
302  *   @ref nrfx_spim_end_event_get can be used to get the address of the END event, which can be
303  *   used to count the number of transfers. If @ref NRFX_SPIM_FLAG_REPEATED_XFER is used,
304  *   the driver does not set the instance into busy state, so you must ensure that the next
305  *   transfers are set up when SPIM is not active.
306  *
307  * @note Peripherals using EasyDMA (including SPIM) require the transfer buffers
308  *       to be placed in the Data RAM region. If this condition is not met,
309  *       this function will fail with the error code NRFX_ERROR_INVALID_ADDR.
310  *
311  * @param p_instance  Pointer to the driver instance structure.
312  * @param p_xfer_desc Pointer to the transfer descriptor.
313  * @param flags       Transfer options (0 for default settings).
314  *
315  * @retval NRFX_SUCCESS             The procedure is successful.
316  * @retval NRFX_ERROR_BUSY          The driver is not ready for a new transfer.
317  * @retval NRFX_ERROR_NOT_SUPPORTED The provided parameters are not supported.
318  * @retval NRFX_ERROR_INVALID_ADDR  The provided buffers are not placed in the Data
319  *                                  RAM region.
320  */
321 nrfx_err_t nrfx_spim_xfer(nrfx_spim_t const *           p_instance,
322                           nrfx_spim_xfer_desc_t const * p_xfer_desc,
323                           uint32_t                      flags);
324 
325 #if NRFX_CHECK(NRFX_SPIM_EXTENDED_ENABLED) || defined(__NRFX_DOXYGEN__)
326 /**
327  * @brief Function for starting the SPIM data transfer with DCX control.
328  *
329  * See @ref nrfx_spim_xfer for description of additional options of transfer
330  * provided by the @c flags parameter.
331  *
332  * @note Peripherals that use EasyDMA (including SPIM) require the transfer buffers
333  *       to be placed in the Data RAM region. If this condition is not met,
334  *       this function will fail with the error code NRFX_ERROR_INVALID_ADDR.
335  *
336  * @param p_instance  Pointer to the driver instance structure.
337  * @param p_xfer_desc Pointer to the transfer descriptor.
338  * @param flags       Transfer options (0 for default settings).
339  * @param cmd_length  Length of the command bytes preceding the data
340  *                    bytes. The DCX line will be low during transmission
341  *                    of command bytes and high during transmission of data bytes.
342  *                    Maximum value available for dividing the transmitted bytes
343  *                    into command bytes and data bytes is @ref NRF_SPIM_DCX_CNT_ALL_CMD - 1.
344  *                    The @ref NRF_SPIM_DCX_CNT_ALL_CMD value passed as the
345  *                    @c cmd_length parameter causes all transmitted bytes
346  *                    to be marked as command bytes.
347  *
348  * @retval NRFX_SUCCESS             The procedure is successful.
349  * @retval NRFX_ERROR_BUSY          The driver is not ready for a new transfer.
350  * @retval NRFX_ERROR_NOT_SUPPORTED The provided parameters are not supported.
351  * @retval NRFX_ERROR_INVALID_ADDR  The provided buffers are not placed in the Data
352  *                                  RAM region.
353  */
354 nrfx_err_t nrfx_spim_xfer_dcx(nrfx_spim_t const *           p_instance,
355                               nrfx_spim_xfer_desc_t const * p_xfer_desc,
356                               uint32_t                      flags,
357                               uint8_t                       cmd_length);
358 #endif
359 
360 /**
361  * @brief Function for returning the address of a SPIM start task.
362  *
363  * This function is to be used if @ref nrfx_spim_xfer was called with the flag @ref NRFX_SPIM_FLAG_HOLD_XFER.
364  * In that case, the transfer is not started by the driver, but it must be started externally by PPI.
365  *
366  * @param[in] p_instance Pointer to the driver instance structure.
367  *
368  * @return Start task address.
369  */
370 uint32_t nrfx_spim_start_task_get(nrfx_spim_t const * p_instance);
371 
372 /**
373  * @brief Function for returning the address of a END SPIM event.
374  *
375  * The END event can be used to detect the end of a transfer
376  * if the @ref NRFX_SPIM_FLAG_NO_XFER_EVT_HANDLER option is used.
377  *
378  * @param[in] p_instance Pointer to the driver instance structure.
379  *
380  * @return END event address.
381  */
382 uint32_t nrfx_spim_end_event_get(nrfx_spim_t const * p_instance);
383 
384 /**
385  * @brief Function for aborting ongoing transfer.
386  *
387  * @param[in] p_instance Pointer to the driver instance structure.
388  */
389 void nrfx_spim_abort(nrfx_spim_t const * p_instance);
390 
391 /** @} */
392 
393 
394 void nrfx_spim_0_irq_handler(void);
395 void nrfx_spim_1_irq_handler(void);
396 void nrfx_spim_2_irq_handler(void);
397 void nrfx_spim_3_irq_handler(void);
398 void nrfx_spim_4_irq_handler(void);
399 
400 
401 #ifdef __cplusplus
402 }
403 #endif
404 
405 #endif // NRFX_SPIM_H__
406