cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 SPI Hardware CS Generation

jhaines
Associate
Posted on December 17, 2012 at 16:00

I'm working with the STM32F407VG chip (both on the STM32F4Discovery board and a custom board) and having trouble with the SPI port chip select in master mode. The following is the minimal code to demonstrate my problem:

int main(void)
{
//
// Enable the right clock domains
//
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
//
// Configure the Pin Muxes
//
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_SPI2);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2);
//
// Configure SPI2
//
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI2, &SPI_InitStructure);
SPI_SSOutputCmd(SPI2, ENABLE);
SPI_Cmd(SPI2, ENABLE);
while(1)
{
SPI_I2S_SendData(SPI2, 0xC3C3);
while(SPI_I2S_GetFlagStatus(SPI2, SPI_FLAG_RXNE) == RESET);
(void)SPI_I2S_ReceiveData(SPI2);
}
}

I would expect this code to automatically generate the CS signal to properly frame the SPI transactions, but it doesn't. The data and clock are correct, but there are no transitions on CS. This code generates the desired signals:

int main(void)
{
//
// Enable the right clock domains
//
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
//
// Configure the Pin Muxes
//
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
//
// Configure SPI2
//
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI2, &SPI_InitStructure);
SPI_SSOutputCmd(SPI2, ENABLE);
SPI_Cmd(SPI2, ENABLE);
while(1)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
SPI_I2S_SendData(SPI2, 0xC3C3);
while(SPI_I2S_GetFlagStatus(SPI2, SPI_FLAG_RXNE) == RESET);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
(void)SPI_I2S_ReceiveData(SPI2);
}
}

The second implementation correctly controls the device on the SPI bus, but requires the busy wait. In my application I have very few clock cycles to spare and would much prefer being able to simply write to the SPI2->DR register and allow the hardware to coordinate the rest. Does anyone know if it is possible to achieve this? Thanks, Justin
5 REPLIES 5
Posted on December 17, 2012 at 16:55

I think you have to make the master transfers non-continuous for the NSS to change.

During discontinuous communications, there is a 2 APB clock period delay between the write operation to SPI_DR and the BSY bit setting. As a consequence, it is mandatory to wait first until TXE=1 and then until BSY=0 after writing the last data.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jhaines
Associate
Posted on December 17, 2012 at 17:31

Thanks for the response. The behavior remains the same with this loop:

while(1)
{
SPI_I2S_SendData(SPI2, 0xC3C3);
delay_us(10);
}

My logic analyzer shows that the word takes 0.75us to clock on to the bus, leaving 9.24us of bus-idle time between transactions with this code, which should be enough to count as ''non-continuous''...
Posted on December 19, 2012 at 15:03

Confirmed here (on the STM3240G-EVAL board), also with setting the NSS on PB9 instead of PB12. The NSS pin stays low. When set to TI mode, it starts toggling, so the GPIO- and AF-setting is correct.In ''standard'' mode it stays low even if the enable bit (CR1.6) is cleared.

I'd say this is simply a design flaw, covered up partially by the following sentence in the manual:

 

Note: When a master is communicating with SPI slaves which need to be de-selected between transmissions, the NSS pin must be configured as GPIO or another GPIO must be used and toggled by software.

It would be nice to hear some comment from ST.

JW

bernat2
Associate
Posted on July 23, 2013 at 00:52

Hi,

Thanks for your tips! I couldn't handle the SS pin by hardware until you mentioned the TI mode. It doesn't work as a standard SPI, since in TI mode the SS pin is at 0, and it just stays high for 1 clock. I'm communicating by SPI with a STM32F103 microcontroller, which has no TI mode, and it seems to be compatible with the TI mode setting this configuration:

  ...

  SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;

  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;

  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;

  SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;

  ...

I could not figure out how to handle the CS in STM32F103 by hardware (I guess no one could...), but since I'm working in slave mode this solution works for me...

Thank you!

Cesar cfg
Associate II
Posted on August 05, 2013 at 10:56

SPI_SSOutputCmd(SPIx, ENABLE);