cancel
Showing results for 
Search instead for 
Did you mean: 

DMA <-> SPI, RX TX for STM32L0

Dude
Associate III

I'm trying to get the SPI communications to a peripheral handled by the DMA. I have reviewed the STM32L0 reference manual, and LL libraries, as well as close examples. I am able to get Tx operation working properly. However, I cannot get Rx to work. The Rx variable always shows as empty.

I have been trying this operation in loopback mode, with an external connection between MISO and MOSI. Whatever I send should end up in the receiver variable location. However, this does not happen. Instead, the Rx data variable never changes.

Can I get some insight here?

while (1)

{

mdsendBuffer[0] = i;

Configure_DMA();

Activate_SPI();

WaitAndCheckEndOfTransfer();

}

void Configure_DMA(void)

{

 /* DMA1 used for SPI1 Transmission

  * DMA1 used for SPI1 Reception

  */

 uint8_t temp = 0;

 /* (1) Enable the clock of DMA1 and DMA1 */

 LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);

 /* (2) Configure NVIC for DMA transfer complete/error interrupts */

 NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0);

 NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);

 NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0); // NVIC_SetPriority(DMA1_Channel3_IRQn, 0);

 NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);

 /* (3) Configure the DMA1_Channel2 functional parameters, the Receive Channel */

 LL_DMA_ConfigTransfer(DMA1, LL_DMA_CHANNEL_2, LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_PRIORITY_HIGH

                   | LL_DMA_MODE_NORMAL        | LL_DMA_PERIPH_NOINCREMENT

 | LL_DMA_MEMORY_INCREMENT      | LL_DMA_PDATAALIGN_BYTE

 | LL_DMA_MDATAALIGN_BYTE);

 LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_2, LL_SPI_DMA_GetRegAddr(SPI1), (uint32_t)&mdrcvBuffer,

                         LL_DMA_DIRECTION_PERIPH_TO_MEMORY);

 LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, sizeof(mdsendBuffer));

 //LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_2, LL_DMA_REQUEST_0); ////////////////

 /* (4) Configure the DMA1_Channel3 functional parameters, the Transmit Channel */

 LL_DMA_ConfigTransfer(DMA1, LL_DMA_CHANNEL_3, LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_PRIORITY_HIGH

                   | LL_DMA_MODE_NORMAL        | LL_DMA_PERIPH_NOINCREMENT

 | LL_DMA_MEMORY_INCREMENT      | LL_DMA_PDATAALIGN_BYTE

 | LL_DMA_MDATAALIGN_BYTE);

 LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_3, (uint32_t)&mdsendBuffer, LL_SPI_DMA_GetRegAddr(SPI1),

                                   LL_DMA_DIRECTION_MEMORY_TO_PERIPH);

 LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, sizeof(mdsendBuffer));

 //LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_3, LL_DMA_REQUEST_0); /////////////////

 /* (5) Enable DMA interrupts complete/error */

 LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2);

 LL_DMA_EnableIT_TC (DMA1, LL_DMA_CHANNEL_2);

 LL_DMA_EnableIT_TE (DMA1, LL_DMA_CHANNEL_2);

 LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_3);

 LL_DMA_EnableIT_TC (DMA1, LL_DMA_CHANNEL_3);

 LL_DMA_EnableIT_TE (DMA1, LL_DMA_CHANNEL_3);

}

void Activate_SPI(void)

{

 /* Enable SPI1 */

 CS_MSA_ON;

 LL_SPI_Enable(SPI1);

 NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0);

 NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);

 NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0);

 NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);

 /* Enable DMA Channels */

 LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);

 LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_3);

}

static void MX_SPI1_Init(void)

