Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of mbed-dev by
targets/TARGET_NUVOTON/TARGET_NANO100/spi_api.c@186:707f6e361f3e, 2018-06-22 (annotated)
- Committer:
- Anna Bridge
- Date:
- Fri Jun 22 16:45:37 2018 +0100
- Revision:
- 186:707f6e361f3e
- Parent:
- 176:447f873cad2f
mbed-dev library. Release version 162
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
AnnaBridge | 174:b96e65c34a4d | 1 | /* mbed Microcontroller Library |
AnnaBridge | 174:b96e65c34a4d | 2 | * Copyright (c) 2015-2017 Nuvoton |
AnnaBridge | 174:b96e65c34a4d | 3 | * |
AnnaBridge | 174:b96e65c34a4d | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
AnnaBridge | 174:b96e65c34a4d | 5 | * you may not use this file except in compliance with the License. |
AnnaBridge | 174:b96e65c34a4d | 6 | * You may obtain a copy of the License at |
AnnaBridge | 174:b96e65c34a4d | 7 | * |
AnnaBridge | 174:b96e65c34a4d | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
AnnaBridge | 174:b96e65c34a4d | 9 | * |
AnnaBridge | 174:b96e65c34a4d | 10 | * Unless required by applicable law or agreed to in writing, software |
AnnaBridge | 174:b96e65c34a4d | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
AnnaBridge | 174:b96e65c34a4d | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
AnnaBridge | 174:b96e65c34a4d | 13 | * See the License for the specific language governing permissions and |
AnnaBridge | 174:b96e65c34a4d | 14 | * limitations under the License. |
AnnaBridge | 174:b96e65c34a4d | 15 | */ |
AnnaBridge | 174:b96e65c34a4d | 16 | |
AnnaBridge | 174:b96e65c34a4d | 17 | #include "spi_api.h" |
AnnaBridge | 174:b96e65c34a4d | 18 | |
AnnaBridge | 174:b96e65c34a4d | 19 | #if DEVICE_SPI |
AnnaBridge | 174:b96e65c34a4d | 20 | |
AnnaBridge | 174:b96e65c34a4d | 21 | #include "cmsis.h" |
AnnaBridge | 174:b96e65c34a4d | 22 | #include "pinmap.h" |
AnnaBridge | 174:b96e65c34a4d | 23 | #include "PeripheralPins.h" |
AnnaBridge | 174:b96e65c34a4d | 24 | #include "nu_modutil.h" |
AnnaBridge | 174:b96e65c34a4d | 25 | #include "nu_miscutil.h" |
AnnaBridge | 174:b96e65c34a4d | 26 | #include "nu_bitutil.h" |
AnnaBridge | 174:b96e65c34a4d | 27 | |
AnnaBridge | 174:b96e65c34a4d | 28 | #if DEVICE_SPI_ASYNCH |
AnnaBridge | 174:b96e65c34a4d | 29 | #include "dma_api.h" |
AnnaBridge | 174:b96e65c34a4d | 30 | #include "dma.h" |
AnnaBridge | 174:b96e65c34a4d | 31 | #endif |
AnnaBridge | 174:b96e65c34a4d | 32 | |
AnnaBridge | 174:b96e65c34a4d | 33 | #define NU_SPI_FRAME_MIN 8 |
AnnaBridge | 174:b96e65c34a4d | 34 | #define NU_SPI_FRAME_MAX 32 |
AnnaBridge | 174:b96e65c34a4d | 35 | #define NU_SPI_FIFO_DEPTH 8 |
AnnaBridge | 174:b96e65c34a4d | 36 | |
AnnaBridge | 174:b96e65c34a4d | 37 | struct nu_spi_var { |
AnnaBridge | 174:b96e65c34a4d | 38 | spi_t * obj; |
AnnaBridge | 174:b96e65c34a4d | 39 | void (*vec)(void); |
AnnaBridge | 174:b96e65c34a4d | 40 | #if DEVICE_SPI_ASYNCH |
AnnaBridge | 174:b96e65c34a4d | 41 | uint8_t pdma_perp_tx; |
AnnaBridge | 174:b96e65c34a4d | 42 | uint8_t pdma_perp_rx; |
AnnaBridge | 174:b96e65c34a4d | 43 | #endif |
AnnaBridge | 174:b96e65c34a4d | 44 | }; |
AnnaBridge | 174:b96e65c34a4d | 45 | |
Anna Bridge |
186:707f6e361f3e | 46 | /* NOTE: NANO130 doesn't support relocating vector table. ISR vector passed into NVIC_SetVector() can |
Anna Bridge |
186:707f6e361f3e | 47 | * only be weak symbol defined in startup_Nano100Series.c. */ |
AnnaBridge | 174:b96e65c34a4d | 48 | void SPI0_IRQHandler(void); |
AnnaBridge | 174:b96e65c34a4d | 49 | void SPI1_IRQHandler(void); |
AnnaBridge | 174:b96e65c34a4d | 50 | void SPI2_IRQHandler(void); |
AnnaBridge | 174:b96e65c34a4d | 51 | static void spi_irq(spi_t *obj); |
AnnaBridge | 174:b96e65c34a4d | 52 | |
AnnaBridge | 174:b96e65c34a4d | 53 | static struct nu_spi_var spi0_var = { |
AnnaBridge | 174:b96e65c34a4d | 54 | .obj = NULL, |
AnnaBridge | 174:b96e65c34a4d | 55 | .vec = SPI0_IRQHandler, |
AnnaBridge | 174:b96e65c34a4d | 56 | #if DEVICE_SPI_ASYNCH |
AnnaBridge | 174:b96e65c34a4d | 57 | .pdma_perp_tx = PDMA_SPI0_TX, |
AnnaBridge | 174:b96e65c34a4d | 58 | .pdma_perp_rx = PDMA_SPI0_RX |
AnnaBridge | 174:b96e65c34a4d | 59 | #endif |
AnnaBridge | 174:b96e65c34a4d | 60 | }; |
AnnaBridge | 174:b96e65c34a4d | 61 | static struct nu_spi_var spi1_var = { |
AnnaBridge | 174:b96e65c34a4d | 62 | .obj = NULL, |
AnnaBridge | 174:b96e65c34a4d | 63 | .vec = SPI1_IRQHandler, |
AnnaBridge | 174:b96e65c34a4d | 64 | #if DEVICE_SPI_ASYNCH |
AnnaBridge | 174:b96e65c34a4d | 65 | .pdma_perp_tx = PDMA_SPI1_TX, |
AnnaBridge | 174:b96e65c34a4d | 66 | .pdma_perp_rx = PDMA_SPI1_RX |
AnnaBridge | 174:b96e65c34a4d | 67 | #endif |
AnnaBridge | 174:b96e65c34a4d | 68 | }; |
AnnaBridge | 174:b96e65c34a4d | 69 | static struct nu_spi_var spi2_var = { |
AnnaBridge | 174:b96e65c34a4d | 70 | .obj = NULL, |
AnnaBridge | 174:b96e65c34a4d | 71 | .vec = SPI2_IRQHandler, |
AnnaBridge | 174:b96e65c34a4d | 72 | #if DEVICE_SPI_ASYNCH |
AnnaBridge | 174:b96e65c34a4d | 73 | .pdma_perp_tx = PDMA_SPI2_TX, |
AnnaBridge | 174:b96e65c34a4d | 74 | .pdma_perp_rx = PDMA_SPI2_RX |
AnnaBridge | 174:b96e65c34a4d | 75 | #endif |
AnnaBridge | 174:b96e65c34a4d | 76 | }; |
AnnaBridge | 174:b96e65c34a4d | 77 | |
Anna Bridge |
186:707f6e361f3e | 78 | /* Synchronous version of SPI_ENABLE()/SPI_DISABLE() macros |
Anna Bridge |
186:707f6e361f3e | 79 | * |
Anna Bridge |
186:707f6e361f3e | 80 | * The SPI peripheral clock is asynchronous with the system clock. In order to make sure the SPI |
Anna Bridge |
186:707f6e361f3e | 81 | * control logic is enabled/disabled, this bit indicates the real status of SPI controller. |
Anna Bridge |
186:707f6e361f3e | 82 | * |
Anna Bridge |
186:707f6e361f3e | 83 | * NOTE: All configurations shall be ready before calling SPI_ENABLE_SYNC(). |
Anna Bridge |
186:707f6e361f3e | 84 | * NOTE: Before changing the configurations of SPIx_CTL, SPIx_CLKDIV, SPIx_SSCTL and SPIx_FIFOCTL registers, |
Anna Bridge |
186:707f6e361f3e | 85 | * user shall clear the SPIEN (SPIx_CTL[0]) and confirm the SPIENSTS (SPIx_STATUS[15]) is 0 |
Anna Bridge |
186:707f6e361f3e | 86 | * (by SPI_DISABLE_SYNC here). |
Anna Bridge |
186:707f6e361f3e | 87 | * |
Anna Bridge |
186:707f6e361f3e | 88 | * NOTE: |
Anna Bridge |
186:707f6e361f3e | 89 | * 1. NUC472/M453/M487: SPI_CTL.SPIEN is controlled by software (in FIFO mode). |
Anna Bridge |
186:707f6e361f3e | 90 | * 2. NANO130: SPI_CTL.GO_BUSY is controlled by hardware in FIFO mode. |
Anna Bridge |
186:707f6e361f3e | 91 | */ |
Anna Bridge |
186:707f6e361f3e | 92 | __STATIC_INLINE void SPI_ENABLE_SYNC(SPI_T *spi_base) |
Anna Bridge |
186:707f6e361f3e | 93 | { |
Anna Bridge |
186:707f6e361f3e | 94 | /* NOTE: On NANO130, FIFO mode defaults to disabled. */ |
Anna Bridge |
186:707f6e361f3e | 95 | if (! (spi_base->CTL & SPI_CTL_FIFOM_Msk)) { |
Anna Bridge |
186:707f6e361f3e | 96 | SPI_EnableFIFO(spi_base, NU_SPI_FIFO_DEPTH / 2, NU_SPI_FIFO_DEPTH / 2); |
Anna Bridge |
186:707f6e361f3e | 97 | } |
Anna Bridge |
186:707f6e361f3e | 98 | } |
Anna Bridge |
186:707f6e361f3e | 99 | __STATIC_INLINE void SPI_DISABLE_SYNC(SPI_T *spi_base) |
Anna Bridge |
186:707f6e361f3e | 100 | { |
Anna Bridge |
186:707f6e361f3e | 101 | /* NOTE: On NANO130, SPI_CTL.GO_BUSY always reads as 1 in slave/FIFO mode. So disable FIFO first. */ |
Anna Bridge |
186:707f6e361f3e | 102 | if (spi_base->CTL & SPI_CTL_FIFOM_Msk) { |
Anna Bridge |
186:707f6e361f3e | 103 | SPI_DisableFIFO(spi_base); |
Anna Bridge |
186:707f6e361f3e | 104 | } |
Anna Bridge |
186:707f6e361f3e | 105 | |
Anna Bridge |
186:707f6e361f3e | 106 | /* NOTE: SPI H/W may get out of state without the busy check */ |
Anna Bridge |
186:707f6e361f3e | 107 | while (SPI_IS_BUSY(spi_base)); |
Anna Bridge |
186:707f6e361f3e | 108 | } |
Anna Bridge |
186:707f6e361f3e | 109 | |
AnnaBridge | 174:b96e65c34a4d | 110 | #if DEVICE_SPI_ASYNCH |
AnnaBridge | 174:b96e65c34a4d | 111 | static void spi_enable_vector_interrupt(spi_t *obj, uint32_t handler, uint8_t enable); |
AnnaBridge | 174:b96e65c34a4d | 112 | static void spi_master_enable_interrupt(spi_t *obj, uint8_t enable, uint32_t mask); |
AnnaBridge | 174:b96e65c34a4d | 113 | static uint32_t spi_master_write_asynch(spi_t *obj, uint32_t tx_limit); |
AnnaBridge | 174:b96e65c34a4d | 114 | static uint32_t spi_master_read_asynch(spi_t *obj); |
AnnaBridge | 174:b96e65c34a4d | 115 | static uint32_t spi_event_check(spi_t *obj); |
AnnaBridge | 174:b96e65c34a4d | 116 | static void spi_enable_event(spi_t *obj, uint32_t event, uint8_t enable); |
AnnaBridge | 174:b96e65c34a4d | 117 | static void spi_buffer_set(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length); |
AnnaBridge | 174:b96e65c34a4d | 118 | static void spi_check_dma_usage(DMAUsage *dma_usage, int *dma_ch_tx, int *dma_ch_rx); |
AnnaBridge | 174:b96e65c34a4d | 119 | static uint8_t spi_get_data_width(spi_t *obj); |
AnnaBridge | 174:b96e65c34a4d | 120 | static int spi_is_tx_complete(spi_t *obj); |
AnnaBridge | 174:b96e65c34a4d | 121 | static int spi_is_rx_complete(spi_t *obj); |
AnnaBridge | 174:b96e65c34a4d | 122 | static int spi_writeable(spi_t * obj); |
AnnaBridge | 174:b96e65c34a4d | 123 | static int spi_readable(spi_t * obj); |
AnnaBridge | 174:b96e65c34a4d | 124 | static void spi_dma_handler_tx(uint32_t id, uint32_t event_dma); |
AnnaBridge | 174:b96e65c34a4d | 125 | static void spi_dma_handler_rx(uint32_t id, uint32_t event_dma); |
AnnaBridge | 174:b96e65c34a4d | 126 | #endif |
AnnaBridge | 174:b96e65c34a4d | 127 | |
AnnaBridge | 174:b96e65c34a4d | 128 | static uint32_t spi_modinit_mask = 0; |
AnnaBridge | 174:b96e65c34a4d | 129 | |
AnnaBridge | 174:b96e65c34a4d | 130 | static const struct nu_modinit_s spi_modinit_tab[] = { |
AnnaBridge | 174:b96e65c34a4d | 131 | {SPI_0, SPI0_MODULE, CLK_CLKSEL2_SPI0_S_HCLK, MODULE_NoMsk, SPI0_RST, SPI0_IRQn, &spi0_var}, |
AnnaBridge | 174:b96e65c34a4d | 132 | {SPI_1, SPI1_MODULE, CLK_CLKSEL2_SPI1_S_HCLK, MODULE_NoMsk, SPI1_RST, SPI1_IRQn, &spi1_var}, |
AnnaBridge | 174:b96e65c34a4d | 133 | {SPI_2, SPI2_MODULE, CLK_CLKSEL2_SPI2_S_HCLK, MODULE_NoMsk, SPI2_RST, SPI2_IRQn, &spi2_var}, |
AnnaBridge | 174:b96e65c34a4d | 134 | |
AnnaBridge | 174:b96e65c34a4d | 135 | {NC, 0, 0, 0, 0, (IRQn_Type) 0, NULL} |
AnnaBridge | 174:b96e65c34a4d | 136 | }; |
AnnaBridge | 174:b96e65c34a4d | 137 | |
AnnaBridge | 174:b96e65c34a4d | 138 | void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel) { |
AnnaBridge | 174:b96e65c34a4d | 139 | // Determine which SPI_x the pins are used for |
AnnaBridge | 174:b96e65c34a4d | 140 | uint32_t spi_mosi = pinmap_peripheral(mosi, PinMap_SPI_MOSI); |
AnnaBridge | 174:b96e65c34a4d | 141 | uint32_t spi_miso = pinmap_peripheral(miso, PinMap_SPI_MISO); |
AnnaBridge | 174:b96e65c34a4d | 142 | uint32_t spi_sclk = pinmap_peripheral(sclk, PinMap_SPI_SCLK); |
AnnaBridge | 174:b96e65c34a4d | 143 | uint32_t spi_ssel = pinmap_peripheral(ssel, PinMap_SPI_SSEL); |
AnnaBridge | 174:b96e65c34a4d | 144 | uint32_t spi_data = pinmap_merge(spi_mosi, spi_miso); |
AnnaBridge | 174:b96e65c34a4d | 145 | uint32_t spi_cntl = pinmap_merge(spi_sclk, spi_ssel); |
AnnaBridge | 174:b96e65c34a4d | 146 | // NOTE: |
AnnaBridge | 174:b96e65c34a4d | 147 | // NANO130: Support two-port SPI MOSI/MISO 0/1 |
AnnaBridge | 174:b96e65c34a4d | 148 | if (NU_MODBASE(spi_data) == NU_MODBASE(spi_cntl)) { |
AnnaBridge | 174:b96e65c34a4d | 149 | // NOTE: spi_data has subindex(port) encoded but spi_cntl hasn't. |
AnnaBridge | 174:b96e65c34a4d | 150 | obj->spi.spi = (SPIName) spi_data; |
AnnaBridge | 174:b96e65c34a4d | 151 | } |
AnnaBridge | 174:b96e65c34a4d | 152 | else { |
AnnaBridge | 174:b96e65c34a4d | 153 | obj->spi.spi = (SPIName) NC; |
AnnaBridge | 174:b96e65c34a4d | 154 | } |
AnnaBridge | 174:b96e65c34a4d | 155 | MBED_ASSERT((int)obj->spi.spi != NC); |
AnnaBridge | 174:b96e65c34a4d | 156 | |
AnnaBridge | 174:b96e65c34a4d | 157 | const struct nu_modinit_s *modinit = get_modinit(obj->spi.spi, spi_modinit_tab); |
AnnaBridge | 174:b96e65c34a4d | 158 | MBED_ASSERT(modinit != NULL); |
Anna Bridge |
186:707f6e361f3e | 159 | MBED_ASSERT(modinit->modname == (int) obj->spi.spi); |
Anna Bridge |
186:707f6e361f3e | 160 | |
AnnaBridge | 174:b96e65c34a4d | 161 | // Reset this module |
AnnaBridge | 174:b96e65c34a4d | 162 | SYS_ResetModule(modinit->rsetidx); |
Anna Bridge |
186:707f6e361f3e | 163 | |
AnnaBridge | 174:b96e65c34a4d | 164 | // Select IP clock source |
AnnaBridge | 174:b96e65c34a4d | 165 | CLK_SetModuleClock(modinit->clkidx, modinit->clksrc, modinit->clkdiv); |
AnnaBridge | 174:b96e65c34a4d | 166 | // Enable IP clock |
AnnaBridge | 174:b96e65c34a4d | 167 | CLK_EnableModuleClock(modinit->clkidx); |
Anna Bridge |
186:707f6e361f3e | 168 | |
AnnaBridge | 174:b96e65c34a4d | 169 | pinmap_pinout(mosi, PinMap_SPI_MOSI); |
AnnaBridge | 174:b96e65c34a4d | 170 | pinmap_pinout(miso, PinMap_SPI_MISO); |
AnnaBridge | 174:b96e65c34a4d | 171 | pinmap_pinout(sclk, PinMap_SPI_SCLK); |
AnnaBridge | 174:b96e65c34a4d | 172 | pinmap_pinout(ssel, PinMap_SPI_SSEL); |
Anna Bridge |
186:707f6e361f3e | 173 | |
AnnaBridge | 174:b96e65c34a4d | 174 | obj->spi.pin_mosi = mosi; |
AnnaBridge | 174:b96e65c34a4d | 175 | obj->spi.pin_miso = miso; |
AnnaBridge | 174:b96e65c34a4d | 176 | obj->spi.pin_sclk = sclk; |
AnnaBridge | 174:b96e65c34a4d | 177 | obj->spi.pin_ssel = ssel; |
AnnaBridge | 174:b96e65c34a4d | 178 | |
Anna Bridge |
186:707f6e361f3e | 179 | |
AnnaBridge | 174:b96e65c34a4d | 180 | #if DEVICE_SPI_ASYNCH |
AnnaBridge | 174:b96e65c34a4d | 181 | obj->spi.dma_usage = DMA_USAGE_NEVER; |
AnnaBridge | 174:b96e65c34a4d | 182 | obj->spi.event = 0; |
AnnaBridge | 174:b96e65c34a4d | 183 | obj->spi.dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS; |
AnnaBridge | 174:b96e65c34a4d | 184 | obj->spi.dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS; |
Anna Bridge |
186:707f6e361f3e | 185 | |
Anna Bridge |
186:707f6e361f3e | 186 | /* NOTE: We use vector to judge if asynchronous transfer is on-going (spi_active). |
Anna Bridge |
186:707f6e361f3e | 187 | * At initial time, asynchronous transfer is not on-going and so vector must |
Anna Bridge |
186:707f6e361f3e | 188 | * be cleared to zero for correct judgement. */ |
Anna Bridge |
186:707f6e361f3e | 189 | /* NOTE: On NANO130, vector table is fixed in ROM and cannot be modified. */ |
Anna Bridge |
186:707f6e361f3e | 190 | obj->spi.hdlr_async = 0; |
AnnaBridge | 174:b96e65c34a4d | 191 | #endif |
AnnaBridge | 174:b96e65c34a4d | 192 | |
AnnaBridge | 174:b96e65c34a4d | 193 | // Mark this module to be inited. |
AnnaBridge | 174:b96e65c34a4d | 194 | int i = modinit - spi_modinit_tab; |
AnnaBridge | 174:b96e65c34a4d | 195 | spi_modinit_mask |= 1 << i; |
AnnaBridge | 174:b96e65c34a4d | 196 | } |
AnnaBridge | 174:b96e65c34a4d | 197 | |
AnnaBridge | 174:b96e65c34a4d | 198 | void spi_free(spi_t *obj) |
AnnaBridge | 174:b96e65c34a4d | 199 | { |
AnnaBridge | 174:b96e65c34a4d | 200 | #if DEVICE_SPI_ASYNCH |
AnnaBridge | 174:b96e65c34a4d | 201 | if (obj->spi.dma_chn_id_tx != DMA_ERROR_OUT_OF_CHANNELS) { |
AnnaBridge | 174:b96e65c34a4d | 202 | dma_channel_free(obj->spi.dma_chn_id_tx); |
AnnaBridge | 174:b96e65c34a4d | 203 | obj->spi.dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS; |
AnnaBridge | 174:b96e65c34a4d | 204 | } |
AnnaBridge | 174:b96e65c34a4d | 205 | if (obj->spi.dma_chn_id_rx != DMA_ERROR_OUT_OF_CHANNELS) { |
AnnaBridge | 174:b96e65c34a4d | 206 | dma_channel_free(obj->spi.dma_chn_id_rx); |
AnnaBridge | 174:b96e65c34a4d | 207 | obj->spi.dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS; |
AnnaBridge | 174:b96e65c34a4d | 208 | } |
AnnaBridge | 174:b96e65c34a4d | 209 | #endif |
AnnaBridge | 174:b96e65c34a4d | 210 | |
AnnaBridge | 174:b96e65c34a4d | 211 | SPI_Close((SPI_T *) NU_MODBASE(obj->spi.spi)); |
Anna Bridge |
186:707f6e361f3e | 212 | |
AnnaBridge | 174:b96e65c34a4d | 213 | const struct nu_modinit_s *modinit = get_modinit(obj->spi.spi, spi_modinit_tab); |
AnnaBridge | 174:b96e65c34a4d | 214 | MBED_ASSERT(modinit != NULL); |
Anna Bridge |
186:707f6e361f3e | 215 | MBED_ASSERT(modinit->modname == (int) obj->spi.spi); |
Anna Bridge |
186:707f6e361f3e | 216 | |
AnnaBridge | 174:b96e65c34a4d | 217 | SPI_DisableInt(((SPI_T *) NU_MODBASE(obj->spi.spi)), (SPI_FIFO_RXOVR_INTEN_MASK | SPI_FIFO_RX_INTEN_MASK | SPI_FIFO_TX_INTEN_MASK)); |
AnnaBridge | 174:b96e65c34a4d | 218 | NVIC_DisableIRQ(modinit->irq_n); |
Anna Bridge |
186:707f6e361f3e | 219 | |
AnnaBridge | 174:b96e65c34a4d | 220 | // Disable IP clock |
AnnaBridge | 174:b96e65c34a4d | 221 | CLK_DisableModuleClock(modinit->clkidx); |
Anna Bridge |
186:707f6e361f3e | 222 | |
AnnaBridge | 174:b96e65c34a4d | 223 | // Mark this module to be deinited. |
AnnaBridge | 174:b96e65c34a4d | 224 | int i = modinit - spi_modinit_tab; |
AnnaBridge | 174:b96e65c34a4d | 225 | spi_modinit_mask &= ~(1 << i); |
AnnaBridge | 174:b96e65c34a4d | 226 | } |
Anna Bridge |
186:707f6e361f3e | 227 | |
AnnaBridge | 174:b96e65c34a4d | 228 | void spi_format(spi_t *obj, int bits, int mode, int slave) |
AnnaBridge | 174:b96e65c34a4d | 229 | { |
AnnaBridge | 174:b96e65c34a4d | 230 | MBED_ASSERT(bits >= NU_SPI_FRAME_MIN && bits <= NU_SPI_FRAME_MAX); |
Anna Bridge |
186:707f6e361f3e | 231 | |
AnnaBridge | 174:b96e65c34a4d | 232 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
AnnaBridge | 174:b96e65c34a4d | 233 | |
Anna Bridge |
186:707f6e361f3e | 234 | SPI_DISABLE_SYNC(spi_base); |
AnnaBridge | 174:b96e65c34a4d | 235 | |
AnnaBridge | 174:b96e65c34a4d | 236 | SPI_Open(spi_base, |
AnnaBridge | 174:b96e65c34a4d | 237 | slave ? SPI_SLAVE : SPI_MASTER, |
AnnaBridge | 174:b96e65c34a4d | 238 | (mode == 0) ? SPI_MODE_0 : (mode == 1) ? SPI_MODE_1 : (mode == 2) ? SPI_MODE_2 : SPI_MODE_3, |
AnnaBridge | 174:b96e65c34a4d | 239 | bits, |
AnnaBridge | 174:b96e65c34a4d | 240 | SPI_GetBusClock(spi_base)); |
AnnaBridge | 174:b96e65c34a4d | 241 | // NOTE: Hardcode to be MSB first. |
AnnaBridge | 174:b96e65c34a4d | 242 | SPI_SET_MSB_FIRST(spi_base); |
AnnaBridge | 174:b96e65c34a4d | 243 | |
AnnaBridge | 174:b96e65c34a4d | 244 | if (! slave) { |
AnnaBridge | 174:b96e65c34a4d | 245 | // Master |
AnnaBridge | 174:b96e65c34a4d | 246 | if (obj->spi.pin_ssel != NC) { |
AnnaBridge | 174:b96e65c34a4d | 247 | // Configure SS as low active. |
AnnaBridge | 174:b96e65c34a4d | 248 | switch (NU_MODSUBINDEX(obj->spi.spi)) { |
AnnaBridge | 174:b96e65c34a4d | 249 | case 0: |
AnnaBridge | 174:b96e65c34a4d | 250 | SPI_EnableAutoSS(spi_base, SPI_SS0, SPI_SS0_ACTIVE_LOW); |
AnnaBridge | 174:b96e65c34a4d | 251 | break; |
AnnaBridge | 174:b96e65c34a4d | 252 | |
AnnaBridge | 174:b96e65c34a4d | 253 | case 1: |
AnnaBridge | 174:b96e65c34a4d | 254 | SPI_EnableAutoSS(spi_base, SPI_SS1, SPI_SS1_ACTIVE_LOW); |
AnnaBridge | 174:b96e65c34a4d | 255 | break; |
AnnaBridge | 174:b96e65c34a4d | 256 | } |
AnnaBridge | 174:b96e65c34a4d | 257 | } |
AnnaBridge | 174:b96e65c34a4d | 258 | else { |
AnnaBridge | 174:b96e65c34a4d | 259 | SPI_DisableAutoSS(spi_base); |
AnnaBridge | 174:b96e65c34a4d | 260 | } |
AnnaBridge | 174:b96e65c34a4d | 261 | } |
AnnaBridge | 174:b96e65c34a4d | 262 | else { |
AnnaBridge | 174:b96e65c34a4d | 263 | // Slave |
AnnaBridge | 174:b96e65c34a4d | 264 | // Configure SS as low active. |
AnnaBridge | 174:b96e65c34a4d | 265 | switch (NU_MODSUBINDEX(obj->spi.spi)) { |
AnnaBridge | 174:b96e65c34a4d | 266 | case 0: |
AnnaBridge | 174:b96e65c34a4d | 267 | spi_base->SSR &= ~SPI_SS0_ACTIVE_HIGH; |
AnnaBridge | 174:b96e65c34a4d | 268 | break; |
AnnaBridge | 174:b96e65c34a4d | 269 | case 1: |
AnnaBridge | 174:b96e65c34a4d | 270 | spi_base->SSR &= ~SPI_SS1_ACTIVE_HIGH; |
AnnaBridge | 174:b96e65c34a4d | 271 | break; |
AnnaBridge | 174:b96e65c34a4d | 272 | } |
AnnaBridge | 174:b96e65c34a4d | 273 | // NOTE: |
AnnaBridge | 174:b96e65c34a4d | 274 | // NANO130: Configure slave select signal to edge-trigger rather than level-trigger |
AnnaBridge | 174:b96e65c34a4d | 275 | spi_base->SSR |= SPI_SSR_SS_LTRIG_Msk; |
AnnaBridge | 174:b96e65c34a4d | 276 | } |
AnnaBridge | 174:b96e65c34a4d | 277 | |
Anna Bridge |
186:707f6e361f3e | 278 | /* NOTE: M451's/M480's/M2351's SPI_Open() will enable SPI transfer (SPI_CTL_SPIEN_Msk). |
Anna Bridge |
186:707f6e361f3e | 279 | * We cannot use SPI_CTL_SPIEN_Msk for judgement of spi_active(). |
Anna Bridge |
186:707f6e361f3e | 280 | * Judge with vector instead. */ |
AnnaBridge | 174:b96e65c34a4d | 281 | } |
AnnaBridge | 174:b96e65c34a4d | 282 | |
AnnaBridge | 174:b96e65c34a4d | 283 | void spi_frequency(spi_t *obj, int hz) |
AnnaBridge | 174:b96e65c34a4d | 284 | { |
AnnaBridge | 174:b96e65c34a4d | 285 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
AnnaBridge | 174:b96e65c34a4d | 286 | |
Anna Bridge |
186:707f6e361f3e | 287 | SPI_DISABLE_SYNC(spi_base); |
AnnaBridge | 174:b96e65c34a4d | 288 | |
AnnaBridge | 174:b96e65c34a4d | 289 | SPI_SetBusClock((SPI_T *) NU_MODBASE(obj->spi.spi), hz); |
AnnaBridge | 174:b96e65c34a4d | 290 | } |
AnnaBridge | 174:b96e65c34a4d | 291 | |
AnnaBridge | 174:b96e65c34a4d | 292 | |
AnnaBridge | 174:b96e65c34a4d | 293 | int spi_master_write(spi_t *obj, int value) |
AnnaBridge | 174:b96e65c34a4d | 294 | { |
AnnaBridge | 174:b96e65c34a4d | 295 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
Anna Bridge |
186:707f6e361f3e | 296 | |
AnnaBridge | 174:b96e65c34a4d | 297 | // NOTE: Data in receive FIFO can be read out via ICE. |
AnnaBridge | 174:b96e65c34a4d | 298 | // NOTE: |
AnnaBridge | 174:b96e65c34a4d | 299 | // NUC472/M453/M487: SPI_CTL.SPIEN is controlled by software (in FIFO mode). |
AnnaBridge | 174:b96e65c34a4d | 300 | // NANO130: SPI_CTL.GO_BUSY is controlled by hardware in FIFO mode. |
Anna Bridge |
186:707f6e361f3e | 301 | SPI_ENABLE_SYNC(spi_base); |
Anna Bridge |
186:707f6e361f3e | 302 | |
AnnaBridge | 174:b96e65c34a4d | 303 | // Wait for tx buffer empty |
AnnaBridge | 174:b96e65c34a4d | 304 | while(! spi_writeable(obj)); |
AnnaBridge | 174:b96e65c34a4d | 305 | uint32_t TX = (NU_MODSUBINDEX(obj->spi.spi) == 0) ? ((uint32_t) &spi_base->TX0) : ((uint32_t) &spi_base->TX1); |
AnnaBridge | 174:b96e65c34a4d | 306 | M32(TX) = value; |
Anna Bridge |
186:707f6e361f3e | 307 | |
AnnaBridge | 174:b96e65c34a4d | 308 | // Wait for rx buffer full |
AnnaBridge | 174:b96e65c34a4d | 309 | while (! spi_readable(obj)); |
AnnaBridge | 174:b96e65c34a4d | 310 | uint32_t RX = (NU_MODSUBINDEX(obj->spi.spi) == 0) ? ((uint32_t) &spi_base->RX0) : ((uint32_t) &spi_base->RX1); |
AnnaBridge | 174:b96e65c34a4d | 311 | int value2 = M32(RX); |
Anna Bridge |
186:707f6e361f3e | 312 | |
Anna Bridge |
186:707f6e361f3e | 313 | /* We don't call SPI_DISABLE_SYNC here for performance. */ |
Anna Bridge |
186:707f6e361f3e | 314 | |
AnnaBridge | 174:b96e65c34a4d | 315 | return value2; |
AnnaBridge | 174:b96e65c34a4d | 316 | } |
AnnaBridge | 174:b96e65c34a4d | 317 | |
AnnaBridge | 174:b96e65c34a4d | 318 | int spi_master_block_write(spi_t *obj, const char *tx_buffer, int tx_length, |
AnnaBridge | 174:b96e65c34a4d | 319 | char *rx_buffer, int rx_length, char write_fill) |
AnnaBridge | 174:b96e65c34a4d | 320 | { |
AnnaBridge | 174:b96e65c34a4d | 321 | int total = (tx_length > rx_length) ? tx_length : rx_length; |
AnnaBridge | 174:b96e65c34a4d | 322 | |
AnnaBridge | 174:b96e65c34a4d | 323 | for (int i = 0; i < total; i++) { |
AnnaBridge | 174:b96e65c34a4d | 324 | char out = (i < tx_length) ? tx_buffer[i] : write_fill; |
AnnaBridge | 174:b96e65c34a4d | 325 | char in = spi_master_write(obj, out); |
AnnaBridge | 174:b96e65c34a4d | 326 | if (i < rx_length) { |
AnnaBridge | 174:b96e65c34a4d | 327 | rx_buffer[i] = in; |
AnnaBridge | 174:b96e65c34a4d | 328 | } |
AnnaBridge | 174:b96e65c34a4d | 329 | } |
AnnaBridge | 174:b96e65c34a4d | 330 | |
AnnaBridge | 174:b96e65c34a4d | 331 | return total; |
AnnaBridge | 174:b96e65c34a4d | 332 | } |
AnnaBridge | 174:b96e65c34a4d | 333 | |
AnnaBridge | 174:b96e65c34a4d | 334 | #if DEVICE_SPISLAVE |
AnnaBridge | 174:b96e65c34a4d | 335 | int spi_slave_receive(spi_t *obj) |
AnnaBridge | 174:b96e65c34a4d | 336 | { |
Anna Bridge |
186:707f6e361f3e | 337 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
Anna Bridge |
186:707f6e361f3e | 338 | |
Anna Bridge |
186:707f6e361f3e | 339 | SPI_ENABLE_SYNC(spi_base); |
Anna Bridge |
186:707f6e361f3e | 340 | |
AnnaBridge | 174:b96e65c34a4d | 341 | return spi_readable(obj); |
AnnaBridge | 174:b96e65c34a4d | 342 | }; |
AnnaBridge | 174:b96e65c34a4d | 343 | |
AnnaBridge | 174:b96e65c34a4d | 344 | int spi_slave_read(spi_t *obj) |
AnnaBridge | 174:b96e65c34a4d | 345 | { |
AnnaBridge | 174:b96e65c34a4d | 346 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
Anna Bridge |
186:707f6e361f3e | 347 | |
Anna Bridge |
186:707f6e361f3e | 348 | SPI_ENABLE_SYNC(spi_base); |
Anna Bridge |
186:707f6e361f3e | 349 | |
AnnaBridge | 174:b96e65c34a4d | 350 | // Wait for rx buffer full |
AnnaBridge | 174:b96e65c34a4d | 351 | while (! spi_readable(obj)); |
AnnaBridge | 174:b96e65c34a4d | 352 | uint32_t RX = (NU_MODSUBINDEX(obj->spi.spi) == 0) ? ((uint32_t) &spi_base->RX0) : ((uint32_t) &spi_base->RX1); |
AnnaBridge | 174:b96e65c34a4d | 353 | int value = M32(RX); |
AnnaBridge | 174:b96e65c34a4d | 354 | return value; |
AnnaBridge | 174:b96e65c34a4d | 355 | } |
AnnaBridge | 174:b96e65c34a4d | 356 | |
AnnaBridge | 174:b96e65c34a4d | 357 | void spi_slave_write(spi_t *obj, int value) |
AnnaBridge | 174:b96e65c34a4d | 358 | { |
AnnaBridge | 174:b96e65c34a4d | 359 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
Anna Bridge |
186:707f6e361f3e | 360 | |
Anna Bridge |
186:707f6e361f3e | 361 | SPI_ENABLE_SYNC(spi_base); |
Anna Bridge |
186:707f6e361f3e | 362 | |
AnnaBridge | 174:b96e65c34a4d | 363 | // Wait for tx buffer empty |
AnnaBridge | 174:b96e65c34a4d | 364 | while(! spi_writeable(obj)); |
AnnaBridge | 174:b96e65c34a4d | 365 | uint32_t TX = (NU_MODSUBINDEX(obj->spi.spi) == 0) ? ((uint32_t) &spi_base->TX0) : ((uint32_t) &spi_base->TX1); |
AnnaBridge | 174:b96e65c34a4d | 366 | M32(TX) = value; |
AnnaBridge | 174:b96e65c34a4d | 367 | } |
AnnaBridge | 174:b96e65c34a4d | 368 | #endif |
AnnaBridge | 174:b96e65c34a4d | 369 | |
AnnaBridge | 174:b96e65c34a4d | 370 | #if DEVICE_SPI_ASYNCH |
AnnaBridge | 174:b96e65c34a4d | 371 | void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) |
AnnaBridge | 174:b96e65c34a4d | 372 | { |
AnnaBridge | 174:b96e65c34a4d | 373 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
AnnaBridge | 174:b96e65c34a4d | 374 | SPI_SET_DATA_WIDTH(spi_base, bit_width); |
AnnaBridge | 174:b96e65c34a4d | 375 | |
AnnaBridge | 174:b96e65c34a4d | 376 | obj->spi.dma_usage = hint; |
AnnaBridge | 174:b96e65c34a4d | 377 | spi_check_dma_usage(&obj->spi.dma_usage, &obj->spi.dma_chn_id_tx, &obj->spi.dma_chn_id_rx); |
AnnaBridge | 174:b96e65c34a4d | 378 | uint32_t data_width = spi_get_data_width(obj); |
AnnaBridge | 174:b96e65c34a4d | 379 | // Conditions to go DMA way: |
AnnaBridge | 174:b96e65c34a4d | 380 | // (1) No DMA support for non-8 multiple data width. |
AnnaBridge | 174:b96e65c34a4d | 381 | // (2) tx length >= rx length. Otherwise, as tx DMA is done, no bus activity for remaining rx. |
AnnaBridge | 174:b96e65c34a4d | 382 | if ((data_width % 8) || |
AnnaBridge | 174:b96e65c34a4d | 383 | (tx_length < rx_length)) { |
AnnaBridge | 174:b96e65c34a4d | 384 | obj->spi.dma_usage = DMA_USAGE_NEVER; |
AnnaBridge | 174:b96e65c34a4d | 385 | dma_channel_free(obj->spi.dma_chn_id_tx); |
AnnaBridge | 174:b96e65c34a4d | 386 | obj->spi.dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS; |
AnnaBridge | 174:b96e65c34a4d | 387 | dma_channel_free(obj->spi.dma_chn_id_rx); |
AnnaBridge | 174:b96e65c34a4d | 388 | obj->spi.dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS; |
AnnaBridge | 174:b96e65c34a4d | 389 | } |
Anna Bridge |
186:707f6e361f3e | 390 | |
AnnaBridge | 174:b96e65c34a4d | 391 | // SPI IRQ is necessary for both interrupt way and DMA way |
AnnaBridge | 174:b96e65c34a4d | 392 | spi_enable_event(obj, event, 1); |
AnnaBridge | 174:b96e65c34a4d | 393 | spi_buffer_set(obj, tx, tx_length, rx, rx_length); |
Anna Bridge |
186:707f6e361f3e | 394 | |
Anna Bridge |
186:707f6e361f3e | 395 | SPI_ENABLE_SYNC(spi_base); |
Anna Bridge |
186:707f6e361f3e | 396 | |
AnnaBridge | 174:b96e65c34a4d | 397 | if (obj->spi.dma_usage == DMA_USAGE_NEVER) { |
AnnaBridge | 174:b96e65c34a4d | 398 | // Interrupt way |
AnnaBridge | 174:b96e65c34a4d | 399 | spi_master_write_asynch(obj, NU_SPI_FIFO_DEPTH / 2); |
AnnaBridge | 174:b96e65c34a4d | 400 | spi_enable_vector_interrupt(obj, handler, 1); |
AnnaBridge | 174:b96e65c34a4d | 401 | spi_master_enable_interrupt(obj, 1, SPI_FIFO_RX_INTEN_MASK | SPI_FIFO_TX_INTEN_MASK); |
AnnaBridge | 174:b96e65c34a4d | 402 | } else { |
AnnaBridge | 174:b96e65c34a4d | 403 | // DMA way |
AnnaBridge | 174:b96e65c34a4d | 404 | const struct nu_modinit_s *modinit = get_modinit(obj->spi.spi, spi_modinit_tab); |
AnnaBridge | 174:b96e65c34a4d | 405 | MBED_ASSERT(modinit != NULL); |
Anna Bridge |
186:707f6e361f3e | 406 | MBED_ASSERT(modinit->modname == (int) obj->spi.spi); |
AnnaBridge | 174:b96e65c34a4d | 407 | |
AnnaBridge | 174:b96e65c34a4d | 408 | // Configure tx DMA |
AnnaBridge | 174:b96e65c34a4d | 409 | dma_enable(obj->spi.dma_chn_id_tx, 1); // Enable this DMA channel |
AnnaBridge | 174:b96e65c34a4d | 410 | PDMA_SetTransferMode(obj->spi.dma_chn_id_tx, |
AnnaBridge | 174:b96e65c34a4d | 411 | ((struct nu_spi_var *) modinit->var)->pdma_perp_tx, // Peripheral connected to this PDMA |
AnnaBridge | 174:b96e65c34a4d | 412 | 0, // Scatter-gather disabled |
AnnaBridge | 174:b96e65c34a4d | 413 | 0); // Scatter-gather descriptor address |
AnnaBridge | 174:b96e65c34a4d | 414 | PDMA_SetTransferCnt(obj->spi.dma_chn_id_tx, |
AnnaBridge | 174:b96e65c34a4d | 415 | (data_width == 8) ? PDMA_WIDTH_8 : (data_width == 16) ? PDMA_WIDTH_16 : PDMA_WIDTH_32, |
AnnaBridge | 174:b96e65c34a4d | 416 | tx_length); |
AnnaBridge | 174:b96e65c34a4d | 417 | PDMA_SetTransferAddr(obj->spi.dma_chn_id_tx, |
AnnaBridge | 174:b96e65c34a4d | 418 | (uint32_t) tx, // NOTE: |
AnnaBridge | 174:b96e65c34a4d | 419 | // NUC472: End of source address |
AnnaBridge | 174:b96e65c34a4d | 420 | // M451: Start of source address |
AnnaBridge | 174:b96e65c34a4d | 421 | // NANO130: Start of destination address |
AnnaBridge | 174:b96e65c34a4d | 422 | PDMA_SAR_INC, // Source address incremental |
AnnaBridge | 174:b96e65c34a4d | 423 | NU_MODSUBINDEX(obj->spi.spi) == 0 ? (uint32_t) &spi_base->TX0 : (uint32_t) &spi_base->TX1, // Destination address |
AnnaBridge | 174:b96e65c34a4d | 424 | PDMA_DAR_FIX); // Destination address fixed |
AnnaBridge | 174:b96e65c34a4d | 425 | PDMA_EnableInt(obj->spi.dma_chn_id_tx, |
AnnaBridge | 174:b96e65c34a4d | 426 | PDMA_IER_TD_IE_Msk); // Interrupt type |
AnnaBridge | 174:b96e65c34a4d | 427 | // Register DMA event handler |
AnnaBridge | 174:b96e65c34a4d | 428 | dma_set_handler(obj->spi.dma_chn_id_tx, (uint32_t) spi_dma_handler_tx, (uint32_t) obj, DMA_EVENT_ALL); |
AnnaBridge | 174:b96e65c34a4d | 429 | |
AnnaBridge | 174:b96e65c34a4d | 430 | // Configure rx DMA |
AnnaBridge | 174:b96e65c34a4d | 431 | dma_enable(obj->spi.dma_chn_id_rx, 1); // Enable this DMA channel |
AnnaBridge | 174:b96e65c34a4d | 432 | PDMA_SetTransferMode(obj->spi.dma_chn_id_rx, |
AnnaBridge | 174:b96e65c34a4d | 433 | ((struct nu_spi_var *) modinit->var)->pdma_perp_rx, // Peripheral connected to this PDMA |
AnnaBridge | 174:b96e65c34a4d | 434 | 0, // Scatter-gather disabled |
AnnaBridge | 174:b96e65c34a4d | 435 | 0); // Scatter-gather descriptor address |
AnnaBridge | 174:b96e65c34a4d | 436 | PDMA_SetTransferCnt(obj->spi.dma_chn_id_rx, |
AnnaBridge | 174:b96e65c34a4d | 437 | (data_width == 8) ? PDMA_WIDTH_8 : (data_width == 16) ? PDMA_WIDTH_16 : PDMA_WIDTH_32, |
AnnaBridge | 174:b96e65c34a4d | 438 | rx_length); |
AnnaBridge | 174:b96e65c34a4d | 439 | PDMA_SetTransferAddr(obj->spi.dma_chn_id_rx, |
AnnaBridge | 174:b96e65c34a4d | 440 | NU_MODSUBINDEX(obj->spi.spi) == 0 ? (uint32_t) &spi_base->RX0 : (uint32_t) &spi_base->RX1, // Source address |
AnnaBridge | 174:b96e65c34a4d | 441 | PDMA_SAR_FIX, // Source address fixed |
AnnaBridge | 174:b96e65c34a4d | 442 | (uint32_t) rx, // NOTE: |
AnnaBridge | 174:b96e65c34a4d | 443 | // NUC472: End of destination address |
AnnaBridge | 174:b96e65c34a4d | 444 | // M451: Start of destination address |
AnnaBridge | 174:b96e65c34a4d | 445 | // NANO130: Start of destination address |
AnnaBridge | 174:b96e65c34a4d | 446 | PDMA_DAR_INC); // Destination address incremental |
AnnaBridge | 174:b96e65c34a4d | 447 | PDMA_EnableInt(obj->spi.dma_chn_id_rx, |
AnnaBridge | 174:b96e65c34a4d | 448 | PDMA_IER_TD_IE_Msk); // Interrupt type |
AnnaBridge | 174:b96e65c34a4d | 449 | // Register DMA event handler |
AnnaBridge | 174:b96e65c34a4d | 450 | dma_set_handler(obj->spi.dma_chn_id_rx, (uint32_t) spi_dma_handler_rx, (uint32_t) obj, DMA_EVENT_ALL); |
Anna Bridge |
186:707f6e361f3e | 451 | |
Anna Bridge |
186:707f6e361f3e | 452 | /* Start tx/rx DMA transfer |
Anna Bridge |
186:707f6e361f3e | 453 | * |
Anna Bridge |
186:707f6e361f3e | 454 | * If we have both PDMA and SPI interrupts enabled and PDMA priority is lower than SPI priority, |
Anna Bridge |
186:707f6e361f3e | 455 | * we would trap in SPI interrupt handler endlessly with the sequence: |
Anna Bridge |
186:707f6e361f3e | 456 | * |
Anna Bridge |
186:707f6e361f3e | 457 | * 1. PDMA TX transfer done interrupt occurs and is well handled. |
Anna Bridge |
186:707f6e361f3e | 458 | * 2. SPI RX FIFO threshold interrupt occurs. Trap here because PDMA RX transfer done interrupt doesn't get handled. |
Anna Bridge |
186:707f6e361f3e | 459 | * 3. PDMA RX transfer done interrupt occurs but it cannot be handled due to above. |
Anna Bridge |
186:707f6e361f3e | 460 | * |
Anna Bridge |
186:707f6e361f3e | 461 | * To fix it, we don't enable SPI TX/RX threshold interrupts but keep SPI vector handler set to be called |
Anna Bridge |
186:707f6e361f3e | 462 | * in PDMA TX/RX transfer done interrupt handlers (spi_dma_handler_tx/spi_dma_handler_rx). |
Anna Bridge |
186:707f6e361f3e | 463 | */ |
AnnaBridge | 174:b96e65c34a4d | 464 | spi_enable_vector_interrupt(obj, handler, 1); |
Anna Bridge |
186:707f6e361f3e | 465 | /* No TX/RX FIFO threshold interrupt */ |
AnnaBridge | 174:b96e65c34a4d | 466 | spi_master_enable_interrupt(obj, 0, SPI_FIFO_RX_INTEN_MASK | SPI_FIFO_TX_INTEN_MASK); |
Anna Bridge |
186:707f6e361f3e | 467 | |
Anna Bridge |
186:707f6e361f3e | 468 | /* Order to enable PDMA TX/RX functions |
Anna Bridge |
186:707f6e361f3e | 469 | * |
Anna Bridge |
186:707f6e361f3e | 470 | * H/W spec: In SPI Master mode with full duplex transfer, if both TX and RX PDMA functions are |
Anna Bridge |
186:707f6e361f3e | 471 | * enabled, RX PDMA function cannot be enabled prior to TX PDMA function. User can enable |
Anna Bridge |
186:707f6e361f3e | 472 | * TX PDMA function firstly or enable both functions simultaneously. |
Anna Bridge |
186:707f6e361f3e | 473 | * Per real test, it is safer to start RX PDMA first and then TX PDMA. Otherwise, receive FIFO is |
Anna Bridge |
186:707f6e361f3e | 474 | * subject to overflow by TX DMA. |
Anna Bridge |
186:707f6e361f3e | 475 | * |
Anna Bridge |
186:707f6e361f3e | 476 | * With the above conflicts, we enable PDMA TX/RX functions simultaneously. |
Anna Bridge |
186:707f6e361f3e | 477 | */ |
Anna Bridge |
186:707f6e361f3e | 478 | spi_base->DMA |= (SPI_DMA_TX_DMA_EN_Msk | SPI_DMA_RX_DMA_EN_Msk); |
Anna Bridge |
186:707f6e361f3e | 479 | |
AnnaBridge | 174:b96e65c34a4d | 480 | PDMA_Trigger(obj->spi.dma_chn_id_rx); |
AnnaBridge | 174:b96e65c34a4d | 481 | PDMA_Trigger(obj->spi.dma_chn_id_tx); |
AnnaBridge | 174:b96e65c34a4d | 482 | } |
AnnaBridge | 174:b96e65c34a4d | 483 | } |
AnnaBridge | 174:b96e65c34a4d | 484 | |
AnnaBridge | 174:b96e65c34a4d | 485 | /** |
AnnaBridge | 174:b96e65c34a4d | 486 | * Abort an SPI transfer |
AnnaBridge | 174:b96e65c34a4d | 487 | * This is a helper function for event handling. When any of the events listed occurs, the HAL will abort any ongoing |
AnnaBridge | 174:b96e65c34a4d | 488 | * transfers |
AnnaBridge | 174:b96e65c34a4d | 489 | * @param[in] obj The SPI peripheral to stop |
AnnaBridge | 174:b96e65c34a4d | 490 | */ |
AnnaBridge | 174:b96e65c34a4d | 491 | void spi_abort_asynch(spi_t *obj) |
AnnaBridge | 174:b96e65c34a4d | 492 | { |
AnnaBridge | 174:b96e65c34a4d | 493 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
Anna Bridge |
186:707f6e361f3e | 494 | |
AnnaBridge | 174:b96e65c34a4d | 495 | if (obj->spi.dma_usage != DMA_USAGE_NEVER) { |
AnnaBridge | 174:b96e65c34a4d | 496 | // Receive FIFO Overrun in case of tx length > rx length on DMA way |
AnnaBridge | 174:b96e65c34a4d | 497 | if (spi_base->STATUS & SPI_STATUS_RX_OVER_RUN_Msk) { |
AnnaBridge | 174:b96e65c34a4d | 498 | spi_base->STATUS = SPI_STATUS_RX_OVER_RUN_Msk; |
AnnaBridge | 174:b96e65c34a4d | 499 | } |
AnnaBridge | 174:b96e65c34a4d | 500 | |
AnnaBridge | 174:b96e65c34a4d | 501 | if (obj->spi.dma_chn_id_tx != DMA_ERROR_OUT_OF_CHANNELS) { |
AnnaBridge | 174:b96e65c34a4d | 502 | PDMA_DisableInt(obj->spi.dma_chn_id_tx, PDMA_IER_TD_IE_Msk); |
AnnaBridge | 174:b96e65c34a4d | 503 | // NOTE: On NUC472, next PDMA transfer will fail with PDMA_STOP() called. |
AnnaBridge | 174:b96e65c34a4d | 504 | dma_enable(obj->spi.dma_chn_id_tx, 0); |
AnnaBridge | 174:b96e65c34a4d | 505 | } |
AnnaBridge | 174:b96e65c34a4d | 506 | spi_base->DMA &= ~SPI_DMA_TX_DMA_EN_Msk; |
Anna Bridge |
186:707f6e361f3e | 507 | |
AnnaBridge | 174:b96e65c34a4d | 508 | if (obj->spi.dma_chn_id_rx != DMA_ERROR_OUT_OF_CHANNELS) { |
AnnaBridge | 174:b96e65c34a4d | 509 | PDMA_DisableInt(obj->spi.dma_chn_id_rx, PDMA_IER_TD_IE_Msk); |
AnnaBridge | 174:b96e65c34a4d | 510 | // NOTE: On NUC472, next PDMA transfer will fail with PDMA_STOP() called. |
AnnaBridge | 174:b96e65c34a4d | 511 | dma_enable(obj->spi.dma_chn_id_rx, 0); |
AnnaBridge | 174:b96e65c34a4d | 512 | } |
AnnaBridge | 174:b96e65c34a4d | 513 | spi_base->DMA &= ~SPI_DMA_RX_DMA_EN_Msk; |
AnnaBridge | 174:b96e65c34a4d | 514 | } |
Anna Bridge |
186:707f6e361f3e | 515 | |
AnnaBridge | 174:b96e65c34a4d | 516 | // Necessary for both interrupt way and DMA way |
AnnaBridge | 174:b96e65c34a4d | 517 | spi_enable_vector_interrupt(obj, 0, 0); |
AnnaBridge | 174:b96e65c34a4d | 518 | spi_master_enable_interrupt(obj, 0, SPI_FIFO_RX_INTEN_MASK | SPI_FIFO_TX_INTEN_MASK); |
AnnaBridge | 174:b96e65c34a4d | 519 | |
Anna Bridge |
186:707f6e361f3e | 520 | /* Necessary for accessing FIFOCTL below */ |
Anna Bridge |
186:707f6e361f3e | 521 | SPI_DISABLE_SYNC(spi_base); |
Anna Bridge |
186:707f6e361f3e | 522 | |
AnnaBridge | 174:b96e65c34a4d | 523 | SPI_ClearRxFIFO(spi_base); |
AnnaBridge | 174:b96e65c34a4d | 524 | SPI_ClearTxFIFO(spi_base); |
AnnaBridge | 174:b96e65c34a4d | 525 | } |
AnnaBridge | 174:b96e65c34a4d | 526 | |
AnnaBridge | 174:b96e65c34a4d | 527 | /** |
AnnaBridge | 174:b96e65c34a4d | 528 | * Handle the SPI interrupt |
AnnaBridge | 174:b96e65c34a4d | 529 | * Read frames until the RX FIFO is empty. Write at most as many frames as were read. This way, |
AnnaBridge | 174:b96e65c34a4d | 530 | * it is unlikely that the RX FIFO will overflow. |
AnnaBridge | 174:b96e65c34a4d | 531 | * @param[in] obj The SPI peripheral that generated the interrupt |
AnnaBridge | 174:b96e65c34a4d | 532 | * @return |
AnnaBridge | 174:b96e65c34a4d | 533 | */ |
AnnaBridge | 174:b96e65c34a4d | 534 | uint32_t spi_irq_handler_asynch(spi_t *obj) |
AnnaBridge | 174:b96e65c34a4d | 535 | { |
AnnaBridge | 174:b96e65c34a4d | 536 | // Check for SPI events |
AnnaBridge | 174:b96e65c34a4d | 537 | uint32_t event = spi_event_check(obj); |
AnnaBridge | 174:b96e65c34a4d | 538 | if (event) { |
AnnaBridge | 174:b96e65c34a4d | 539 | spi_abort_asynch(obj); |
AnnaBridge | 174:b96e65c34a4d | 540 | } |
AnnaBridge | 174:b96e65c34a4d | 541 | |
AnnaBridge | 174:b96e65c34a4d | 542 | return (obj->spi.event & event) | ((event & SPI_EVENT_COMPLETE) ? SPI_EVENT_INTERNAL_TRANSFER_COMPLETE : 0); |
AnnaBridge | 174:b96e65c34a4d | 543 | } |
AnnaBridge | 174:b96e65c34a4d | 544 | |
AnnaBridge | 174:b96e65c34a4d | 545 | uint8_t spi_active(spi_t *obj) |
AnnaBridge | 174:b96e65c34a4d | 546 | { |
Anna Bridge |
186:707f6e361f3e | 547 | const struct nu_modinit_s *modinit = get_modinit(obj->spi.spi, spi_modinit_tab); |
Anna Bridge |
186:707f6e361f3e | 548 | MBED_ASSERT(modinit != NULL); |
Anna Bridge |
186:707f6e361f3e | 549 | MBED_ASSERT(modinit->modname == (int) obj->spi.spi); |
Anna Bridge |
186:707f6e361f3e | 550 | |
Anna Bridge |
186:707f6e361f3e | 551 | /* Vector will be cleared when asynchronous transfer is finished or aborted. |
Anna Bridge |
186:707f6e361f3e | 552 | Use it to judge if asynchronous transfer is on-going. */ |
Anna Bridge |
186:707f6e361f3e | 553 | /* NOTE: On NANO130, vector table is fixed in ROM and cannot be modified. */ |
Anna Bridge |
186:707f6e361f3e | 554 | return obj->spi.hdlr_async ? 1 : 0; |
AnnaBridge | 174:b96e65c34a4d | 555 | } |
AnnaBridge | 174:b96e65c34a4d | 556 | |
AnnaBridge | 174:b96e65c34a4d | 557 | void SPI0_IRQHandler(void) |
AnnaBridge | 174:b96e65c34a4d | 558 | { |
AnnaBridge | 174:b96e65c34a4d | 559 | spi_irq(spi0_var.obj); |
AnnaBridge | 174:b96e65c34a4d | 560 | } |
AnnaBridge | 174:b96e65c34a4d | 561 | void SPI1_IRQHandler(void) |
AnnaBridge | 174:b96e65c34a4d | 562 | { |
AnnaBridge | 174:b96e65c34a4d | 563 | spi_irq(spi1_var.obj); |
AnnaBridge | 174:b96e65c34a4d | 564 | } |
AnnaBridge | 174:b96e65c34a4d | 565 | void SPI2_IRQHandler(void) |
AnnaBridge | 174:b96e65c34a4d | 566 | { |
AnnaBridge | 174:b96e65c34a4d | 567 | spi_irq(spi2_var.obj); |
AnnaBridge | 174:b96e65c34a4d | 568 | } |
AnnaBridge | 174:b96e65c34a4d | 569 | static void spi_irq(spi_t *obj) |
AnnaBridge | 174:b96e65c34a4d | 570 | { |
AnnaBridge | 174:b96e65c34a4d | 571 | if (obj && obj->spi.hdlr_async) { |
AnnaBridge | 174:b96e65c34a4d | 572 | void (*hdlr_async)(void) = (void(*)(void))(obj->spi.hdlr_async); |
AnnaBridge | 174:b96e65c34a4d | 573 | hdlr_async(); |
AnnaBridge | 174:b96e65c34a4d | 574 | } |
AnnaBridge | 174:b96e65c34a4d | 575 | } |
AnnaBridge | 174:b96e65c34a4d | 576 | |
AnnaBridge | 174:b96e65c34a4d | 577 | static int spi_writeable(spi_t * obj) |
AnnaBridge | 174:b96e65c34a4d | 578 | { |
AnnaBridge | 174:b96e65c34a4d | 579 | // Receive FIFO must not be full to avoid receive FIFO overflow on next transmit/receive |
AnnaBridge | 174:b96e65c34a4d | 580 | return (! SPI_GET_TX_FIFO_FULL_FLAG(((SPI_T *) NU_MODBASE(obj->spi.spi)))); |
AnnaBridge | 174:b96e65c34a4d | 581 | } |
AnnaBridge | 174:b96e65c34a4d | 582 | |
AnnaBridge | 174:b96e65c34a4d | 583 | static int spi_readable(spi_t * obj) |
AnnaBridge | 174:b96e65c34a4d | 584 | { |
AnnaBridge | 174:b96e65c34a4d | 585 | return ! SPI_GET_RX_FIFO_EMPTY_FLAG(((SPI_T *) NU_MODBASE(obj->spi.spi))); |
AnnaBridge | 174:b96e65c34a4d | 586 | } |
AnnaBridge | 174:b96e65c34a4d | 587 | |
AnnaBridge | 174:b96e65c34a4d | 588 | static void spi_enable_event(spi_t *obj, uint32_t event, uint8_t enable) |
Anna Bridge |
186:707f6e361f3e | 589 | { |
AnnaBridge | 174:b96e65c34a4d | 590 | obj->spi.event &= ~SPI_EVENT_ALL; |
AnnaBridge | 174:b96e65c34a4d | 591 | obj->spi.event |= (event & SPI_EVENT_ALL); |
AnnaBridge | 174:b96e65c34a4d | 592 | if (event & SPI_EVENT_RX_OVERFLOW) { |
AnnaBridge | 174:b96e65c34a4d | 593 | SPI_EnableInt((SPI_T *) NU_MODBASE(obj->spi.spi), SPI_FIFO_RXOVR_INTEN_MASK); |
AnnaBridge | 174:b96e65c34a4d | 594 | } |
AnnaBridge | 174:b96e65c34a4d | 595 | } |
AnnaBridge | 174:b96e65c34a4d | 596 | |
AnnaBridge | 174:b96e65c34a4d | 597 | static void spi_enable_vector_interrupt(spi_t *obj, uint32_t handler, uint8_t enable) |
AnnaBridge | 174:b96e65c34a4d | 598 | { |
AnnaBridge | 174:b96e65c34a4d | 599 | const struct nu_modinit_s *modinit = get_modinit(obj->spi.spi, spi_modinit_tab); |
AnnaBridge | 174:b96e65c34a4d | 600 | MBED_ASSERT(modinit != NULL); |
Anna Bridge |
186:707f6e361f3e | 601 | MBED_ASSERT(modinit->modname == (int) obj->spi.spi); |
AnnaBridge | 174:b96e65c34a4d | 602 | |
AnnaBridge | 174:b96e65c34a4d | 603 | struct nu_spi_var *var = (struct nu_spi_var *) modinit->var; |
AnnaBridge | 174:b96e65c34a4d | 604 | |
AnnaBridge | 174:b96e65c34a4d | 605 | if (enable) { |
AnnaBridge | 174:b96e65c34a4d | 606 | var->obj = obj; |
AnnaBridge | 174:b96e65c34a4d | 607 | obj->spi.hdlr_async = handler; |
Anna Bridge |
186:707f6e361f3e | 608 | /* NOTE: On NANO130, vector table is fixed in ROM and cannot be modified. */ |
AnnaBridge | 174:b96e65c34a4d | 609 | NVIC_EnableIRQ(modinit->irq_n); |
AnnaBridge | 174:b96e65c34a4d | 610 | } |
AnnaBridge | 174:b96e65c34a4d | 611 | else { |
AnnaBridge | 174:b96e65c34a4d | 612 | NVIC_DisableIRQ(modinit->irq_n); |
Anna Bridge |
186:707f6e361f3e | 613 | /* NOTE: On NANO130, vector table is fixed in ROM and cannot be modified. */ |
AnnaBridge | 174:b96e65c34a4d | 614 | var->obj = NULL; |
Anna Bridge |
186:707f6e361f3e | 615 | obj->spi.hdlr_async = 0; |
AnnaBridge | 174:b96e65c34a4d | 616 | } |
AnnaBridge | 174:b96e65c34a4d | 617 | } |
AnnaBridge | 174:b96e65c34a4d | 618 | |
AnnaBridge | 174:b96e65c34a4d | 619 | static void spi_master_enable_interrupt(spi_t *obj, uint8_t enable, uint32_t mask) |
AnnaBridge | 174:b96e65c34a4d | 620 | { |
AnnaBridge | 174:b96e65c34a4d | 621 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
AnnaBridge | 174:b96e65c34a4d | 622 | |
AnnaBridge | 174:b96e65c34a4d | 623 | // NOTE: |
AnnaBridge | 174:b96e65c34a4d | 624 | // NANO130: SPI_IE_MASK/SPI_STATUS_INTSTS_Msk are for unit transfer IE/EF. Don't get confused. |
AnnaBridge | 174:b96e65c34a4d | 625 | if (enable) { |
AnnaBridge | 174:b96e65c34a4d | 626 | // Enable tx/rx FIFO threshold interrupt |
AnnaBridge | 174:b96e65c34a4d | 627 | SPI_EnableInt(spi_base, mask); |
AnnaBridge | 174:b96e65c34a4d | 628 | } |
AnnaBridge | 174:b96e65c34a4d | 629 | else { |
AnnaBridge | 174:b96e65c34a4d | 630 | SPI_DisableInt(spi_base, mask); |
AnnaBridge | 174:b96e65c34a4d | 631 | } |
AnnaBridge | 174:b96e65c34a4d | 632 | } |
AnnaBridge | 174:b96e65c34a4d | 633 | |
AnnaBridge | 174:b96e65c34a4d | 634 | static uint32_t spi_event_check(spi_t *obj) |
AnnaBridge | 174:b96e65c34a4d | 635 | { |
AnnaBridge | 174:b96e65c34a4d | 636 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
AnnaBridge | 174:b96e65c34a4d | 637 | uint32_t event = 0; |
Anna Bridge |
186:707f6e361f3e | 638 | |
AnnaBridge | 174:b96e65c34a4d | 639 | if (obj->spi.dma_usage == DMA_USAGE_NEVER) { |
AnnaBridge | 174:b96e65c34a4d | 640 | uint32_t n_rec = spi_master_read_asynch(obj); |
AnnaBridge | 174:b96e65c34a4d | 641 | spi_master_write_asynch(obj, n_rec); |
AnnaBridge | 174:b96e65c34a4d | 642 | } |
Anna Bridge |
186:707f6e361f3e | 643 | |
AnnaBridge | 174:b96e65c34a4d | 644 | if (spi_is_tx_complete(obj) && spi_is_rx_complete(obj)) { |
AnnaBridge | 174:b96e65c34a4d | 645 | event |= SPI_EVENT_COMPLETE; |
AnnaBridge | 174:b96e65c34a4d | 646 | } |
Anna Bridge |
186:707f6e361f3e | 647 | |
AnnaBridge | 174:b96e65c34a4d | 648 | // Receive FIFO Overrun |
AnnaBridge | 174:b96e65c34a4d | 649 | if (spi_base->STATUS & SPI_STATUS_RX_OVER_RUN_Msk) { |
AnnaBridge | 174:b96e65c34a4d | 650 | spi_base->STATUS = SPI_STATUS_RX_OVER_RUN_Msk; |
AnnaBridge | 174:b96e65c34a4d | 651 | // In case of tx length > rx length on DMA way |
AnnaBridge | 174:b96e65c34a4d | 652 | if (obj->spi.dma_usage == DMA_USAGE_NEVER) { |
AnnaBridge | 174:b96e65c34a4d | 653 | event |= SPI_EVENT_RX_OVERFLOW; |
AnnaBridge | 174:b96e65c34a4d | 654 | } |
AnnaBridge | 174:b96e65c34a4d | 655 | } |
Anna Bridge |
186:707f6e361f3e | 656 | |
AnnaBridge | 174:b96e65c34a4d | 657 | // Receive Time-Out |
AnnaBridge | 174:b96e65c34a4d | 658 | if (spi_base->STATUS & SPI_STATUS_TIME_OUT_STS_Msk) { |
AnnaBridge | 174:b96e65c34a4d | 659 | spi_base->STATUS = SPI_STATUS_TIME_OUT_STS_Msk; |
Anna Bridge |
186:707f6e361f3e | 660 | // Not using this IF. Just clear it. |
AnnaBridge | 174:b96e65c34a4d | 661 | } |
AnnaBridge | 174:b96e65c34a4d | 662 | |
AnnaBridge | 174:b96e65c34a4d | 663 | return event; |
AnnaBridge | 174:b96e65c34a4d | 664 | } |
AnnaBridge | 174:b96e65c34a4d | 665 | |
AnnaBridge | 174:b96e65c34a4d | 666 | /** |
AnnaBridge | 174:b96e65c34a4d | 667 | * Send words from the SPI TX buffer until the send limit is reached or the TX FIFO is full |
AnnaBridge | 174:b96e65c34a4d | 668 | * tx_limit is provided to ensure that the number of SPI frames (words) in flight can be managed. |
AnnaBridge | 174:b96e65c34a4d | 669 | * @param[in] obj The SPI object on which to operate |
AnnaBridge | 174:b96e65c34a4d | 670 | * @param[in] tx_limit The maximum number of words to send |
AnnaBridge | 174:b96e65c34a4d | 671 | * @return The number of SPI words that have been transfered |
AnnaBridge | 174:b96e65c34a4d | 672 | */ |
AnnaBridge | 174:b96e65c34a4d | 673 | static uint32_t spi_master_write_asynch(spi_t *obj, uint32_t tx_limit) |
AnnaBridge | 174:b96e65c34a4d | 674 | { |
AnnaBridge | 174:b96e65c34a4d | 675 | uint32_t n_words = 0; |
AnnaBridge | 174:b96e65c34a4d | 676 | uint32_t tx_rmn = obj->tx_buff.length - obj->tx_buff.pos; |
AnnaBridge | 174:b96e65c34a4d | 677 | uint32_t rx_rmn = obj->rx_buff.length - obj->rx_buff.pos; |
AnnaBridge | 174:b96e65c34a4d | 678 | uint32_t max_tx = NU_MAX(tx_rmn, rx_rmn); |
AnnaBridge | 174:b96e65c34a4d | 679 | max_tx = NU_MIN(max_tx, tx_limit); |
AnnaBridge | 174:b96e65c34a4d | 680 | uint8_t data_width = spi_get_data_width(obj); |
AnnaBridge | 174:b96e65c34a4d | 681 | uint8_t bytes_per_word = (data_width + 7) / 8; |
AnnaBridge | 174:b96e65c34a4d | 682 | uint8_t *tx = (uint8_t *)(obj->tx_buff.buffer) + bytes_per_word * obj->tx_buff.pos; |
AnnaBridge | 174:b96e65c34a4d | 683 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
AnnaBridge | 174:b96e65c34a4d | 684 | uint32_t TX = (NU_MODSUBINDEX(obj->spi.spi) == 0) ? ((uint32_t) &spi_base->TX0) : ((uint32_t) &spi_base->TX1); |
AnnaBridge | 174:b96e65c34a4d | 685 | |
AnnaBridge | 174:b96e65c34a4d | 686 | while ((n_words < max_tx) && spi_writeable(obj)) { |
AnnaBridge | 174:b96e65c34a4d | 687 | if (spi_is_tx_complete(obj)) { |
AnnaBridge | 174:b96e65c34a4d | 688 | // Transmit dummy as transmit buffer is empty |
AnnaBridge | 174:b96e65c34a4d | 689 | M32(TX) = 0; |
AnnaBridge | 174:b96e65c34a4d | 690 | } |
AnnaBridge | 174:b96e65c34a4d | 691 | else { |
AnnaBridge | 174:b96e65c34a4d | 692 | switch (bytes_per_word) { |
AnnaBridge | 174:b96e65c34a4d | 693 | case 4: |
AnnaBridge | 174:b96e65c34a4d | 694 | M32(TX) = nu_get32_le(tx); |
AnnaBridge | 174:b96e65c34a4d | 695 | tx += 4; |
AnnaBridge | 174:b96e65c34a4d | 696 | break; |
AnnaBridge | 174:b96e65c34a4d | 697 | case 2: |
AnnaBridge | 174:b96e65c34a4d | 698 | M32(TX) = nu_get16_le(tx); |
AnnaBridge | 174:b96e65c34a4d | 699 | tx += 2; |
AnnaBridge | 174:b96e65c34a4d | 700 | break; |
AnnaBridge | 174:b96e65c34a4d | 701 | case 1: |
AnnaBridge | 174:b96e65c34a4d | 702 | M32(TX) = *((uint8_t *) tx); |
AnnaBridge | 174:b96e65c34a4d | 703 | tx += 1; |
AnnaBridge | 174:b96e65c34a4d | 704 | break; |
AnnaBridge | 174:b96e65c34a4d | 705 | } |
Anna Bridge |
186:707f6e361f3e | 706 | |
AnnaBridge | 174:b96e65c34a4d | 707 | obj->tx_buff.pos ++; |
AnnaBridge | 174:b96e65c34a4d | 708 | } |
AnnaBridge | 174:b96e65c34a4d | 709 | n_words ++; |
AnnaBridge | 174:b96e65c34a4d | 710 | } |
Anna Bridge |
186:707f6e361f3e | 711 | |
AnnaBridge | 174:b96e65c34a4d | 712 | //Return the number of words that have been sent |
AnnaBridge | 174:b96e65c34a4d | 713 | return n_words; |
AnnaBridge | 174:b96e65c34a4d | 714 | } |
AnnaBridge | 174:b96e65c34a4d | 715 | |
AnnaBridge | 174:b96e65c34a4d | 716 | /** |
AnnaBridge | 174:b96e65c34a4d | 717 | * Read SPI words out of the RX FIFO |
AnnaBridge | 174:b96e65c34a4d | 718 | * Continues reading words out of the RX FIFO until the following condition is met: |
AnnaBridge | 174:b96e65c34a4d | 719 | * o There are no more words in the FIFO |
AnnaBridge | 174:b96e65c34a4d | 720 | * OR BOTH OF: |
AnnaBridge | 174:b96e65c34a4d | 721 | * o At least as many words as the TX buffer have been received |
AnnaBridge | 174:b96e65c34a4d | 722 | * o At least as many words as the RX buffer have been received |
AnnaBridge | 174:b96e65c34a4d | 723 | * This way, RX overflows are not generated when the TX buffer size exceeds the RX buffer size |
AnnaBridge | 174:b96e65c34a4d | 724 | * @param[in] obj The SPI object on which to operate |
AnnaBridge | 174:b96e65c34a4d | 725 | * @return Returns the number of words extracted from the RX FIFO |
AnnaBridge | 174:b96e65c34a4d | 726 | */ |
AnnaBridge | 174:b96e65c34a4d | 727 | static uint32_t spi_master_read_asynch(spi_t *obj) |
AnnaBridge | 174:b96e65c34a4d | 728 | { |
AnnaBridge | 174:b96e65c34a4d | 729 | uint32_t n_words = 0; |
AnnaBridge | 174:b96e65c34a4d | 730 | uint32_t tx_rmn = obj->tx_buff.length - obj->tx_buff.pos; |
AnnaBridge | 174:b96e65c34a4d | 731 | uint32_t rx_rmn = obj->rx_buff.length - obj->rx_buff.pos; |
AnnaBridge | 174:b96e65c34a4d | 732 | uint32_t max_rx = NU_MAX(tx_rmn, rx_rmn); |
AnnaBridge | 174:b96e65c34a4d | 733 | uint8_t data_width = spi_get_data_width(obj); |
AnnaBridge | 174:b96e65c34a4d | 734 | uint8_t bytes_per_word = (data_width + 7) / 8; |
AnnaBridge | 174:b96e65c34a4d | 735 | uint8_t *rx = (uint8_t *)(obj->rx_buff.buffer) + bytes_per_word * obj->rx_buff.pos; |
AnnaBridge | 174:b96e65c34a4d | 736 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
AnnaBridge | 174:b96e65c34a4d | 737 | uint32_t RX = (NU_MODSUBINDEX(obj->spi.spi) == 0) ? ((uint32_t) &spi_base->RX0) : ((uint32_t) &spi_base->RX1); |
AnnaBridge | 174:b96e65c34a4d | 738 | |
AnnaBridge | 174:b96e65c34a4d | 739 | while ((n_words < max_rx) && spi_readable(obj)) { |
AnnaBridge | 174:b96e65c34a4d | 740 | if (spi_is_rx_complete(obj)) { |
AnnaBridge | 174:b96e65c34a4d | 741 | // Disregard as receive buffer is full |
AnnaBridge | 174:b96e65c34a4d | 742 | M32(RX); |
AnnaBridge | 174:b96e65c34a4d | 743 | } |
AnnaBridge | 174:b96e65c34a4d | 744 | else { |
AnnaBridge | 174:b96e65c34a4d | 745 | switch (bytes_per_word) { |
AnnaBridge | 174:b96e65c34a4d | 746 | case 4: { |
AnnaBridge | 174:b96e65c34a4d | 747 | uint32_t val = M32(RX); |
AnnaBridge | 174:b96e65c34a4d | 748 | nu_set32_le(rx, val); |
AnnaBridge | 174:b96e65c34a4d | 749 | rx += 4; |
AnnaBridge | 174:b96e65c34a4d | 750 | break; |
AnnaBridge | 174:b96e65c34a4d | 751 | } |
AnnaBridge | 174:b96e65c34a4d | 752 | case 2: { |
AnnaBridge | 174:b96e65c34a4d | 753 | uint16_t val = M32(RX); |
AnnaBridge | 174:b96e65c34a4d | 754 | nu_set16_le(rx, val); |
AnnaBridge | 174:b96e65c34a4d | 755 | rx += 2; |
AnnaBridge | 174:b96e65c34a4d | 756 | break; |
AnnaBridge | 174:b96e65c34a4d | 757 | } |
AnnaBridge | 174:b96e65c34a4d | 758 | case 1: |
AnnaBridge | 174:b96e65c34a4d | 759 | *rx ++ = M32(RX); |
AnnaBridge | 174:b96e65c34a4d | 760 | break; |
AnnaBridge | 174:b96e65c34a4d | 761 | } |
Anna Bridge |
186:707f6e361f3e | 762 | |
AnnaBridge | 174:b96e65c34a4d | 763 | obj->rx_buff.pos ++; |
AnnaBridge | 174:b96e65c34a4d | 764 | } |
AnnaBridge | 174:b96e65c34a4d | 765 | n_words ++; |
AnnaBridge | 174:b96e65c34a4d | 766 | } |
Anna Bridge |
186:707f6e361f3e | 767 | |
AnnaBridge | 174:b96e65c34a4d | 768 | // Return the number of words received |
AnnaBridge | 174:b96e65c34a4d | 769 | return n_words; |
AnnaBridge | 174:b96e65c34a4d | 770 | } |
AnnaBridge | 174:b96e65c34a4d | 771 | |
AnnaBridge | 174:b96e65c34a4d | 772 | static void spi_buffer_set(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length) |
AnnaBridge | 174:b96e65c34a4d | 773 | { |
AnnaBridge | 174:b96e65c34a4d | 774 | obj->tx_buff.buffer = (void *) tx; |
AnnaBridge | 174:b96e65c34a4d | 775 | obj->tx_buff.length = tx_length; |
AnnaBridge | 174:b96e65c34a4d | 776 | obj->tx_buff.pos = 0; |
AnnaBridge | 174:b96e65c34a4d | 777 | obj->tx_buff.width = spi_get_data_width(obj); |
AnnaBridge | 174:b96e65c34a4d | 778 | obj->rx_buff.buffer = rx; |
AnnaBridge | 174:b96e65c34a4d | 779 | obj->rx_buff.length = rx_length; |
AnnaBridge | 174:b96e65c34a4d | 780 | obj->rx_buff.pos = 0; |
AnnaBridge | 174:b96e65c34a4d | 781 | obj->rx_buff.width = spi_get_data_width(obj); |
AnnaBridge | 174:b96e65c34a4d | 782 | } |
AnnaBridge | 174:b96e65c34a4d | 783 | |
AnnaBridge | 174:b96e65c34a4d | 784 | static void spi_check_dma_usage(DMAUsage *dma_usage, int *dma_ch_tx, int *dma_ch_rx) |
AnnaBridge | 174:b96e65c34a4d | 785 | { |
AnnaBridge | 174:b96e65c34a4d | 786 | if (*dma_usage != DMA_USAGE_NEVER) { |
AnnaBridge | 174:b96e65c34a4d | 787 | if (*dma_ch_tx == DMA_ERROR_OUT_OF_CHANNELS) { |
AnnaBridge | 174:b96e65c34a4d | 788 | *dma_ch_tx = dma_channel_allocate(DMA_CAP_NONE); |
AnnaBridge | 174:b96e65c34a4d | 789 | } |
AnnaBridge | 174:b96e65c34a4d | 790 | if (*dma_ch_rx == DMA_ERROR_OUT_OF_CHANNELS) { |
AnnaBridge | 174:b96e65c34a4d | 791 | *dma_ch_rx = dma_channel_allocate(DMA_CAP_NONE); |
AnnaBridge | 174:b96e65c34a4d | 792 | } |
Anna Bridge |
186:707f6e361f3e | 793 | |
AnnaBridge | 174:b96e65c34a4d | 794 | if (*dma_ch_tx == DMA_ERROR_OUT_OF_CHANNELS || *dma_ch_rx == DMA_ERROR_OUT_OF_CHANNELS) { |
AnnaBridge | 174:b96e65c34a4d | 795 | *dma_usage = DMA_USAGE_NEVER; |
AnnaBridge | 174:b96e65c34a4d | 796 | } |
AnnaBridge | 174:b96e65c34a4d | 797 | } |
Anna Bridge |
186:707f6e361f3e | 798 | |
AnnaBridge | 174:b96e65c34a4d | 799 | if (*dma_usage == DMA_USAGE_NEVER) { |
AnnaBridge | 174:b96e65c34a4d | 800 | dma_channel_free(*dma_ch_tx); |
AnnaBridge | 174:b96e65c34a4d | 801 | *dma_ch_tx = DMA_ERROR_OUT_OF_CHANNELS; |
AnnaBridge | 174:b96e65c34a4d | 802 | dma_channel_free(*dma_ch_rx); |
AnnaBridge | 174:b96e65c34a4d | 803 | *dma_ch_rx = DMA_ERROR_OUT_OF_CHANNELS; |
AnnaBridge | 174:b96e65c34a4d | 804 | } |
AnnaBridge | 174:b96e65c34a4d | 805 | } |
AnnaBridge | 174:b96e65c34a4d | 806 | |
AnnaBridge | 174:b96e65c34a4d | 807 | static uint8_t spi_get_data_width(spi_t *obj) |
AnnaBridge | 174:b96e65c34a4d | 808 | { |
AnnaBridge | 174:b96e65c34a4d | 809 | SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); |
Anna Bridge |
186:707f6e361f3e | 810 | |
AnnaBridge | 174:b96e65c34a4d | 811 | uint32_t data_width = ((spi_base->CTL & SPI_CTL_TX_BIT_LEN_Msk) >> SPI_CTL_TX_BIT_LEN_Pos); |
AnnaBridge | 174:b96e65c34a4d | 812 | if (data_width == 0) { |
AnnaBridge | 174:b96e65c34a4d | 813 | data_width = 32; |
AnnaBridge | 174:b96e65c34a4d | 814 | } |
Anna Bridge |
186:707f6e361f3e | 815 | |
AnnaBridge | 174:b96e65c34a4d | 816 | return data_width; |
AnnaBridge | 174:b96e65c34a4d | 817 | } |
AnnaBridge | 174:b96e65c34a4d | 818 | |
AnnaBridge | 174:b96e65c34a4d | 819 | static int spi_is_tx_complete(spi_t *obj) |
AnnaBridge | 174:b96e65c34a4d | 820 | { |
AnnaBridge | 174:b96e65c34a4d | 821 | return (obj->tx_buff.pos == obj->tx_buff.length); |
AnnaBridge | 174:b96e65c34a4d | 822 | } |
AnnaBridge | 174:b96e65c34a4d | 823 | |
AnnaBridge | 174:b96e65c34a4d | 824 | static int spi_is_rx_complete(spi_t *obj) |
AnnaBridge | 174:b96e65c34a4d | 825 | { |
AnnaBridge | 174:b96e65c34a4d | 826 | return (obj->rx_buff.pos == obj->rx_buff.length); |
AnnaBridge | 174:b96e65c34a4d | 827 | } |
AnnaBridge | 174:b96e65c34a4d | 828 | |
AnnaBridge | 174:b96e65c34a4d | 829 | static void spi_dma_handler_tx(uint32_t id, uint32_t event_dma) |
AnnaBridge | 174:b96e65c34a4d | 830 | { |
AnnaBridge | 174:b96e65c34a4d | 831 | spi_t *obj = (spi_t *) id; |
Anna Bridge |
186:707f6e361f3e | 832 | |
AnnaBridge | 174:b96e65c34a4d | 833 | // TODO: Pass this error to caller |
AnnaBridge | 174:b96e65c34a4d | 834 | if (event_dma & DMA_EVENT_ABORT) { |
AnnaBridge | 174:b96e65c34a4d | 835 | } |
AnnaBridge | 174:b96e65c34a4d | 836 | // Expect SPI IRQ will catch this transfer done event |
AnnaBridge | 174:b96e65c34a4d | 837 | if (event_dma & DMA_EVENT_TRANSFER_DONE) { |
AnnaBridge | 174:b96e65c34a4d | 838 | obj->tx_buff.pos = obj->tx_buff.length; |
AnnaBridge | 174:b96e65c34a4d | 839 | } |
AnnaBridge | 174:b96e65c34a4d | 840 | // TODO: Pass this error to caller |
AnnaBridge | 174:b96e65c34a4d | 841 | if (event_dma & DMA_EVENT_TIMEOUT) { |
AnnaBridge | 174:b96e65c34a4d | 842 | } |
Anna Bridge |
186:707f6e361f3e | 843 | |
AnnaBridge | 174:b96e65c34a4d | 844 | const struct nu_modinit_s *modinit = get_modinit(obj->spi.spi, spi_modinit_tab); |
AnnaBridge | 174:b96e65c34a4d | 845 | MBED_ASSERT(modinit != NULL); |
Anna Bridge |
186:707f6e361f3e | 846 | MBED_ASSERT(modinit->modname == (int) obj->spi.spi); |
Anna Bridge |
186:707f6e361f3e | 847 | |
AnnaBridge | 174:b96e65c34a4d | 848 | void (*vec)(void) = (void (*)(void)) NVIC_GetVector(modinit->irq_n); |
AnnaBridge | 174:b96e65c34a4d | 849 | vec(); |
AnnaBridge | 174:b96e65c34a4d | 850 | } |
AnnaBridge | 174:b96e65c34a4d | 851 | |
AnnaBridge | 174:b96e65c34a4d | 852 | static void spi_dma_handler_rx(uint32_t id, uint32_t event_dma) |
AnnaBridge | 174:b96e65c34a4d | 853 | { |
AnnaBridge | 174:b96e65c34a4d | 854 | spi_t *obj = (spi_t *) id; |
Anna Bridge |
186:707f6e361f3e | 855 | |
AnnaBridge | 174:b96e65c34a4d | 856 | // TODO: Pass this error to caller |
AnnaBridge | 174:b96e65c34a4d | 857 | if (event_dma & DMA_EVENT_ABORT) { |
AnnaBridge | 174:b96e65c34a4d | 858 | } |
AnnaBridge | 174:b96e65c34a4d | 859 | // Expect SPI IRQ will catch this transfer done event |
AnnaBridge | 174:b96e65c34a4d | 860 | if (event_dma & DMA_EVENT_TRANSFER_DONE) { |
AnnaBridge | 174:b96e65c34a4d | 861 | obj->rx_buff.pos = obj->rx_buff.length; |
AnnaBridge | 174:b96e65c34a4d | 862 | } |
AnnaBridge | 174:b96e65c34a4d | 863 | // TODO: Pass this error to caller |
AnnaBridge | 174:b96e65c34a4d | 864 | if (event_dma & DMA_EVENT_TIMEOUT) { |
AnnaBridge | 174:b96e65c34a4d | 865 | } |
Anna Bridge |
186:707f6e361f3e | 866 | |
AnnaBridge | 174:b96e65c34a4d | 867 | const struct nu_modinit_s *modinit = get_modinit(obj->spi.spi, spi_modinit_tab); |
AnnaBridge | 174:b96e65c34a4d | 868 | MBED_ASSERT(modinit != NULL); |
Anna Bridge |
186:707f6e361f3e | 869 | MBED_ASSERT(modinit->modname == (int) obj->spi.spi); |
Anna Bridge |
186:707f6e361f3e | 870 | |
AnnaBridge | 174:b96e65c34a4d | 871 | void (*vec)(void) = (void (*)(void)) NVIC_GetVector(modinit->irq_n); |
AnnaBridge | 174:b96e65c34a4d | 872 | vec(); |
AnnaBridge | 174:b96e65c34a4d | 873 | } |
AnnaBridge | 174:b96e65c34a4d | 874 | |
AnnaBridge | 174:b96e65c34a4d | 875 | #endif |
AnnaBridge | 174:b96e65c34a4d | 876 | |
AnnaBridge | 174:b96e65c34a4d | 877 | #endif |