2020-06-13 05:42 AM
Hi,
I started now programming the STM32F767ZI MCU and do not have any experience with 32bit uController.
I have to programm a project, which includes a SPI communication to an external ADC. It must be programmed with the LL library and I'd like to do this by DMA.
My problem is, that my programm sends out only one byte at the first frame although it is four bytes long (I checked the datalength register: 4 bytes). Additionally the last bit will not be send correctly, because the clock falls at the same time the MOSI signal falls to zero (see attachment).
The two graph shows the signals after sending two frames ('0x41' , '0x55', '0x55'). The second frame starts after the ready interrupt of the DMA.
I think, that both problems causes in the initialization:
/* Configure the 2.DMA functional parameters (write)*/
LL_DMA_SetChannelSelection(m_Param.pDMAx_TX, m_Param.u32DMAStream_TX, m_Param.u32DMAChannel_TX);
LL_DMA_ConfigTransfer(m_Param.pDMAx_TX, m_Param.u32DMAStream_TX, 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(m_Param.pDMAx_TX, m_Param.u32DMAStream_TX, (uint32_t)m_Results.au8TxBuffer, LL_SPI_DMA_GetRegAddr(m_Param.pSPIx), LL_DMA_GetDataTransferDirection(m_Param.pDMAx_TX, m_Param.u32DMAStream_TX));
LL_DMA_ConfigFifo(m_Param.pDMAx_TX, m_Param.u32DMAStream_TX, LL_DMA_FIFOMODE_ENABLE, LL_DMA_FIFOTHRESHOLD_1_4);
LL_DMA_SetMemoryBurstxfer(m_Param.pDMAx_TX, m_Param.u32DMAStream_TX, LL_DMA_MBURST_SINGLE);
LL_DMA_SetPeriphBurstxfer(m_Param.pDMAx_RX, m_Param.u32DMAStream_TX, LL_DMA_PBURST_SINGLE);
LL_DMA_SetDataLength(m_Param.pDMAx_TX, m_Param.u32DMAStream_TX, 0);
// LL_DMA_DisableFifoMode(m_Param.pDMAx_RX, m_Param.u32DMAStream_TX);
/* (5) Enable DMA interrupts complete/error */
LL_DMA_EnableIT_TC(m_Param.pDMAx_RX, m_Param.u32DMAStream_RX);
LL_DMA_EnableIT_TE(m_Param.pDMAx_RX, m_Param.u32DMAStream_RX);
LL_DMA_EnableIT_TC(m_Param.pDMAx_TX, m_Param.u32DMAStream_TX);
LL_DMA_EnableIT_TE(m_Param.pDMAx_TX, m_Param.u32DMAStream_TX);
It would be very kind, if somebody would like to help me solving my problem.
2020-06-13 05:58 AM
DMA initialization seems fine. How are you starting/stopping the transfer? I wouldn't recommend using the FIFO unless you are only sending data which is a multiple of the FIFO length you're using.
2020-06-13 06:04 AM
Hi TDK,
thanks for your reply.
The sending function is:
uint32_t CSpi::Send(uint8_t *pData, uint32_t u32Bytes)
{
/* Variable declarations */
uint32_t u32RetVal;
static uint16_t u16State = 0;
/* End - Variable declarations */
/* Variable initialisations */
u32RetVal = __SAFETY_NO_ERROR__;
/* End - Variable initialisations */
/* Start user code here */
CHECK_INIT(__CSPI_FCT_SEND__);
if(u32Bytes <= CSPI_BUFFERSIZE_TX)
{
if(memcpy(m_Results.au8TxBuffer, pData,u32Bytes) != 0U)
{
m_Param.u32DataToTransmit = u32Bytes;
m_Param.u32DataToReceive = u32Bytes;
LL_DMA_SetDataLength(m_Param.pDMAx_TX, m_Param.u32DMAStream_TX, m_Param.u32DataToTransmit);
LL_DMA_SetDataLength(m_Param.pDMAx_RX, m_Param.u32DMAStream_RX, m_Param.u32DataToReceive);
Start();
/*
while(m_Results.bDataSent == false)
{
;
}
*/
}
else
{
u32RetVal = SAFETYERROR(__CSPI_FCT_SEND__,__CSPI_MEMCPY__);
}
}
else
{
u32RetVal = SAFETYERROR(__CSPI_FCT_SEND__,__CSPI_DATA_SIZE__);
}
/* End user code */
/* Return section */
SAFETY_RETURN(u32RetVal);
/* End return section */
}
I tried to disable the Fifo by 'LL_DMA_DisableFifoMode(DMA2, LL_DMA_STREAM_0);' but nothing changed.
2020-06-13 06:04 AM
uint32_t CSpi::Start(void)
{
/* Variable declarations */
uint32_t u32RetVal;
/* End - Variable declarations */
/* Variable initialisations */
u32RetVal = __SAFETY_NO_ERROR__;
/* End - Variable initialisations */
/* Start user code here */
CHECK_INIT(__CSPI_FCT_START__);
m_Results.bDataReceived = false;
m_Results.bDataSent = false;
assert_param(m_Param.u32DataToTransmit == LL_DMA_GetDataLength(m_Param.pDMAx_TX, m_Param.u32DMAStream_TX));
/* Enable SPI1 */
LL_SPI_Enable(m_Param.pSPIx);
/* Enable DMA Channels */
LL_DMA_EnableStream(m_Param.pDMAx_RX, m_Param.u32DMAStream_RX);
LL_DMA_EnableStream(m_Param.pDMAx_TX, m_Param.u32DMAStream_TX);
/* End user code */
/* Return section */
SAFETY_RETURN(u32RetVal);
/* End return section */
}
2020-06-13 06:55 AM
Well, a few things:
Since you have CPHA=1, there is no delay between when the last bit is sent (falling edge) and when the transmission completes. If you want a delay there, you'll need to add it.
I don't ever see you calling LL_SPI_EnableDMAReq_TX or LL_SPI_EnableDMAReq_RX, but since it's working, presumably you are doing this somewhere.
It looks like you can send a frame of 1 byte and 2 bytes. I see no reason why a frame of 4 bytes wouldn't work based on the code you showed.
2020-06-13 07:01 AM
Hi TDK,
uint32_t CSpi::InitSPI(void)
{
/* Variable declarations */
uint32_t u32RetVal;
/* End - Variable declarations */
/* Variable initialisations */
u32RetVal = __SAFETY_NO_ERROR__;
/* End - Variable initialisations */
/* Start user code here */
/* Enable the peripheral clock of SPIx */
if(m_Param.pSPIx == SPI1)
{
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
}
else if(m_Param.pSPIx == SPI2)
{
LL_APB2_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2);
}
else if(m_Param.pSPIx == SPI3)
{
LL_APB2_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI3);
}
else if(m_Param.pSPIx == SPI4)
{
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI4);
}
else if(m_Param.pSPIx == SPI5)
{
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI5);
}
else if(m_Param.pSPIx == SPI6)
{
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI6);
}
else
{
u32RetVal = SAFETYERROR(__CSPI_FCT_INIT_SPI__,__CSPI_INDEX_INTERFACE__);
}
if(u32RetVal == __SAFETY_NO_ERROR__)
{
LL_SPI_SetBaudRatePrescaler(m_Param.pSPIx, LL_SPI_BAUDRATEPRESCALER_DIV32);
LL_SPI_SetBaudRatePrescaler(m_Param.pSPIx, LL_SPI_BAUDRATEPRESCALER_DIV256);
LL_SPI_SetTransferDirection(m_Param.pSPIx,LL_SPI_FULL_DUPLEX);
/* LL_SPI_SetClockPhase(m_Param.pSPIx, LL_SPI_PHASE_1EDGE); */ /* CPOL = 1 */
LL_SPI_SetClockPhase(m_Param.pSPIx, LL_SPI_PHASE_2EDGE); /* CPOL = 0 */
/* LL_SPI_SetClockPolarity(m_Param.pSPIx, LL_SPI_POLARITY_HIGH); */ /* CPHA = 0 */
LL_SPI_SetClockPolarity(m_Param.pSPIx, LL_SPI_POLARITY_LOW); /* CPHA = 1 */
/* Reset value is LL_SPI_MSB_FIRST */
/* LL_SPI_SetTransferBitOrder(m_Param.pSPIx, LL_SPI_MSB_FIRST); */
LL_SPI_SetDataWidth(m_Param.pSPIx, LL_SPI_DATAWIDTH_8BIT);
// LL_SPI_EnableNSSPulseMgt(SPI1);
if((m_Param.u16PortPinNSS != 0U)&&(m_Param.u16PortNSS != 0U))
{
if(m_Param.bMasterMode == false)
{
LL_SPI_SetNSSMode(m_Param.pSPIx, LL_SPI_NSS_HARD_INPUT);
}
else
{
if(m_Param.bNSSSoft == false)
{
LL_SPI_SetNSSMode(m_Param.pSPIx, LL_SPI_NSS_HARD_OUTPUT);
}
else
{
LL_SPI_SetNSSMode(m_Param.pSPIx, LL_SPI_NSS_SOFT);
}
}
LL_SPI_EnableNSSPulseMgt(m_Param.pSPIx);
LL_SPI_EnableNSSPulseMgt(SPI1);
}
LL_SPI_SetRxFIFOThreshold(m_Param.pSPIx, LL_SPI_RX_FIFO_TH_QUARTER);
if(m_Param.bMasterMode == true)
{
LL_SPI_SetMode(m_Param.pSPIx, LL_SPI_MODE_MASTER);
}
else
{
LL_SPI_SetMode(m_Param.pSPIx, LL_SPI_MODE_SLAVE);
}
/* Configure SPI1 DMA transfer interrupts */
/* Enable DMA RX Interrupt */
LL_SPI_EnableDMAReq_RX(m_Param.pSPIx);
/* Enable DMA TX Interrupt */
LL_SPI_EnableDMAReq_TX(m_Param.pSPIx);
LL_SPI_EnableIT_TXE(m_Param.pSPIx);
}
/* End user code */
/* Return section */
SAFETY_RETURN(u32RetVal);
/* End return section */
}
3. If I add a third frame simular, it will be send completly (3bytes)
Best regards
Michael
2020-06-13 07:27 AM
Options:
2020-06-13 09:22 AM
Okay... fine!
Does it work with the hardware controled NLL signal?
Do you have some ideas because of the missing frame data?
2020-06-13 11:19 AM
I don't recommend using the hardware NSS signal. I'd use a GPIO pin and toggle it manually.
> LL_SPI_EnableIT_TXE(m_Param.pSPIx);
Why are you enabling this? With DMA you shouldn't care about TXE being asserted.
Not sure on the underlying issue. Examine DMA registers to see if NDTR gets to 0 eventually, or if errors have occurred.
2020-06-14 10:58 PM
Hi TDK,
I think I have found the mean problem, but do not know how to solve it in a good way.
When I get the DMA interrupt I thought that all transmissions are done and disabled the SPI. But thats wrong because the TXE flag of the SPI is still cleared and the communication not completed. So I think, the best way is to wait for the SPI_TXE interrupt to be sure, that everything is completed. But the interrupt doesn't work (???).
If you have good ideas, please post it to me. I will go on to fix this problem by my own.