cancel
Showing results for 
Search instead for 
Did you mean: 

1. SPI communication between MCU and MPU.

SPare.9
Associate II

Hi,

I am working on SPI bus communication between IMX6dl MPU and STM32L476 MCU.

MPU work as a master device and MCU work as a slave device.

Configuration of MPU is:

pinctrl_ecspi3: ecspi3grp {

               fsl,pins = <

                       MX6QDL_PAD_DISP0_DAT2__ECSPI3_MISO     0x100b1

                       MX6QDL_PAD_DISP0_DAT1__ECSPI3_MOSI     0x100b1

                       MX6QDL_PAD_DISP0_DAT0__ECSPI3_SCLK     0x000b1

#define GP_ECSPI3_NOR_CS       <&gpio4 24 GPIO_ACTIVE_HIGH>

                       MX6QDL_PAD_DISP0_DAT3__GPIO4_IO24      0x0b0b1

               >;

       };

&ecspi3 {

       fsl,spi-num-chipselects = <1>;

       cs-gpios = GP_ECSPI3_NOR_CS;

       /*spi-slave;*/

       pinctrl-names = "default";

       pinctrl-0 = <&pinctrl_ecspi3>;

       status = "okay";

       spidev2: spi@0 {

               compatible = "spidev";

               reg = <0>;

               spi-cpha;

               spi-max-frequency = <34000000>;

       };

};

Master clock frequency: 312500

Master Mode: CPOL = 0, CPOH = 1 (i.e. Mode 1)

Datasize = 8 bit

Configuration of MCU is:

hspi3.Instance = SPI3;

 hspi3.Init.Mode = SPI_MODE_SLAVE;

 hspi3.Init.Direction = SPI_DIRECTION_2LINES;

 hspi3.Init.DataSize = SPI_DATASIZE_8BIT;

 hspi3.Init.CLKPolarity = SPI_POLARITY_LOW;

 hspi3.Init.CLKPhase = SPI_PHASE_2EDGE;

hspi3.Init.NSS = SPI_NSS_SOFT;

hspi3.Init.FirstBit = SPI_FIRSTBIT_LSB;

hspi3.Init.TIMode = SPI_TIMODE_DISABLE;

hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

hspi3.Init.CRCPolynomial = 7;

hspi3.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;

hspi3.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;

My queries are:

1. Is master and slave configuration is ok??

2. How can i check loop back mode in MCU??

First of all i have checked Loop back mode in Master.

Master loop back communication is working fine.

When i am testing master to slave SPI communication, master is sending dataframe "10101010" at slave end receiving dataframe is "01010101".

Please let me know what could be the reason for above point.

Thank you.

6 REPLIES 6
S.Ma
Principal

My advice:

Use DMA in slave receive mode. Use DMA in Circular mode with an SRAM buffer.

Use EXTI interrupt on NSS to reset the DMA to the beginning of the buffer (falling edge) and process the received data on NSS rising edge. (NSS would be a GPIO)

Make sure the master wait few microsecond after changing NSS level for the slave interrupt to have the time to react and init DMA. Then you'll be ready to go, No DMA interrupt needed here.

Program the SPI in 8 bit mode. And remember you have to program both DMA TX and RX. Use SPI 4 wire interface mode to get started.

SPare.9
Associate II

Thanks for input. I have enable RX/TX DMA and global interrupt as per your suggestion and I see there is 2 bits shifting most of the time during Tx/RX operation.

i.e.

  1. If I send 0x0F from Master (i.MX6) on MOSI on slave side (MCU-STM32l4) it receives 0xC3 on MCU.
  2. Same way if I send 0x0F on MISO line (from MCU to MPU) then it shows 0x3C on MPU.

Below is configuration in MCU-STM32l4:

File: main.c

static void MX_SPI3_Init(void)

{

 hspi3.Instance = SPI3;

 hspi3.Init.Mode = SPI_MODE_SLAVE;

 hspi3.Init.Direction = SPI_DIRECTION_2LINES;

 hspi3.Init.DataSize = SPI_DATASIZE_8BIT;

 hspi3.Init.CLKPolarity = SPI_POLARITY_LOW;

 hspi3.Init.CLKPhase = SPI_PHASE_2EDGE;

 hspi3.Init.NSS = SPI_NSS_SOFT;

 hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB;

 hspi3.Init.TIMode = SPI_TIMODE_DISABLE;

 hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

 hspi3.Init.CRCPolynomial = 7;

 hspi3.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;

 hspi3.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;

 if (HAL_SPI_Init(&hspi3) != HAL_OK)

 {

  Error_Handler();

 }

}

===

/**

 * Enable DMA controller clock

 */

static void MX_DMA_Init(void)

