New SPI API supporting DMA
[betaflight.git] / src / main / drivers / bus_spi_stdperiph.c
blob47d614f3050ce083b6a5baee42fc86fc77c7af45
1 /*
2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
8 * any later version.
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <string.h>
25 #include "platform.h"
27 #ifdef USE_SPI
29 // STM32F405 can't DMA to/from FASTRAM (CCM SRAM)
30 #define IS_CCM(p) (((uint32_t)p & 0xffff0000) == 0x10000000)
32 #include "common/maths.h"
33 #include "drivers/bus.h"
34 #include "drivers/bus_spi.h"
35 #include "drivers/bus_spi_impl.h"
36 #include "drivers/exti.h"
37 #include "drivers/io.h"
38 #include "drivers/rcc.h"
40 static SPI_InitTypeDef defaultInit = {
41 .SPI_Mode = SPI_Mode_Master,
42 .SPI_Direction = SPI_Direction_2Lines_FullDuplex,
43 .SPI_DataSize = SPI_DataSize_8b,
44 .SPI_NSS = SPI_NSS_Soft,
45 .SPI_FirstBit = SPI_FirstBit_MSB,
46 .SPI_CRCPolynomial = 7,
47 .SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8,
48 .SPI_CPOL = SPI_CPOL_High,
49 .SPI_CPHA = SPI_CPHA_2Edge
52 static uint16_t spiDivisorToBRbits(SPI_TypeDef *instance, uint16_t divisor)
54 // SPI2 and SPI3 are on APB1/AHB1 which PCLK is half that of APB2/AHB2.
55 #if defined(STM32F410xx) || defined(STM32F411xE)
56 UNUSED(instance);
57 #else
58 if (instance == SPI2 || instance == SPI3) {
59 divisor /= 2; // Safe for divisor == 0 or 1
61 #endif
63 divisor = constrain(divisor, 2, 256);
65 return (ffs(divisor) - 2) << 3; // SPI_CR1_BR_Pos
68 static void spiSetDivisorBRreg(SPI_TypeDef *instance, uint16_t divisor)
70 #define BR_BITS ((BIT(5) | BIT(4) | BIT(3)))
71 const uint16_t tempRegister = (instance->CR1 & ~BR_BITS);
72 instance->CR1 = tempRegister | spiDivisorToBRbits(instance, divisor);
73 #undef BR_BITS
77 void spiInitDevice(SPIDevice device)
79 spiDevice_t *spi = &(spiDevice[device]);
81 if (!spi->dev) {
82 return;
85 // Enable SPI clock
86 RCC_ClockCmd(spi->rcc, ENABLE);
87 RCC_ResetCmd(spi->rcc, ENABLE);
89 IOInit(IOGetByTag(spi->sck), OWNER_SPI_SCK, RESOURCE_INDEX(device));
90 IOInit(IOGetByTag(spi->miso), OWNER_SPI_MISO, RESOURCE_INDEX(device));
91 IOInit(IOGetByTag(spi->mosi), OWNER_SPI_MOSI, RESOURCE_INDEX(device));
93 IOConfigGPIOAF(IOGetByTag(spi->sck), SPI_IO_AF_SCK_CFG, spi->af);
94 IOConfigGPIOAF(IOGetByTag(spi->miso), SPI_IO_AF_MISO_CFG, spi->af);
95 IOConfigGPIOAF(IOGetByTag(spi->mosi), SPI_IO_AF_CFG, spi->af);
97 // Init SPI hardware
98 SPI_I2S_DeInit(spi->dev);
100 SPI_I2S_DMACmd(spi->dev, SPI_I2S_DMAReq_Tx | SPI_I2S_DMAReq_Rx, DISABLE);
101 SPI_Init(spi->dev, &defaultInit);
102 SPI_Cmd(spi->dev, ENABLE);
105 void spiInternalResetDescriptors(busDevice_t *bus)
107 DMA_InitTypeDef *initTx = bus->initTx;
108 DMA_InitTypeDef *initRx = bus->initRx;
110 DMA_StructInit(initTx);
111 initTx->DMA_Channel = bus->dmaTxChannel;
112 initTx->DMA_DIR = DMA_DIR_MemoryToPeripheral;
113 initTx->DMA_Mode = DMA_Mode_Normal;
114 initTx->DMA_PeripheralBaseAddr = (uint32_t)&bus->busType_u.spi.instance->DR;
115 initTx->DMA_Priority = DMA_Priority_Low;
116 initTx->DMA_PeripheralInc = DMA_PeripheralInc_Disable;
117 initTx->DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
118 initTx->DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
120 DMA_StructInit(initRx);
121 initRx->DMA_Channel = bus->dmaRxChannel;
122 initRx->DMA_DIR = DMA_DIR_PeripheralToMemory;
123 initRx->DMA_Mode = DMA_Mode_Normal;
124 initRx->DMA_PeripheralBaseAddr = (uint32_t)&bus->busType_u.spi.instance->DR;
125 initRx->DMA_Priority = DMA_Priority_Low;
126 initRx->DMA_PeripheralInc = DMA_PeripheralInc_Disable;
127 initRx->DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
130 void spiInternalResetStream(dmaChannelDescriptor_t *descriptor)
132 DMA_Stream_TypeDef *streamRegs = (DMA_Stream_TypeDef *)descriptor->ref;
134 // Disable the stream
135 streamRegs->CR = 0U;
137 // Clear any pending interrupt flags
138 DMA_CLEAR_FLAG(descriptor, DMA_IT_HTIF | DMA_IT_TEIF | DMA_IT_TCIF);
141 static bool spiInternalReadWriteBufPolled(SPI_TypeDef *instance, const uint8_t *txData, uint8_t *rxData, int len)
143 uint8_t b;
145 while (len--) {
146 b = txData ? *(txData++) : 0xFF;
147 while (SPI_I2S_GetFlagStatus(instance, SPI_I2S_FLAG_TXE) == RESET);
148 SPI_I2S_SendData(instance, b);
150 while (SPI_I2S_GetFlagStatus(instance, SPI_I2S_FLAG_RXNE) == RESET);
151 b = SPI_I2S_ReceiveData(instance);
152 if (rxData) {
153 *(rxData++) = b;
157 return true;
160 void spiInternalInitStream(const extDevice_t *dev, bool preInit)
162 static uint8_t dummyTxByte = 0xff;
163 static uint8_t dummyRxByte;
164 busDevice_t *bus = dev->bus;
166 volatile busSegment_t *segment = bus->curSegment;
168 if (preInit) {
169 // Prepare the init structure for the next segment to reduce inter-segment interval
170 segment++;
171 if(segment->len == 0) {
172 // There's no following segment
173 return;
177 uint8_t *txData = segment->txData;
178 uint8_t *rxData = segment->rxData;
179 int len = segment->len;
181 DMA_InitTypeDef *initTx = bus->initTx;
182 DMA_InitTypeDef *initRx = bus->initRx;
184 if (txData) {
185 initTx->DMA_Memory0BaseAddr = (uint32_t)txData;
186 initTx->DMA_MemoryInc = DMA_MemoryInc_Enable;
187 } else {
188 dummyTxByte = 0xff;
189 initTx->DMA_Memory0BaseAddr = (uint32_t)&dummyTxByte;
190 initTx->DMA_MemoryInc = DMA_MemoryInc_Disable;
192 initTx->DMA_BufferSize = len;
194 if (rxData) {
195 initRx->DMA_Memory0BaseAddr = (uint32_t)rxData;
196 initRx->DMA_MemoryInc = DMA_MemoryInc_Enable;
197 } else {
198 initRx->DMA_Memory0BaseAddr = (uint32_t)&dummyRxByte;
199 initRx->DMA_MemoryInc = DMA_MemoryInc_Disable;
201 // If possible use 16 bit memory writes to prevent atomic access issues on gyro data
202 if ((initRx->DMA_Memory0BaseAddr & 0x1) || (len & 0x1))
204 initRx->DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
205 } else {
206 initRx->DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
208 initRx->DMA_BufferSize = len;
211 void spiInternalStartDMA(const extDevice_t *dev)
213 // Assert Chip Select
214 IOLo(dev->busType_u.spi.csnPin);
216 dmaChannelDescriptor_t *dmaTx = dev->bus->dmaTx;
217 dmaChannelDescriptor_t *dmaRx = dev->bus->dmaRx;
218 DMA_Stream_TypeDef *streamRegsTx = (DMA_Stream_TypeDef *)dmaTx->ref;
219 DMA_Stream_TypeDef *streamRegsRx = (DMA_Stream_TypeDef *)dmaRx->ref;
221 // Use the correct callback argument
222 dmaRx->userParam = (uint32_t)dev;
224 // Clear transfer flags
225 DMA_CLEAR_FLAG(dmaTx, DMA_IT_HTIF | DMA_IT_TEIF | DMA_IT_TCIF);
226 DMA_CLEAR_FLAG(dmaRx, DMA_IT_HTIF | DMA_IT_TEIF | DMA_IT_TCIF);
228 // Disable streams to enable update
229 streamRegsTx->CR = 0U;
230 streamRegsRx->CR = 0U;
232 /* Use the Rx interrupt as this occurs once the SPI operation is complete whereas the Tx interrupt
233 * occurs earlier when the Tx FIFO is empty, but the SPI operation is still in progress
235 DMA_ITConfig(streamRegsRx, DMA_IT_TC, ENABLE);
237 // Update streams
238 DMA_Init(streamRegsTx, dev->bus->initTx);
239 DMA_Init(streamRegsRx, dev->bus->initRx);
241 /* Note from AN4031
243 * If the user enables the used peripheral before the corresponding DMA stream, a “FEIF”
244 * (FIFO Error Interrupt Flag) may be set due to the fact the DMA is not ready to provide
245 * the first required data to the peripheral (in case of memory-to-peripheral transfer).
248 // Enable streams
249 DMA_Cmd(streamRegsTx, ENABLE);
250 DMA_Cmd(streamRegsRx, ENABLE);
252 /* Enable the SPI DMA Tx & Rx requests */
253 SPI_I2S_DMACmd(dev->bus->busType_u.spi.instance, SPI_I2S_DMAReq_Tx | SPI_I2S_DMAReq_Rx, ENABLE);
257 void spiInternalStopDMA (const extDevice_t *dev)
259 dmaChannelDescriptor_t *dmaTx = dev->bus->dmaTx;
260 dmaChannelDescriptor_t *dmaRx = dev->bus->dmaRx;
261 DMA_Stream_TypeDef *streamRegsTx = (DMA_Stream_TypeDef *)dmaTx->ref;
262 DMA_Stream_TypeDef *streamRegsRx = (DMA_Stream_TypeDef *)dmaRx->ref;
263 SPI_TypeDef *instance = dev->bus->busType_u.spi.instance;
265 // Disable streams
266 streamRegsTx->CR = 0U;
267 streamRegsRx->CR = 0U;
269 SPI_I2S_DMACmd(instance, SPI_I2S_DMAReq_Tx | SPI_I2S_DMAReq_Rx, DISABLE);
272 // DMA transfer setup and start
273 void spiSequence(const extDevice_t *dev, busSegment_t *segments)
275 busDevice_t *bus = dev->bus;
276 SPI_TypeDef *instance = bus->busType_u.spi.instance;
277 bool dmaSafe = dev->useDMA;
278 uint32_t xferLen = 0;
279 uint32_t segmentCount = 0;
281 dev->bus->initSegment = true;
282 dev->bus->curSegment = segments;
284 SPI_Cmd(instance, DISABLE);
286 // Switch bus speed
287 if (dev->busType_u.spi.speed != bus->busType_u.spi.speed) {
288 spiSetDivisorBRreg(bus->busType_u.spi.instance, dev->busType_u.spi.speed);
289 bus->busType_u.spi.speed = dev->busType_u.spi.speed;
292 if (dev->busType_u.spi.leadingEdge != bus->busType_u.spi.leadingEdge) {
293 // Switch SPI clock polarity/phase
294 instance->CR1 &= ~(SPI_CPOL_High | SPI_CPHA_2Edge);
296 // Apply setting
297 if (dev->busType_u.spi.leadingEdge) {
298 instance->CR1 |= SPI_CPOL_Low | SPI_CPHA_1Edge;
299 } else
301 instance->CR1 |= SPI_CPOL_High | SPI_CPHA_2Edge;
303 bus->busType_u.spi.leadingEdge = dev->busType_u.spi.leadingEdge;
306 SPI_Cmd(instance, ENABLE);
308 // Check that any there are no attempts to DMA to/from CCD SRAM
309 for (busSegment_t *checkSegment = bus->curSegment; checkSegment->len; checkSegment++) {
310 if (((checkSegment->rxData) && IS_CCM(checkSegment->rxData)) ||
311 ((checkSegment->txData) && IS_CCM(checkSegment->txData))) {
312 dmaSafe = false;
313 break;
315 // Note that these counts are only valid if dmaSafe is true
316 segmentCount++;
317 xferLen += checkSegment->len;
319 // Use DMA if possible
320 if (bus->useDMA && dmaSafe && ((segmentCount > 1) || (xferLen > 8))) {
321 // Intialise the init structures for the first transfer
322 spiInternalInitStream(dev, false);
324 // Start the transfers
325 spiInternalStartDMA(dev);
326 } else {
327 // Manually work through the segment list performing a transfer for each
328 while (bus->curSegment->len) {
329 // Assert Chip Select
330 IOLo(dev->busType_u.spi.csnPin);
332 spiInternalReadWriteBufPolled(
333 bus->busType_u.spi.instance,
334 bus->curSegment->txData,
335 bus->curSegment->rxData,
336 bus->curSegment->len);
338 if (bus->curSegment->negateCS) {
339 // Negate Chip Select
340 IOHi(dev->busType_u.spi.csnPin);
343 if (bus->curSegment->callback) {
344 switch(bus->curSegment->callback(dev->callbackArg)) {
345 case BUS_BUSY:
346 // Repeat the last DMA segment
347 bus->curSegment--;
348 break;
350 case BUS_ABORT:
351 bus->curSegment = (busSegment_t *)NULL;
352 return;
354 case BUS_READY:
355 default:
356 // Advance to the next DMA segment
357 break;
360 bus->curSegment++;
363 bus->curSegment = (busSegment_t *)NULL;
366 #endif