... | ... | @@ -77,4 +77,113 @@ A DMA access is requested when the enble bit in the SPI_CR2 register is enabled. |
|
|
- In transmission a DMA request is issued **each time TXE is set to 1**. The DMA then writes to the SPI_DR register (this clears TXE).
|
|
|
- In reception a DMA request is issued **each time RXNE is set to 1**. The DMA then reads the SPI_DR register (this clears RXNE).
|
|
|
|
|
|
The BSY flag can be monitored to ensure that the SPI communication is complete. |
|
|
\ No newline at end of file |
|
|
The BSY flag can be monitored to ensure that the SPI communication is complete.
|
|
|
|
|
|
## Example
|
|
|
|
|
|
The following code is part of a complete example that can be found [here](https://git.skywarder.eu/alberto.nidasio/skyward-dma-examples/-/blob/master/src/entrypoints/spi-ads1118.cpp).
|
|
|
|
|
|
The use of SPI with DMA requires 3 main steps:
|
|
|
- Configuring the SPI controller
|
|
|
- Configuring the DMA streams for transmission and reception
|
|
|
- Start the data transfer
|
|
|
|
|
|
Configuring the SPI controller has nothing special except enabling the use of DMA.
|
|
|
|
|
|
This must be done after the DMA stream configuration because it'll immediately trigger a DMA access, thus you need the stream already configured.
|
|
|
|
|
|
```c++
|
|
|
SPI2->CR2 |= SPI_CR2_TXDMAEN;
|
|
|
SPI2->CR2 |= SPI_CR2_RXDMAEN;
|
|
|
```
|
|
|
|
|
|
### Configuring the DMA streams
|
|
|
|
|
|
A DMA stream configuration consists in 4 registers: CR, PAR, M0AR and NDTR.
|
|
|
|
|
|
Keep in mind that you firsts need to enable the DMA controller clock. For DMA1 this is done with:
|
|
|
```c++
|
|
|
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
|
|
|
```
|
|
|
|
|
|
Here is shown the configuration for the stream that handles the data transmission:
|
|
|
```c++
|
|
|
// 1: Disable the stream before configuration
|
|
|
|
|
|
// Disable stream
|
|
|
DMA1_Stream4->CR &= ~DMA_SxCR_EN;
|
|
|
|
|
|
// Wait for the stream to be disabled
|
|
|
while (DMA1_Stream4->CR & DMA_SxCR_EN)
|
|
|
;
|
|
|
|
|
|
// Reset DMA configuration
|
|
|
DMA1_Stream4->CR = 0;
|
|
|
|
|
|
// 2: Set the peripheral address
|
|
|
DMA1_Stream4->PAR = (uint32_t) & (SPI2->DR);
|
|
|
|
|
|
// 3: Set the memory address
|
|
|
DMA1_Stream4->M0AR = (uint32_t)dataOut;
|
|
|
|
|
|
// 4: Configure the total number of data items
|
|
|
DMA1_Stream4->NDTR = 2;
|
|
|
|
|
|
// 5: Select the DMA channel
|
|
|
DMA1_Stream4->CR &= ~DMA_SxCR_CHSEL; // Channel 0 for SPI2 Tx
|
|
|
|
|
|
// 7: Configure the stream priority to very high
|
|
|
DMA1_Stream4->CR |= DMA_SxCR_PL;
|
|
|
|
|
|
// 9: Other configuration
|
|
|
|
|
|
// Data transfer memory-to-peripheral
|
|
|
DMA1_Stream4->CR |= DMA_SxCR_DIR_0;
|
|
|
|
|
|
// Address increment mode
|
|
|
DMA1_Stream4->CR &= ~DMA_SxCR_PINC; // Fixed
|
|
|
DMA1_Stream4->CR |= DMA_SxCR_MINC;
|
|
|
```
|
|
|
|
|
|
### Data transfer
|
|
|
|
|
|
To start the transfer process you need to enable the DMA streams and then the SPI DMA function. DMA access requests will occur when either the TXE and RXNE flags are asserted. The communication will end when the number of data items specified in the NDTR register have been transferred.
|
|
|
|
|
|
Here is shown how the transfer can occur:
|
|
|
```c++
|
|
|
// First ensure that the DMA streams are properly configured
|
|
|
configureDMA(dataOut, dataIn);
|
|
|
|
|
|
// Enable DMA streams
|
|
|
DMA1_Stream4->CR |= DMA_SxCR_EN;
|
|
|
DMA1_Stream3->CR |= DMA_SxCR_EN;
|
|
|
|
|
|
// Clear SPI RXNE (or wait if there could still be other spi communication ongoing)
|
|
|
SPI2->DR;
|
|
|
|
|
|
// Enable SPI DMA
|
|
|
SPI2->CR2 |= SPI_CR2_TXDMAEN;
|
|
|
SPI2->CR2 |= SPI_CR2_RXDMAEN;
|
|
|
|
|
|
// Select device
|
|
|
csPin.low();
|
|
|
|
|
|
// Wait for transfer to occur
|
|
|
while (SPI2->SR & SPI_SR_BSY)
|
|
|
;
|
|
|
|
|
|
// Deselect device
|
|
|
csPin.high();
|
|
|
|
|
|
// Disable SPI DMA
|
|
|
SPI2->CR2 &= ~SPI_CR2_TXDMAEN;
|
|
|
SPI2->CR2 &= ~SPI_CR2_RXDMAEN;
|
|
|
|
|
|
// The DMA streams are automatically disabled when the NDTR reaches 0
|
|
|
|
|
|
// Ensures all DMA status bits are cleared by explicitly clearing them
|
|
|
DMA1->LIFCR = 0x0F7D0F7D; // Mask excluding reserved bits
|
|
|
DMA1->HIFCR = 0x0F7D0F7D;
|
|
|
```
|
|
|
|
|
|
Note that you could omit configuring the DMA streams at each time you want to transfer data but you must ensure that there is no other code using the streams. In the example we use DMA1 stream 3 for SPI Rx but miosix uses it for serial communication thus the stream configuration is changed each time a printf occurs. |
|
|
\ No newline at end of file |