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 // Assert Chip Select
219 IOLo(dev
->busType_u
.spi
.csnPin
);
221 dmaChannelDescriptor_t
*dmaTx
= dev
->bus
->dmaTx
;
222 dmaChannelDescriptor_t
*dmaRx
= dev
->bus
->dmaRx
;
223 DMA_Stream_TypeDef
*streamRegsTx
= (DMA_Stream_TypeDef
*)dmaTx
->ref
;
225 DMA_Stream_TypeDef
*streamRegsRx
= (DMA_Stream_TypeDef
*)dmaRx
->ref
;
227 // Use the correct callback argument
228 dmaRx
->userParam
= (uint32_t)dev
;
230 // Clear transfer flags
231 DMA_CLEAR_FLAG(dmaTx
, DMA_IT_HTIF
| DMA_IT_TEIF
| DMA_IT_TCIF
);
232 DMA_CLEAR_FLAG(dmaRx
, DMA_IT_HTIF
| DMA_IT_TEIF
| DMA_IT_TCIF
);
234 // Disable streams to enable update
235 streamRegsTx
->CR
= 0U;
236 streamRegsRx
->CR
= 0U;
238 /* Use the Rx interrupt as this occurs once the SPI operation is complete whereas the Tx interrupt
239 * occurs earlier when the Tx FIFO is empty, but the SPI operation is still in progress
241 DMA_ITConfig(streamRegsRx
, DMA_IT_TC
, ENABLE
);
244 DMA_Init(streamRegsTx
, dev
->bus
->initTx
);
245 DMA_Init(streamRegsRx
, dev
->bus
->initRx
);
249 * If the user enables the used peripheral before the corresponding DMA stream, a “FEIF”
250 * (FIFO Error Interrupt Flag) may be set due to the fact the DMA is not ready to provide
251 * the first required data to the peripheral (in case of memory-to-peripheral transfer).
255 DMA_Cmd(streamRegsTx
, ENABLE
);
256 DMA_Cmd(streamRegsRx
, ENABLE
);
258 /* Enable the SPI DMA Tx & Rx requests */
259 SPI_I2S_DMACmd(dev
->bus
->busType_u
.spi
.instance
, SPI_I2S_DMAReq_Tx
| SPI_I2S_DMAReq_Rx
, ENABLE
);
261 // Use the correct callback argument
262 dmaTx
->userParam
= (uint32_t)dev
;
264 // Clear transfer flags
265 DMA_CLEAR_FLAG(dmaTx
, DMA_IT_HTIF
| DMA_IT_TEIF
| DMA_IT_TCIF
);
267 // Disable stream to enable update
268 streamRegsTx
->CR
= 0U;
270 DMA_ITConfig(streamRegsTx
, DMA_IT_TC
, ENABLE
);
273 DMA_Init(streamRegsTx
, dev
->bus
->initTx
);
277 * If the user enables the used peripheral before the corresponding DMA stream, a “FEIF”
278 * (FIFO Error Interrupt Flag) may be set due to the fact the DMA is not ready to provide
279 * the first required data to the peripheral (in case of memory-to-peripheral transfer).
283 DMA_Cmd(streamRegsTx
, ENABLE
);
285 /* Enable the SPI DMA Tx request */
286 SPI_I2S_DMACmd(dev
->bus
->busType_u
.spi
.instance
, SPI_I2S_DMAReq_Tx
, ENABLE
);
291 void spiInternalStopDMA (const extDevice_t
*dev
)
293 dmaChannelDescriptor_t
*dmaTx
= dev
->bus
->dmaTx
;
294 dmaChannelDescriptor_t
*dmaRx
= dev
->bus
->dmaRx
;
295 SPI_TypeDef
*instance
= dev
->bus
->busType_u
.spi
.instance
;
296 DMA_Stream_TypeDef
*streamRegsTx
= (DMA_Stream_TypeDef
*)dmaTx
->ref
;
299 DMA_Stream_TypeDef
*streamRegsRx
= (DMA_Stream_TypeDef
*)dmaRx
->ref
;
302 streamRegsTx
->CR
= 0U;
303 streamRegsRx
->CR
= 0U;
305 SPI_I2S_DMACmd(instance
, SPI_I2S_DMAReq_Tx
| SPI_I2S_DMAReq_Rx
, DISABLE
);
307 // Ensure the current transmission is complete
308 while (SPI_I2S_GetFlagStatus(instance
, SPI_I2S_FLAG_BSY
));
310 // Drain the RX buffer
311 while (SPI_I2S_GetFlagStatus(instance
, SPI_I2S_FLAG_RXNE
)) {
316 streamRegsTx
->CR
= 0U;
318 SPI_I2S_DMACmd(instance
, SPI_I2S_DMAReq_Tx
, DISABLE
);
322 // DMA transfer setup and start
323 void spiSequenceStart(const extDevice_t
*dev
)
325 busDevice_t
*bus
= dev
->bus
;
326 SPI_TypeDef
*instance
= bus
->busType_u
.spi
.instance
;
327 bool dmaSafe
= dev
->useDMA
;
328 uint32_t xferLen
= 0;
329 uint32_t segmentCount
= 0;
331 dev
->bus
->initSegment
= true;
333 SPI_Cmd(instance
, DISABLE
);
336 if (dev
->busType_u
.spi
.speed
!= bus
->busType_u
.spi
.speed
) {
337 spiSetDivisorBRreg(bus
->busType_u
.spi
.instance
, dev
->busType_u
.spi
.speed
);
338 bus
->busType_u
.spi
.speed
= dev
->busType_u
.spi
.speed
;
341 if (dev
->busType_u
.spi
.leadingEdge
!= bus
->busType_u
.spi
.leadingEdge
) {
342 // Switch SPI clock polarity/phase
343 instance
->CR1
&= ~(SPI_CPOL_High
| SPI_CPHA_2Edge
);
346 if (dev
->busType_u
.spi
.leadingEdge
) {
347 instance
->CR1
|= SPI_CPOL_Low
| SPI_CPHA_1Edge
;
350 instance
->CR1
|= SPI_CPOL_High
| SPI_CPHA_2Edge
;
352 bus
->busType_u
.spi
.leadingEdge
= dev
->busType_u
.spi
.leadingEdge
;
355 SPI_Cmd(instance
, ENABLE
);
357 // Check that any there are no attempts to DMA to/from CCD SRAM
358 for (busSegment_t
*checkSegment
= bus
->curSegment
; checkSegment
->len
; checkSegment
++) {
359 // Check there is no receive data as only transmit DMA is available
360 if (((checkSegment
->u
.buffers
.rxData
) && (IS_CCM(checkSegment
->u
.buffers
.rxData
) || (bus
->dmaRx
== (dmaChannelDescriptor_t
*)NULL
))) ||
361 ((checkSegment
->u
.buffers
.txData
) && IS_CCM(checkSegment
->u
.buffers
.txData
))) {
365 // Note that these counts are only valid if dmaSafe is true
367 xferLen
+= checkSegment
->len
;
369 // Use DMA if possible
370 if (bus
->useDMA
&& dmaSafe
&& ((segmentCount
> 1) || (xferLen
> 8))) {
371 // Intialise the init structures for the first transfer
372 spiInternalInitStream(dev
, false);
374 // Start the transfers
375 spiInternalStartDMA(dev
);
377 // Manually work through the segment list performing a transfer for each
378 while (bus
->curSegment
->len
) {
379 // Assert Chip Select
380 IOLo(dev
->busType_u
.spi
.csnPin
);
382 spiInternalReadWriteBufPolled(
383 bus
->busType_u
.spi
.instance
,
384 bus
->curSegment
->u
.buffers
.txData
,
385 bus
->curSegment
->u
.buffers
.rxData
,
386 bus
->curSegment
->len
);
388 if (bus
->curSegment
->negateCS
) {
389 // Negate Chip Select
390 IOHi(dev
->busType_u
.spi
.csnPin
);
393 if (bus
->curSegment
->callback
) {
394 switch(bus
->curSegment
->callback(dev
->callbackArg
)) {
396 // Repeat the last DMA segment
401 bus
->curSegment
= (busSegment_t
*)BUS_SPI_FREE
;
406 // Advance to the next DMA segment
413 // If a following transaction has been linked, start it
414 if (bus
->curSegment
->u
.link
.dev
) {
415 const extDevice_t
*nextDev
= bus
->curSegment
->u
.link
.dev
;
416 busSegment_t
*nextSegments
= bus
->curSegment
->u
.link
.segments
;
417 busSegment_t
*endSegment
= bus
->curSegment
;
418 bus
->curSegment
= nextSegments
;
419 endSegment
->u
.link
.dev
= NULL
;
420 spiSequenceStart(nextDev
);
422 // The end of the segment list has been reached, so mark transactions as complete
423 bus
->curSegment
= (busSegment_t
*)BUS_SPI_FREE
;