{

 /* DMA controller clock enable */

 __HAL_RCC_DMA2_CLK_ENABLE();

 /* DMA interrupt init */

 /* DMA2_Channel1_IRQn interrupt configuration */

 HAL_NVIC_SetPriority(DMA2_Channel1_IRQn, 5, 0);

 HAL_NVIC_EnableIRQ(DMA2_Channel1_IRQn);

 /* DMA2_Channel2_IRQn interrupt configuration */

 HAL_NVIC_SetPriority(DMA2_Channel2_IRQn, 5, 0);

 HAL_NVIC_EnableIRQ(DMA2_Channel2_IRQn);

}

===

uint8_t spi_RX_data;

int main(void)

{

 /* MCU Configuration--------------------------------------------------------*/

 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */

 HAL_Init();

 /* USER CODE BEGIN Init */

 /* USER CODE END Init */

 /* Configure the system clock */

 SystemClock_Config();

 /* USER CODE BEGIN SysInit */

 /* USER CODE END SysInit */

 /* Initialize all configured peripherals */

 MX_GPIO_Init();

 MX_DMA_Init();

 MX_USART3_UART_Init();

 MX_QUADSPI_Init();

 MX_I2C2_Init();

 MX_UART4_Init();

 MX_USART1_UART_Init();

 MX_UART5_Init();

 MX_SPI3_Init();

 MX_I2C1_Init();

 MX_I2C3_Init();

 MX_CRC_Init();

 /* USER CODE BEGIN 2 */

  HAL_SPI_Receive_DMA(&hspi3, &spi_RX_data, 1);

}

===========

File: stm32l4xx_hal_msp.c

/**

* @brief SPI MSP Initialization

* This function configures the hardware resources used in this example

* @param hspi: SPI handle pointer

* @retval None

*/

void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)

{

 GPIO_InitTypeDef GPIO_InitStruct = {0};

 if(hspi->Instance==SPI3)

 {

 /* USER CODE BEGIN SPI3_MspInit 0 */

 /* USER CODE END SPI3_MspInit 0 */

  /* Peripheral clock enable */

  __HAL_RCC_SPI3_CLK_ENABLE();

  __HAL_RCC_GPIOG_CLK_ENABLE();

  HAL_PWREx_EnableVddIO2();

  /**SPI3 GPIO Configuration

  PG9   ------> SPI3_SCK

  PG10   ------> SPI3_MISO

  PG11   ------> SPI3_MOSI

  */

  GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11;

  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

  GPIO_InitStruct.Pull = GPIO_NOPULL;

  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

  GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;

  HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

  /* SPI3 DMA Init */

  /* SPI3_RX Init */

  hdma_spi3_rx.Instance = DMA2_Channel1;

  hdma_spi3_rx.Init.Request = DMA_REQUEST_3;

  hdma_spi3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;

  hdma_spi3_rx.Init.PeriphInc = DMA_PINC_DISABLE;

  hdma_spi3_rx.Init.MemInc = DMA_MINC_ENABLE;

  hdma_spi3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;

  hdma_spi3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;

  hdma_spi3_rx.Init.Mode = DMA_CIRCULAR;

  hdma_spi3_rx.Init.Priority = DMA_PRIORITY_LOW;

  if (HAL_DMA_Init(&hdma_spi3_rx) != HAL_OK)

  {

   Error_Handler();

  }

  __HAL_LINKDMA(hspi,hdmarx,hdma_spi3_rx);

  /* SPI3_TX Init */

  hdma_spi3_tx.Instance = DMA2_Channel2;

  hdma_spi3_tx.Init.Request = DMA_REQUEST_3;

  hdma_spi3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;

  hdma_spi3_tx.Init.PeriphInc = DMA_PINC_DISABLE;

  hdma_spi3_tx.Init.MemInc = DMA_MINC_ENABLE;

  hdma_spi3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;

  hdma_spi3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;

  hdma_spi3_tx.Init.Mode = DMA_CIRCULAR;

  hdma_spi3_tx.Init.Priority = DMA_PRIORITY_LOW;

  if (HAL_DMA_Init(&hdma_spi3_tx) != HAL_OK)

  {

   Error_Handler();

  }

  __HAL_LINKDMA(hspi,hdmatx,hdma_spi3_tx);

  /* SPI3 interrupt Init */

  HAL_NVIC_SetPriority(SPI3_IRQn, 5, 0);

  HAL_NVIC_EnableIRQ(SPI3_IRQn);

 /* USER CODE BEGIN SPI3_MspInit 1 */

 /* USER CODE END SPI3_MspInit 1 */

 }

}

S.Ma
Principal

Before optimizing do the following:

  1. When MPU change NSS level, alway add 100usec delay before exchanging data (SCK active)
  2. You need to synchronise the clock counting: Enable NSS EXTI interrupt on STM32 side
    1. When NSS goes low, reset the DMA and SPI, set the TX and RX buffer
    2. When NSS goes high, process the received Data buffer.

