2021-11-25 07:19 AM
Hello,
I have tried to implement a simple SPI driver.
Only with LL Lib. I would like to use DMA and I will do it without interrupts.
So it works now. But for me it is not clear how to poll if the transfer is finish in the right way.
So I use rx and tx dma therefore I taught the byte shifted in, guarantees the last shift out is done. That means i only wait for the rx dma stream to become ready.
So my dma transfer and init functions looks like that way
static void dma_init(void)
{
// dma init snippet
/* DMA controller clock enable */
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
/* SPI3_TX Init */
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_1, LL_DMAMUX1_REQ_SPI3_TX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_1, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_1, LL_DMA_PRIORITY_VERYHIGH);
LL_DMA_SetMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_1, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_1, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_1, LL_DMA_MDATAALIGN_BYTE);
LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_1);
/* SPI3_RX Init */
LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_0, LL_DMAMUX1_REQ_SPI3_RX);
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_0, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_0, LL_DMA_PRIORITY_VERYHIGH);
LL_DMA_SetMode(DMA1, LL_DMA_STREAM_0, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_STREAM_0, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_STREAM_0, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_0, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_0, LL_DMA_MDATAALIGN_BYTE);
LL_DMA_DisableFifoMode(DMA1, LL_DMA_STREAM_0);
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_0, 0);
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, 0);
/* SPI3 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 = PLATFORM_NETX_STM32H_SPI_CLOCK;
SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 0x0;
LL_SPI_EnableGPIOControl(SPI3);
LL_SPI_SetStandard(SPI3, LL_SPI_PROTOCOL_MOTOROLA);
LL_SPI_Init(SPI3, &SPI_InitStruct);
LL_SPI_EnableDMAReq_RX(SPI3);
LL_SPI_EnableDMAReq_TX(SPI3);
LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_0);
LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_1);
/* LL_DMA_STREAM_0 => RX */
LL_DMA_ConfigAddresses(DMA1,
LL_DMA_STREAM_0,
(uint32_t) &(SPI3->RXDR),
(uint32_t) receiveBuffer,
LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_STREAM_0));
/* LL_DMA_STREAM_1 => TX */
LL_DMA_ConfigAddresses(DMA1,
LL_DMA_STREAM_1,
(uint32_t)transmitBuffer,
(uint32_t) &(SPI3->TXDR),
LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_STREAM_1));
/* clear flags */
LL_DMA_ClearFlag_TC0(DMA1);
LL_DMA_ClearFlag_TC1(DMA1);
LL_DMA_ClearFlag_HT0(DMA1);
LL_DMA_ClearFlag_HT1(DMA1);
LL_DMA_ClearFlag_TE0(DMA1);
LL_DMA_ClearFlag_TE1(DMA1);
/* Enable SPI1 */
LL_SPI_Enable(SPI3);
}
static void tranfsere_spi_dma(uint32_t ulLength)
{
// disable DMA streams
LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_0);
LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_1);
// disable spi needed on h7
LL_SPI_Disable(SPI3);
LL_DMA_ClearFlag_TC0(DMA1);
LL_DMA_ClearFlag_TC1(DMA1);
LL_DMA_ClearFlag_HT0(DMA1);
LL_DMA_ClearFlag_HT1(DMA1);
LL_DMA_ClearFlag_FE0(DMA1);
LL_DMA_ClearFlag_FE1(DMA1);
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_0, ulLength);
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, ulLength);
// Enable DMA Channels
LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_0);
LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_1);
// Enable SPI and start master transfer
LL_SPI_Enable(SPI3);
LL_SPI_StartMasterTransfer(SPI3);
while (!LL_DMA_IsActiveFlag_TC0(DMA1))
{
// todo error handling
}
}
So is it enough to wait only for TC0 active ?
Is it better to use an Interrupt in relation to performance?
Do I need caching in any way? My buffers are on 0x24xxxxxx addresses.
By the way is there an example for H7 with DMA and LL ??
many thanks
cheers mathias
2021-11-25 07:42 AM
TC is set when the last data is transferred by the DMA to the SPI TXFIFO, but that doesn't mean the SPI is done. You would need to wait until it's done sending the data.
One way of doing this is by setting TSIZE appropriately and monitoring for EOT bit to be set, which is what HAL does.
2021-11-25 11:17 PM
Hi TDK,
Many thanks for your answer. I did not find the where they set TSIZE.
2021-11-25 11:37 PM
Oh sorry i didn´t seen that.
/* Set the number of data at current transfer */
MODIFY_REG(hspi->Instance->CR2, SPI_CR2_TSIZE, Size);
So but what I do not understand I use spi with dma and I taught the dma controlls the spi.
I should also set the TSIZE?
LL_DMA_ClearFlag_FE1(DMA1);
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_0, ulLength);
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, ulLength);
//????
/* Set the number of data at current transfer */
LL_SPI_SetTransferSize(SPI3, ulLength);
// Enable DMA Channels
LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_0);
But why I do not found that in any example code.
2021-11-26 07:20 AM
I don't know what example code you're referring to. Read the RM if you want the intimate details of how the complicated SPI peripheral works on the H7. The peripheral has multiple modes of operation and TSIZE is not required to be used.