cancel
Showing results for 
Search instead for 
Did you mean: 

SPI multislave implementation - what am i doing wrong ? (solved)

Tom Schrauf
Associate II

Hello community !

The last part needed for my implementation is an MCU to MCU multislave communication.

From a hardware point of view LTC6820 (isoSPI) devices will be used attached to an STM32F4 on the host side and up to 10 STM32F103 as slaves.

The LTC6820 will drive CS low software driven to allow the slaves to wakeup from sleep and have their SPI ports setup (this is done in EXTI ISR).

The master STM32F4 uses DMA buffering for both Xmit and Receive.

In order to analyze the incoming SPI frames i setup the STM32F103 to use interrupts for receiving and start communication in "SPI_Direction_2Lines_RxOnly" Mode (i.e. Bit 10 rxonly is 1). The slaves shall send their response only when addressed (i.e: address matches) and i wanted to use TXDMA (DMA1Channel5). For this reason all slaves have their DMA setup accordingly and whichever of them is addressed starts transmission after the command block was received with the matching address.

So after the command block of 8 bytes the RX ISR switches off Bit 10 and I thought this would trigger transmission but it does not.

I also tried to disable SPI - reset bit - enable SPI again, do a full reinit, reinit DMA ... nothing helped.

In the RM0008 on page 743 it is mentioned that this bit is intended to be used in multislave applications but what am i doing wrong here ? SPI2 is functional and bytes are received as intended only the slave transmission hesitates to start. Which "kick" is necessary to start transmission ?

Thanks, Tom

edit: so the problem was the DMA configuration in my case (Peripherial address). In the RXInterrupt after knowing that the address matches:

            SPI_Cmd( SPI2, DISABLE );

            SPI2->CR1 &= ~SPI_Direction_2Lines_RxOnly;

            DMA_Cmd( SPI2_XMIT_DMA, ENABLE );

            SPI2->CR2 |= 0x0002; // SPI_I2S_DMACmd( SPI2, SPI_I2S_DMAReq_Tx, ENABLE );

            SPI_Cmd( SPI2, ENABLE );

the RDONLY bit is reset and the slave starts responding at the next character.

0693W00000Hq7hdQAB.jpg 

pinConfiguration spi2pinsMasterRole[] = {

   { GPIOB, { GPIO_Pin_12, GPIO_Speed_50MHz, GPIO_Mode_IPU } }          // CR from LTC6820

   ,{ GPIOB, { GPIO_Pin_13, GPIO_Speed_50MHz, GPIO_Mode_AF_PP } }       // SPI2 CLK

   ,{ GPIOB, { GPIO_Pin_14, GPIO_Speed_50MHz, GPIO_Mode_AF_OD } }       // SPI2 MISO

   ,{ GPIOB, { GPIO_Pin_15, GPIO_Speed_50MHz, GPIO_Mode_AF_PP } }       // SPI2 MOSI

};

pinConfiguration spi2pinsSlaveRole[] = {

   { GPIOB, { GPIO_Pin_12, GPIO_Speed_50MHz, GPIO_Mode_IPU } }          // CS from LTC6820

   ,{ GPIOB, { GPIO_Pin_13, GPIO_Speed_50MHz, GPIO_Mode_AF_OD } }       // SPI2 CLK

   ,{ GPIOB, { GPIO_Pin_14, GPIO_Speed_50MHz, GPIO_Mode_AF_OD } }       // SPI2 MISO

   ,{ GPIOB, { GPIO_Pin_15, GPIO_Speed_50MHz, GPIO_Mode_AF_OD } }       // SPI2 MOSI

};

   SPI_StructInit( &SPI_InitStructure );

   SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

   if (!isMaster) {

       initPins( spi2pinsSlaveRole, ARRAY_SIZE(spi2pinsSlaveRole) );

      SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;

       SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_RxOnly;

       SPI_I2S_ITConfig( SPI2, SPI_I2S_IT_RXNE, ENABLE );

       SPI_I2S_DMACmd( SPI2, SPI_I2S_DMAReq_Tx, ENABLE );

       NVIC_SetPriority(SPI2_IRQn, 1); // very high priority

       NVIC_EnableIRQ( SPI2_IRQn );

   }

   else {

        initPins( spi2pinsMasterRole, ARRAY_SIZE(spi2pinsMasterRole) );

        SPI_InitStructure.SPI_BaudRatePrescaler = findSPIPrescaler( 36000, baudKHz ); //

      SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

       SPI_I2S_DMACmd( SPI2, SPI_I2S_DMAReq_Tx, DISABLE );

       SPI_I2S_ITConfig( SPI2, SPI_I2S_IT_RXNE, DISABLE );

       NVIC_DisableIRQ( SPI2_IRQn );

   }

   SPI_Init( SPI2, &SPI_InitStructure );

   RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );

   DMA_InitStructure.DMA_PeripheralBaseAddr = SPI2->DR;

   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;

   DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

   DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

   DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;

   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

   DMA_Init( SPI2_XMIT_DMA, &DMA_InitStructure );

