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
;
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
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
)
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
);
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
;
169 // Prepare the init structure for the next segment to reduce inter-segment interval
171 if(segment
->len
== 0) {
172 // There's no following segment
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
;
185 initTx
->DMA_Memory0BaseAddr
= (uint32_t)txData
;
186 initTx
->DMA_MemoryInc
= DMA_MemoryInc_Enable
;
189 initTx
->DMA_Memory0BaseAddr
= (uint32_t)&dummyTxByte
;
190 initTx
->DMA_MemoryInc
= DMA_MemoryInc_Disable
;
192 initTx
->DMA_BufferSize
= len
;
195 initRx
->DMA_Memory0BaseAddr
= (uint32_t)rxData
;
196 initRx
->DMA_MemoryInc
= DMA_MemoryInc_Enable
;
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
;
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
);
238 DMA_Init(streamRegsTx
, dev
->bus
->initTx
);
239 DMA_Init(streamRegsRx
, dev
->bus
->initRx
);
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).
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
;
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
);
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
);
297 if (dev
->busType_u
.spi
.leadingEdge
) {
298 instance
->CR1
|= SPI_CPOL_Low
| SPI_CPHA_1Edge
;
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
))) {
315 // Note that these counts are only valid if dmaSafe is true
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
);
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
)) {
346 // Repeat the last DMA segment
351 bus
->curSegment
= (busSegment_t
*)NULL
;
356 // Advance to the next DMA segment
363 bus
->curSegment
= (busSegment_t
*)NULL
;