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)
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/>.
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)
58 if (instance
== SPI2
|| instance
== SPI3
) {
59 divisor
/= 2; // Safe for divisor == 0 or 1
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
);
77 void spiInitDevice(SPIDevice device
)
79 spiDevice_t
*spi
= &(spiDevice
[device
]);
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
);
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
;
109 DMA_StructInit(initTx
);
110 initTx
->DMA_Channel
= bus
->dmaTx
->channel
;
111 initTx
->DMA_DIR
= DMA_DIR_MemoryToPeripheral
;
112 initTx
->DMA_Mode
= DMA_Mode_Normal
;
113 initTx
->DMA_PeripheralBaseAddr
= (uint32_t)&bus
->busType_u
.spi
.instance
->DR
;
114 initTx
->DMA_Priority
= DMA_Priority_Low
;
115 initTx
->DMA_PeripheralInc
= DMA_PeripheralInc_Disable
;
116 initTx
->DMA_PeripheralDataSize
= DMA_PeripheralDataSize_Byte
;
117 initTx
->DMA_MemoryDataSize
= DMA_MemoryDataSize_Byte
;
120 DMA_InitTypeDef
*initRx
= bus
->initRx
;
122 DMA_StructInit(initRx
);
123 initRx
->DMA_Channel
= bus
->dmaRx
->channel
;
124 initRx
->DMA_DIR
= DMA_DIR_PeripheralToMemory
;
125 initRx
->DMA_Mode
= DMA_Mode_Normal
;
126 initRx
->DMA_PeripheralBaseAddr
= (uint32_t)&bus
->busType_u
.spi
.instance
->DR
;
127 initRx
->DMA_Priority
= DMA_Priority_Low
;
128 initRx
->DMA_PeripheralInc
= DMA_PeripheralInc_Disable
;
129 initRx
->DMA_PeripheralDataSize
= DMA_PeripheralDataSize_Byte
;
133 void spiInternalResetStream(dmaChannelDescriptor_t
*descriptor
)
135 DMA_Stream_TypeDef
*streamRegs
= (DMA_Stream_TypeDef
*)descriptor
->ref
;
137 // Disable the stream
140 // Clear any pending interrupt flags
141 DMA_CLEAR_FLAG(descriptor
, DMA_IT_HTIF
| DMA_IT_TEIF
| DMA_IT_TCIF
);
144 static bool spiInternalReadWriteBufPolled(SPI_TypeDef
*instance
, const uint8_t *txData
, uint8_t *rxData
, int len
)
149 b
= txData
? *(txData
++) : 0xFF;
150 while (SPI_I2S_GetFlagStatus(instance
, SPI_I2S_FLAG_TXE
) == RESET
);
151 SPI_I2S_SendData(instance
, b
);
153 while (SPI_I2S_GetFlagStatus(instance
, SPI_I2S_FLAG_RXNE
) == RESET
);
154 b
= SPI_I2S_ReceiveData(instance
);
163 void spiInternalInitStream(const extDevice_t
*dev
, bool preInit
)
165 STATIC_DMA_DATA_AUTO
uint8_t dummyTxByte
= 0xff;
166 STATIC_DMA_DATA_AUTO
uint8_t dummyRxByte
;
167 busDevice_t
*bus
= dev
->bus
;
169 volatile busSegment_t
*segment
= bus
->curSegment
;
172 // Prepare the init structure for the next segment to reduce inter-segment interval
174 if(segment
->len
== 0) {
175 // There's no following segment
180 int len
= segment
->len
;
182 uint8_t *txData
= segment
->u
.buffers
.txData
;
183 DMA_InitTypeDef
*initTx
= bus
->initTx
;
186 initTx
->DMA_Memory0BaseAddr
= (uint32_t)txData
;
187 initTx
->DMA_MemoryInc
= DMA_MemoryInc_Enable
;
190 initTx
->DMA_Memory0BaseAddr
= (uint32_t)&dummyTxByte
;
191 initTx
->DMA_MemoryInc
= DMA_MemoryInc_Disable
;
193 initTx
->DMA_BufferSize
= len
;
195 if (dev
->bus
->dmaRx
) {
196 uint8_t *rxData
= segment
->u
.buffers
.rxData
;
197 DMA_InitTypeDef
*initRx
= bus
->initRx
;
200 initRx
->DMA_Memory0BaseAddr
= (uint32_t)rxData
;
201 initRx
->DMA_MemoryInc
= DMA_MemoryInc_Enable
;
203 initRx
->DMA_Memory0BaseAddr
= (uint32_t)&dummyRxByte
;
204 initRx
->DMA_MemoryInc
= DMA_MemoryInc_Disable
;
206 // If possible use 16 bit memory writes to prevent atomic access issues on gyro data
207 if ((initRx
->DMA_Memory0BaseAddr
& 0x1) || (len
& 0x1)) {
208 initRx
->DMA_MemoryDataSize
= DMA_MemoryDataSize_Byte
;
210 initRx
->DMA_MemoryDataSize
= DMA_MemoryDataSize_HalfWord
;
212 initRx
->DMA_BufferSize
= len
;
216 void spiInternalStartDMA(const extDevice_t
*dev
)
218 dmaChannelDescriptor_t
*dmaTx
= dev
->bus
->dmaTx
;
219 dmaChannelDescriptor_t
*dmaRx
= dev
->bus
->dmaRx
;
220 DMA_Stream_TypeDef
*streamRegsTx
= (DMA_Stream_TypeDef
*)dmaTx
->ref
;
222 DMA_Stream_TypeDef
*streamRegsRx
= (DMA_Stream_TypeDef
*)dmaRx
->ref
;
224 // Use the correct callback argument
225 dmaRx
->userParam
= (uint32_t)dev
;
227 // Clear transfer flags
228 DMA_CLEAR_FLAG(dmaTx
, DMA_IT_HTIF
| DMA_IT_TEIF
| DMA_IT_TCIF
);
229 DMA_CLEAR_FLAG(dmaRx
, DMA_IT_HTIF
| DMA_IT_TEIF
| DMA_IT_TCIF
);
231 // Disable streams to enable update
232 streamRegsTx
->CR
= 0U;
233 streamRegsRx
->CR
= 0U;
235 /* Use the Rx interrupt as this occurs once the SPI operation is complete whereas the Tx interrupt
236 * occurs earlier when the Tx FIFO is empty, but the SPI operation is still in progress
238 DMA_ITConfig(streamRegsRx
, DMA_IT_TC
, ENABLE
);
241 DMA_Init(streamRegsTx
, dev
->bus
->initTx
);
242 DMA_Init(streamRegsRx
, dev
->bus
->initRx
);
246 * If the user enables the used peripheral before the corresponding DMA stream, a “FEIF”
247 * (FIFO Error Interrupt Flag) may be set due to the fact the DMA is not ready to provide
248 * the first required data to the peripheral (in case of memory-to-peripheral transfer).
252 DMA_Cmd(streamRegsTx
, ENABLE
);
253 DMA_Cmd(streamRegsRx
, ENABLE
);
255 /* Enable the SPI DMA Tx & Rx requests */
256 SPI_I2S_DMACmd(dev
->bus
->busType_u
.spi
.instance
, SPI_I2S_DMAReq_Tx
| SPI_I2S_DMAReq_Rx
, ENABLE
);
258 // Use the correct callback argument
259 dmaTx
->userParam
= (uint32_t)dev
;
261 // Clear transfer flags
262 DMA_CLEAR_FLAG(dmaTx
, DMA_IT_HTIF
| DMA_IT_TEIF
| DMA_IT_TCIF
);
264 // Disable stream to enable update
265 streamRegsTx
->CR
= 0U;
267 DMA_ITConfig(streamRegsTx
, DMA_IT_TC
, ENABLE
);
270 DMA_Init(streamRegsTx
, dev
->bus
->initTx
);
274 * If the user enables the used peripheral before the corresponding DMA stream, a “FEIF”
275 * (FIFO Error Interrupt Flag) may be set due to the fact the DMA is not ready to provide
276 * the first required data to the peripheral (in case of memory-to-peripheral transfer).
280 DMA_Cmd(streamRegsTx
, ENABLE
);
282 /* Enable the SPI DMA Tx request */
283 SPI_I2S_DMACmd(dev
->bus
->busType_u
.spi
.instance
, SPI_I2S_DMAReq_Tx
, ENABLE
);
288 void spiInternalStopDMA (const extDevice_t
*dev
)
290 dmaChannelDescriptor_t
*dmaTx
= dev
->bus
->dmaTx
;
291 dmaChannelDescriptor_t
*dmaRx
= dev
->bus
->dmaRx
;
292 SPI_TypeDef
*instance
= dev
->bus
->busType_u
.spi
.instance
;
293 DMA_Stream_TypeDef
*streamRegsTx
= (DMA_Stream_TypeDef
*)dmaTx
->ref
;
296 DMA_Stream_TypeDef
*streamRegsRx
= (DMA_Stream_TypeDef
*)dmaRx
->ref
;
299 streamRegsTx
->CR
= 0U;
300 streamRegsRx
->CR
= 0U;
302 SPI_I2S_DMACmd(instance
, SPI_I2S_DMAReq_Tx
| SPI_I2S_DMAReq_Rx
, DISABLE
);
304 // Ensure the current transmission is complete
305 while (SPI_I2S_GetFlagStatus(instance
, SPI_I2S_FLAG_BSY
));
307 // Drain the RX buffer
308 while (SPI_I2S_GetFlagStatus(instance
, SPI_I2S_FLAG_RXNE
)) {
313 streamRegsTx
->CR
= 0U;
315 SPI_I2S_DMACmd(instance
, SPI_I2S_DMAReq_Tx
, DISABLE
);
319 // DMA transfer setup and start
320 void spiSequenceStart(const extDevice_t
*dev
)
322 busDevice_t
*bus
= dev
->bus
;
323 SPI_TypeDef
*instance
= bus
->busType_u
.spi
.instance
;
324 bool dmaSafe
= dev
->useDMA
;
325 uint32_t xferLen
= 0;
326 uint32_t segmentCount
= 0;
328 dev
->bus
->initSegment
= true;
330 SPI_Cmd(instance
, DISABLE
);
333 if (dev
->busType_u
.spi
.speed
!= bus
->busType_u
.spi
.speed
) {
334 spiSetDivisorBRreg(bus
->busType_u
.spi
.instance
, dev
->busType_u
.spi
.speed
);
335 bus
->busType_u
.spi
.speed
= dev
->busType_u
.spi
.speed
;
338 if (dev
->busType_u
.spi
.leadingEdge
!= bus
->busType_u
.spi
.leadingEdge
) {
339 // Switch SPI clock polarity/phase
340 instance
->CR1
&= ~(SPI_CPOL_High
| SPI_CPHA_2Edge
);
343 if (dev
->busType_u
.spi
.leadingEdge
) {
344 instance
->CR1
|= SPI_CPOL_Low
| SPI_CPHA_1Edge
;
347 instance
->CR1
|= SPI_CPOL_High
| SPI_CPHA_2Edge
;
349 bus
->busType_u
.spi
.leadingEdge
= dev
->busType_u
.spi
.leadingEdge
;
352 SPI_Cmd(instance
, ENABLE
);
354 // Check that any there are no attempts to DMA to/from CCD SRAM
355 for (busSegment_t
*checkSegment
= (busSegment_t
*)bus
->curSegment
; checkSegment
->len
; checkSegment
++) {
356 // Check there is no receive data as only transmit DMA is available
357 if (((checkSegment
->u
.buffers
.rxData
) && (IS_CCM(checkSegment
->u
.buffers
.rxData
) || (bus
->dmaRx
== (dmaChannelDescriptor_t
*)NULL
))) ||
358 ((checkSegment
->u
.buffers
.txData
) && IS_CCM(checkSegment
->u
.buffers
.txData
))) {
362 // Note that these counts are only valid if dmaSafe is true
364 xferLen
+= checkSegment
->len
;
366 // Use DMA if possible
367 // If there are more than one segments, or a single segment with negateCS negated then force DMA irrespective of length
368 if (bus
->useDMA
&& dmaSafe
&& ((segmentCount
> 1) || (xferLen
>= 8) || !bus
->curSegment
->negateCS
)) {
369 // Intialise the init structures for the first transfer
370 spiInternalInitStream(dev
, false);
372 // Assert Chip Select
373 IOLo(dev
->busType_u
.spi
.csnPin
);
375 // Start the transfers
376 spiInternalStartDMA(dev
);
378 busSegment_t
*lastSegment
= NULL
;
380 // Manually work through the segment list performing a transfer for each
381 while (bus
->curSegment
->len
) {
382 if (!lastSegment
|| lastSegment
->negateCS
) {
383 // Assert Chip Select if necessary - it's costly so only do so if necessary
384 IOLo(dev
->busType_u
.spi
.csnPin
);
387 spiInternalReadWriteBufPolled(
388 bus
->busType_u
.spi
.instance
,
389 bus
->curSegment
->u
.buffers
.txData
,
390 bus
->curSegment
->u
.buffers
.rxData
,
391 bus
->curSegment
->len
);
393 if (bus
->curSegment
->negateCS
) {
394 // Negate Chip Select
395 IOHi(dev
->busType_u
.spi
.csnPin
);
398 if (bus
->curSegment
->callback
) {
399 switch(bus
->curSegment
->callback(dev
->callbackArg
)) {
401 // Repeat the last DMA segment
406 bus
->curSegment
= (busSegment_t
*)BUS_SPI_FREE
;
411 // Advance to the next DMA segment
415 lastSegment
= (busSegment_t
*)bus
->curSegment
;
419 if (lastSegment
&& !lastSegment
->negateCS
) {
420 // Negate Chip Select if not done so already
421 IOHi(dev
->busType_u
.spi
.csnPin
);
424 // If a following transaction has been linked, start it
425 if (bus
->curSegment
->u
.link
.dev
) {
426 const extDevice_t
*nextDev
= bus
->curSegment
->u
.link
.dev
;
427 busSegment_t
*nextSegments
= (busSegment_t
*)bus
->curSegment
->u
.link
.segments
;
428 busSegment_t
*endSegment
= (busSegment_t
*)bus
->curSegment
;
429 bus
->curSegment
= nextSegments
;
430 endSegment
->u
.link
.dev
= NULL
;
431 spiSequenceStart(nextDev
);
433 // The end of the segment list has been reached, so mark transactions as complete
434 bus
->curSegment
= (busSegment_t
*)BUS_SPI_FREE
;