.......

1 ACCEPTED SOLUTION

Accepted Solutions

> So after the command block of 8 bytes the RX ISR switches off Bit 10

Are you sure it does? How do you debug this exactly? Does the SPI/DMA work at all? What happens if you don't fiddle with SPI_CR1.RXONLY?

Generally, start with reading out and checking/posting SPI, DMA and relevant GPIO registers content.

JW

View solution in original post

11 REPLIES 11

> So after the command block of 8 bytes the RX ISR switches off Bit 10

Are you sure it does? How do you debug this exactly? Does the SPI/DMA work at all? What happens if you don't fiddle with SPI_CR1.RXONLY?

Generally, start with reading out and checking/posting SPI, DMA and relevant GPIO registers content.

JW

Tom Schrauf
Associate II

I made a little progress when writing 0x77 to the DR register so the problem seems to exist with linking/triggering the DMA ?

0693W00000Hq7ucQAB.jpgI thought this would be enough besides initializing the appropriate DMA channel.

SPI_I2S_DMACmd( SPI2, SPI_I2S_DMAReq_Tx, ENABLE );

But whatever is written to the DR register manually stays there and no DMA fetch takes place hence the DMA TC int is not fired either.

Tom Schrauf
Associate II

The DMA always reads 00 and DMA TC interrupt never occurs. According to the reference manual SPI2 TX DMA should be DMA1 channel 5.

So proceeding a little and at least the SPI slave is now responding 😉

Thank you !

Tom Schrauf
Associate II

What also happens sometimes is that the shifting starts at the wrong bit (9D 55 56 80 10 1D 06 AA AA AA AA AA AA AA AA AA AA AA AA AA

)

My SPI master sends out a datastream (more or less continuously) and when the slave board is reset it sometimes received a shifted bit pattern.

Do i have to switch off/on SPI2 clock to fully reset the receiver shift registers or is there an easier way to do so ?

I was thinking disabling and reenabling the SPI within the EXTI ISR triggered when Select line is activated would reset the shifting state to a well known state.

S.Ma
Principal

Are you daisy chaining the slaves? Only one NSS signal? Most spi implementations are not based on a single shift register with in and out port. Rx and Tx shift registers are different and not linked. Personnaly I would use the full duplex mode, even if mosi or miso is not connected to a pin. Now the slaves will need some usec time to process EXTI on NSS. You need to factor this with worst case which is worst compiler with no optimisations.

Slaves works ok if using rx and tx buffer with cyclic dma. If your stm32 has spi with fifo, you will need to flush the fifo by resetting the spi through the sys or rcc registers.

After dodging few bullets, you'll be fine.

Tom Schrauf
Associate II

omg - shame on me ! see that line up there: DMA_InitStructure.DMA_PeripheralBaseAddr = SPI2->DR;

I took this into my dreams and when I got up it was (uint32_t) &SPI2->DR

I now also get the TC interrupt so the SPI peripherial must have detected that the peripherial register of the DMA was not pointing at the Data-register i guess.

@S.Ma​ the slaves are all on the same line. As long as all non addressed keep silent this will work. I will introduce a long enough lead-time with the Select line to allow the slaves to wakeup from sleep and setup the SPI. transmission itself will be pretty slow close to the minimum (~160kBaud) but continuous while the system is active.

I just have to figure out now how to properly reset the SPI to always start shifting at the right time. There is no SPI FIFO in the STM32F103.

The manual describes the BUSY bit which should indicate that a reception is ongoing (remainings of the last incomplete shift). It is supposed to be cleared when the SPI is disabled. What will waiting for the BUSY bit to reset mean if there are no SPI clocks ?

tbc .....

 As a workaround I am waiting for the CS to go high when the MCU resets so not to start the SPI peripherial while a SPI transaction is taking place.

I am just wondering how it would be possible to really RESET the SPI so it resets the shift counter. Also what might happen if a single clock pulse was missing and affecting the next transaction.

> I am just wondering how it would be possible to really RESET the SPI so it resets the shift counter.

In slave, NSS being brought up resets the internal shift counter.

JW

Thank you Jan !

But I am using SPI_NSS_Soft (although B12 pin (SPI2_NSS) being used on my PCB would allow NSS).

I wanted to use the same SPI also locally in master mode and switch to slave mode only when necessary that is why thought having software NSS would ease things.

Tom

Besides this possible problem communication works like a charm now allowing me to collect hundreds of sensor data from 10 boards fully dma/interrupt driven.

😍 STM

Hi Jan,

Is there any other way to reset the slave's shifter ?

If not i consider this a real design bug and software NSS is not really useable.

Missing one clock pulse and your communication dies or do i miss a thing ?

I will try later to activate and deactivate hardware NSS with the select pin being high. Maybe that helps. Disabling and reenabling SPI should reset the shifter imo but it doesn't.

Tom