cancel
Showing results for 
Search instead for 
Did you mean: 

SPI DMA HELP! what is going on ?????

JHERI
Senior

any help would be very much appreciated.

for the past few weeks I have been trying to get SPI & DMA working with HAL

what an up hill struggle !

still only scratching the surface of it and this coding is not for the faint hearted or people that give up easily.

OK

I gave up using NSS hardware chip select at this doesn't seem to work correctly and more hassle than its worth ! you have to keep disabling the spi module and then re-enabling bit 6

So I decided use a standard GPIO pin for my chip select, so much easier!

1) set the Chip Select GPIO pin Low before the HAL_SPI_Transmit_DMA

2) issue the HAL_SPI_Transmit_DMA data

3) in the spi transmit complete interrupt, set the Chip Select Pin High

so I am able to send 3 packets of SPI DATA via the DMA in my init code before my main loop

SPI INIT

/* SPI2 init function */
void MX_SPI2_Init(void)
{
  hspi2.Instance               = SPI2;
  hspi2.Init.Mode              = SPI_MODE_MASTER;
  hspi2.Init.Direction         = SPI_DIRECTION_1LINE;
  hspi2.Init.DataSize          = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity       = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase          = SPI_PHASE_1EDGE;
  hspi2.Init.NSS               = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi2.Init.FirstBit          = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode            = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial     = 10;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }
 
}

DMA INIT

  hdma_spi2_tx.Instance                 = DMA1_Stream4;
    hdma_spi2_tx.Init.Channel             = DMA_CHANNEL_0;
    hdma_spi2_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;
    hdma_spi2_tx.Init.PeriphInc           = DMA_PINC_DISABLE;
    hdma_spi2_tx.Init.MemInc              = DMA_MINC_ENABLE;
    hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_spi2_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
    hdma_spi2_tx.Init.Mode                = DMA_NORMAL;
    hdma_spi2_tx.Init.Priority            = DMA_PRIORITY_HIGH;
    hdma_spi2_tx.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;
    hdma_spi2_tx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_HALFFULL;
    hdma_spi2_tx.Init.MemBurst            = DMA_MBURST_SINGLE;
    hdma_spi2_tx.Init.PeriphBurst         = DMA_PBURST_SINGLE;
    if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK)
    {
      Error_Handler(); 
    }
 
    __HAL_LINKDMA(spiHandle, hdmatx, hdma_spi2_tx);
 
    /* SPI2 interrupt Init */
    HAL_NVIC_SetPriority(SPI2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(SPI2_IRQn);

while (HAL_SPI_GetState(&hspi2) != HAL_SPI_STATE_READY)
{
}
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, LOW);
  HAL_SPI_Transmit_DMA( &hspi2, (uint8_t*) MCP23017_INIT,  3 ) ;
 
  
 while (HAL_SPI_GetState(&hspi2) != HAL_SPI_STATE_READY)
{
}
   HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, LOW);
   HAL_SPI_Transmit_DMA( &hspi2, (uint8_t*) MCP23017_INITB, 3 ) ;
 
 
 
while (HAL_SPI_GetState(&hspi2) != HAL_SPI_STATE_READY)
{
}
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, LOW);
    HAL_SPI_Transmit_DMA( &hspi2, (uint8_t*) MCP23017_INITB2, 3 ) ;

Then when I try to send some more SPI data and check first for SPI busy it always reports busy ???

What is going on ???

I sent 3 packets and only 3 packets, how can the SPI TX report it busy ??

have had a logic analyser and only 3 spi packet are show, no more spi data should be in any buffer and this spi peripheral should NOT report BUSY!

can anyone please help, these HAL drivers and ST32 chips are seriously hard work!

any help will be very much appreciated

7 REPLIES 7
JHERI
Senior

ok so I have done a little more digging and have found a problem area

I call a routine to tx_uart data, if I rem this routine out of my main loop the spi peripheral is not busy ???

