2022-12-02 02:26 PM
STM32L0 SPI communication with DMA. The SPI CS line is controlled with software. It appears to stay low for about 72uS when the communication only takes about 4uS.
Right before setting the DMA to roll, I set the CS to 0. Nothing else is pending for the DMA. When the DMA RX full interrupt triggers, I set the CS to 1.
Why does the CS take as long as it does? How can I make this time shorter? I would like to do communications more often, if possible.
2022-12-02 03:40 PM
Core frequency?
HAL or LL/direct register writes?
Only DMA RX interrupt for SPI?
Repeatable?
No other higher prio interrupts running?
2022-12-19 10:25 AM
To answer your questions:
2022-12-19 10:27 AM
void restart_DMA(void)
{
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_3);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, Rx_buffer_size);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, Tx_buffer_size);
/* (5) Enable DMA interrupts complete/error */
// Rx receive channel interrupts
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);
// Tx transmit channel interrupts
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);
//Enable DMA interrupts
NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0);
NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
}
/**
* @brief This function configures the DMA Channels for SPI1
* @note This function is used to :
* -1- Enable DMA1 clock
* -2- Configure NVIC for DMA1 transfer complete/error interrupts
* -3- Configure the DMA1_Channel2 functional parameters
* -4- Configure the DMA1_Channel3 functional parameters
* -5- Enable DMA1 interrupts complete/error
* @param None
* @retval None
*/
void Configure_DMA(void)
{
/* DMA1 used for SPI1 Transmission
* DMA1 used for SPI1 Reception
*/
//LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
//LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_3);
//uint8_t temp = 0;
/* (1) Enable the clock of DMA1 and DMA1 */
LL_SPI_Disable(SPI1);
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, &(SPI1 -> DR), (uint32_t)&mdrcvBuffer,
LL_DMA_DIRECTION_PERIPH_TO_MEMORY); //LL_SPI_DMA_GetRegAddr(SPI1)
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, Rx_buffer_size);
//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, &(SPI1 -> DR),
LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_3, Tx_buffer_size);
//LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_3, LL_DMA_REQUEST_0); /////////////////
/* (5) Enable DMA interrupts complete/error */
// Rx receive channel interrupts
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);
// Tx transmit channel interrupts
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);
/* Check what interrupts are or are not enabled -> everything checked out fine, leaving for debugging in the future */
//temp = LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_2); // Channel 2, TC or Transfer Complete: 1 (interrupt enabled)
//temp = LL_DMA_IsEnabledIT_HT(DMA1, LL_DMA_CHANNEL_2); // Channel 2, HT or Half Transfer Complete: 0 (interrupt not enabled)
//temp = LL_DMA_IsEnabledIT_TE(DMA1, LL_DMA_CHANNEL_2); // Channel 2, TE or Transfer Error: 1 (interrupt enabled)
//temp = LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_3); // Channel 3, TC or Transfer Complete: 1 (interrupt enabled)
//temp = LL_DMA_IsEnabledIT_HT(DMA1, LL_DMA_CHANNEL_3); // Channel 3, HT or Half Transfer Complete: 0 (interrupt not enabled)
//temp = LL_DMA_IsEnabledIT_TE(DMA1, LL_DMA_CHANNEL_3); // Channel 3, TE or Transfer Error: 1 (interrupt enabled)
}
/**
* @brief This function configures SPI1.
* @note This function is used to :
* -1- Enables GPIO clock and configures the SPI1 pins.
* -2- Configure SPI1 functional parameters.
* @note Peripheral configuration is minimal configuration from reset values.
* Thus, some useless LL unitary functions calls below are provided as
* commented examples - setting is default configuration from reset.
* @param None
* @retval None
*/
/**
* @brief This function Activate SPI1
* @param None
* @retval None
*/
void Activate_SPI(void)
{
/*
* 1. SPI -> Enable DMA Rx with RXDMAEN bit
* 2. DMA -> Enable RX and TX DMA streams or channels
* 3. SPI -> Enable DMA Tx with TXDMAEN bit
* 4. SPI -> Enable SPI with SPE bit
*
*/
/* Enable DMA RX / TX Interrupt */
LL_SPI_DisableDMAReq_RX(SPI1);
LL_SPI_DisableDMAReq_TX(SPI1);
//Enable DMA interrupts
NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0);
NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
// 1. SPI -> Enable DMA Rx with RXDMAEN bit
LL_SPI_EnableDMAReq_RX(SPI1);
// 2. DMA -> Enable RX and TX DMA streams or channels
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_3);
// 3. SPI -> Enable DMA Tx with TXDMAEN bit
LL_SPI_EnableDMAReq_TX(SPI1);
//4. SPI -> Enable SPI with SPE bit
LL_SPI_Enable(SPI1);
}
2022-12-19 10:27 AM
-----------------------------------------------------------------------------
------------------------------------------------------------------------------
----- stm32L0xx_it.c
------------------------------------------------------------------------------
------------------------------------------------------------------------------
void DMA1_Channel2_3_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel2_3_IRQn 0 */
// ---- External Variable Declaration
extern volatile uint32_t _Data[num_];
extern volatile uint8_t Index;
extern volatile __IO uint8_t ubTransmissionComplete;
extern volatile __IO uint8_t ubReceptionComplete;
//extern volatile uint8_t mdrcvBuffer [Rx_buffer_size]; // SPI Rx buffer, 4-bytes long
// ---- Internal Variable Declaration
uint8_t i = 0;
// ---- Channel 2 interrupts, the Rx channel
if( LL_DMA_IsActiveFlag_GI2(DMA1)) // An interrupt of some kind on DMA channel 2 has occurred
{
if( LL_DMA_IsActiveFlag_TE2(DMA1) & LL_DMA_IsEnabledIT_TE(DMA1, LL_DMA_CHANNEL_2)) //Transfer Error Interrupt?
{
/* Disable DMA1 Rx Channel */
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
/* Disable DMA1 Tx Channel */
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_3);
}
else if ( LL_DMA_IsActiveFlag_HT2(DMA1) & LL_DMA_IsEnabledIT_HT(DMA1, LL_DMA_CHANNEL_2) ) // Half complete interrupt?
{
LL_DMA_ClearFlag_HT2(DMA1);
}
else if( LL_DMA_IsActiveFlag_TC2(DMA1) & LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_2)) // Fully Complete Interrupt?
{
LL_DMA_ClearFlag_TC2(DMA1); // Clear Rx interrupt
// ---- Transmission Complete, turn off respective Chip Select
if(Index == CHIPA){ CS_MSA_OFF; }
if(Index == CHIPB){ CS_MSB_OFF; }
// ---- Prepare the Send Buffer for the next go around
if(Index == CHIPA)
{
for(i = 0; i < Tx_buffer_size; i++)
{
mdsendBuffer[i] = B_send[i];
A_send[i] = 0x00;
}
}
// ---- Transmission Complete, Obtain respective sensor data
if(Index != num_)
{
_Data[Index ] = 0;
_Data[Index] |= mdrcvBuffer[0];
for ( i = 1; i < 4; i++ )
{
_Data[Index] = _Data[Index] << 8;
_Data[Index] |= mdrcvBuffer[i];
}
}
// ---- Increment Index for the next phase, ie the next sensor
Index += 1;
// ---- If you have reached the end, reset everything
if(Index == num_)
{
Index = 0;
}
// ---- If you haven't reached the end, setup the DMA for the next phase, ie the next sensor
else
{
restart_DMA(); // Get the DMA engine going again
// Asking for data from the second sensor now
if(Index == CHIPB) { data_channel_DMA(CHIPB, sensor_axis); } // Send out the request, should the activate SPI operation be here on in the TMAG function?
}
}
}
// ---- Channel 3 interrupts, the Tx channel
if( LL_DMA_IsActiveFlag_GI3(DMA1)) // An interrupt of some kind on DMA channel 3 has occurred
{
if( LL_DMA_IsActiveFlag_TE3(DMA1) & LL_DMA_IsEnabledIT_TE(DMA1, LL_DMA_CHANNEL_3) ) //Transfer Error Interrupt?
{
/* Disable DMA1 Rx Channel */
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
/* Disable DMA1 Tx Channel */
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_3);
}
else if ( LL_DMA_IsActiveFlag_HT3(DMA1) & LL_DMA_IsEnabledIT_HT(DMA1, LL_DMA_CHANNEL_3) ) // Half complete interrupt?
{
LL_DMA_ClearFlag_HT3(DMA1);
}
else if( LL_DMA_IsActiveFlag_TC3(DMA1) & LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_3) ) // Fully Complete Interrupt?
{
ubTransmissionComplete = 1;
LL_DMA_ClearFlag_TC3(DMA1); //This seems to be required, otherwise the interrupt seems to continually trigger
}
}
2022-12-19 10:28 AM
It seems to take 100uS to transmit and 100uS between transmits (multiple external devices)
2022-12-19 10:31 AM
Goals:
I haven't yet found anything to suggest why the communication seems to be blocked or delayed like this. I sense that the DMA might run in blocked time intervals of ~100uS though. Perhaps I can look into its timing to see if that is the case.
2022-12-19 11:54 AM
In the future please use the code posting tool </> icon, as in-lining the code removes all formatting/indentation.
Sorry not clear on how the two transfers occur or interact. Don't see any of the code related to this.
I guess one would want to do the least possible reconfiguration of the entire SPI/DMA.
The bus is symmetrical, same amount of data going out as coming in.
Watch for FIFO's and thresholds.
2022-12-19 11:59 AM
>>The bus is symmetrical, same amount of data going out as coming in
Point here being, not clear if you check for this, and really no point in doing TWO interrupts to service this. TX will be leading, RX will be trailing, and the better point to address things, as the last bit has gone over the wire. You can drop CS, and pivot directly into the next transfer.
2022-12-19 01:17 PM
Okay fair enough. I had been looking in the past at the bare bones of what I would need to reset to get the DMA rolling again. I should look at this again. (after I got it working, I stopped looking further on optimizing everything)