How to setup a SPI + DMA by the use of the LL lib for the STM32MP151C?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2020-04-19 9:31 PM
Hi All.​
I develop M4 Core firmware using a custom board based on STM32MP151C.
There is no problem with SPI communication without using DMA. However, when DMA is used, waveforms do not occur the pins or only the NSS pins react, resulting in waveforms.
I want to know if I set the DMA wrong.
Below is the corresponding code.
​
​
__IO uint16_t __TEST_BUFFER[3];
​
void
MX_DMA_Init (void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA2_CLK_ENABLE();
__HAL_RCC_DMAMUX_CLK_ENABLE();
​
/* DMA2_Stream1_IRQn interrupt configuration */
HAL_NVIC_SetPriority (DMA2_Stream1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ (DMA2_Stream1_IRQn);
​
return;
}
​
void
MX_SPI2_Init ()
{
LL_SPI_InitTypeDef SPI_InitStruct =
{ 0 };
​
LL_GPIO_InitTypeDef GPIO_InitStruct =
{ 0 };
​
if (IS_ENGINEERING_BOOT_MODE())
{
LL_RCC_SetSPIClockSource (LL_RCC_SPI23_CLKSOURCE_PLL3Q);
}
​
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock (LL_APB1_GRP1_PERIPH_SPI2);
LL_AHB4_GRP1_EnableClock (LL_AHB4_GRP1_PERIPH_GPIOB);
LL_AHB4_GRP1_EnableClock (LL_AHB4_GRP1_PERIPH_GPIOI);
​
/**SPI2 GPIO Configuration
PB4 ------> SPI2_NSS
PI1 ------> SPI2_SCK
PI3 ------> SPI2_MOSI
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_4;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
LL_GPIO_Init (GPIOB, &GPIO_InitStruct);
​
GPIO_InitStruct.Pin = LL_GPIO_PIN_1 | LL_GPIO_PIN_2 | LL_GPIO_PIN_3;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
LL_GPIO_Init (GPIOI, &GPIO_InitStruct);
​
/* SPI2 parameter configuration*/
SPI_InitStruct.TransferDirection = LL_SPI_HALF_DUPLEX_TX;
SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_16BIT;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
SPI_InitStruct.NSS = LL_SPI_NSS_HARD_OUTPUT;
SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2;
SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 0xD;
LL_SPI_Init (SPI2, &SPI_InitStruct);
LL_SPI_SetStandard (SPI2, LL_SPI_PROTOCOL_MOTOROLA);
LL_SPI_EnableNSSPulseMgt (SPI2);
LL_SPI_Enable(SPI2);
​
LL_DMA_SetPeriphRequest(DMA2, LL_DMA_STREAM_1, LL_DMAMUX1_REQ_SPI2_TX);
LL_DMA_SetDataTransferDirection (DMA2,
LL_DMA_STREAM_1,
LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetStreamPriorityLevel (DMA2,
LL_DMA_STREAM_1,
LL_DMA_PRIORITY_VERYHIGH);
LL_DMA_SetMode (DMA2, LL_DMA_STREAM_1, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode (DMA2, LL_DMA_STREAM_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode (DMA2, LL_DMA_STREAM_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize (DMA2, LL_DMA_STREAM_1, LL_DMA_PDATAALIGN_HALFWORD);
LL_DMA_SetMemorySize (DMA2, LL_DMA_STREAM_1, LL_DMA_MDATAALIGN_HALFWORD);
LL_DMA_DisableFifoMode (DMA2, LL_DMA_STREAM_1);
​
LL_DMA_ConfigAddresses (DMA2,
LL_DMA_STREAM_1,
(uint32_t) &__TEST_BUFFER[0],
(uint32_t) &(SPI2->TXDR),
LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
​
LL_DMA_SetDataLength (DMA2, LL_DMA_STREAM_1, 3);
LL_DMA_EnableStream (DMA2, LL_DMA_STREAM_1);
​
LL_DMA_EnableIT_TC (DMA2, LL_DMA_STREAM_1); // Enable transfer complete interrupt.
LL_DMA_EnableIT_TE (DMA2, LL_DMA_STREAM_1); // Enable transfer error interrupt.
​
return;
}
​
​void DMA2_Stream1_IRQHandler(void)
{
if (LL_DMA_IsActiveFlag_TC1 (DMA2) == 1)
{
/* Clear flag DMA transfer complete */
LL_DMA_ClearFlag_TC1 (DMA2);
LL_SPI_DisableDMAReq_TX(SPI2);
}
​
if (LL_DMA_IsActiveFlag_TE1 (DMA2) == 1)
{
/* Clear flag DMA transfer error */
LL_DMA_ClearFlag_TE1 (DMA2);
LL_SPI_DisableDMAReq_TX(SPI2);
}
​
return;
}
​
int
main (void)
{
HAL_Init ();
​
/* If booted from Engineering mode */
if (IS_ENGINEERING_BOOT_MODE())
{
/* Configure the system clock */
SystemClock_Config ();
}
else
{
/*HW semaphore Clock enable*/
__HAL_RCC_HSEM_CLK_ENABLE();
​
/* IPCC initialisation */
MX_IPCC_Init ();
MX_VUART_Init ();
}
​
MX_DMA_Init ();
MX_SPI2_Init ();
__MTR_FEED_BFR = { 4152, 20536, 46700 };
​
while (1)
{
LL_DMA_ClearFlag_TC1(DMA2);
LL_SPI_EnableDMAReq_TX(SPI2);
while(!LL_SPI_IsEnabledDMAReq_TX(SPI2))
{
HAL_Delay(1);
}
}
}
​
need a lot of advice
​
Machilus​
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2020-05-28 8:25 AM
Hello @Machilus​
when looking at your code, after configure the DMA stream, you have to associate it to a DMA channel, to complete the DMA path, by using the function LL_DMA_SetChannelSelection:
LL_DMA_SetDataLength (DMA2, LL_DMA_STREAM_1, 3);
LL_DMA_EnableStream (DMA2, LL_DMA_STREAM_1);
+ LL_DMA_SetChannelSelection(DMA2, LL_DMA_STREAM_1, LL_DMA_CHANNEL_0);
LL_DMA_EnableIT_TC (DMA2, LL_DMA_STREAM_1); // Enable transfer complete interrupt.
LL_DMA_EnableIT_TE (DMA2, LL_DMA_STREAM_1); // Enable transfer error interrupt.
Best regards
Jean-Philippe
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2020-06-03 6:02 PM
None
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2020-06-03 6:02 PM
Hi JeanPhilippeR (ST Employee)
Thank you for your opinion.
However, the "LL_DMA_SetChannelSelection" function is not defined in the "stm32mp1xx_ll_dma.h" file.
Similarly, "LL_DMA_CHANNEL_0" is not defined.
The stm32mp1 chip family does not seem to use channels when using DMA.
Neither the HAL library nor the LL library could find the defined portion for the channel.
I hope you check the above and suggest another solution.
Best regards
Machilus
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2020-06-04 3:42 AM
Hi @Machilus​
Sure, sorry, you're right. I was comparing your usecase with STM32MCU code, as no such example in CubeFW for STM32MP1.
One more suggestion.
LL_DMA_ConfigAddresses is called with LL_DMA_DIRECTION_PERIPH_TO_MEMORY direction parameter.
In previous call of LL_DMA_SetDataTransferDirection, LL_DMA_DIRECTION_MEMORY_TO_PERIPH is set.
In order to avoid any confusion, could you please use "LL_DMA_GetDataTransferDirection(DMA2, LL_DMA_STREAM_1)" function as Direction parameter in LL_DMA_ConfigAddresses function?
Thank you
BR
Jean-Philippe
