nrf_spi_mngr.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. /**
  2. * Copyright (c) 2017 - 2019, Nordic Semiconductor ASA
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without modification,
  7. * are permitted provided that the following conditions are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright notice, this
  10. * list of conditions and the following disclaimer.
  11. *
  12. * 2. Redistributions in binary form, except as embedded into a Nordic
  13. * Semiconductor ASA integrated circuit in a product or a software update for
  14. * such product, must reproduce the above copyright notice, this list of
  15. * conditions and the following disclaimer in the documentation and/or other
  16. * materials provided with the distribution.
  17. *
  18. * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
  19. * contributors may be used to endorse or promote products derived from this
  20. * software without specific prior written permission.
  21. *
  22. * 4. This software, with or without modification, must only be used with a
  23. * Nordic Semiconductor ASA integrated circuit.
  24. *
  25. * 5. Any software provided in binary form under this license must not be reverse
  26. * engineered, decompiled, modified and/or disassembled.
  27. *
  28. * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
  29. * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  30. * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
  31. * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
  32. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  33. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  34. * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  37. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. */
  40. #include "sdk_common.h"
  41. #if NRF_MODULE_ENABLED(NRF_SPI_MNGR)
  42. #include "nrf_spi_mngr.h"
  43. #include "nrf_assert.h"
  44. #include "app_util_platform.h"
  45. typedef volatile struct
  46. {
  47. bool transaction_in_progress;
  48. uint8_t transaction_result;
  49. } nrf_spi_mngr_cb_data_t;
  50. static ret_code_t start_transfer(nrf_spi_mngr_t const * p_nrf_spi_mngr)
  51. {
  52. ASSERT(p_nrf_spi_mngr != NULL);
  53. // use a local variable to avoid using two volatile variables in one
  54. // expression
  55. uint8_t curr_transfer_idx = p_nrf_spi_mngr->p_nrf_spi_mngr_cb->current_transfer_idx;
  56. nrf_spi_mngr_transfer_t const * p_transfer =
  57. &p_nrf_spi_mngr->p_nrf_spi_mngr_cb->p_current_transaction->p_transfers[curr_transfer_idx];
  58. return nrf_drv_spi_transfer(&p_nrf_spi_mngr->spi,
  59. p_transfer->p_tx_data, p_transfer->tx_length,
  60. p_transfer->p_rx_data, p_transfer->rx_length);
  61. }
  62. static void transaction_begin_signal(nrf_spi_mngr_t const * p_nrf_spi_mngr)
  63. {
  64. ASSERT(p_nrf_spi_mngr != NULL);
  65. nrf_spi_mngr_transaction_t const * p_current_transaction =
  66. p_nrf_spi_mngr->p_nrf_spi_mngr_cb->p_current_transaction;
  67. if (p_current_transaction->begin_callback != NULL)
  68. {
  69. void * p_user_data = p_current_transaction->p_user_data;
  70. p_current_transaction->begin_callback(p_user_data);
  71. }
  72. }
  73. static void transaction_end_signal(nrf_spi_mngr_t const * p_nrf_spi_mngr,
  74. ret_code_t result)
  75. {
  76. ASSERT(p_nrf_spi_mngr != NULL);
  77. nrf_spi_mngr_transaction_t const * p_current_transaction =
  78. p_nrf_spi_mngr->p_nrf_spi_mngr_cb->p_current_transaction;
  79. if (p_current_transaction->end_callback != NULL)
  80. {
  81. void * p_user_data = p_current_transaction->p_user_data;
  82. p_current_transaction->end_callback(result, p_user_data);
  83. }
  84. }
  85. static void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
  86. void * p_context);
  87. // This function starts pending transaction if there is no current one or
  88. // when 'switch_transaction' parameter is set to true. It is important to
  89. // switch to new transaction without setting 'p_nrf_spi_mngr->p_curr_transaction'
  90. // to NULL in between, since this pointer is used to check idle status - see
  91. // 'nrf_spi_mngr_is_idle()'.
  92. static void start_pending_transaction(nrf_spi_mngr_t const * p_nrf_spi_mngr,
  93. bool switch_transaction)
  94. {
  95. ASSERT(p_nrf_spi_mngr != NULL);
  96. while (1)
  97. {
  98. bool start_transaction = false;
  99. nrf_spi_mngr_cb_t * p_cb = p_nrf_spi_mngr->p_nrf_spi_mngr_cb;
  100. CRITICAL_REGION_ENTER();
  101. if (switch_transaction || nrf_spi_mngr_is_idle(p_nrf_spi_mngr))
  102. {
  103. if (nrf_queue_pop(p_nrf_spi_mngr->p_queue,
  104. (void *)(&p_cb->p_current_transaction))
  105. == NRF_SUCCESS)
  106. {
  107. start_transaction = true;
  108. }
  109. else
  110. {
  111. p_cb->p_current_transaction = NULL;
  112. }
  113. }
  114. CRITICAL_REGION_EXIT();
  115. if (!start_transaction)
  116. {
  117. return;
  118. }
  119. nrf_drv_spi_config_t const * p_instance_cfg;
  120. if (p_cb->p_current_transaction->p_required_spi_cfg == NULL)
  121. {
  122. p_instance_cfg = &p_cb->default_configuration;
  123. }
  124. else
  125. {
  126. p_instance_cfg = p_cb->p_current_transaction->p_required_spi_cfg;
  127. }
  128. ret_code_t result;
  129. if (memcmp(p_cb->p_current_configuration, p_instance_cfg, sizeof(*p_instance_cfg)) != 0)
  130. {
  131. nrf_drv_spi_uninit(&p_nrf_spi_mngr->spi);
  132. result = nrf_drv_spi_init(&p_nrf_spi_mngr->spi,
  133. p_instance_cfg,
  134. spi_event_handler,
  135. (void *)p_nrf_spi_mngr);
  136. ASSERT(result == NRF_SUCCESS);
  137. p_cb->p_current_configuration = p_instance_cfg;
  138. }
  139. // Try to start first transfer for this new transaction.
  140. p_cb->current_transfer_idx = 0;
  141. // Execute user code if available before starting transaction
  142. transaction_begin_signal(p_nrf_spi_mngr);
  143. result = start_transfer(p_nrf_spi_mngr);
  144. // If transaction started successfully there is nothing more to do here now.
  145. if (result == NRF_SUCCESS)
  146. {
  147. return;
  148. }
  149. // Transfer failed to start - notify user that this transaction
  150. // cannot be started and try with next one (in next iteration of
  151. // the loop).
  152. transaction_end_signal(p_nrf_spi_mngr, result);
  153. switch_transaction = true;
  154. }
  155. }
  156. // This function shall be called to handle SPI events. It shall be mainly used by SPI IRQ for
  157. // finished tranfer.
  158. static void spi_event_handler(nrf_drv_spi_evt_t const * p_event,
  159. void * p_context)
  160. {
  161. ASSERT(p_event != NULL);
  162. ASSERT(p_context != NULL);
  163. ret_code_t result;
  164. nrf_spi_mngr_cb_t * p_cb = ((nrf_spi_mngr_t const *)p_context)->p_nrf_spi_mngr_cb;
  165. // This callback should be called only during transaction.
  166. ASSERT(p_cb->p_current_transaction != NULL);
  167. if (p_event->type == NRF_DRV_SPI_EVENT_DONE)
  168. {
  169. result = NRF_SUCCESS;
  170. // Transfer finished successfully. If there is another one to be
  171. // performed in the current transaction, start it now.
  172. // use a local variable to avoid using two volatile variables in one
  173. // expression
  174. uint8_t curr_transfer_idx = p_cb->current_transfer_idx;
  175. ++curr_transfer_idx;
  176. if (curr_transfer_idx < p_cb->p_current_transaction->number_of_transfers)
  177. {
  178. p_cb->current_transfer_idx = curr_transfer_idx;
  179. result = start_transfer(((nrf_spi_mngr_t const *)p_context));
  180. if (result == NRF_SUCCESS)
  181. {
  182. // The current transaction is running and its next transfer
  183. // has been successfully started. There is nothing more to do.
  184. return;
  185. }
  186. // if the next transfer could not be started due to some error
  187. // we finish the transaction with this error code as the result
  188. }
  189. }
  190. else
  191. {
  192. result = NRF_ERROR_INTERNAL;
  193. }
  194. // The current transaction has been completed or interrupted by some error.
  195. // Notify the user and start next one (if there is any).
  196. transaction_end_signal(((nrf_spi_mngr_t const *)p_context), result);
  197. // we switch transactions here ('p_nrf_spi_mngr->p_current_transaction' is set
  198. // to NULL only if there is nothing more to do) in order to not generate
  199. // spurious idle status (even for a moment)
  200. start_pending_transaction(((nrf_spi_mngr_t const *)p_context), true);
  201. }
  202. ret_code_t nrf_spi_mngr_init(nrf_spi_mngr_t const * p_nrf_spi_mngr,
  203. nrf_drv_spi_config_t const * p_default_spi_config)
  204. {
  205. ASSERT(p_nrf_spi_mngr != NULL);
  206. ASSERT(p_nrf_spi_mngr->p_queue != NULL);
  207. ASSERT(p_nrf_spi_mngr->p_queue->size > 0);
  208. ASSERT(p_default_spi_config != NULL);
  209. ret_code_t err_code;
  210. err_code = nrf_drv_spi_init(&p_nrf_spi_mngr->spi,
  211. p_default_spi_config,
  212. spi_event_handler,
  213. (void *)p_nrf_spi_mngr);
  214. if (err_code == NRF_SUCCESS)
  215. {
  216. nrf_spi_mngr_cb_t * p_cb = p_nrf_spi_mngr->p_nrf_spi_mngr_cb;
  217. p_cb->p_current_transaction = NULL;
  218. p_cb->default_configuration = *p_default_spi_config;
  219. p_cb->p_current_configuration = &p_cb->default_configuration;
  220. }
  221. return err_code;
  222. }
  223. void nrf_spi_mngr_uninit(nrf_spi_mngr_t const * p_nrf_spi_mngr)
  224. {
  225. ASSERT(p_nrf_spi_mngr != NULL);
  226. nrf_drv_spi_uninit(&p_nrf_spi_mngr->spi);
  227. p_nrf_spi_mngr->p_nrf_spi_mngr_cb->p_current_transaction = NULL;
  228. }
  229. ret_code_t nrf_spi_mngr_schedule(nrf_spi_mngr_t const * p_nrf_spi_mngr,
  230. nrf_spi_mngr_transaction_t const * p_transaction)
  231. {
  232. ASSERT(p_nrf_spi_mngr != NULL);
  233. ASSERT(p_transaction != NULL);
  234. ASSERT(p_transaction->p_transfers != NULL);
  235. ASSERT(p_transaction->number_of_transfers != 0);
  236. ret_code_t result = nrf_queue_push(p_nrf_spi_mngr->p_queue, (void *)(&p_transaction));
  237. if (result == NRF_SUCCESS)
  238. {
  239. // New transaction has been successfully added to queue,
  240. // so if we are currently idle it's time to start the job.
  241. start_pending_transaction(p_nrf_spi_mngr, false);
  242. }
  243. return result;
  244. }
  245. static void spi_internal_transaction_cb(ret_code_t result, void * p_user_data)
  246. {
  247. nrf_spi_mngr_cb_data_t * p_cb_data = (nrf_spi_mngr_cb_data_t *)p_user_data;
  248. p_cb_data->transaction_result = result;
  249. p_cb_data->transaction_in_progress = false;
  250. }
  251. ret_code_t nrf_spi_mngr_perform(nrf_spi_mngr_t const * p_nrf_spi_mngr,
  252. nrf_drv_spi_config_t const * p_config,
  253. nrf_spi_mngr_transfer_t const * p_transfers,
  254. uint8_t number_of_transfers,
  255. void (* user_function)(void))
  256. {
  257. ASSERT(p_nrf_spi_mngr != NULL);
  258. ASSERT(p_transfers != NULL);
  259. ASSERT(number_of_transfers != 0);
  260. nrf_spi_mngr_cb_data_t cb_data =
  261. {
  262. .transaction_in_progress = true
  263. };
  264. nrf_spi_mngr_transaction_t internal_transaction =
  265. {
  266. .begin_callback = NULL,
  267. .end_callback = spi_internal_transaction_cb,
  268. .p_user_data = (void *)&cb_data,
  269. .p_transfers = p_transfers,
  270. .number_of_transfers = number_of_transfers,
  271. .p_required_spi_cfg = p_config
  272. };
  273. ret_code_t result = nrf_spi_mngr_schedule(p_nrf_spi_mngr, &internal_transaction);
  274. VERIFY_SUCCESS(result);
  275. while (cb_data.transaction_in_progress)
  276. {
  277. if (user_function)
  278. {
  279. user_function();
  280. }
  281. }
  282. return cb_data.transaction_result;
  283. }
  284. #endif //NRF_MODULE_ENABLED(NRF_SPI_MNGR)