{

 /* USER CODE BEGIN SPI1_Init 0 */

 /* USER CODE END SPI1_Init 0 */

 LL_SPI_InitTypeDef SPI_InitStruct = {0};

 LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

 /* Peripheral clock enable */

 LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);

 LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);

 /**SPI1 GPIO Configuration

 PA5  ------> SPI1_SCK

 PA6  ------> SPI1_MISO

 PA7  ------> SPI1_MOSI

 */

 GPIO_InitStruct.Pin = LL_GPIO_PIN_5;

 GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;

 GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;

 GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;

 GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;

 GPIO_InitStruct.Alternate = LL_GPIO_AF_0;

 LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

 GPIO_InitStruct.Pin = LL_GPIO_PIN_6;

 GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;

 GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;

 GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;

 GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;

 GPIO_InitStruct.Alternate = LL_GPIO_AF_0;

 LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

 GPIO_InitStruct.Pin = LL_GPIO_PIN_7;

 GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;

 GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;

 GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;

 GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;

 GPIO_InitStruct.Alternate = LL_GPIO_AF_0;

 LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

 /* SPI1 DMA Init */

 /* SPI1_TX Init */

 LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_3, LL_DMA_REQUEST_1);

 LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);

 LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PRIORITY_LOW);

 LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_NORMAL);

 LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PERIPH_NOINCREMENT);

 LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MEMORY_INCREMENT);

 LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PDATAALIGN_BYTE);

 LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MDATAALIGN_BYTE);

 /* USER CODE BEGIN SPI1_Init 1 */

 /* USER CODE END SPI1_Init 1 */

 /* SPI1 parameter configuration*/

 SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;

 SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;

 SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;

 SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;

 SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;

 SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;

 SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2;

 SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;

 SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;

 SPI_InitStruct.CRCPoly = 7;

 LL_SPI_Init(SPI1, &SPI_InitStruct);

 LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);

 /* USER CODE BEGIN SPI1_Init 2 */

   GPIO_InitStruct.Mode = LL_GPIO_MODE_INPUT;

LL_SPI_Enable(SPI1);

LL_SPI_EnableDMAReq_RX(SPI1);

/* Enable DMA TX Interrupt */

LL_SPI_EnableDMAReq_TX(SPI1);

 /* USER CODE END SPI1_Init 2 */

}

1 ACCEPTED SOLUTION

Accepted Solutions
Dude
Associate III

Discovered the problem: TIM2_UP conflicting with SPI1_RX in DMA Channel 2. Didn't notice the conflict as it extended over to the backside of the printout.

View solution in original post

7 REPLIES 7

Polled SPI Rx works?

Read out and check/post content of SPI and DMA registers.

JW

Dude
Associate III

DMA1 -> ISR register

0693W00000WJ7Z1QAL.pngDMA1 -> IFCR register

0693W00000WJ7eEQAT.png 

DMA1 -> CCR2 Register

0693W00000WJ7eJQAT.png 

DMA1 -> CNDTR2 Register

0693W00000WJ7eYQAT.pngDMA1 -> CPAR2

0693W00000WJ7edQAD.png 

DMA1 -> CMAR2

0693W00000WJ7eiQAD.png 

DMA1 -> CCR3

0693W00000WJ7enQAD.png 

DMA1 -> CNDTR30693W00000WJ7fHQAT.png 

DMA1 -> CMAR3

0693W00000WJ7fMQAT.png

Dude
Associate III

SPI Registers

SPI1 -> CR10693W00000WJ7fRQAT.png 

SPI1 -> CR2

0693W00000WJ7fWQAT.png 

SPI1 -> SR

0693W00000WJ7flQAD.png 

SPI1 -> DR

0693W00000WJ7fqQAD.png 

SPI1 -> CRCPR

0693W00000WJ7g0QAD.png 

SPI1 -> RXCRCR

0693W00000WJ7gAQAT.png 

SPI1 -> TXCRCR

0693W00000WJ7gKQAT.png 

SPI1 -> I2SCFGR

0693W00000WJ7gUQAT.png 

SPI1 -> I2SPR

0693W00000WJ7gjQAD.png

Dude
Associate III

Noticed Behavior:

  1. Polling the SPI peripheral works (no DMA)
  2. The Tx transmission transfer complete interrupt triggers.
  3. The Rx receive complete interrupt never triggers.
  4. Manually polling the SPI_DR register returns the correct information (or so it appears)

So in short

  1. The DMA is sending
  2. The SPI is receiving
  3. The DMA doesn't seem to be handling receiving from the SPI properly.
Dude
Associate III

Discovered the problem: TIM2_UP conflicting with SPI1_RX in DMA Channel 2. Didn't notice the conflict as it extended over to the backside of the printout.

Thanks for coming back with the solution. Please select your post as Best so that thread is marked as solved.

JW

Dude
Associate III

Thanks JW.

I'm noticing that the SPI CS line stays low for about 72uS, when the communication only takes about 4uS.... The CS is managed in software. Right before setting the DMA to roll, I change the CS. Then, in the DMA RX full interrupt, I change the CS back.

Why does it take as long as it does? How can I make that time shorter? I would like to be able to do communications more often, if possible.