void TX_UART_DATA_IT (void)                                                                         // ROUTINE TO OUTPUT CURRENT CLOCK SETTINGS TO RS232 PORT (UART2)
{
    
  uint16_t out;
 
  if (! (( uart2_tx_flag >> 7) & 1))                                                               // check for clear tx flag for UART2, get set after a uart2 tx of data
     {                                                                                               
        if(! FIFO_GetItem(&uart2_tx_fifo, &out))                                                    // check to see if there is any to display in UART2 tx fifo buffer
          {
          memset(msg, 0, sizeof(msg));                                                              // CLEAR VARIABLE msg
          uart2_tx_flag = uart2_tx_flag | 1 << 6;
       
          switch(out)                                                                                   // set bit6 enable TX write to UART 2
              {
              case 0:
              uart2_tx_flag = uart2_tx_flag  & ~(1 << 6);                                               // clear bit 6disable TX write to UART 2
              break;
            
              case 1:                                                                                   // CASE 0: CLEAR TERATERM SCREEN (VT100) CLEAR
              sprintf(msg,"\033[2J \033[0;0H");
              break;
 
              case 2:                                                                                   // CASE 1: DISPLAY TITLE
              sprintf(msg,"STM32F446 Micro Controller Clock And Oscillator Operating Frequencies \n\r");
              break;
          

the SPI problem seem to be tied up with sprintf(msg,"\033[2J \033[0;0H");

using the SPRINTF ???

the SPRINTF variable msg is an array of 100 bytes

if I rem out sprintf(msg,"\033[2J \033[0;0H");

SPI is not stating busy ??

 char msg[100] = {0}; 
 
 
 

void TX_UART_DATA_IT (void)                                                                         // ROUTINE TO OUTPUT CURRENT CLOCK SETTINGS TO RS232 PORT (UART2)
{
    
  uint16_t out;
 
  if (! (( uart2_tx_flag >> 7) & 1))                                                               // check for clear tx flag for UART2, get set after a uart2 tx of data
     {                                                                                               
        if(! FIFO_GetItem(&uart2_tx_fifo, &out))                                                    // check to see if there is any to display in UART2 tx fifo buffer
          {
          memset(msg, 0, sizeof(msg));                                                              // CLEAR VARIABLE msg
          uart2_tx_flag = uart2_tx_flag | 1 << 6;
       
          switch(out)                                                                                   // set bit6 enable TX write to UART 2
              {
              case 0:
              uart2_tx_flag = uart2_tx_flag  & ~(1 << 6);                                               // clear bit 6disable TX write to UART 2
              break;
            
              case 1:                                                                                   // CASE 0: CLEAR TERATERM SCREEN (VT100) CLEAR
              sprintf(msg,"\033[2J \033[0;0H");
              break;
 
              case 2:                                                                                   // CASE 1: DISPLAY TITLE
              sprintf(msg,"STM32F446 Micro Controller Clock And Oscillator Operating Frequencies \n\r");
              break;
          
              case 3:                                                                                   // CASE 2: DISPLAY (HSI/E & LSI/E Freq)
              sprintf(msg,"HSE Freq : %ldMhz / HSI Freq : %ldMhz / LSE Freq : %ldKhz / LSI Freq : %ldKhz\r\n", (HSE_VALUE / 1000000),(HSI_VALUE / 1000000),  (LSE_VALUE / 1000), (LSI_VALUE / 1000) );
              break;
          
              case 4:                                                                                   // CASE 3: DISPLAY (SYSCLK Freq)
              sprintf(msg,"SYSCLK Freq : %ldMhz\r\n", (HAL_RCC_GetSysClockFreq()  / 1000000));
              break;
          
              case 5:                                                                                   // CASE 4: DISPLAY (HCLK Freq)
              sprintf(msg,"HCLK   Freq : %ldMhz\r\n", (HAL_RCC_GetHCLKFreq()      / 1000000));
              break;
 
              case 6:                                                                                   // CASE 5: DISPLAY (PCLK1 Freq)
              sprintf(msg,"PCLK1  Freq : %ldMhz\r\n", (HAL_RCC_GetPCLK1Freq()     / 1000000));
              break;
 
              case 7:                                                                                   // CASE 6: DISPLAY (PCLK2 Freq)
              sprintf(msg,"PCLK2  Freq : %ldMhz\r\n", (HAL_RCC_GetPCLK2Freq()     / 1000000));
              SET_BIT(TX_Flag1, 1);
              break;
            
              case 8:
              sprintf(msg,"Frequency Is : %.0f\r\n",  user_signal_freq);       
              break;
 
              case 10:
              sprintf(msg,"CAN1 MESSAGE TX --> (Mailbox 0) : ");       
              break;
 
              case 11:
              sprintf(msg,"CAN1 MESSAGE SENT SUCESSFULLY (Mailbox 1) :\r\n");       
              break;
        
              case 12:
              sprintf(msg,"CAN1 MESSAGE SENT SUCESSFULLY (Mailbox 2) :\r\n");       
              break; 
              
              case 13:
              sprintf(msg,"%s (0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X) \r\n", CAN_Tx, CAN_Tx[0], CAN_Tx[1], CAN_Tx[2], CAN_Tx[3], CAN_Tx[4], CAN_Tx[5], CAN_Tx[6], CAN_Tx[7]);       
              break;
 
              case 14:
              sprintf(msg,"CAN1 MESSAGE RX <-- (RX0  FiFo) : %s (0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X) \r\n", CAN_Rx, CAN_Rx[0], CAN_Rx[1], CAN_Rx[2], CAN_Rx[3], CAN_Rx[4], CAN_Rx[5], CAN_Rx[6], CAN_Rx[7]);       
              break;
 
              case 15:
              sprintf(msg,"***** CAN1 ERROR *****  : %s\r\n");       
              break;
 
 
              default:                                                                                  // DEFAULT: NO TX OF DATA, EXIT ROUTINE
              uart2_tx_flag = uart2_tx_flag  & ~(1 << 6);                                               // clear bit 6disable TX write to UART 2
              break;
 
              }                                                                                         // END OF SWITCH
              
              if( uart2_tx_flag & ( 1 << 6))   
                 {
                //  HAL_UART_Transmit_IT(&huart2, (uint8_t*) msg, strlen(msg));                                 // OUTPUT DATA ARRAY IN msg TO UART 2 PERIPHERAL
                 }
                
             uart2_tx_flag = uart2_tx_flag | 1 << 7;                                                   // set flag to avoid this routine (TX to UART)                                                                // SET uart2_tx_flag UNTIL NEXT UART2 TX COMPLETE INTERRUPT.
          }
     }
}

is the SPRINTF causing my dev software CrossStudio to act strange

do i need to set any special parameters ???

my uart data is being sent fine but the SPI peripheral is now stating it is busy after using SPRINTF ???

JHERI
Senior

I have increased the heap and stack size in CrossStudio and the problem with Sprintf & SPI has seemed to disappear ???

Don't really understand, but guess these parameters need setting other than the default values.

Singh.Harjit
Senior II

​Because I don't know how much stack space the library will use, I usually make the main stack 1024 bytes and the heap 256 bytes. The size you need to make these depends on what you do in your program.

JHERI
Senior

Thanks for the reply, not sure what size to set myself ??

but by increasing the default values, there are no more strange things happening in my development software / environment.

regards

Singh.Harjit
Senior II

​In CrossStudio, depending on the startup code you are using, you can create a preprocessor definition: INITIALIZE_STACK and what it does during startup is write 0xCC to the entire stack area. What you can do is after a test session, go look at memory and see how much of the stack was used i.e. it isn't contiguous 0xCC and that should give you an idea of your stack usage.

You set this by going to the Project Explorer, then right clicking on the project, then select Options, then select the proper configuration - I use Common since I want this in all my builds - then use the search area and type in preproc and scroll down until you find "Proprocessor Definitions". Double click on that line and then add INITIALZISE_STACK

In my setup, I use thumb_crt0.s and that is where you can find the code that uses INITIALIZE_STACK.

JHERI
Senior

thanks for the info, I do like cross studio development software 🙂

Khouloud GARSI
Lead II

Hi @JHERI​ ,

I used STM32CubeMX to build a SPI/DMA communication between a master and a slave in half duplex mode where the NSS pin is managed by hardware.

The communication with the slave was done correctly since I was able to receive (on slave board) the buffer that I have sent from the master.

You find attached the ioc file of the project (STM32-Nucleo F446 was used as a master).

I invite you to use the attached project and send me your feedback if you face any issue.

Khouloud.