Repo for ESP32 Weather Station Development
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1138 lines
44KB

  1. // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. /*
  14. ----------------------------------------
  15. Non DMA version of the spi_master driver
  16. ----------------------------------------
  17. ------------------------------------------------------------------------------------
  18. Based on esp-idf 'spi_master', modified by LoBo (https://github.com/loboris) 03/2017
  19. ------------------------------------------------------------------------------------
  20. * Transfers data to SPI device in direct mode, not using DMA
  21. * All configuration options (bus, device, transaction) are the same as in spi_master driver
  22. * Transfers uses the semaphore (taken in select function & given in deselect function) to protect the transfer
  23. * Number of the devices attached to the bus which uses hardware CS can be 3 ('NO_CS')
  24. * Additional devices which uses software CS can be attached to the bus, up to 'NO_DEV'
  25. * 'spi_bus_initialize' & 'spi_bus_remove' functions are removed, spi bus is initiated/removed in spi_lobo_bus_add_device/spi_lobo_bus_remove_device when needed
  26. * 'spi_lobo_bus_add_device' function has added parameter 'bus_config' and automatically initializes spi bus device if not already initialized
  27. * 'spi_lobo_bus_remove_device' automatically removes spi bus device if no other devices are attached to it.
  28. * Devices can have individual bus_configs, so different mosi, miso, sck pins can be configured for each device
  29. Reconfiguring the bus is done automaticaly in 'spi_lobo_device_select' function
  30. * 'spi_lobo_device_select' & 'spi_lobo_device_deselect' functions handles devices configuration changes and software CS
  31. * Some helper functions are added ('spi_lobo_get_speed', 'spi_lobo_set_speed', ...)
  32. * All structures are available in header file for easy creation of user low level spi functions. See **tftfunc.c** source for examples.
  33. * Transimt and receive lenghts are limited only by available memory
  34. Main driver's function is 'spi_lobo_transfer_data()'
  35. * TRANSMIT 8-bit data to spi device from 'trans->tx_buffer' or 'trans->tx_data' (trans->lenght/8 bytes)
  36. * and RECEIVE data to 'trans->rx_buffer' or 'trans->rx_data' (trans->rx_length/8 bytes)
  37. * Lengths must be 8-bit multiples!
  38. * If trans->rx_buffer is NULL or trans->rx_length is 0, only transmits data
  39. * If trans->tx_buffer is NULL or trans->length is 0, only receives data
  40. * If the device is in duplex mode (LB_SPI_DEVICE_HALFDUPLEX flag NOT set), data are transmitted and received simultaneously.
  41. * If the device is in half duplex mode (LB_SPI_DEVICE_HALFDUPLEX flag IS set), data are received after transmission
  42. * 'address', 'command' and 'dummy bits' are transmitted before data phase IF set in device's configuration
  43. * and IF 'trans->length' and 'trans->rx_length' are NOT both 0
  44. * If configured, devices 'pre_cb' callback is called before and 'post_cb' after the transmission
  45. * If device was not previously selected, it will be selected before transmission and deselected after transmission.
  46. */
  47. /*
  48. Replace this include with
  49. #include "driver/spi_master_lobo.h"
  50. if the driver is located in esp-isf/components
  51. */
  52. #include "freertos/FreeRTOS.h"
  53. #include <string.h>
  54. #include "soc/spi_reg.h"
  55. #include "soc/dport_reg.h"
  56. #include "esp_log.h"
  57. #include "freertos/semphr.h"
  58. #include "driver/uart.h"
  59. #include "driver/gpio.h"
  60. #include "spi_master_lobo.h"
  61. #include "driver/periph_ctrl.h"
  62. static spi_lobo_host_t *spihost[3] = {NULL};
  63. static const char *SPI_TAG = "spi_lobo_master";
  64. #define SPI_CHECK(a, str, ret_val) \
  65. if (!(a)) { \
  66. ESP_LOGE(SPI_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
  67. return (ret_val); \
  68. }
  69. /*
  70. Stores a bunch of per-spi-peripheral data.
  71. */
  72. typedef struct {
  73. const uint8_t spiclk_out; //GPIO mux output signals
  74. const uint8_t spid_out;
  75. const uint8_t spiq_out;
  76. const uint8_t spiwp_out;
  77. const uint8_t spihd_out;
  78. const uint8_t spid_in; //GPIO mux input signals
  79. const uint8_t spiq_in;
  80. const uint8_t spiwp_in;
  81. const uint8_t spihd_in;
  82. const uint8_t spics_out[3]; // /CS GPIO output mux signals
  83. const uint8_t spiclk_native; //IO pins of IO_MUX muxed signals
  84. const uint8_t spid_native;
  85. const uint8_t spiq_native;
  86. const uint8_t spiwp_native;
  87. const uint8_t spihd_native;
  88. const uint8_t spics0_native;
  89. const uint8_t irq; //irq source for interrupt mux
  90. const uint8_t irq_dma; //dma irq source for interrupt mux
  91. const periph_module_t module; //peripheral module, for enabling clock etc
  92. spi_dev_t *hw; //Pointer to the hardware registers
  93. } spi_signal_conn_t;
  94. /*
  95. Bunch of constants for every SPI peripheral: GPIO signals, irqs, hw addr of registers etc
  96. */
  97. static const spi_signal_conn_t io_signal[3]={
  98. {
  99. .spiclk_out=SPICLK_OUT_IDX,
  100. .spid_out=SPID_OUT_IDX,
  101. .spiq_out=SPIQ_OUT_IDX,
  102. .spiwp_out=SPIWP_OUT_IDX,
  103. .spihd_out=SPIHD_OUT_IDX,
  104. .spid_in=SPID_IN_IDX,
  105. .spiq_in=SPIQ_IN_IDX,
  106. .spiwp_in=SPIWP_IN_IDX,
  107. .spihd_in=SPIHD_IN_IDX,
  108. .spics_out={SPICS0_OUT_IDX, SPICS1_OUT_IDX, SPICS2_OUT_IDX},
  109. .spiclk_native=6,
  110. .spid_native=8,
  111. .spiq_native=7,
  112. .spiwp_native=10,
  113. .spihd_native=9,
  114. .spics0_native=11,
  115. .irq=ETS_SPI1_INTR_SOURCE,
  116. .irq_dma=ETS_SPI1_DMA_INTR_SOURCE,
  117. .module=PERIPH_SPI_MODULE,
  118. .hw=&SPI1
  119. }, {
  120. .spiclk_out=HSPICLK_OUT_IDX,
  121. .spid_out=HSPID_OUT_IDX,
  122. .spiq_out=HSPIQ_OUT_IDX,
  123. .spiwp_out=HSPIWP_OUT_IDX,
  124. .spihd_out=HSPIHD_OUT_IDX,
  125. .spid_in=HSPID_IN_IDX,
  126. .spiq_in=HSPIQ_IN_IDX,
  127. .spiwp_in=HSPIWP_IN_IDX,
  128. .spihd_in=HSPIHD_IN_IDX,
  129. .spics_out={HSPICS0_OUT_IDX, HSPICS1_OUT_IDX, HSPICS2_OUT_IDX},
  130. .spiclk_native=14,
  131. .spid_native=13,
  132. .spiq_native=12,
  133. .spiwp_native=2,
  134. .spihd_native=4,
  135. .spics0_native=15,
  136. .irq=ETS_SPI2_INTR_SOURCE,
  137. .irq_dma=ETS_SPI2_DMA_INTR_SOURCE,
  138. .module=PERIPH_HSPI_MODULE,
  139. .hw=&SPI2
  140. }, {
  141. .spiclk_out=VSPICLK_OUT_IDX,
  142. .spid_out=VSPID_OUT_IDX,
  143. .spiq_out=VSPIQ_OUT_IDX,
  144. .spiwp_out=VSPIWP_OUT_IDX,
  145. .spihd_out=VSPIHD_OUT_IDX,
  146. .spid_in=VSPID_IN_IDX,
  147. .spiq_in=VSPIQ_IN_IDX,
  148. .spiwp_in=VSPIWP_IN_IDX,
  149. .spihd_in=VSPIHD_IN_IDX,
  150. .spics_out={VSPICS0_OUT_IDX, VSPICS1_OUT_IDX, VSPICS2_OUT_IDX},
  151. .spiclk_native=18,
  152. .spid_native=23,
  153. .spiq_native=19,
  154. .spiwp_native=22,
  155. .spihd_native=21,
  156. .spics0_native=5,
  157. .irq=ETS_SPI3_INTR_SOURCE,
  158. .irq_dma=ETS_SPI3_DMA_INTR_SOURCE,
  159. .module=PERIPH_VSPI_MODULE,
  160. .hw=&SPI3
  161. }
  162. };
  163. //======================================================================================================
  164. #define DMA_CHANNEL_ENABLED(dma_chan) (BIT(dma_chan-1))
  165. typedef void(*dmaworkaround_cb_t)(void *arg);
  166. //Set up a list of dma descriptors. dmadesc is an array of descriptors. Data is the buffer to point to.
  167. //--------------------------------------------------------------------------------------------
  168. void spi_lobo_setup_dma_desc_links(lldesc_t *dmadesc, int len, const uint8_t *data, bool isrx)
  169. {
  170. int n = 0;
  171. while (len) {
  172. int dmachunklen = len;
  173. if (dmachunklen > SPI_MAX_DMA_LEN) dmachunklen = SPI_MAX_DMA_LEN;
  174. if (isrx) {
  175. //Receive needs DMA length rounded to next 32-bit boundary
  176. dmadesc[n].size = (dmachunklen + 3) & (~3);
  177. dmadesc[n].length = (dmachunklen + 3) & (~3);
  178. } else {
  179. dmadesc[n].size = dmachunklen;
  180. dmadesc[n].length = dmachunklen;
  181. }
  182. dmadesc[n].buf = (uint8_t *)data;
  183. dmadesc[n].eof = 0;
  184. dmadesc[n].sosf = 0;
  185. dmadesc[n].owner = 1;
  186. dmadesc[n].qe.stqe_next = &dmadesc[n + 1];
  187. len -= dmachunklen;
  188. data += dmachunklen;
  189. n++;
  190. }
  191. dmadesc[n - 1].eof = 1; //Mark last DMA desc as end of stream.
  192. dmadesc[n - 1].qe.stqe_next = NULL;
  193. }
  194. /*
  195. Code for workaround for DMA issue in ESP32 v0/v1 silicon
  196. */
  197. static volatile int dmaworkaround_channels_busy[2] = {0, 0};
  198. static dmaworkaround_cb_t dmaworkaround_cb;
  199. static void *dmaworkaround_cb_arg;
  200. static portMUX_TYPE dmaworkaround_mux = portMUX_INITIALIZER_UNLOCKED;
  201. static int dmaworkaround_waiting_for_chan = 0;
  202. static bool spi_periph_claimed[3] = {true, false, false};
  203. static uint8_t spi_dma_chan_enabled = 0;
  204. static portMUX_TYPE spi_dma_spinlock = portMUX_INITIALIZER_UNLOCKED;
  205. //--------------------------------------------------------------------------------------------
  206. bool IRAM_ATTR spi_lobo_dmaworkaround_req_reset(int dmachan, dmaworkaround_cb_t cb, void *arg)
  207. {
  208. int otherchan = (dmachan == 1) ? 2 : 1;
  209. bool ret;
  210. portENTER_CRITICAL(&dmaworkaround_mux);
  211. if (dmaworkaround_channels_busy[otherchan-1]) {
  212. //Other channel is busy. Call back when it's done.
  213. dmaworkaround_cb = cb;
  214. dmaworkaround_cb_arg = arg;
  215. dmaworkaround_waiting_for_chan = otherchan;
  216. ret = false;
  217. } else {
  218. //Reset DMA
  219. DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST);
  220. DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST);
  221. ret = true;
  222. }
  223. portEXIT_CRITICAL(&dmaworkaround_mux);
  224. return ret;
  225. }
  226. //-------------------------------------------------------
  227. bool IRAM_ATTR spi_lobo_dmaworkaround_reset_in_progress()
  228. {
  229. return (dmaworkaround_waiting_for_chan != 0);
  230. }
  231. //-----------------------------------------------------
  232. void IRAM_ATTR spi_lobo_dmaworkaround_idle(int dmachan)
  233. {
  234. portENTER_CRITICAL(&dmaworkaround_mux);
  235. dmaworkaround_channels_busy[dmachan-1] = 0;
  236. if (dmaworkaround_waiting_for_chan == dmachan) {
  237. //Reset DMA
  238. DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST);
  239. DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST);
  240. dmaworkaround_waiting_for_chan = 0;
  241. //Call callback
  242. dmaworkaround_cb(dmaworkaround_cb_arg);
  243. }
  244. portEXIT_CRITICAL(&dmaworkaround_mux);
  245. }
  246. //----------------------------------------------------------------
  247. void IRAM_ATTR spi_lobo_dmaworkaround_transfer_active(int dmachan)
  248. {
  249. portENTER_CRITICAL(&dmaworkaround_mux);
  250. dmaworkaround_channels_busy[dmachan-1] = 1;
  251. portEXIT_CRITICAL(&dmaworkaround_mux);
  252. }
  253. //Returns true if this peripheral is successfully claimed, false if otherwise.
  254. //-----------------------------------------------------
  255. bool spi_lobo_periph_claim(spi_lobo_host_device_t host)
  256. {
  257. bool ret = __sync_bool_compare_and_swap(&spi_periph_claimed[host], false, true);
  258. if (ret) periph_module_enable(io_signal[host].module);
  259. return ret;
  260. }
  261. //Returns true if this peripheral is successfully freed, false if otherwise.
  262. //-----------------------------------------------
  263. bool spi_lobo_periph_free(spi_lobo_host_device_t host)
  264. {
  265. bool ret = __sync_bool_compare_and_swap(&spi_periph_claimed[host], true, false);
  266. if (ret) periph_module_disable(io_signal[host].module);
  267. return ret;
  268. }
  269. //-----------------------------------------
  270. bool spi_lobo_dma_chan_claim (int dma_chan)
  271. {
  272. bool ret = false;
  273. assert( dma_chan == 1 || dma_chan == 2 );
  274. portENTER_CRITICAL(&spi_dma_spinlock);
  275. if ( !(spi_dma_chan_enabled & DMA_CHANNEL_ENABLED(dma_chan)) ) {
  276. // get the channel only when it's not claimed yet.
  277. spi_dma_chan_enabled |= DMA_CHANNEL_ENABLED(dma_chan);
  278. ret = true;
  279. }
  280. periph_module_enable( PERIPH_SPI_DMA_MODULE );
  281. portEXIT_CRITICAL(&spi_dma_spinlock);
  282. return ret;
  283. }
  284. //---------------------------------------
  285. bool spi_lobo_dma_chan_free(int dma_chan)
  286. {
  287. assert( dma_chan == 1 || dma_chan == 2 );
  288. assert( spi_dma_chan_enabled & DMA_CHANNEL_ENABLED(dma_chan) );
  289. portENTER_CRITICAL(&spi_dma_spinlock);
  290. spi_dma_chan_enabled &= ~DMA_CHANNEL_ENABLED(dma_chan);
  291. if ( spi_dma_chan_enabled == 0 ) {
  292. //disable the DMA only when all the channels are freed.
  293. periph_module_disable( PERIPH_SPI_DMA_MODULE );
  294. }
  295. portEXIT_CRITICAL(&spi_dma_spinlock);
  296. return true;
  297. }
  298. //======================================================================================================
  299. //----------------------------------------------------------------------------------------------------------------
  300. static esp_err_t spi_lobo_bus_initialize(spi_lobo_host_device_t host, spi_lobo_bus_config_t *bus_config, int init)
  301. {
  302. bool native=true, spi_chan_claimed, dma_chan_claimed;
  303. if (init > 0) {
  304. /* ToDo: remove this when we have flash operations cooperating with this */
  305. SPI_CHECK(host!=TFT_SPI_HOST, "SPI1 is not supported", ESP_ERR_NOT_SUPPORTED);
  306. SPI_CHECK(host>=TFT_SPI_HOST && host<=TFT_VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG);
  307. SPI_CHECK(spihost[host]==NULL, "host already in use", ESP_ERR_INVALID_STATE);
  308. }
  309. else {
  310. SPI_CHECK(spihost[host]!=NULL, "host not in use", ESP_ERR_INVALID_STATE);
  311. }
  312. SPI_CHECK(bus_config->mosi_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->mosi_io_num), "spid pin invalid", ESP_ERR_INVALID_ARG);
  313. SPI_CHECK(bus_config->sclk_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->sclk_io_num), "spiclk pin invalid", ESP_ERR_INVALID_ARG);
  314. SPI_CHECK(bus_config->miso_io_num<0 || GPIO_IS_VALID_GPIO(bus_config->miso_io_num), "spiq pin invalid", ESP_ERR_INVALID_ARG);
  315. SPI_CHECK(bus_config->quadwp_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->quadwp_io_num), "spiwp pin invalid", ESP_ERR_INVALID_ARG);
  316. SPI_CHECK(bus_config->quadhd_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->quadhd_io_num), "spihd pin invalid", ESP_ERR_INVALID_ARG);
  317. if (init > 0) {
  318. spi_chan_claimed=spi_lobo_periph_claim(host);
  319. SPI_CHECK(spi_chan_claimed, "host already in use", ESP_ERR_INVALID_STATE);
  320. //spihost[host]=malloc(sizeof(spi_lobo_host_t));
  321. spihost[host]=heap_caps_malloc(sizeof(spi_lobo_host_t), MALLOC_CAP_DMA);
  322. if (spihost[host]==NULL) return ESP_ERR_NO_MEM;
  323. memset(spihost[host], 0, sizeof(spi_lobo_host_t));
  324. // Create semaphore
  325. spihost[host]->spi_lobo_bus_mutex = xSemaphoreCreateMutex();
  326. if (!spihost[host]->spi_lobo_bus_mutex) return ESP_ERR_NO_MEM;
  327. }
  328. spihost[host]->cur_device = -1;
  329. memcpy(&spihost[host]->cur_bus_config, bus_config, sizeof(spi_lobo_bus_config_t));
  330. //Check if the selected pins correspond to the native pins of the peripheral
  331. if (bus_config->mosi_io_num >= 0 && bus_config->mosi_io_num!=io_signal[host].spid_native) native=false;
  332. if (bus_config->miso_io_num >= 0 && bus_config->miso_io_num!=io_signal[host].spiq_native) native=false;
  333. if (bus_config->sclk_io_num >= 0 && bus_config->sclk_io_num!=io_signal[host].spiclk_native) native=false;
  334. if (bus_config->quadwp_io_num >= 0 && bus_config->quadwp_io_num!=io_signal[host].spiwp_native) native=false;
  335. if (bus_config->quadhd_io_num >= 0 && bus_config->quadhd_io_num!=io_signal[host].spihd_native) native=false;
  336. spihost[host]->no_gpio_matrix=native;
  337. if (native) {
  338. //All SPI native pin selections resolve to 1, so we put that here instead of trying to figure
  339. //out which FUNC_GPIOx_xSPIxx to grab; they all are defined to 1 anyway.
  340. if (bus_config->mosi_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->mosi_io_num], 1);
  341. if (bus_config->miso_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], 1);
  342. if (bus_config->quadwp_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadwp_io_num], 1);
  343. if (bus_config->quadhd_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadhd_io_num], 1);
  344. if (bus_config->sclk_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], 1);
  345. } else {
  346. //Use GPIO
  347. if (bus_config->mosi_io_num>0) {
  348. PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->mosi_io_num], PIN_FUNC_GPIO);
  349. gpio_set_direction(bus_config->mosi_io_num, GPIO_MODE_OUTPUT);
  350. gpio_matrix_out(bus_config->mosi_io_num, io_signal[host].spid_out, false, false);
  351. gpio_matrix_in(bus_config->mosi_io_num, io_signal[host].spid_in, false);
  352. }
  353. if (bus_config->miso_io_num>0) {
  354. PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], PIN_FUNC_GPIO);
  355. gpio_set_direction(bus_config->miso_io_num, GPIO_MODE_INPUT);
  356. gpio_matrix_out(bus_config->miso_io_num, io_signal[host].spiq_out, false, false);
  357. gpio_matrix_in(bus_config->miso_io_num, io_signal[host].spiq_in, false);
  358. }
  359. if (bus_config->quadwp_io_num>0) {
  360. PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadwp_io_num], PIN_FUNC_GPIO);
  361. gpio_set_direction(bus_config->quadwp_io_num, GPIO_MODE_OUTPUT);
  362. gpio_matrix_out(bus_config->quadwp_io_num, io_signal[host].spiwp_out, false, false);
  363. gpio_matrix_in(bus_config->quadwp_io_num, io_signal[host].spiwp_in, false);
  364. }
  365. if (bus_config->quadhd_io_num>0) {
  366. PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadhd_io_num], PIN_FUNC_GPIO);
  367. gpio_set_direction(bus_config->quadhd_io_num, GPIO_MODE_OUTPUT);
  368. gpio_matrix_out(bus_config->quadhd_io_num, io_signal[host].spihd_out, false, false);
  369. gpio_matrix_in(bus_config->quadhd_io_num, io_signal[host].spihd_in, false);
  370. }
  371. if (bus_config->sclk_io_num>0) {
  372. PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], PIN_FUNC_GPIO);
  373. gpio_set_direction(bus_config->sclk_io_num, GPIO_MODE_OUTPUT);
  374. gpio_matrix_out(bus_config->sclk_io_num, io_signal[host].spiclk_out, false, false);
  375. }
  376. }
  377. periph_module_enable(io_signal[host].module);
  378. spihost[host]->hw=io_signal[host].hw;
  379. if (init > 0) {
  380. dma_chan_claimed=spi_lobo_dma_chan_claim(init);
  381. if ( !dma_chan_claimed ) {
  382. spi_lobo_periph_free( host );
  383. SPI_CHECK(dma_chan_claimed, "dma channel already in use", ESP_ERR_INVALID_STATE);
  384. }
  385. spihost[host]->dma_chan = init;
  386. //See how many dma descriptors we need and allocate them
  387. int dma_desc_ct=(bus_config->max_transfer_sz+SPI_MAX_DMA_LEN-1)/SPI_MAX_DMA_LEN;
  388. if (dma_desc_ct==0) dma_desc_ct=1; //default to 4k when max is not given
  389. spihost[host]->max_transfer_sz = dma_desc_ct*SPI_MAX_DMA_LEN;
  390. spihost[host]->dmadesc_tx=heap_caps_malloc(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA);
  391. spihost[host]->dmadesc_rx=heap_caps_malloc(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA);
  392. if (!spihost[host]->dmadesc_tx || !spihost[host]->dmadesc_rx) goto nomem;
  393. //Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset.
  394. spi_lobo_dmaworkaround_idle(spihost[host]->dma_chan);
  395. // Reset DMA
  396. spihost[host]->hw->dma_conf.val |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST;
  397. spihost[host]->hw->dma_out_link.start=0;
  398. spihost[host]->hw->dma_in_link.start=0;
  399. spihost[host]->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST);
  400. spihost[host]->hw->dma_conf.out_data_burst_en=1;
  401. //Reset timing
  402. spihost[host]->hw->ctrl2.val=0;
  403. //Disable unneeded ints
  404. spihost[host]->hw->slave.rd_buf_done=0;
  405. spihost[host]->hw->slave.wr_buf_done=0;
  406. spihost[host]->hw->slave.rd_sta_done=0;
  407. spihost[host]->hw->slave.wr_sta_done=0;
  408. spihost[host]->hw->slave.rd_buf_inten=0;
  409. spihost[host]->hw->slave.wr_buf_inten=0;
  410. spihost[host]->hw->slave.rd_sta_inten=0;
  411. spihost[host]->hw->slave.wr_sta_inten=0;
  412. //Force a transaction done interrupt. This interrupt won't fire yet because we initialized the SPI interrupt as
  413. //disabled. This way, we can just enable the SPI interrupt and the interrupt handler will kick in, handling
  414. //any transactions that are queued.
  415. spihost[host]->hw->slave.trans_inten=1;
  416. spihost[host]->hw->slave.trans_done=1;
  417. //Select DMA channel.
  418. DPORT_SET_PERI_REG_BITS(DPORT_SPI_DMA_CHAN_SEL_REG, 3, init, (host * 2));
  419. }
  420. return ESP_OK;
  421. nomem:
  422. if (spihost[host]) {
  423. free(spihost[host]->dmadesc_tx);
  424. free(spihost[host]->dmadesc_rx);
  425. }
  426. free(spihost[host]);
  427. spi_lobo_periph_free(host);
  428. return ESP_ERR_NO_MEM;
  429. }
  430. //---------------------------------------------------------------------------
  431. static esp_err_t spi_lobo_bus_free(spi_lobo_host_device_t host, int dofree)
  432. {
  433. if ((host == TFT_SPI_HOST) || (host > TFT_VSPI_HOST)) return ESP_ERR_NOT_SUPPORTED; // invalid host
  434. if (spihost[host] == NULL) return ESP_ERR_INVALID_STATE; // host not in use
  435. if (dofree) {
  436. for (int x=0; x<NO_DEV; x++) {
  437. if (spihost[host]->device[x] != NULL) return ESP_ERR_INVALID_STATE; // not all devices freed
  438. }
  439. }
  440. if ( spihost[host]->dma_chan > 0 ) {
  441. spi_lobo_dma_chan_free ( spihost[host]->dma_chan );
  442. }
  443. spihost[host]->hw->slave.trans_inten=0;
  444. spihost[host]->hw->slave.trans_done=0;
  445. spi_lobo_periph_free(host);
  446. if (dofree) {
  447. vSemaphoreDelete(spihost[host]->spi_lobo_bus_mutex);
  448. free(spihost[host]->dmadesc_tx);
  449. free(spihost[host]->dmadesc_rx);
  450. free(spihost[host]);
  451. spihost[host] = NULL;
  452. }
  453. return ESP_OK;
  454. }
  455. //---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  456. esp_err_t spi_lobo_bus_add_device(spi_lobo_host_device_t host, spi_lobo_bus_config_t *bus_config, spi_lobo_device_interface_config_t *dev_config, spi_lobo_device_handle_t *handle)
  457. {
  458. if ((host == TFT_SPI_HOST) || (host > TFT_VSPI_HOST)) return ESP_ERR_NOT_SUPPORTED; // invalid host
  459. if (spihost[host] == NULL) {
  460. esp_err_t ret = spi_lobo_bus_initialize(host, bus_config, 1);
  461. if (ret) return ret;
  462. }
  463. int freecs, maxdev;
  464. int apbclk=APB_CLK_FREQ;
  465. if (spihost[host] == NULL) return ESP_ERR_INVALID_STATE;
  466. if (dev_config->spics_io_num >= 0) {
  467. if (!GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_io_num)) return ESP_ERR_INVALID_ARG;
  468. if (dev_config->spics_ext_io_num > 0) dev_config->spics_ext_io_num = -1;
  469. }
  470. else {
  471. //if ((dev_config->spics_ext_io_num <= 0) || (!GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_ext_io_num))) return ESP_ERR_INVALID_ARG;
  472. }
  473. //ToDo: Check if some other device uses the same 'spics_ext_io_num'
  474. if (dev_config->clock_speed_hz == 0) return ESP_ERR_INVALID_ARG;
  475. if (dev_config->spics_io_num > 0) maxdev = NO_CS;
  476. else maxdev = NO_DEV;
  477. for (freecs=0; freecs<maxdev; freecs++) {
  478. //See if this slot is free; reserve if it is by putting a dummy pointer in the slot. We use an atomic compare&swap to make this thread-safe.
  479. if (__sync_bool_compare_and_swap(&spihost[host]->device[freecs], NULL, (spi_lobo_device_t *)1)) break;
  480. }
  481. if (freecs == maxdev) return ESP_ERR_NOT_FOUND;
  482. // The hardware looks like it would support this, but actually setting cs_ena_pretrans when transferring in full
  483. // duplex mode does absolutely nothing on the ESP32.
  484. if ((dev_config->cs_ena_pretrans != 0) && (dev_config->flags & LB_SPI_DEVICE_HALFDUPLEX)) return ESP_ERR_INVALID_ARG;
  485. // Speeds >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections.
  486. if (((dev_config->flags & LB_SPI_DEVICE_HALFDUPLEX)==0) && (dev_config->clock_speed_hz > ((apbclk*2)/5)) && (!spihost[host]->no_gpio_matrix)) return ESP_ERR_INVALID_ARG;
  487. //Allocate memory for device
  488. spi_lobo_device_t *dev=malloc(sizeof(spi_lobo_device_t));
  489. if (dev==NULL) return ESP_ERR_NO_MEM;
  490. memset(dev, 0, sizeof(spi_lobo_device_t));
  491. spihost[host]->device[freecs]=dev;
  492. if (dev_config->duty_cycle_pos==0) dev_config->duty_cycle_pos=128;
  493. dev->host=spihost[host];
  494. dev->host_dev = host;
  495. //We want to save a copy of the dev config in the dev struct.
  496. memcpy(&dev->cfg, dev_config, sizeof(spi_lobo_device_interface_config_t));
  497. //We want to save a copy of the bus config in the dev struct.
  498. memcpy(&dev->bus_config, bus_config, sizeof(spi_lobo_bus_config_t));
  499. //Set CS pin, CS options
  500. if (dev_config->spics_io_num > 0) {
  501. if (spihost[host]->no_gpio_matrix &&dev_config->spics_io_num == io_signal[host].spics0_native && freecs==0) {
  502. //Again, the cs0s for all SPI peripherals map to pin mux source 1, so we use that instead of a define.
  503. PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[dev_config->spics_io_num], 1);
  504. } else {
  505. //Use GPIO matrix
  506. PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[dev_config->spics_io_num], PIN_FUNC_GPIO);
  507. gpio_set_direction(dev_config->spics_io_num, GPIO_MODE_OUTPUT);
  508. gpio_matrix_out(dev_config->spics_io_num, io_signal[host].spics_out[freecs], false, false);
  509. }
  510. }
  511. else if (dev_config->spics_ext_io_num >= 0) {
  512. gpio_set_direction(dev_config->spics_ext_io_num, GPIO_MODE_OUTPUT);
  513. gpio_set_level(dev_config->spics_ext_io_num, 1);
  514. }
  515. if (dev_config->flags & LB_SPI_DEVICE_CLK_AS_CS) {
  516. spihost[host]->hw->pin.master_ck_sel |= (1<<freecs);
  517. } else {
  518. spihost[host]->hw->pin.master_ck_sel &= (1<<freecs);
  519. }
  520. if (dev_config->flags & LB_SPI_DEVICE_POSITIVE_CS) {
  521. spihost[host]->hw->pin.master_cs_pol |= (1<<freecs);
  522. } else {
  523. spihost[host]->hw->pin.master_cs_pol &= (1<<freecs);
  524. }
  525. *handle = dev;
  526. return ESP_OK;
  527. }
  528. //-------------------------------------------------------------------
  529. esp_err_t spi_lobo_bus_remove_device(spi_lobo_device_handle_t handle)
  530. {
  531. int x;
  532. if (handle == NULL) return ESP_ERR_INVALID_ARG;
  533. //Remove device from list of csses and free memory
  534. for (x=0; x<NO_DEV; x++) {
  535. if (handle->host->device[x] == handle) handle->host->device[x]=NULL;
  536. }
  537. // Check if all devices are removed from this host and free the bus if yes
  538. for (x=0; x<NO_DEV; x++) {
  539. if (spihost[handle->host_dev]->device[x] !=NULL) break;
  540. }
  541. if (x == NO_DEV) {
  542. free(handle);
  543. spi_lobo_bus_free(handle->host_dev, 1);
  544. }
  545. else free(handle);
  546. return ESP_OK;
  547. }
  548. //-----------------------------------------------------------------
  549. static int IRAM_ATTR spi_freq_for_pre_n(int fapb, int pre, int n) {
  550. return (fapb / (pre * n));
  551. }
  552. /*
  553. * Set the SPI clock to a certain frequency. Returns the effective frequency set, which may be slightly
  554. * different from the requested frequency.
  555. */
  556. //-----------------------------------------------------------------------------------
  557. static int IRAM_ATTR spi_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle) {
  558. int pre, n, h, l, eff_clk;
  559. //In hw, n, h and l are 1-64, pre is 1-8K. Value written to register is one lower than used value.
  560. if (hz>((fapb/4)*3)) {
  561. //Using Fapb directly will give us the best result here.
  562. hw->clock.clkcnt_l=0;
  563. hw->clock.clkcnt_h=0;
  564. hw->clock.clkcnt_n=0;
  565. hw->clock.clkdiv_pre=0;
  566. hw->clock.clk_equ_sysclk=1;
  567. eff_clk=fapb;
  568. } else {
  569. //For best duty cycle resolution, we want n to be as close to 32 as possible, but
  570. //we also need a pre/n combo that gets us as close as possible to the intended freq.
  571. //To do this, we bruteforce n and calculate the best pre to go along with that.
  572. //If there's a choice between pre/n combos that give the same result, use the one
  573. //with the higher n.
  574. int bestn=-1;
  575. int bestpre=-1;
  576. int besterr=0;
  577. int errval;
  578. for (n=1; n<=64; n++) {
  579. //Effectively, this does pre=round((fapb/n)/hz).
  580. pre=((fapb/n)+(hz/2))/hz;
  581. if (pre<=0) pre=1;
  582. if (pre>8192) pre=8192;
  583. errval=abs(spi_freq_for_pre_n(fapb, pre, n)-hz);
  584. if (bestn==-1 || errval<=besterr) {
  585. besterr=errval;
  586. bestn=n;
  587. bestpre=pre;
  588. }
  589. }
  590. n=bestn;
  591. pre=bestpre;
  592. l=n;
  593. //This effectively does round((duty_cycle*n)/256)
  594. h=(duty_cycle*n+127)/256;
  595. if (h<=0) h=1;
  596. hw->clock.clk_equ_sysclk=0;
  597. hw->clock.clkcnt_n=n-1;
  598. hw->clock.clkdiv_pre=pre-1;
  599. hw->clock.clkcnt_h=h-1;
  600. hw->clock.clkcnt_l=l-1;
  601. eff_clk=spi_freq_for_pre_n(fapb, pre, n);
  602. }
  603. return eff_clk;
  604. }
  605. //------------------------------------------------------------------------------------
  606. esp_err_t IRAM_ATTR spi_lobo_device_select(spi_lobo_device_handle_t handle, int force)
  607. {
  608. if (handle == NULL) return ESP_ERR_INVALID_ARG;
  609. if ((handle->cfg.selected == 1) && (!force)) return ESP_OK; // already selected
  610. int i;
  611. spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host;
  612. // find device's host bus
  613. for (i=0; i<NO_DEV; i++) {
  614. if (host->device[i] == handle) break;
  615. }
  616. if (i == NO_DEV) return ESP_ERR_INVALID_ARG;
  617. if (!(xSemaphoreTake(host->spi_lobo_bus_mutex, SPI_SEMAPHORE_WAIT))) return ESP_ERR_INVALID_STATE;
  618. // Check if previously used device's bus device is the same
  619. if (memcmp(&host->cur_bus_config, &handle->bus_config, sizeof(spi_lobo_bus_config_t)) != 0) {
  620. // device has different bus configuration, we need to reconfigure the bus
  621. esp_err_t err = spi_lobo_bus_free(1, 0);
  622. if (err) {
  623. xSemaphoreGive(host->spi_lobo_bus_mutex);
  624. return err;
  625. }
  626. err = spi_lobo_bus_initialize(i, &handle->bus_config, -1);
  627. if (err) {
  628. xSemaphoreGive(host->spi_lobo_bus_mutex);
  629. return err;
  630. }
  631. }
  632. //Reconfigure according to device settings, but only if the device changed or forced.
  633. if ((force) || (host->device[host->cur_device] != handle)) {
  634. //Assumes a hardcoded 80MHz Fapb for now. ToDo: figure out something better once we have clock scaling working.
  635. int apbclk=APB_CLK_FREQ;
  636. //Speeds >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections.
  637. if (((handle->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX) == 0) && (handle->cfg.clock_speed_hz > ((apbclk*2)/5)) && (!host->no_gpio_matrix)) {
  638. // set speed to 32 MHz
  639. handle->cfg.clock_speed_hz = (apbclk*2)/5;
  640. }
  641. int effclk=spi_set_clock(host->hw, apbclk, handle->cfg.clock_speed_hz, handle->cfg.duty_cycle_pos);
  642. //Configure bit order
  643. host->hw->ctrl.rd_bit_order=(handle->cfg.flags & LB_SPI_DEVICE_RXBIT_LSBFIRST)?1:0;
  644. host->hw->ctrl.wr_bit_order=(handle->cfg.flags & LB_SPI_DEVICE_TXBIT_LSBFIRST)?1:0;
  645. //Configure polarity
  646. //SPI iface needs to be configured for a delay in some cases.
  647. int nodelay=0;
  648. int extra_dummy=0;
  649. if (host->no_gpio_matrix) {
  650. if (effclk >= apbclk/2) {
  651. nodelay=1;
  652. }
  653. } else {
  654. if (effclk >= apbclk/2) {
  655. nodelay=1;
  656. extra_dummy=1; //Note: This only works on half-duplex connections. spi_lobo_bus_add_device checks for this.
  657. } else if (effclk >= apbclk/4) {
  658. nodelay=1;
  659. }
  660. }
  661. if (handle->cfg.mode==0) {
  662. host->hw->pin.ck_idle_edge=0;
  663. host->hw->user.ck_out_edge=0;
  664. host->hw->ctrl2.miso_delay_mode=nodelay?0:2;
  665. } else if (handle->cfg.mode==1) {
  666. host->hw->pin.ck_idle_edge=0;
  667. host->hw->user.ck_out_edge=1;
  668. host->hw->ctrl2.miso_delay_mode=nodelay?0:1;
  669. } else if (handle->cfg.mode==2) {
  670. host->hw->pin.ck_idle_edge=1;
  671. host->hw->user.ck_out_edge=1;
  672. host->hw->ctrl2.miso_delay_mode=nodelay?0:1;
  673. } else if (handle->cfg.mode==3) {
  674. host->hw->pin.ck_idle_edge=1;
  675. host->hw->user.ck_out_edge=0;
  676. host->hw->ctrl2.miso_delay_mode=nodelay?0:2;
  677. }
  678. //Configure bit sizes, load addr and command
  679. host->hw->user.usr_dummy=(handle->cfg.dummy_bits+extra_dummy)?1:0;
  680. host->hw->user.usr_addr=(handle->cfg.address_bits)?1:0;
  681. host->hw->user.usr_command=(handle->cfg.command_bits)?1:0;
  682. host->hw->user1.usr_addr_bitlen=handle->cfg.address_bits-1;
  683. host->hw->user1.usr_dummy_cyclelen=handle->cfg.dummy_bits+extra_dummy-1;
  684. host->hw->user2.usr_command_bitlen=handle->cfg.command_bits-1;
  685. //Configure misc stuff
  686. host->hw->user.doutdin=(handle->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX)?0:1;
  687. host->hw->user.sio=(handle->cfg.flags & LB_SPI_DEVICE_3WIRE)?1:0;
  688. host->hw->ctrl2.setup_time=handle->cfg.cs_ena_pretrans-1;
  689. host->hw->user.cs_setup=handle->cfg.cs_ena_pretrans?1:0;
  690. host->hw->ctrl2.hold_time=handle->cfg.cs_ena_posttrans-1;
  691. host->hw->user.cs_hold=(handle->cfg.cs_ena_posttrans)?1:0;
  692. //Configure CS pin
  693. host->hw->pin.cs0_dis=(i==0)?0:1;
  694. host->hw->pin.cs1_dis=(i==1)?0:1;
  695. host->hw->pin.cs2_dis=(i==2)?0:1;
  696. host->cur_device = i;
  697. }
  698. if ((handle->cfg.spics_io_num < 0) && (handle->cfg.spics_ext_io_num > 0)) {
  699. gpio_set_level(handle->cfg.spics_ext_io_num, 0);
  700. }
  701. handle->cfg.selected = 1;
  702. return ESP_OK;
  703. }
  704. //---------------------------------------------------------------------------
  705. esp_err_t IRAM_ATTR spi_lobo_device_deselect(spi_lobo_device_handle_t handle)
  706. {
  707. if (handle == NULL) return ESP_ERR_INVALID_ARG;
  708. if (handle->cfg.selected == 0) return ESP_OK; // already deselected
  709. int i;
  710. spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host;
  711. for (i=0; i<NO_DEV; i++) {
  712. if (host->device[i] == handle) break;
  713. }
  714. if (i == NO_DEV) return ESP_ERR_INVALID_ARG;
  715. if (host->device[host->cur_device] == handle) {
  716. if ((handle->cfg.spics_io_num < 0) && (handle->cfg.spics_ext_io_num > 0)) {
  717. gpio_set_level(handle->cfg.spics_ext_io_num, 1);
  718. }
  719. }
  720. handle->cfg.selected = 0;
  721. xSemaphoreGive(host->spi_lobo_bus_mutex);
  722. return ESP_OK;
  723. }
  724. //--------------------------------------------------------------------------------
  725. esp_err_t IRAM_ATTR spi_lobo_device_TakeSemaphore(spi_lobo_device_handle_t handle)
  726. {
  727. if (!(xSemaphoreTake(handle->host->spi_lobo_bus_mutex, SPI_SEMAPHORE_WAIT))) return ESP_ERR_INVALID_STATE;
  728. else return ESP_OK;
  729. }
  730. //---------------------------------------------------------------------------
  731. void IRAM_ATTR spi_lobo_device_GiveSemaphore(spi_lobo_device_handle_t handle)
  732. {
  733. xSemaphoreTake(handle->host->spi_lobo_bus_mutex, portMAX_DELAY);
  734. }
  735. //----------------------------------------------------------
  736. uint32_t spi_lobo_get_speed(spi_lobo_device_handle_t handle)
  737. {
  738. spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host;
  739. uint32_t speed = 0;
  740. if (spi_lobo_device_select(handle, 0) == ESP_OK) {
  741. if (host->hw->clock.clk_equ_sysclk == 1) speed = 80000000;
  742. else speed = 80000000/(host->hw->clock.clkdiv_pre+1)/(host->hw->clock.clkcnt_n+1);
  743. }
  744. spi_lobo_device_deselect(handle);
  745. return speed;
  746. }
  747. //--------------------------------------------------------------------------
  748. uint32_t spi_lobo_set_speed(spi_lobo_device_handle_t handle, uint32_t speed)
  749. {
  750. spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host;
  751. uint32_t newspeed = 0;
  752. if (spi_lobo_device_select(handle, 0) == ESP_OK) {
  753. spi_lobo_device_deselect(handle);
  754. handle->cfg.clock_speed_hz = speed;
  755. if (spi_lobo_device_select(handle, 1) == ESP_OK) {
  756. if (host->hw->clock.clk_equ_sysclk == 1) newspeed = 80000000;
  757. else newspeed = 80000000/(host->hw->clock.clkdiv_pre+1)/(host->hw->clock.clkcnt_n+1);
  758. }
  759. }
  760. spi_lobo_device_deselect(handle);
  761. return newspeed;
  762. }
  763. //-------------------------------------------------------------
  764. bool spi_lobo_uses_native_pins(spi_lobo_device_handle_t handle)
  765. {
  766. return handle->host->no_gpio_matrix;
  767. }
  768. //-------------------------------------------------------------------
  769. void spi_lobo_get_native_pins(int host, int *sdi, int *sdo, int *sck)
  770. {
  771. *sdo = io_signal[host].spid_native;
  772. *sdi = io_signal[host].spiq_native;
  773. *sck = io_signal[host].spiclk_native;
  774. }
  775. /*
  776. When using 'spi_lobo_transfer_data' function we can have several scenarios:
  777. A: Send only (trans->rxlength = 0)
  778. B: Receive only (trans->txlength = 0)
  779. C: Send & receive (trans->txlength > 0 & trans->rxlength > 0)
  780. D: No operation (trans->txlength = 0 & trans->rxlength = 0)
  781. */
  782. //----------------------------------------------------------------------------------------------------------
  783. esp_err_t IRAM_ATTR spi_lobo_transfer_data(spi_lobo_device_handle_t handle, spi_lobo_transaction_t *trans) {
  784. if (!handle) return ESP_ERR_INVALID_ARG;
  785. // *** For now we can only handle 8-bit bytes transmission
  786. if (((trans->length % 8) != 0) || ((trans->rxlength % 8) != 0)) return ESP_ERR_INVALID_ARG;
  787. spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host;
  788. esp_err_t ret;
  789. uint8_t do_deselect = 0;
  790. const uint8_t *txbuffer = NULL;
  791. uint8_t *rxbuffer = NULL;
  792. if (trans->flags & LB_SPI_TRANS_USE_TXDATA) {
  793. // Send data from 'trans->tx_data'
  794. txbuffer=(uint8_t*)&trans->tx_data[0];
  795. } else {
  796. // Send data from 'trans->tx_buffer'
  797. txbuffer=(uint8_t*)trans->tx_buffer;
  798. }
  799. if (trans->flags & LB_SPI_TRANS_USE_RXDATA) {
  800. // Receive data to 'trans->rx_data'
  801. rxbuffer=(uint8_t*)&trans->rx_data[0];
  802. } else {
  803. // Receive data to 'trans->rx_buffer'
  804. rxbuffer=(uint8_t*)trans->rx_buffer;
  805. }
  806. // ** Set transmit & receive length in bytes
  807. uint32_t txlen = trans->length / 8;
  808. uint32_t rxlen = trans->rxlength / 8;
  809. if (txbuffer == NULL) txlen = 0;
  810. if (rxbuffer == NULL) rxlen = 0;
  811. if ((rxlen == 0) && (txlen == 0)) {
  812. // ** NOTHING TO SEND or RECEIVE, return
  813. return ESP_ERR_INVALID_ARG;
  814. }
  815. // If using 'trans->tx_data' and/or 'trans->rx_data', maximum 4 bytes can be sent/received
  816. if ((txbuffer == &trans->tx_data[0]) && (txlen > 4)) return ESP_ERR_INVALID_ARG;
  817. if ((rxbuffer == &trans->rx_data[0]) && (rxlen > 4)) return ESP_ERR_INVALID_ARG;
  818. // --- Wait for SPI bus ready ---
  819. while (host->hw->cmd.usr);
  820. // ** If the device was not selected, select it
  821. if (handle->cfg.selected == 0) {
  822. ret = spi_lobo_device_select(handle, 0);
  823. if (ret) return ret;
  824. do_deselect = 1; // We will deselect the device after the operation !
  825. }
  826. // ** Call pre-transmission callback, if any
  827. if (handle->cfg.pre_cb) handle->cfg.pre_cb(trans);
  828. // Test if operating in full duplex mode
  829. uint8_t duplex = 1;
  830. if (handle->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX) duplex = 0; // Half duplex mode !
  831. uint32_t bits, rdbits;
  832. uint32_t wd;
  833. uint8_t bc, rdidx;
  834. uint32_t rdcount = rxlen; // Total number of bytes to read
  835. uint32_t count = 0; // number of bytes transmitted
  836. uint32_t rd_read = 0; // Number of bytes read so far
  837. host->hw->user.usr_mosi_highpart = 0; // use the whole spi buffer
  838. // ** Check if address phase will be used
  839. host->hw->user2.usr_command_value=trans->command;
  840. if (handle->cfg.address_bits>32) {
  841. host->hw->addr=trans->address >> 32;
  842. host->hw->slv_wr_status=trans->address & 0xffffffff;
  843. } else {
  844. host->hw->addr=trans->address & 0xffffffff;
  845. }
  846. // Check if we have to transmit some data
  847. if (txlen > 0) {
  848. host->hw->user.usr_mosi = 1;
  849. uint8_t idx;
  850. bits = 0; // remaining bits to send
  851. idx = 0; // index to spi hw data_buf (16 32-bit words, 64 bytes, 512 bits)
  852. // ** Transmit 'txlen' bytes
  853. while (count < txlen) {
  854. wd = 0;
  855. for (bc=0;bc<32;bc+=8) {
  856. wd |= (uint32_t)txbuffer[count] << bc;
  857. count++; // Increment sent data count
  858. bits += 8; // Increment bits count
  859. if (count == txlen) break; // If all transmit data pushed to hw spi buffer break from the loop
  860. }
  861. host->hw->data_buf[idx] = wd;
  862. idx++;
  863. if (idx == 16) {
  864. // hw SPI buffer full (all 64 bytes filled, START THE TRANSSACTION
  865. host->hw->mosi_dlen.usr_mosi_dbitlen=bits-1; // Set mosi dbitlen
  866. if ((duplex) && (rdcount > 0)) {
  867. // In full duplex mode we are receiving while sending !
  868. host->hw->miso_dlen.usr_miso_dbitlen = bits-1; // Set miso dbitlen
  869. host->hw->user.usr_miso = 1;
  870. }
  871. else {
  872. host->hw->miso_dlen.usr_miso_dbitlen = 0; // In half duplex mode nothing will be received
  873. host->hw->user.usr_miso = 0;
  874. }
  875. // ** Start the transaction ***
  876. host->hw->cmd.usr=1;
  877. // Wait the transaction to finish
  878. while (host->hw->cmd.usr);
  879. if ((duplex) && (rdcount > 0)) {
  880. // *** in full duplex mode transfer received data to input buffer ***
  881. rdidx = 0;
  882. while (bits > 0) {
  883. wd = host->hw->data_buf[rdidx];
  884. rdidx++;
  885. for (bc=0;bc<32;bc+=8) { // get max 4 bytes
  886. rxbuffer[rd_read++] = (uint8_t)((wd >> bc) & 0xFF);
  887. rdcount--;
  888. bits -= 8;
  889. if (rdcount == 0) {
  890. bits = 0;
  891. break; // Finished reading data
  892. }
  893. }
  894. }
  895. }
  896. bits = 0; // nothing in hw spi buffer yet
  897. idx = 0; // start from the beginning of the hw spi buffer
  898. }
  899. }
  900. // *** All transmit data are sent or pushed to hw spi buffer
  901. // bits > 0 IF THERE ARE SOME DATA STILL WAITING IN THE HW SPI TRANSMIT BUFFER
  902. if (bits > 0) {
  903. // ** WE HAVE SOME DATA IN THE HW SPI TRANSMIT BUFFER
  904. host->hw->mosi_dlen.usr_mosi_dbitlen = bits-1; // Set mosi dbitlen
  905. if ((duplex) && (rdcount > 0)) {
  906. // In full duplex mode we are receiving while sending !
  907. host->hw->miso_dlen.usr_miso_dbitlen = bits-1; // Set miso dbitlen
  908. host->hw->user.usr_miso = 1;
  909. }
  910. else {
  911. host->hw->miso_dlen.usr_miso_dbitlen = 0; // In half duplex mode nothing will be received
  912. host->hw->user.usr_miso = 0;
  913. }
  914. // ** Start the transaction ***
  915. host->hw->cmd.usr=1;
  916. // Wait the transaction to finish
  917. while (host->hw->cmd.usr);
  918. if ((duplex) && (rdcount > 0)) {
  919. // *** in full duplex mode transfer received data to input buffer ***
  920. rdidx = 0;
  921. while (bits > 0) {
  922. wd = host->hw->data_buf[rdidx];
  923. rdidx++;
  924. for (bc=0;bc<32;bc+=8) { // get max 4 bytes
  925. rxbuffer[rd_read++] = (uint8_t)((wd >> bc) & 0xFF);
  926. rdcount--;
  927. bits -= 8;
  928. if (bits == 0) break;
  929. if (rdcount == 0) {
  930. bits = 0;
  931. break; // Finished reading data
  932. }
  933. }
  934. }
  935. }
  936. }
  937. //if (duplex) rdcount = 0; // In duplex mode receive only as many bytes as was transmitted
  938. }
  939. // ------------------------------------------------------------------------
  940. // *** If rdcount = 0 we have nothing to receive and we exit the function
  941. // This is true if no data receive was requested,
  942. // or all the data was received in Full duplex mode during the transmission
  943. // ------------------------------------------------------------------------
  944. if (rdcount > 0) {
  945. // ----------------------------------------------------------------------------------------------------------------
  946. // *** rdcount > 0, we have to receive some data
  947. // This is true if we operate in Half duplex mode when receiving after transmission is done,
  948. // or not all data was received in Full duplex mode during the transmission (trans->rxlength > trans->txlength)
  949. // ----------------------------------------------------------------------------------------------------------------
  950. host->hw->user.usr_mosi = 0; // do not send
  951. host->hw->user.usr_miso = 1; // do receive
  952. while (rdcount > 0) {
  953. if (rdcount <= 64) rdbits = rdcount * 8;
  954. else rdbits = 64 * 8;
  955. // Load receive buffer
  956. host->hw->mosi_dlen.usr_mosi_dbitlen=0;
  957. host->hw->miso_dlen.usr_miso_dbitlen=rdbits-1;
  958. // ** Start the transaction ***
  959. host->hw->cmd.usr=1;
  960. // Wait the transaction to finish
  961. while (host->hw->cmd.usr);
  962. // *** transfer received data to input buffer ***
  963. rdidx = 0;
  964. while (rdbits > 0) {
  965. wd = host->hw->data_buf[rdidx];
  966. rdidx++;
  967. for (bc=0;bc<32;bc+=8) {
  968. rxbuffer[rd_read++] = (uint8_t)((wd >> bc) & 0xFF);
  969. rdcount--;
  970. rdbits -= 8;
  971. if (rdcount == 0) {
  972. rdbits = 0;
  973. break;
  974. }
  975. }
  976. }
  977. }
  978. }
  979. // ** Call post-transmission callback, if any
  980. if (handle->cfg.post_cb) handle->cfg.post_cb(trans);
  981. if (do_deselect) {
  982. // Spi device was selected in this function, we have to deselect it now
  983. ret = spi_lobo_device_deselect(handle);
  984. if (ret) return ret;
  985. }
  986. return ESP_OK;
  987. }