cancel
Showing results for 
Search instead for 
Did you mean: 

multi master spi

con3
Senior

Hey everyone,

I've been struggling with the multi-master spi. I can send and receive seperately but when I try to integrate it into one program it bogs out. My understanding is that I can set the SPI line as a slave and then whenever I want it to act as master, I need to manually toggle a secondary GPIO PIN to act as the cs line and call HAL_SPI_Transmit_DMA.

This is the configuration I have going for the SPI line as slave:

	hspi1.Instance = SPI1;
	hspi1.Init.Mode = SPI_MODE_SLAVE;
	hspi1.Init.Direction = SPI_DIRECTION_2LINES;
	hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
	hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
	hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
	hspi1.Init.NSS = SPI_NSS_HARD_INPUT;
	hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
	hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
	hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
	hspi1.Init.CRCPolynomial = 7;
	hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
	hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;

This works well for the slave line, but doing the cs line toggle and sending a transmit doesn't work. Is my approach correct?

Thanks in advance for any help!

7 REPLIES 7
S.Ma
Principal

Multi-master SPI is probably quite challenging to implement. In the I2C domain, it was called Access Bus long ago and was eclipsed by USB. Using one MCU as master to control multiple MCUs as slave might be easier intermediate step (done 1 master + 12 slaves @ 12 Mbps).

My guess is that you probably have to read carefully the reference manual for the SPI behaviour.

Do you plan one NSS line to talk to all devices on the bus?

Hi @S.Ma​ ,

I've got it implemented as shown in this slideshow, specifically slide 10 which shows multi-master:

https://www.st.com/content/ccc/resource/training/technical/product_training/group0/3e/ee/cd/b7/84/4b/45/ee/STM32F7_Peripheral_SPI/files/STM32F7_Peripheral_SPI.pdf/_jcr_content/translations/en.STM32F7_Peripheral_SPI.pdf

The device is configured in slave mode but when the device switched to being the master a secondary gpio is used as the NSS, which is manually toggled with software as shown above. I've read the ref manual, but might have missed something, so I'll reread.

Thanks for the input!

S.Ma
Principal

Are you using like the slides 10 with 2 MCU as multi-master ?

Exactly like that

S.Ma
Principal

ok, my guess (not expert here) would be to have all device by default in slave mode with NSS HW wired between both MCU. Once you want to turn as master, hopefully NSS will go low for the one who succeed. I would activate EXTI on NSS to monitor what's going on (you'll have to hack as HAL is not formatted to help you), assess who won the master seat and then decide to transfer data.

I would guess that slaves enable their MISO and MOSI from HiZ when NSS goes low.

Remains to be double checked the behaviour of SCK.

Have you checked if there is an SPI multimaster example on Nucleo board (with same STM32family)?

Good luck!

S.Ma
Principal

And if using DMA, use Cyclic mode for the slave device, no interrupt. The end of transfer will be the NSS goes high (EXTI interrupt). Remember to allow some time for the slave interrupt to complete when NSS toggles.

Hi @S.Ma​ ,

I planned on using two NSS lines like in the example on the slide. Whenever a device decides to be master on the bus, it would manually toggle the GPIO to act as the NSS.

Here's how the slave mode is setup for my SPI line:

hspi1.Instance = SPI1;
	hspi1.Init.Mode = SPI_MODE_SLAVE;
	hspi1.Init.Direction = SPI_DIRECTION_2LINES;
	hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
	hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
	hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
	hspi1.Init.NSS = SPI_NSS_HARD_INPUT;
	hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
	hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
	hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
	hspi1.Init.CRCPolynomial = 7;
	hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
	hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
	if (HAL_SPI_Init(&hspi1) != HAL_OK)
	{
		Error_Handler();
	}

Then to have the device actively waiting for data I run the following:

   HAL_SPI_Receive_DMA(&hspi1, (uint8_t *) FromESP, sizeof(FromESP));

I have noticed that If I run the following:

transmit_stat = HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *) Fromstm, sizeof(Fromstm));

The device does absolutely nothing, I'm assuming because it's still in slave mode. So each time I want to have the device act as master, I seem to need to disable the SPI and re-initiate it as master like this:

static void MX_SPI1_Init(void)
{
 
	/* USER CODE BEGIN SPI1_Init 0 */
 
	/* USER CODE END SPI1_Init 0 */
 
	/* USER CODE BEGIN SPI1_Init 1 */
 
	/* USER CODE END SPI1_Init 1 */
	/* SPI1 parameter configuration*/
	hspi1.Instance = SPI1;
	hspi1.Init.Mode = SPI_MODE_MASTER;
	hspi1.Init.Direction = SPI_DIRECTION_2LINES;
	hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
	hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
	hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
	hspi1.Init.NSS = SPI_NSS_SOFT;
	hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
	hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
	hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
	hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
	hspi1.Init.CRCPolynomial = 7;
	hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
	hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
	if (HAL_SPI_Init(&hspi1) != HAL_OK)
	{
		Error_Handler();
	}
	/* USER CODE BEGIN SPI1_Init 2 */
 
	/* USER CODE END SPI1_Init 2 */
 
}

When I try to do the change to master by directly manipulating the registers it doesn't seem possible as I need to pass hspi1 to the transmit function. Would this be the correct way of doing this?

At this point, after I reconfigure the bus, The stm acts as master and transmits data. I'm still receiving junk, but it feels like a step in the right direction as I wasn't receiving anything prior to this.

Thank you for the help thus far. I really appreciate it.