Depending when the MPU and MCU start to listen to SPI lines, there might be dummy glitches where the bit counters in slave might be shifted. Easy to spot with oscilloscope, not so easy with logic analyser.

Also make sure the levels are same both sides.

Thank you for your quick response.

Below are few observations during my expirmints:

I used the oscilloscope and found that the data coming on MISO / MOSI are same which I received during Tx and RX.

RX Path (MPU to MCU)

Also note that as I was getting the 2 bits shift issue while receiving data on MCU ( MP to MCU [master to slave line] ) - for testing purpose I tried following and except the 1st byte ( as I did some runtime bits configuration changes to play with shift register) all bytes (with different burst size) received correctly .

  • configured the spi in 10 bits (called from main) // due to this first byte will not received correctly

hspi3.Init.DataSize = SPI_DATASIZE_10BIT;

  • on receiving the first byte from the MCU I am getting HAL_SPI_RxCpltCallback and I do change the configuration to 8 bits

void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)

{

    static int i = 0;

    if(i == 0)

    {

        i = 1;

        LOG_Print("Chaning Configuration to 8 bits\n");

        if (HAL_SPI_DeInit(&hspi3) != HAL_OK)

        {

            Error_Handler();

        }

        MX_SPI3_Init_8bits();

}    

}

==

static void MX_SPI3_Init_8bits(void)

{

    LOG_Print("In 8 Bits config\n");

 /* USER CODE BEGIN SPI3_Init 0 */

 /* USER CODE END SPI3_Init 0 */

 /* USER CODE BEGIN SPI3_Init 1 */

 /* USER CODE END SPI3_Init 1 */

 /* SPI3 parameter configuration*/

 hspi3.Instance = SPI3;

 hspi3.Init.Mode = SPI_MODE_SLAVE;

 hspi3.Init.Direction = SPI_DIRECTION_2LINES;

 hspi3.Init.DataSize = SPI_DATASIZE_8BIT;

 hspi3.Init.CLKPolarity = SPI_POLARITY_LOW;

 hspi3.Init.CLKPhase = SPI_PHASE_2EDGE;

 hspi3.Init.NSS = SPI_NSS_SOFT;

 hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB;

 hspi3.Init.TIMode = SPI_TIMODE_DISABLE;

 hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

 hspi3.Init.CRCPolynomial = 7;

 hspi3.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;

 hspi3.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;

 if (HAL_SPI_Init(&hspi3) != HAL_OK)

 {

  Error_Handler();

 }

 /* USER CODE BEGIN SPI3_Init 2 */

 /* USER CODE END SPI3_Init 2 */

}

============================

After this all the byte received correctly. note that I sent one byte and than I tried with different amount of bytes sending and all were received correctly.

Tx Path: (MCU to MPU)

I tried to find same kind of shift register ( data register) combination on Tx side and there is no luck !

Is there any API which we do have to use to flush /clear the Data register ( shift register) before we do start communicating over the SPI line ?

Am I missing anything here... As this looks like workaround (rx path) - how should I fix it in correct way.

Thank you again for your continuous help.

S.Ma
Principal

If the MPU is controlling NSS and sends 8 bit data SPI, then it seems to be the rootcause hasn't been found. Can you take a scope snapshot of NSS / MISO / MOSI / SCK ?

In slave mode, the MCU doesn't control the SCK clock so the callback might not happen when the SCK is idle. The only way to reset the SPI and its FIFO is to run a RCC/CFG Reset of the peripheral when NSS goes high.

In DMA cyclic mode, the SPI TX FIFO will always be filled by DMA. Once NSS goes up, the SCK stops and the TX FIFO is filled while the transmission ended. Next NSS going low, the first 3 old bytes in the FIFO will be sent wrongly. The refman warning for RX FIFO also applies to TX FIFO. If you properly "reset the SPI and flush its FIFO" at least the conditions will improve without the lucky and probably full of side-effect 10 bit SPI mode.

Some of my code extract for SPI Slave in STM32L4R, in case it give clues:

void SPIP_SlaveReStart(SPIP_t* pSPIP) {
  HAL_SPI_DeInit(pSPIP->hspi); // back to original state
  // first, we reset the cell (yes!)
  /*RCC->AHB1RSTR |= 1<<0; // DMA1
  NOPs(2);
  RCC->AHB1RSTR &= ~(1<<0);*/
  NOPs(2);
  RCC->APB1RSTR1 |= (1<<14);// SPI2
  NOPs(2);
  RCC->APB1RSTR1 &= ~(1<<14);
  SPIP_SlaveConfigureSpi(pSPIP->hspi);
  SPIP_SlaveConfigureDma(pSPIP->hspi);
}  

krishna1
Associate

Hi @SPare.9

Could you please share the details on how you are able establish communication between mcu and mpu. We are aslo in a same situation where we need establish an spi communicaiton between imx6 solo and MCU.